1. Executive decision
將現有 CLI + reports 變成 single, source-aware research data service:
CLI / nightly / weekly → daemon queue → one IBKR connection + _ib_lock
↘ Futu OpenD (independent read-only plane)四個 decision:
- Daemon-only IBKR access. Report 唔再開 direct client 87/88。
- Source-aware rich contract. source / timestamp / age / market_data_type / availability 每個 field 都要有。
- Research baseline. Rich quote + held-option liquidity + cached daily history。
- One-call agent bundle. Hermes 由 3–4 calls / 2–3 分鐘 → 1 call,budget warm ≤3s / cold ≤10s / worst 60s。Futu 係 universe market data 嘅速度答案;IBKR 保持 account/execution authority。
2. Non-negotiable rules
- IBKR first — gateway 健康就用 IBKR 做 current price / range / fundamentals / history / options / portfolio。
- One daemon connection — research call 全部經 daemon queue +
_ib_lock。 - Read-only scope — 唔加 auto order / roll / rebalance。
- Backward compatibility — 舊 quote keys(price/close/bid/ask)保留,rich fields additive。
- No silent fallback — missing field = null + availability state;external fallback 要 source/timestamp/reason。
- No disguised delay —
marketDataTypelive/frozen/delayed/delayed-frozen 要 expose。 - No fabricated values — missing/not_entitled/timeout ≠ zero ≠ 成功。
- Preserve raw data — raw ratios / XML / WSH JSON / headline IDs 保留。
- Bound every request — outer deadline +
try/finallycleanup。 - Trading math stays in core/ — PMCC/vertical/max-loss math 唔入 Futu 或 report。
- Account truth = IBKR/Flex only — Futu 永遠唔係 positions/cash/fills/orders/P&L/BAG 權威。
- Indicators ≠ predictions — derived metrics 係 advisor context。
Source arbitration:
| Domain | Authority | Futu role |
|---|---|---|
| Account/cash/margin/positions/fills/orders/BAG | IBKR/Flex | None — never |
| Current executable quote | IBKR when healthy | cross-check + display fallback |
| Historical daily bars | IBKR by default | cached supplement / explicit fallback |
| Valuation / financials / analyst consensus | — | first-class Futu research |
| Capital flow / short interest / ownership | — | first-class Futu research |
| Earnings/economic/Fed | Futu(WSH optional) | first-class Futu macro |
| News / sentiment | — | source=futu_content |
| Strategy analytics | core combo math | Futu informational cross-check |
3. Implementation status(2026-08-16,已 ship)
| Capability | Command | Measured |
|---|---|---|
| Batched universe quotes ≤400 codes,bad code 隔離 | ibkr futu quote SYM... / futu universe | 118 codes = 242ms cold / 108ms warm |
| Cross-process 60s disk cache | automatic | repeat ≈ 1ms |
| Full-row normalization | — | 60+ fields/row(was 7) |
| One-call research bundle | ibkr bundle [--with-history] [--with-research] | 1.65s(was worst 72 calls / 390s) |
| Fast portfolio(skip Greek enrichment) | ibkr portfolio --fast | ~1.7s,combos 一致 |
| History cache + quota guard | ibkr futu history SYM --interval 1d --count N / futu quota | latest-N bars,6h cache |
| Research shortcuts | futu short / flow / fedwatch / earnings | live-verified |
| Symmetric speed log | ~/.kit-ibkr/logs/speed.log | IBKR + Futu JSON lines |
| CN code mapping fixed | to_futu_code | SH./SZ./SG./MY./JP./CC./EC. pass-through |
| Economic calendar pagination | economic_calendar() | ≤20 pages |
| Pure JSON stdout | Futu SDK console silenced | agent-parseable |
Bundle resilience contract(normative):
- Bundle 永遠唔 raise provider error — 全部變
warnings[]+ partial sections。 - account/portfolio/quotes/events always run;optional sections symbol-bounded、per-symbol error。
- 60s wall budget:optional section skip +
skipped: bundle wall budget exhausted。 - Portfolio 3 層 fallback:raw+local combos(fast)→ enriched daemon(slow)→ direct client;每次 fallback 寫 warning。
warnings[]非空 = 該 section 不可信 → Hermes fallback 個別 command,唔可以當成功。
Monday comparison:
grep '"command": "futu' ~/.kit-ibkr/logs/speed.log | tail -50
grep -E 'cmd=(quote|batch quote|portfolio)' ~/.kit-ibkr/logs/audit.log | tail -50快/同等 + data 更豐富 → merge futu-speed-p0 → main。慢 → git checkout pre-futu-speed-2026-08-16(即時回舊)。
4. Hermes bundle contract
ibkr bundle # account+portfolio+universe+events 一個 call
ibkr bundle --with-history # 每 holding 最新 250 daily bars
ibkr bundle --with-research # short interest + capital flow| State | Budget |
|---|---|
| Warm(cache fresh) | ≤3s(實測 1.65s closed) |
| Cold | ≤10s typical |
--fresh | ≤60s hard bound |
| Futu down | IBKR-only degraded + FUTU_UNAVAILABLE warning |
| IBKR down | explicit error,no silent fallback |
機制:CLI 同時打 IBKR daemon + Futu OpenD(獨立 planes),wall ≈ max(t_ibkr, t_futu)。
5. ib_async API assessment(精簡)
5.1 Contract identity / metadata
| API | Use | Priority |
|---|---|---|
qualifyContractsAsync | 身份權威,research 前必行 | P0 |
reqContractDetailsAsync | 擴 details;industry/tz/hours/market rules | P1 |
reqHistoricalScheduleAsync | US/HK session-aware 窗口,取代 hardcode NYSE calendar | P1 |
reqHeadTimeStampAsync / reqMarketRuleAsync / reqMatchingSymbolsAsync | 診斷 / 價格規則 / symbol search | P2 |
5.2 Rich market data(reqMktData generic ticks)
| Ticks | Fields | Use |
|---|---|---|
165 | 13/26/52 週 range + avVolume | range position、volume context |
221 233 236 | mark / RT volume+VWAP / shortable | mark、intraday、liquidity |
258 456 | fundamentalRatios / Dividends | valuation、股息 |
293–295 411 | tradeCount/Rate/VolumeRate + RT hist vol | activity、volatility |
Option 100/101/104/105/106 | volume / OI / HV / avg vol / IV | held-leg liquidity |
Collector:qualify → reqMktData → bounded wait → copy → finally cancelMktData。reqTickersAsync 唔支援 generic ticks。
5.3 Options / Greeks
Ticker.bidGreeks/askGreeks/lastGreeks/modelGreeks → OptionComputation{impliedVol,delta,optPrice,pvDividend,gamma,vega,theta,undPrice}。保留 side-specific provenance;唔好再 modelGreeks or bidGreeks or askGreeks。reqSecDefOptParamsAsync 只返 chain metadata(strike/expiry 可能有 invalid combo,要 qualify selected legs)。Option volume/OI 可以計 put/call ratio,但唔可以宣稱 dealer flow(冇 aggressor side / open-close 分類)。
5.4 Historical(reqHistoricalDataAsync)
BarDataList → BarData{date,open,high,low,close,volume,average,barCount}。whatToShow 包括 TRADES / ADJUSTED_LAST / HISTORICAL_VOLATILITY / OPTION_IMPLIED_VOLATILITY。官方限制:50 concurrent、identical ≤15s 禁止、同 contract 6/2s、60/10min、BID_ASK 計兩次、few-thousand bars、≤30s bars 6 個月外不可用、expired options EOD 不可用。scheduled jobs 用 cached daily bars + exact-request cache。
5.5 Fundamentals / WSH / News
reqFundamentalDataAsync→ XML:ReportsFinSummary / ReportsOwnership / ReportSnapshot / ReportsFinStatements / RESC / CalendarReport。保留 raw XML + LATESTADATE;normalize 只用實測 aliases。- WSH:
getWshMetaDataAsync→getWshEventDataAsync。要 Wall Street Horizon subscription;metadata 先行、一次一個 request(duplicate 會被拒)。Optional,NOT_ENTITLED唔當 failure。 - News:
reqNewsProvidersAsync/reqHistoricalNewsAsync(≤300)/reqNewsArticleAsync。API-specific subscriptions。Weekly/on-demand only。 - 注意:ib_async 冇 macro calendar API — economic calendar/Fed 用 Futu。
5.6 Account / Executions / PnL
reqExecutionsAsync+Fill/CommissionReport→ 佣金、realized P&L、exchange — weekly attribution;Flex 仍然係歷史 authority。reqPnL/reqPnLSingle係 sync subscription-start(實測 2.1.0 冇 Async variants),要 cancel lifecycle;intraday only,歷史 attribution 要 persisted snapshots + Flex reconcile。accountSummaryAsync/portfolio()保持 source-of-truth。
5.7 Scanner / Deferred
reqScannerDataAsync on-demand only(50 rows / 10 active scans,結果只係 contracts 要 follow-up quote)。Deferred:reqMktDepth / reqTickByTickData / reqRealTimeBars / reqHistoricalTicks / calculateImpliedVolatilityAsync / calculateOptionPriceAsync — 全部 explicit research tool,唔入 nightly baseline。
6. Futu companion plan(精簡)
6.1 Position
First-class read-only companion plane,經 OpenD 127.0.0.1:11111,唔經 IBKR socket / _ib_lock。現時權限:US LV3、US Options LV1/OPRA、HK。
6.2 Capability matrix
| Futu API | State | Use | Cadence |
|---|---|---|---|
get_market_snapshot(≤400/call,60/30s) | ✅ | universe quotes | nightly / on-demand |
request_history_kline(≤1000/page,60/30s) | ✅ | history cache | weekly / on-demand |
get_history_kl_quota | ✅ | quota guard | before batches |
get_option_strategy_analysis(30/30s,免 subscription quota) | ✅ | combo bid/ask + probability cross-check | on-demand |
get_option_chain / get_option_quote | ✅ / inventory | contract discovery、multi-leg quote | on-demand |
get_earnings_calendar(7-day window) | ✅ | earnings | weekly |
get_economic_calendar | ✅ paginated | macro | weekly |
get_fed_watch_target_rate | ✅ | LEAPS rate sensitivity | weekly |
get_short_interest / get_capital_flow | ✅ | nightly flow | nightly |
| Financials / valuation / analyst / corporate actions / ownership | inventory | research enrichment | weekly / on-demand |
| Rankings / screeners / plates | inventory | candidate discovery | on-demand |
| Content skills(news/research/sentiment) | public HTTP | source=futu_content digest | on-demand |
6.3 Quota / lifecycle rules
- Snapshot 1 call ≤400 codes;unknown/delisted code 會令成 batch fail → recursive bisect 隔離(已實作)。
- Subscription = lease:每個 code×type 食 1 quota,≥1 分鐘先可 unsubscribe;nightly 唔好用 subscription 代替 snapshot。
- History quota 7 日 dedup(官方 authority page)vs Agent Hub 寫 30 日 — 以 runtime
get_history_kl_quota(get_detail=True)為準,唔 hardcode。 - 一個 scoped context per report run;close 喺 finally;OpenD down =
FUTU_UNAVAILABLE,唔 auto-restart。 futu_api/OpenD 版本要 pin + record(FP-00)。
6.4 Provenance envelope
{
"source": "futu",
"endpoint": "get_market_snapshot",
"requested_at": "...", "received_at": "...", "as_of": "...", "age_ms": 340,
"delayed_or_realtime": "realtime",
"availability": "available",
"quota": {"checked": true, "remaining": null},
"errors": [],
"data": []
}Content skills 用 source=futu_content。futu_enrich.py 唔可以丟 envelope。
7. Data contracts(精簡)
Provenance(所有 result 必有): source / source_detail / requested_at / received_at / as_of / age_ms / market_data_type / availability / errors / request
Availability vocabulary: available / missing / not_entitled / unsupported / timeout / error / stale
Market snapshot(stock,Futu 已實作 60+ fields): identity(conid/sec_type/exchange/currency)· market(bid/ask/last/mark/close/open/high/low/volume/vwap/sizes)· range(13/26/52w + position%)· quality(spread、volume ratio、trade rates)· fundamentals(raw + normalized + report_date)· dividends(past/next/date/amount)· sessions(pre/after/overnight)· provenance。
Option snapshot(additive): bid/ask/mid/spread$/%、IV/HV、side-specific/model Greeks、volume/OI/avg option volume、underlying price、DTE/BE distance、risk flags(OPTION_BID_ASK_MISSING / WIDE_SPREAD / DELAYED_DATA / NOT_ENTITLED)。
Historical(derived 要標 formula): returns、realized vol、ATR、SMA20/50/200、max drawdown、benchmark-relative;insufficient history = null + missing,唔 fabricated。
8. CLI surface
ibkr quote / batch quote additive rich fields(P0 collectors)
ibkr snapshot SYM [--history 1y] [--fundamentals] [--events] [--options --expiry]
ibkr portfolio [--raw] [--fast] --fast = no Greek enrichment ~1.7s(已實作)
ibkr bundle [--with-history] [--with-research] ← Hermes preferred(已實作)
ibkr futu universe / quote / history / quota / short / flow / fedwatch / earnings(已實作)
ibkr futu health / chain / option / bars / strategy(原有)
ibkr screen / news / events P1/P2 on-demand規則:Futu commands 永遠唔經 IBKR daemon;成功/error 都標 source=futu;所有 speed 相關 call 寫 speed.log。
9. Development plan(32 work packages,精簡 table)
9.1 IBKR DP-*
| ID | Priority | Deps | Effort | Scope | Key files |
|---|---|---|---|---|---|
| DP-00 | P0 | — | S | pin/test ib_async,source hierarchy | pyproject, AGENTS |
| DP-01 | P0 | DP-00 | M | report IBKR work 收歸 daemon(拆 client 87/88) | daemon, data, weekly |
| DP-02 | P0 | DP-00 | S/M | provenance/availability schema | models/market_data |
| DP-03 | P0 | DP-01,02 | M | IBClient research wrappers | core/connection |
| DP-04 | P0 | DP-02,03 | M | generic-tick collector + rich quote | daemon, cli |
| DP-05 | P0 | DP-04 | M/H | held-option liquidity + risk flags | daemon, portfolio, analysis |
| DP-06 | P0 | DP-01,03 | M | IBKR history cache + derived metrics | market_data, weekly |
| DP-07 | P0 | DP-02,04–06 | M | provenance 入 nightly/weekly/AI/HTML | report/* |
| DP-08 | P1 | DP-03,04 | M/H | fundamentals XML + RESC | core, daemon |
| DP-09 | P1 | DP-03,07 | M/H | WSH events(optional entitlement) | upcoming, weekly |
| DP-10 | P1 | DP-01,07 | S/M | executions commission attribution | daemon, weekly |
| DP-11 | P1 | DP-03,04 | M | on-demand scanner + bounded top-N | daemon, cli |
| DP-12 | P2 | DP-03,07 | M | news digest | daemon, weekly |
| DP-13 | P2 | DP-05,07 | M/H | PnLSingle snapshots + reconcile | daemon, weekly |
| DP-14 | P2 | DP-04,05 | S/M | calculate* scenario tools | daemon, cli |
| DP-15 | P0+ | DP-01.. | M | rollout + smoke + runbook | — |
9.2 Futu FP-*
| ID | Priority | Deps | Effort | Scope | Key files |
|---|---|---|---|---|---|
| FP-00 | P0 | — | S | pin futu-api/OpenD + quota window 實測 | pyproject, config |
| FP-01 | P0 | FP-00 | S/M | provenance envelope 全 section(✅部分已做) | futu/models |
| FP-02 | P0 | FP-01 | M | scoped context + batch snapshot + cache(✅已做) | futu/client, service |
| FP-03 | P0 | FP-00,02 | M | history cache + quota guard(✅已做) | futu/service |
| FP-04 | P0 | FP-02,03 | S/M | speed/freshness diagnostics(✅speed.log 已做) | futu, cli |
| FP-05 | P1 | FP-01,02 | M | option/strategy research adapters | futu/service |
| FP-06 | P1 | FP-01,02 | M/H | fundamentals + corporate actions | futu/service |
| FP-07 | P1 | FP-01,02,04 | M | flow/calendar hardening(✅pagination 已做) | futu, reports |
| FP-08 | P1 | FP-01..07 | M | 完整 Futu CLI(✅core commands 已做) | cli |
| FP-09 | P2 | FP-01,04 | M | content skills digest | futu/content |
| FP-10 | P0+ | FP-02–04 | M | cross-provider benchmark gate(星期一) | — |
9.3 Bundle DB-*
| ID | Priority | Deps | Effort | Scope |
|---|---|---|---|---|
| DB-00 | P0 | DP-00,02,FP-00,01 | S/M | ibkr bundle contract + dispatcher(✅已做:1.65s + resilience) |
| DB-01 | P0 | FP-01,02 | M | Futu batch universe quotes + 60s cache(✅已做) |
| DB-02 | P0 | DP-06,FP-03 | M | history precompute(nightly/weekly 餵 cache)— 剩低 |
| DB-03 | P0 | FP-01,02,04,07 | M | research/event precompute — 剩低 |
| DB-04 | P0 | DB-00..03 | S/M | latency benchmark + regression gate — 星期一執行 |
10. Pacing / lifecycle / error policy
- Async 規則:
*Asyncmethods + outerasyncio.wait_for;唔time.sleep;唔喺 event handler 內發新 request。 - Cleanup: 所有 subscription
finally cancel(mktData / historical / depth / tickByTick / realtimeBars / scanner / WSH / PnL)。 - Error 分類:
CONTRACT_NOT_FOUND / AMBIGUOUS / NOT_ENTITLED / UNSUPPORTED / TIMEOUT / PACING / DISCONNECTED / PARSE_ERROR / STALE / UNKNOWN。empty result ≠ 成功(ib_asyncRaiseRequestErrors=False陷阱)。 - IBKR pacing: 50 concurrent max、identical 15s、6/2s 同 contract、60/10min、BID_ASK×2;exact-request cache + serial queue。
- Futu pacing: snapshot 60/30s、history 60/30s first page、search 10/30s、strategy 30/30s;subscription ≥1min lease。
- Timezone:
formatDate=2(UTC)、RTH explicit、contract session data 取代 hardcode NYSE;holiday/early-close ≠ missing。
11. Rejected / deferred
- ib_async macro calendar — 唔存在,用 Futu。
- Duplicate Futu capabilities 過 IBKR — 除非 data quality 原因。
- Full-universe nightly scanner / full-chain nightly — bounded only。
- Silent Yahoo primary/fallback — 如保留要 source+reason。
- Depth/tick-by-tick 做 report baseline — execution research only。
- Persistent streaming by default — snapshot 評估計比較安全。
- 由 current PnL 重建歷史 attribution — persisted snapshots 先啱。
- Option volume = dealer flow — 冇 aggressor/open-close 分類,唔可以咁講。
- Trading/order enhancements — 唔屬於本 plan。
- Cosmetic HTML/3D 先過 data quality — defer。
12. Tests & verification
Unit(391 passing baseline): test_market_data(schema/availability/delayed mapping)· test_historical_market_data(bars/cache/metrics)· test_bundle(combo detection/budget/fallback)· test_futu(27:allowlist/batch/cache/error isolation/source)· test_futu_report · test_fundamentals · test_wsh_events · test_news · test_scanner · test_pnl_snapshots + 現有 suites(batch_quote/connection/portfolio_raw/analysis/weekly/fallback/upcoming/advisor/html_v3)。
Paper smoke(read-only):
.venv/bin/ibkr health && .venv/bin/ibkr portfolio --fast
.venv/bin/ibkr bundle && .venv/bin/ibkr futu universe
.venv/bin/python scripts/nightly_report.py --no-ai --no-discord --no-pagedrop
.venv/bin/python scripts/weekly_report.py --no-ai --no-discordFailure matrix: daemon down / gateway reconnecting / invalid + ambiguous contract / no live subscription / delayed only / generic tick not entitled / WSH not entitled / news provider absent / history timeout / stale cache / OpenD down / Flex 1019 / mixed per-symbol fallback / cancelled request + follow-up quote。
13. Rollout + rollback
Stages: schema-only → paper rich quote → paper nightly → paper weekly → optional entitlements(逐個開)→ live read-only → docs。
Rollback(即時):
git checkout pre-futu-speed-2026-08-16 # 完整 rollback point(tag)
# 繼續修:
git checkout futu-speed-p0Rollback 邊界: 只有 Futu quote path + bundle 改咗;IBKR portfolio/orders/BAG/P&L 未動;disk cache passive(刪 ~/.kit-ibkr/cache/futu/ 即 disable);speed.log append-only。
Handover checklist(short):
- 所有 IBKR request 經 daemon;無 direct client 87/88
- Rich fields 帶 source/as-of/age/market-data type/availability
- Missing ≠ zero;delayed ≠ live;per-symbol mixed sources 可見
- Bundle warm ≤3s 實測;warnings[] 非空時 Hermes 識 fallback
- Historical pacing + exact-request cache + subscriptions cancel
- Futu quota 先 check;WSH serialized;
futu-apipin - Paper + live read-only smoke pass
- 無 order 行為、無 Futu account authority、無 silent Yahoo、無 dealer-flow claims
14. Schedule & backlog
已 ship(2026-08-16): speed milestone 完成 — bundle 1.65s、Futu batch 242ms/118 codes、60s cache、history cache、symmetric speed.log、CN fix、economic pagination。
剩低(順序):
| # | Task | Est |
|---|---|---|
| 1 | DB-04 星期一 open-market 實測 + baseline gate → merge 決定 | 1d |
| 2 | DB-02 nightly/weekly precompute history cache | 3d |
| 3 | DB-03 research/event precompute | 3d |
| 4 | DP-01 report IBKR work 收歸 daemon | 3d |
| 5 | DP-04/05 daemon 14s Greek enrichment 拆解 + rich quote | 5.5d |
| 6 | DP-08 fundamentals / DP-09 WSH probes | 5.5d each |
| 7 | FP-05/06 Futu option + research adapters | 3–5.5d |
| 8 | P2:news / PnL snapshots / scenario tools | 3–5.5d each |
Estimate: P0 剩低 ≈ 6–8 週;全部連 P1/P2 ≈ 10–12 週(一人)。