OHLC.dev editorialIDX

BBCA Fundachart Dashboard with Global Impact and Latest OHLCV

The BBCA Fundachart Dashboard is a Python-based Google Colab project designed to combine fundamental chart data, global market impact information, and the latest OHLCV data into...

August 19, 20269 min readRafatar
BBCA Fundachart Dashboard with Global Impact and Latest OHLCV

The BBCA Fundachart Dashboard is a Python-based Google Colab project designed to combine fundamental chart data, global market impact information, and the latest OHLCV data into one analysis workflow.

The project uses three IDX API endpoints:

  • Global Impact Analysis

  • Latest OHLCV for BBCA

  • Fundachart for BBCA and TLKM

The original notebook is configured to analyze BBCA OHLCV data between February 10 and February 18, 2026, retrieve up to 100 latest OHLCV records, and request one-year Fundachart data for BBCA and TLKM using item 2661.

The entire workflow is intentionally separated into five cells so the project remains easy to run, inspect, and modify in Google Colab.

Cell 1 — Configuration

The first cell prepares the Python libraries, RapidAPI configuration, stock symbol, OHLCV period, latest-data limit, and API base URL.

# ============================================================
# CELL 1 — CONFIGURATION
# ============================================================

import requests
import pandas as pd
import numpy as np
from datetime import datetime
from IPython.display import display
import json
import warnings

warnings.filterwarnings("ignore")

# =========================
# RAPIDAPI CONFIG
# =========================
RAPIDAPI_KEY = "YOUR_RAPIDAPI_KEY"

HOST = "indonesia-stock-exchange-idx.p.rapidapi.com"

HEADERS = {
    "Content-Type": "application/json",
    "x-rapidapi-host": HOST,
    "x-rapidapi-key": RAPIDAPI_KEY
}

# =========================
# PARAMETER SAHAM
# =========================
SYMBOL = "BBCA"

# Periode OHLCV
FROM_DATE = "2026-02-10"
TO_DATE   = "2026-02-18"

# Jumlah data latest OHLCV
LATEST_LIMIT = 100

BASE_URL = f"https://{HOST}/api"

print("=" * 100)
print("IDX GLOBAL IMPACT & OHLCV ANALYSIS")
print("=" * 100)
print(f"Symbol        : {SYMBOL}")
print(f"OHLCV Period  : {FROM_DATE} s/d {TO_DATE}")
print(f"Latest Limit  : {LATEST_LIMIT}")
print("Configuration : READY")

Cell 2 — Request Three API Endpoints

The second cell handles the API requests. It uses a reusable call_api() function with timeout and exception handling before requesting the three datasets.

# ============================================================
# CELL 2 — API REQUEST
# ============================================================

def call_api(name, url, params=None):
    try:
        response = requests.get(
            url,
            headers=HEADERS,
            params=params,
            timeout=30
        )

        result = {
            "name": name,
            "status_code": response.status_code,
            "success": response.ok,
            "data": None,
            "error": None,
            "url": response.url
        }

        try:
            result["data"] = response.json()
        except:
            result["data"] = response.text

        if not response.ok:
            result["error"] = f"HTTP {response.status_code}"

        return result

    except Exception as e:
        return {
            "name": name,
            "status_code": None,
            "success": False,
            "data": None,
            "error": str(e),
            "url": url
        }


# ============================================================
# 1. GLOBAL IMPACT ANALYSIS
# ============================================================

global_impact = call_api(
    "Global Impact Analysis",
    f"{BASE_URL}/global/impact-analysis"
)


# ============================================================
# 2. LATEST OHLCV — BBCA
# ============================================================

latest_ohlcv = call_api(
    "Latest OHLCV",
    f"{BASE_URL}/chart/{SYMBOL}/daily/latest",
    params={
        "limit": LATEST_LIMIT
    }
)


# ============================================================
# 3. FUNDACHART — BBCA & TLKM
# ============================================================

fundachart = call_api(
    "Fundachart",
    f"{BASE_URL}/emiten/fundachart",
    params={
        "companies": "BBCA,TLKM",
        "timeframe": "1y",
        "item": 2661
    }
)


# ============================================================
# STATUS
# ============================================================

results = [
    global_impact,
    latest_ohlcv,
    fundachart
]

print("=" * 100)
print("API REQUEST STATUS")
print("=" * 100)

for r in results:
    status = "BERHASIL" if r["success"] else "GAGAL"

    print(
        f"{r['name']:<25} : "
        f"{status:<10} | "
        f"HTTP {r['status_code']}"
    )

print("=" * 100)
print("Request selesai.")

Cell 3 — Data Extraction and Normalization

Raw API responses are not always structured as a simple list. Cell 3 therefore searches nested responses for common structures such as data, results, rows, items, records, and content.

For OHLCV specifically, the code standardizes different possible column names into date, open, high, low, close, and volume.

# ============================================================
# CELL 3 — DATA EXTRACTION & NORMALIZATION
# ============================================================

def find_records(obj):

    if isinstance(obj, list):

        if all(isinstance(x, dict) for x in obj):
            return obj

        for item in obj:

            found = find_records(item)

            if found:
                return found

    elif isinstance(obj, dict):

        priority_keys = [
            "data",
            "results",
            "result",
            "rows",
            "items",
            "records",
            "content",
            "response"
        ]

        for key in priority_keys:

            if key in obj:

                found = find_records(
                    obj[key]
                )

                if found:
                    return found

        for value in obj.values():

            found = find_records(value)

            if found:
                return found

    return []


def make_dataframe(api_result):

    records = find_records(
        api_result.get("data")
    )

    if not records:
        return pd.DataFrame()

    return pd.json_normalize(records)


def standardize_ohlcv(df):

    if df.empty:
        return df

    rename_map = {}

    for col in df.columns:

        c = str(col).lower().strip()

        if c in [
            "date",
            "datetime",
            "timestamp",
            "time",
            "tradingdate"
        ]:
            rename_map[col] = "date"

        elif c in [
            "open",
            "opening",
            "openprice"
        ]:
            rename_map[col] = "open"

        elif c in [
            "high",
            "highest",
            "highprice"
        ]:
            rename_map[col] = "high"

        elif c in [
            "low",
            "lowest",
            "lowprice"
        ]:
            rename_map[col] = "low"

        elif c in [
            "close",
            "closing",
            "closeprice"
        ]:
            rename_map[col] = "close"

        elif c in [
            "volume",
            "vol"
        ]:
            rename_map[col] = "volume"

    df = df.rename(
        columns=rename_map
    )

    for col in [
        "open",
        "high",
        "low",
        "close",
        "volume"
    ]:

        if col in df.columns:

            df[col] = pd.to_numeric(
                df[col],
                errors="coerce"
            )

    if "date" in df.columns:

        df["date"] = pd.to_datetime(
            df["date"],
            errors="coerce"
        )

    return df


# ============================================================
# CREATE DATAFRAME
# ============================================================

df_global = make_dataframe(
    global_impact
)

df_latest = standardize_ohlcv(
    make_dataframe(
        latest_ohlcv
    )
)

df_fundachart = make_dataframe(
    fundachart
)


# ============================================================
# SORT OHLCV
# ============================================================

if not df_latest.empty and "date" in df_latest.columns:

    df_latest = (
        df_latest
        .sort_values("date")
        .reset_index(drop=True)
    )


# ============================================================
# INFORMATION
# ============================================================

print("=" * 100)
print("DATA PROCESSING")
print("=" * 100)

print(
    f"Global Impact : {len(df_global):,} rows"
)

print(
    f"Latest OHLCV  : {len(df_latest):,} rows"
)

print(
    f"Fundachart    : {len(df_fundachart):,} rows"
)

print("\nKolom Global Impact:")
print(list(df_global.columns))

print("\nKolom Latest OHLCV:")
print(list(df_latest.columns))

print("\nKolom Fundachart:")
print(list(df_fundachart.columns))

print("\nProcessing selesai.")


The OHLCV data is sorted chronologically after normalization.

Cell 4 — Market and Fundachart Analysis

Cell 4 focuses on the analytical layer.

For BBCA, it calculates:

  • first closing price

  • latest closing price

  • period change

  • period change percentage

  • latest daily change

  • period high

  • period low

The Fundachart section also records the available columns returned by the endpoint.

# ============================================================
# CELL 4 — MARKET & FUNDACHART ANALYSIS
# ============================================================

analysis = {}


# ============================================================
# OHLCV ANALYSIS
# ============================================================

if not df_latest.empty and "close" in df_latest.columns:

    price_data = df_latest.dropna(
        subset=["close"]
    ).copy()

    if len(price_data) >= 1:

        last_close = price_data["close"].iloc[-1]

        first_close = price_data["close"].iloc[0]

        change = last_close - first_close

        change_pct = (
            change / first_close * 100
            if first_close != 0
            else np.nan
        )

        if len(price_data) >= 2:

            previous_close = (
                price_data["close"].iloc[-2]
            )

            daily_change_pct = (
                (last_close - previous_close)
                / previous_close
                * 100
                if previous_close != 0
                else np.nan
            )

        else:

            daily_change_pct = np.nan

        period_high = (
            price_data["high"].max()
            if "high" in price_data.columns
            else np.nan
        )

        period_low = (
            price_data["low"].min()
            if "low" in price_data.columns
            else np.nan
        )

        analysis = {
            "first_close": first_close,
            "last_close": last_close,
            "change": change,
            "change_pct": change_pct,
            "daily_change_pct": daily_change_pct,
            "period_high": period_high,
            "period_low": period_low
        }


# ============================================================
# FUNDACHART SUMMARY
# ============================================================

fundachart_columns = (
    list(df_fundachart.columns)
    if not df_fundachart.empty
    else []
)


# ============================================================
# OUTPUT
# ============================================================

print("=" * 100)
print("ANALYSIS RESULT")
print("=" * 100)

if analysis:

    print(f"Symbol              : {SYMBOL}")

    print(
        f"Latest Close        : "
        f"{analysis['last_close']:,.2f}"
    )

    print(
        f"Period Change       : "
        f"{analysis['change_pct']:+.2f}%"
    )

    print(
        f"Latest Daily Change : "
        f"{analysis['daily_change_pct']:+.2f}%"
    )

    if not pd.isna(
        analysis["period_high"]
    ):

        print(
            f"Period High         : "
            f"{analysis['period_high']:,.2f}"
        )

    if not pd.isna(
        analysis["period_low"]
    ):

        print(
            f"Period Low          : "
            f"{analysis['period_low']:,.2f}"
        )

else:

    print(
        "Data Latest OHLCV belum tersedia."
    )


print("\nFundachart:")

print(
    f"Jumlah Data : "
    f"{len(df_fundachart):,}"
)

print(
    "Kolom       :",
    fundachart_columns
)

This analysis layer makes the OHLCV dataset more meaningful by turning raw price records into a concise market summary.


Cell 5 — IDX Global Impact OHLCV and Fundachart Dashboard

Cell 5 becomes the presentation layer of the entire project.

It brings together the BBCA price summary, Fundachart data for BBCA and TLKM, global market impact information, latest OHLCV records, and API status.

# ============================================================
# CELL 5 — IDX GLOBAL IMPACT, OHLCV & FUNDACHART DASHBOARD
# ============================================================

print("\n" + "=" * 100)
print("IDX GLOBAL IMPACT, OHLCV & FUNDACHART DASHBOARD")
print("=" * 100)

print("📌 Saham OHLCV : BBCA")
print("📊 Fundachart  : BBCA, TLKM")
print("⏱️ Timeframe   : 1 Year")
print("🔢 Item        : 2661")

print("-" * 100)


# ============================================================
# PRICE SUMMARY
# ============================================================

print("📈 BBCA — LATEST PRICE SUMMARY")
print("-" * 100)

if analysis:

    pct = analysis["change_pct"]

    if pct > 0:
        trend = "🟢 NAIK"

    elif pct < 0:
        trend = "🔴 TURUN"

    else:
        trend = "⚪ STABIL"

    print(
        f"Harga terakhir       : "
        f"Rp {analysis['last_close']:,.0f}"
    )

    print(
        f"Perubahan periode    : "
        f"{pct:+.2f}%"
    )

    print(
        f"Perubahan terakhir   : "
        f"{analysis['daily_change_pct']:+.2f}%"
    )

    print(
        f"Trend                : {trend}"
    )

    if not pd.isna(
        analysis["period_low"]
    ) and not pd.isna(
        analysis["period_high"]
    ):

        print(
            f"Range harga          : "
            f"Rp {analysis['period_low']:,.0f}"
            f" - "
            f"Rp {analysis['period_high']:,.0f}"
        )

else:

    print(
        "Data harga tidak tersedia."
    )


# ============================================================
# FUNDACHART
# ============================================================

print("\n" + "-" * 100)
print("📊 FUNDACHART — BBCA & TLKM")
print("-" * 100)

if not df_fundachart.empty:

    print(
        f"Jumlah Data : "
        f"{len(df_fundachart):,}"
    )

    display(
        df_fundachart
    )

else:

    print(
        "Data Fundachart tidak tersedia "
        "atau endpoint mengembalikan response kosong."
    )


# ============================================================
# GLOBAL IMPACT
# ============================================================

print("\n" + "-" * 100)
print("🌎 GLOBAL MARKET IMPACT")
print("-" * 100)

if not df_global.empty:

    print(
        f"Jumlah Data : "
        f"{len(df_global):,}"
    )

    display(
        df_global.head(10)
    )

else:

    print(
        "Data Global Impact tidak tersedia "
        "atau endpoint mengembalikan response kosong."
    )


# ============================================================
# LATEST OHLCV
# ============================================================

print("\n" + "-" * 100)
print("📋 LATEST OHLCV — BBCA")
print("-" * 100)

if not df_latest.empty:

    preferred = [
        "date",
        "open",
        "high",
        "low",
        "close",
        "volume"
    ]

    available = [
        c for c in preferred
        if c in df_latest.columns
    ]

    display(
        df_latest[available]
        .tail(10)
    )

else:

    print(
        "Data Latest OHLCV tidak tersedia."
    )


# ============================================================
# API STATUS
# ============================================================

print("\n" + "=" * 100)
print("API STATUS")
print("=" * 100)

print(
    "🌎 Global Impact Analysis :",
    "BERHASIL"
    if global_impact["success"]
    else f"GAGAL ({global_impact['status_code']})"
)

print(
    "📈 Latest OHLCV           :",
    "BERHASIL"
    if latest_ohlcv["success"]
    else f"GAGAL ({latest_ohlcv['status_code']})"
)

print(
    "📊 Fundachart             :",
    "BERHASIL"
    if fundachart["success"]
    else f"GAGAL ({fundachart['status_code']})"
)

print("\n" + "=" * 100)

print(
    f"🕒 Selesai diproses : "
    f"{datetime.now().strftime('%d-%m-%Y %H:%M:%S')}"
)

print("=" * 100)

The dashboard starts by identifying BBCA as the OHLCV stock and BBCA/TLKM as the Fundachart companies, with a one-year timeframe and item 2661.

It then presents the BBCA latest price summary, including the latest close, period change, latest daily change, trend, and price range.

The next sections display Fundachart, global market impact, and the latest ten OHLCV observations.

Finally, the dashboard reports the API status for all three endpoints and records the processing timestamp.


What This Dashboard Provides

The completed BBCA Fundachart Dashboard brings several market perspectives together.

BBCA Price Analysis

The OHLCV component provides a concise view of BBCA's latest price behavior, including period performance, daily movement, high, low, and price range.

Fundachart Comparison

The Fundachart component retrieves information for BBCA and TLKM over a one-year timeframe, allowing the returned fundamental chart data to be inspected directly.

Global Market Impact

The Global Impact Analysis component adds broader market context to the stock-level analysis.

Latest OHLCV Data

The latest OHLCV table provides the most recent records with standardized fields such as date, open, high, low, close, and volume.

API Monitoring

The final dashboard also makes it easy to identify whether each API endpoint returned successfully.


Conclusion

The BBCA Fundachart Dashboard demonstrates how Python can transform several IDX API endpoints into a single structured market-analysis workflow.

Instead of looking at raw API responses independently, the project separates the process into configuration, data retrieval, normalization, analysis, and dashboard presentation.

The result is a compact five-cell Google Colab project that combines BBCA price behavior, BBCA and TLKM Fundachart data, global market impact, and latest OHLCV information in one workflow.

This structure can also be extended later with additional technical indicators, valuation metrics, charts, or comparisons between multiple Indonesian stocks while keeping the same five-cell architecture.