A BBCA Tradebook Dashboard provides a structured way to combine market activity, company information, and broker trading data in a single Python project.
This project connects three IDX API endpoints: getTradebookChart, getProfileBackground, and getBrokerTradeChart.
The Tradebook Chart retrieves BBCA market data using a one-minute interval. The Company Profile endpoint provides background information about BBCA, while the Broker Trade Chart analyzes trading activity for broker codes XL and AK using the regular market and the last one-day period.
The entire workflow is organized into five Google Colab cells covering configuration, API requests, response normalization, detailed data inspection, and the final dashboard.
Cell 1 — Import Library and API Configuration
# ============================================================
# CELL 1 — IMPORT LIBRARY & KONFIGURASI API
# ============================================================
import requests
import pandas as pd
import json
import time
from datetime import datetime
from IPython.display import display
# ------------------------------------------------------------
# RAPIDAPI CONFIG
# ------------------------------------------------------------
RAPIDAPI_KEY = "YOUR_RAPIDAPI_KEY"
RAPIDAPI_HOST = "indonesia-stock-exchange-idx.p.rapidapi.com"
BASE_URL = "https://indonesia-stock-exchange-idx.p.rapidapi.com"
HEADERS = {
"Content-Type": "application/json",
"x-rapidapi-host": RAPIDAPI_HOST,
"x-rapidapi-key": RAPIDAPI_KEY
}
# ------------------------------------------------------------
# PARAMETER PROJECT
# ------------------------------------------------------------
SYMBOL = "BBCA"
TRADEBOOK_INTERVAL = "1m"
BROKER_PERIOD = "RT_PERIOD_LAST_1_DAY"
MARKET_BOARD = "BOARD_TYPE_REGULAR"
INVESTOR_TYPE = "INVESTOR_TYPE_ALL"
BROKER_CODES = "XL,AK"
print("=" * 100)
print("IDX BBCA TRADEBOOK, COMPANY PROFILE & BROKER TRADE PROJECT")
print("=" * 100)
print(f"Symbol : {SYMBOL}")
print(f"Tradebook Interval : {TRADEBOOK_INTERVAL}")
print(f"Broker Codes : {BROKER_CODES}")
print(f"Broker Period : {BROKER_PERIOD}")
print(f"Market Board : {MARKET_BOARD}")
print("=" * 100)
Cell 1 prepares the libraries and API configuration. BBCA is used as the stock symbol with a one-minute Tradebook interval and XL and AK as the selected broker codes.
Cell 2 — Request Data from Three IDX API Endpoints
# ============================================================
# CELL 2 — REQUEST DATA DARI 3 ENDPOINT API
# ============================================================
def request_api(name, endpoint, params=None):
"""
Melakukan GET request dengan error handling.
"""
url = BASE_URL + endpoint
print("\n" + "=" * 100)
print(name)
print("-" * 100)
print("Endpoint :", endpoint)
try:
response = requests.get(
url,
headers=HEADERS,
params=params,
timeout=30
)
print("Request URL :", response.url)
print("Status :", response.status_code)
try:
result = response.json()
except Exception:
result = {
"success": False,
"message": "Response bukan JSON",
"raw": response.text[:1000]
}
success = (
response.status_code == 200
and isinstance(result, (dict, list))
)
if isinstance(result, dict):
api_success = result.get("success")
if api_success is False:
success = False
message = (
result.get("message")
or result.get("error")
or ("Request berhasil" if success else "Request gagal")
)
else:
message = "Request berhasil" if success else "Request gagal"
print("Success :", success)
print("Pesan :", message)
return {
"name": name,
"success": success,
"status_code": response.status_code,
"url": response.url,
"response": result
}
except requests.exceptions.Timeout:
print("Success : False")
print("Pesan : Request timeout")
return {
"name": name,
"success": False,
"status_code": None,
"url": url,
"response": {
"success": False,
"message": "Request timeout"
}
}
except requests.exceptions.RequestException as e:
print("Success : False")
print("Pesan :", str(e))
return {
"name": name,
"success": False,
"status_code": None,
"url": url,
"response": {
"success": False,
"message": str(e)
}
}
# ============================================================
# 1. getTradebookChart
# ============================================================
tradebook_result = request_api(
"getTradebookChart",
"/api/emiten/tradebook-chart",
params={
"symbol": SYMBOL,
"timeInterval": TRADEBOOK_INTERVAL
}
)
time.sleep(2)
# ============================================================
# 2. getProfileBackground
# ============================================================
profile_result = request_api(
"getProfileBackground",
f"/api/emiten/{SYMBOL}/profile"
)
time.sleep(2)
# ============================================================
# 3. getBrokerTradeChart
# ============================================================
broker_trade_result = request_api(
"getBrokerTradeChart",
f"/api/emiten/{SYMBOL}/broker-trade-chart",
params={
"period": BROKER_PERIOD,
"market_board": MARKET_BOARD,
"investor_type": INVESTOR_TYPE,
"brokerCodes": BROKER_CODES
}
)
print("\n" + "=" * 100)
print("SEMUA REQUEST SELESAI")
print("=" * 100)
Cell 2 creates a reusable API request function with error handling and retrieves all three datasets. A two-second delay separates the requests to reduce consecutive API calls.
Cell 3 — Normalize the API Responses
# ============================================================
# CELL 3 — NORMALISASI RESPONSE API
# ============================================================
def extract_data(response):
"""
Mengambil bagian data dari response API secara fleksibel.
"""
if response is None:
return []
if isinstance(response, list):
return response
if not isinstance(response, dict):
return []
data = response.get("data")
if data is None:
return []
return data
def smart_records(data):
"""
Mengubah nested response menjadi record yang lebih mudah
ditampilkan sebagai DataFrame.
"""
if data is None:
return []
# --------------------------------------------------------
# LIST
# --------------------------------------------------------
if isinstance(data, list):
if not data:
return []
if all(isinstance(x, dict) for x in data):
return data
return [{"value": x} for x in data]
# --------------------------------------------------------
# DICT
# --------------------------------------------------------
if isinstance(data, dict):
# Cari list of dict yang kemungkinan merupakan
# kumpulan data utama.
list_candidates = []
for key, value in data.items():
if (
isinstance(value, list)
and value
and all(isinstance(x, dict) for x in value)
):
list_candidates.append((key, value))
if list_candidates:
# pilih list dengan jumlah record terbanyak
key, records = max(
list_candidates,
key=lambda x: len(x[1])
)
output = []
for row in records:
new_row = {"source": key}
new_row.update(row)
output.append(new_row)
return output
# Jika tidak ada list, simpan dict sebagai satu record
return [data]
# --------------------------------------------------------
# VALUE BIASA
# --------------------------------------------------------
return [{"value": data}]
tradebook_data = extract_data(
tradebook_result["response"]
)
profile_data = extract_data(
profile_result["response"]
)
broker_trade_data = extract_data(
broker_trade_result["response"]
)
tradebook_records = smart_records(tradebook_data)
profile_records = smart_records(profile_data)
broker_trade_records = smart_records(broker_trade_data)
tradebook_df = pd.DataFrame(tradebook_records)
profile_df = pd.DataFrame(profile_records)
broker_trade_df = pd.DataFrame(broker_trade_records)
print("=" * 100)
print("HASIL NORMALISASI")
print("=" * 100)
print(f"Tradebook Chart Records : {len(tradebook_df)}")
print(f"Profile Records : {len(profile_df)}")
print(f"Broker Trade Records : {len(broker_trade_df)}")
print("\n" + "=" * 100)
print("STRUKTUR RESPONSE")
print("=" * 100)
def show_structure(name, response):
print(f"\n{name}:")
print("Tipe :", type(response).__name__)
if isinstance(response, dict):
print("Keys :", list(response.keys()))
data = response.get("data")
print("Tipe Data :", type(data).__name__)
if isinstance(data, dict):
print("Data Keys :", list(data.keys())[:30])
elif isinstance(data, list):
print("Jumlah Data :", len(data))
if data and isinstance(data[0], dict):
print("Sample Keys :", list(data[0].keys())[:30])
show_structure(
"Tradebook Chart",
tradebook_result["response"]
)
show_structure(
"Profile Background",
profile_result["response"]
)
show_structure(
"Broker Trade Chart",
broker_trade_result["response"]
)
Cell 3 extracts the data section from each API response and converts different nested structures into records that can be stored in pandas DataFrames.
It then reports the number of Tradebook, Profile, and Broker Trade records while inspecting the original API response structure.
Cell 4 — Display Detailed Data
# ============================================================
# CELL 4 — TAMPILKAN DATA DETAIL
# ============================================================
pd.set_option("display.max_columns", 50)
pd.set_option("display.max_colwidth", 120)
pd.set_option("display.width", 200)
def display_dataset(title, df, original_data):
print("\n" + "=" * 100)
print(title)
print("=" * 100)
if not df.empty:
print(f"Jumlah Record : {len(df)}")
print("Kolom :", list(df.columns))
print()
display(df.head(20))
else:
print("Tidak ada record tabular yang dapat ditampilkan.")
if original_data:
print("\nData tersedia tetapi formatnya tidak berbentuk tabel:")
print(
json.dumps(
original_data,
indent=2,
ensure_ascii=False,
default=str
)[:3000]
)
display_dataset(
"📈 TRADEBOOK CHART — BBCA",
tradebook_df,
tradebook_data
)
display_dataset(
"🏢 COMPANY PROFILE — BBCA",
profile_df,
profile_data
)
display_dataset(
"🏦 BROKER TRADE CHART — BBCA | XL & AK",
broker_trade_df,
broker_trade_data
)
Cell 4 displays up to 20 records from each normalized dataset. When tabular records are unavailable, the original data can still be displayed in JSON format.
Cell 5 — Build the Final BBCA Dashboard
# ============================================================
# CELL 5 — IDX BBCA DASHBOARD
# ============================================================
def is_empty(value):
if value is None:
return True
if isinstance(value, str):
return not value.strip()
if isinstance(value, (list, dict, tuple, set)):
return len(value) == 0
return False
def clean_value(value, max_length=180):
if is_empty(value):
return "-"
if isinstance(value, dict):
text = ", ".join(
f"{k}: {v}"
for k, v in list(value.items())[:5]
)
elif isinstance(value, list):
if all(not isinstance(x, (dict, list)) for x in value):
text = ", ".join(map(str, value[:10]))
else:
text = json.dumps(
value[:3],
ensure_ascii=False,
default=str
)
else:
text = str(value)
if len(text) > max_length:
text = text[:max_length] + "..."
return text
def first_value(data, keys):
if not isinstance(data, dict):
return None
# direct
for key in keys:
if key in data and not is_empty(data[key]):
return data[key]
# recursive
for value in data.values():
if isinstance(value, dict):
result = first_value(value, keys)
if not is_empty(result):
return result
return None
def status_text(result):
if result.get("success"):
return "Berhasil"
status = result.get("status_code")
if status:
return f"Gagal — HTTP {status}"
return "Gagal"
print("=" * 100)
print("IDX BBCA TRADEBOOK, PROFILE & BROKER TRADE DASHBOARD")
print("=" * 100)
# ============================================================
# TRADEBOOK CHART
# ============================================================
print("\n📈 Tradebook Chart — BBCA")
print("-" * 100)
print("Interval :", TRADEBOOK_INTERVAL)
print("Jumlah Data :", len(tradebook_df))
print("Status :", status_text(tradebook_result))
if not tradebook_df.empty:
preferred = [
"time",
"timestamp",
"date",
"price",
"last",
"open",
"high",
"low",
"close",
"volume",
"value",
"frequency",
"buy",
"sell",
"change",
"changePercent"
]
available = [
c for c in preferred
if c in tradebook_df.columns
]
if not available:
available = list(tradebook_df.columns)[:6]
for i, (_, row) in enumerate(
tradebook_df.head(10).iterrows(),
start=1
):
values = []
for col in available:
value = row.get(col)
if not is_empty(value):
values.append(
f"{col}: {clean_value(value, 50)}"
)
print(
f"{i:02d}. "
+ (" | ".join(values) if values else "-")
)
else:
print("Belum ada data tradebook yang dapat ditampilkan.")
# ============================================================
# COMPANY PROFILE
# ============================================================
print("\n\n🏢 Company Profile — BBCA")
print("-" * 100)
print("Jumlah Data :", len(profile_df))
print("Status :", status_text(profile_result))
if isinstance(profile_data, dict):
company_name = first_value(
profile_data,
[
"companyName",
"company_name",
"name",
"company"
]
)
sector = first_value(
profile_data,
[
"sectorName",
"sector",
"sector_name"
]
)
subsector = first_value(
profile_data,
[
"subSectorName",
"subsector",
"sub_sector",
"subSector"
]
)
industry = first_value(
profile_data,
[
"industry",
"industryName"
]
)
address = first_value(
profile_data,
[
"address",
"officeAddress",
"headOffice"
]
)
website = first_value(
profile_data,
[
"website",
"web",
"url"
]
)
description = first_value(
profile_data,
[
"description",
"background",
"businessDescription",
"companyDescription",
"overview"
]
)
print("Kode :", SYMBOL)
print("Perusahaan :", clean_value(company_name))
print("Sector :", clean_value(sector))
print("Subsector :", clean_value(subsector))
print("Industry :", clean_value(industry))
print("Website :", clean_value(website))
print("Alamat :", clean_value(address, 250))
if description:
print("\nProfil Singkat:")
print(clean_value(description, 700))
else:
print("Data profil tersedia dalam format non-dictionary.")
# ============================================================
# BROKER TRADE CHART
# ============================================================
print("\n\n🏦 Broker Trade Chart — BBCA")
print("-" * 100)
print("Broker :", BROKER_CODES)
print("Periode :", BROKER_PERIOD)
print("Market :", MARKET_BOARD)
print("Jumlah Data :", len(broker_trade_df))
print("Status :", status_text(broker_trade_result))
if not broker_trade_df.empty:
preferred = [
"brokerCode",
"broker",
"code",
"name",
"time",
"date",
"buy",
"sell",
"net",
"buyValue",
"sellValue",
"netValue",
"buyVolume",
"sellVolume",
"volume",
"value"
]
available = [
c for c in preferred
if c in broker_trade_df.columns
]
if not available:
available = list(broker_trade_df.columns)[:7]
for i, (_, row) in enumerate(
broker_trade_df.head(10).iterrows(),
start=1
):
values = []
for col in available:
value = row.get(col)
if not is_empty(value):
values.append(
f"{col}: {clean_value(value, 60)}"
)
print(
f"{i:02d}. "
+ (" | ".join(values) if values else "-")
)
else:
print("Belum ada data broker trade yang dapat ditampilkan.")
# ============================================================
# RINGKASAN
# ============================================================
print("\n\n" + "=" * 100)
print("RINGKASAN")
print("=" * 100)
print(f"📈 Tradebook Chart BBCA : {len(tradebook_df)} record")
print(f"🏢 Company Profile BBCA : {len(profile_df)} record")
print(f"🏦 Broker Trade XL & AK : {len(broker_trade_df)} record")
print()
print(
"Status Tradebook Chart :",
status_text(tradebook_result)
)
print(
"Status Company Profile :",
status_text(profile_result)
)
print(
"Status Broker Trade :",
status_text(broker_trade_result)
)
print(
"\nSelesai diproses :",
datetime.now().strftime("%d-%m-%Y %H:%M:%S")
)
print("=" * 100)
Cell 5 transforms the three datasets into the final BBCA dashboard. Tradebook Chart prioritizes market fields such as time, price, OHLC, volume, value, frequency, buy, sell, and price changes when available.
The Company Profile section extracts available company information including company name, sector, subsector, industry, website, address, and description. The Broker Trade section focuses on the available trading activity for XL and AK.
Final Result
The completed BBCA Tradebook Dashboard combines three different perspectives on BBCA within one Google Colab workflow.
Tradebook Chart provides short-interval market activity. Company Profile provides background information about BBCA. Broker Trade Chart provides broker trading information for XL and AK.
The final dashboard also reports the number of records returned by each dataset, individual API status, and the final processing timestamp.
Conclusion
The BBCA Tradebook Dashboard demonstrates how Python and RapidAPI can combine Tradebook Chart, Company Profile, and Broker Trade Chart data into a complete five-cell Google Colab project.
The workflow handles API requests, different response structures, nested data normalization, detailed inspection, and a simplified final dashboard while keeping the original project structure intact.
