12 endpoints
https://api.blave.orgapi-key + secret-key headers on every request/studio/market/twfutures/ohlcv/<symbol>/<schema> Max range per request (end − start): 1m 31 d · 5m 62 d · 15m 93 d · 30m 186 d · 60m 365 d · 1d 3650 d.
from datetime import date, timedeltadata = requests.get(f"{BASE_URL}/studio/market/twfutures/ohlcv/TXF/1d", headers=headers, params={"start": "2024-01-01", "end": "2024-12-31"}, timeout=60).json()["data"]# [{"ts": "2024-01-01 16:00:00+00:00", "open": 17838.0, "high": 17920.0, "low": 17751.0, "close": 17798.0, "volume": 82820},# {"ts": "2024-01-02 16:00:00+00:00", "open": 17793.0, "high": 17817.0, "low": 17507.0, "close": 17546.0, "volume": 149638}, ...]def fetch_txf_chunked(schema, start, end, chunk_days=28): result, cur, end_date = [], date.fromisoformat(start), date.fromisoformat(end) while cur < end_date: chunk_end = min(cur + timedelta(days=chunk_days), end_date) resp = requests.get(f"{BASE_URL}/studio/market/twfutures/ohlcv/TXF/{schema}", headers=headers, params={"start": cur.isoformat(), "end": chunk_end.isoformat()}, timeout=60) result.extend(resp.json().get("data", [])) cur = chunk_end return result{"symbol", "schema", "data": [...]}.
| Field | Type | Unit | Description |
|---|---|---|---|
ts | string | UTC ISO | Bar open time |
open / high / low / close | float | index points (price for stock futures) | Prices |
volume | int | contracts (口) | Volume |
| Status | Body | When |
|---|---|---|
| 400 | {"error": "Invalid symbol. Allowed: [...]"} | Symbol not covered |
| 400 | {"error": "Invalid schema. Allowed: [...]"} | Bad schema |
| 400 | {"error": "Invalid date format. Use YYYY-MM-DD"} | Malformed date |
| 400 | {"error": "date_range_too_large", "message": "...", "max_days": <n>} | Range over the cap |
| 503 | error message | Data source temporarily unavailable |
TXF, MXF, and stock futures alike): monthly settlement day — the 3rd Wednesday, or the next trading day when that Wednesday is closed — is the expiring month for the whole day. It trades through its 13:30 Taipei close, no bar is stamped 13:31–14:59 Taipei on that day, and the series is the next month from the 15:00 night session on. The settlement day's 1d bar is the expiring month's, so its close is that contract's last trade at 13:30.ohlcv/<symbol>/export/<year> instead of chunked JSON.200 with [].MXF and CAF asked for everything up to the request date both came back with a last bar months old. Read data[-1]["ts"].1d bars carry a 16:00:00+00:00 time component (00:00 Taipei), not midnight UTC — take the date from ts rather than assuming the timestamp is date-aligned./studio/market/twfutures/ohlcv/symbols None.
symbols = requests.get(f"{BASE_URL}/studio/market/twfutures/ohlcv/symbols", headers=headers, timeout=30).json()["data"]{"data": ["CDF", "DHF", "MXF", "TXF", ...]} — symbols accepted by ohlcv, ohlcv/export, and bid_ask_vol: TXF, MXF, plus the liquidity-picked subset of stock futures with minute-line coverage (most of the 231 stock futures are not covered).
Shared errors only.
/studio/market/twfutures/ohlcv/<symbol>/export/<year> import ioimport pandas as pdr = requests.get(f"{BASE_URL}/studio/market/twfutures/ohlcv/TXF/export/2024", headers=headers, timeout=120)raw = pd.read_parquet(io.BytesIO(r.content))raw.index = pd.to_datetime(raw["ts"], utc=True)bars_60m = (raw[["open", "high", "low", "close", "volume"]] .resample("60min") .agg(open=("open", "first"), high=("high", "max"), low=("low", "min"), close=("close", "last"), volume=("volume", "sum")) .dropna(subset=["open"]))application/octet-stream, a parquet file (<SYMBOL>_<year>_1m.parquet) with columns ts, open, high, low, close, volume (1m bars).
| Status | Body | When |
|---|---|---|
| 400 | {"error": "Invalid symbol. Allowed: [...]"} | Symbol not covered |
| 400 | {"error": "Invalid year. Allowed: 2014-<current>"} | Year out of range |
| 404 | {"error": "no_data", "message": "No 1m data on disk for <symbol> in <year>"} | No file |
ohlcv/<symbol>/1m, under the same settlement-day roll rule (no bars stamped 13:31–14:59 Taipei on monthly settlement day)./studio/market/twfutures/bid_ask_vol/<symbol> end − start ≤ 31 daysdata = requests.get(f"{BASE_URL}/studio/market/twfutures/bid_ask_vol/TXF", headers=headers, params={"start": "2026-05-29", "end": "2026-05-29"}, timeout=60).json()["data"]# [{"ts": "2026-05-29 00:45:00+00:00", "bid_vol": 669, "ask_vol": 447, "total_vol": 1156}, ...]{"symbol", "start", "end", "data": [...]}, 1-minute rows, day and night sessions.
| Field | Type | Unit | Description |
|---|---|---|---|
ts | string | UTC ISO | Bar open time |
bid_vol | int | contracts | 內盤 — trades hitting the bid (seller-initiated) |
ask_vol | int | contracts | 外盤 — trades lifting the ask (buyer-initiated) |
total_vol | int | contracts | Total, including unclassified ticks |
| Status | Body | When |
|---|---|---|
| 400 | {"error": "Invalid symbol. Allowed: [...]"} | Symbol not covered |
| 400 | {"error": "Invalid date format. Use YYYY-MM-DD"} | Malformed date |
| 400 | {"error": "date_range_too_large", "message": "...", "max_days": 31} | Range over 31 days |
| 503 | error message | Data source temporarily unavailable |
ohlcv: on monthly settlement day there are no rows between the expiring month's 13:30 Taipei close and the 15:00 night open; from the night session on the rows are the next month./studio/market/twfutures/daily/<futures_id> data = requests.get(f"{BASE_URL}/studio/market/twfutures/daily/TX", headers=headers, params={"start": "2026-08-01"}, timeout=60).json()["data"]{"futures_id", "data": [...]}; several rows per day (every contract month × trading session).
| Field | Description |
|---|---|
date, futures_id | Day, product |
contract_date | Contract month (spread contracts contain /) |
open / max / min / close | Prices (max = high, min = low) |
spread / spread_per | Change / change % |
volume | Contracts |
settlement_price | Settlement price |
open_interest | Open interest |
trading_session | position (regular) or after_market (night) |
| Status | Body | When |
|---|---|---|
| 400 | {"error": "Invalid futures_id"} | Format check failed |
| 404 | {"error": "No data found for futures_id: <id>"} | No data |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Upstream quota exhausted |
/studio/market/twfutures/stock_futures/batch/daily body = requests.get(f"{BASE_URL}/studio/market/twfutures/stock_futures/batch/daily", headers=headers, params={"futures_ids": "CDF,DHF", "start": "2026-08-01"}, timeout=120).json(){"data": {"<futures_id>": [...rows as /daily...]}, "failed": [...]}. failed = ids whose fetch failed (retry); an id absent from both genuinely has no data.
| Status | Body | When |
|---|---|---|
| 400 | {"error": "futures_ids parameter required"} | Missing |
| 400 | {"error": "futures_ids exceeds max 250 per request"} | Too many |
| 400 | {"error": "Invalid stock futures_id(s): [...]"} | Any id is not a stock future |
| 503 | {"error": "Stock futures symbol list temporarily unavailable. Please retry shortly."} | Symbol list unavailable |
/studio/market/twfutures/institutional/<futures_id> data = requests.get(f"{BASE_URL}/studio/market/twfutures/institutional/TX", headers=headers, params={"start": "2026-08-01"}, timeout=60).json()["data"]{"futures_id", "data": [...]}, 3 rows per day (自營商 / 投信 / 外資).
| Field | Description |
|---|---|
date, futures_id | Day, product |
institutional_investors | Investor type |
long_deal_volume / long_deal_amount | Long trades (contracts / TWD thousand) |
short_deal_volume / short_deal_amount | Short trades |
long_open_interest_balance_volume / long_open_interest_balance_amount | Long OI |
short_open_interest_balance_volume / short_open_interest_balance_amount | Short OI |
| Status | Body | When |
|---|---|---|
| 400 | {"error": "Invalid futures_id"} | Format check failed |
| 404 | {"error": "institutional data not available for stock futures (<id>)"} | Stock future |
| 404 | error message | No data |
| 503 | upstream rate limit / symbol list unavailable | Retry later |
*_amount is TWD thousand — inferred from magnitude, not stated upstream, but the arithmetic backs it: a TX row with long_open_interest_balance_amount 722870036 over ..._volume 78292 is 9,233 per contract, and a TX contract is index × 200, so 9,233 thousand ÷ 200 ≈ 46,160 index points, which is where the index was trading that day. Read as TWD it would be 1,000× too small./studio/market/twfutures/large_traders/<futures_id> data = requests.get(f"{BASE_URL}/studio/market/twfutures/large_traders/TX", headers=headers, params={"start": "2026-08-01"}, timeout=60).json()["data"]{"futures_id", "data": [...]}, 3 rows per day (contract_type: week / current month / all).
| Field | Description |
|---|---|
date, futures_id, name, contract_type | Day, product, product name, contract scope |
buy_top5_trader_open_interest / buy_top10_trader_open_interest (+ _per) | Long OI of top 5 / 10 traders (contracts, % of market) |
sell_top5_trader_open_interest / sell_top10_trader_open_interest (+ _per) | Short OI of top 5 / 10 traders |
buy_top5_specific_open_interest / buy_top10_specific_open_interest (+ _per) | Long OI of top 5 / 10 specific legal persons |
sell_top5_specific_open_interest / sell_top10_specific_open_interest (+ _per) | Short OI of top 5 / 10 specific legal persons |
market_open_interest | Total market OI |
| Status | Body | When |
|---|---|---|
| 400 | {"error": "Invalid futures_id"} | Format check failed |
| 404 | {"error": "large_traders data not available for stock futures (<id>)"} | Stock future |
| 404 | error message | No data |
| 503 | upstream rate limit / symbol list unavailable | Retry later |
/studio/market/twfutures/option/institutional/<option_id> data = requests.get(f"{BASE_URL}/studio/market/twfutures/option/institutional/TXO", headers=headers, params={"start": "2026-08-01"}, timeout=60).json()["data"]{"option_id", "data": [...]}, 6 rows per day (3 investor types × call / put). Fields: date, option_id, call_put (買權 / 賣權), institutional_investors, and the same long_* / short_* deal and open-interest volume / amount fields as futures institutional.
400 {"error": "Invalid option_id"}; 404 no data; 503 upstream rate limit.
/studio/market/twfutures/option/large_traders/<option_id> data = requests.get(f"{BASE_URL}/studio/market/twfutures/option/large_traders/TXO", headers=headers, params={"start": "2026-08-01"}, timeout=60).json()["data"]{"option_id", "data": [...]}, 6 rows per day (call / put × week / current month / all). Fields: date, option_id, name, put_call, contract_type, and the same top-5 / top-10 trader and specific-legal-person open-interest fields as futures large traders, plus market_open_interest.
400 {"error": "Invalid option_id"}; 404 no data; 503 upstream rate limit.
/studio/market/twfutures/option/pcr data = requests.get(f"{BASE_URL}/studio/market/twfutures/option/pcr", headers=headers, params={"start": "2024-01-01", "end": "2024-12-31"}, timeout=60).json()["data"]# [{"date": "2024-01-02", "put_volume": 309583, "call_volume": 404231, "volume_pcr": 76.59,# "put_oi": 263673, "call_oi": 246495, "oi_pcr": 106.97, "pcr": 106.97},# {"date": "2024-01-03", "put_volume": 622854, "call_volume": 729547, "volume_pcr": 85.38,# "put_oi": 177203, "call_oi": 178062, "oi_pcr": 99.52, "pcr": 99.52}, ...]{"data": [...]}, one row per trading day.
| Field | Unit | Description |
|---|---|---|
date | — | Trading day |
put_volume / call_volume | contracts | Put / call volume |
volume_pcr | % | Put/call volume ratio |
put_oi / call_oi | contracts | Put / call open interest |
oi_pcr | % | Put/call open-interest ratio (買賣權未平倉量比率) |
pcr | % | Same as oi_pcr (kept for compatibility) |
404 no data; 503 store not ready or upstream rate limit.
/studio/market/twfutures/carrying_cost/<identity> Blave's estimate of the average cost of one institution's net TAIEX-futures position, with its unrealized and realized PnL. An estimate from daily aggregates, not the institution's real fills.
Method:
scope=all), or TX only (scope=tx).all = TX + MTX + TMF in TX-equivalent contracts; tx = 大台 only (the basis most Taiwanese sites quote)The ledger always runs from the start of the stored history; start / end only cut the window, so the same day has the same values whatever window you ask for.
body = requests.get(f"{BASE_URL}/studio/market/twfutures/carrying_cost/foreign", headers=headers, params={"start": "2026-10-06"}, timeout=60).json()rows = body["data"]# Live capture 2026-10-07 (scope=all):# {"identity": "foreign", "scope": "all", "data": [# {"date": "2026-10-06", "net_open_interest": -78570.7, "net_open_interest_change": -3923.5,# "cost": 46527.57, "txf_close": 50060.0, "mark_price": 50088.51,# "unrealized_pnl": -55957022956, "realized_pnl": -5893982992, "total_pnl": -61851005948,# "cycle_start": "2026-09-16"},# {"date": "2026-10-07", "net_open_interest": -78435.8, "net_open_interest_change": 134.9,# "cost": 46527.57, "txf_close": 49979.0, "mark_price": 49974.38,# "unrealized_pnl": -54070636128, "realized_pnl": -5988669182, "total_pnl": -60059305310,# "cycle_start": "2026-09-16"}]}gap = rows[-1]["txf_close"] - rows[-1]["cost"] # points the market is above (+) / below (−) their cost{"identity", "scope", "data": [...]}, one row per trading day, oldest → newest; "stale": true is added only when the server's copy is behind TAIFEX's last published day (the newest row may be missing).
| Field | Type | Unit | Description |
|---|---|---|---|
date | string | — | Trading day (Taipei) |
net_open_interest | float | TX-equivalent contracts | Long − short OI (negative = net short) |
net_open_interest_change | float | contracts | Change vs the previous trading day |
cost | float | null | index points | Weighted-average cost of the net position; null when flat |
txf_close | float | null | index points | TX near-month day-session close (the expiring contract is skipped on settlement day) |
mark_price | float | null | index points | TAIFEX valuation of the position, at settlement prices |
unrealized_pnl | int | TWD | (mark − cost) × net contracts × 200 |
realized_pnl | int | TWD | Realized since cycle_start; resets to 0 on every settlement day |
total_pnl | int | TWD | unrealized_pnl + realized_pnl |
cycle_start | string | — | The settlement day that opened the current cycle |
| Status | Body | When |
|---|---|---|
| 400 | {"error": "Invalid identity. Allowed: ['dealer', 'foreign', 'investment_trust']"} | Unknown identity |
| 400 | {"error": "Invalid scope. Allowed: ['all', 'tx']"} | Unknown scope |
| 400 | {"error": "Invalid start date, expected YYYY-MM-DD"} (or end) / {"error": "start must not be after end"} | Bad date / reversed range |
| 503 | {"error": "Market data is not ready on this server. Please retry later."} | Store not ready — retry later, never read as "no position" |
| 500 | {"error": "Internal error"} | Unexpected server error |
scope=tx).