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.
