後端模組 / Backend Modules

後端位於 backend/app/。以下逐一說明每個模組。

schemas.py — 共用領域型別

集中所有 enum 與 Pydantic 模型,跨 brokers / strategies / ai / workflow 重用:

  • Enums:MarketKind(crypto / tw_stock / us_stock)、TradingMode(paper / live)、 OrderSideOrderTypeSignalAction(buy / sell / hold)
  • Models:CandleTickerSignalOrderRequestOrderResultBalancePosition

config.py — 環境設定

pydantic-settings 讀取 .env。欄位名對應 UPPER_CASE 環境變數。見 configuration.md

db.py — 資料庫

SQLModel engine(預設 SQLite)。init_db() 建表;get_session() 為 FastAPI 依賴。

models.py — 資料表

OrderRecordWorkflowRunLogScheduleNotificationPaperAccountPaperPositionRuntimeFlagRuntimeFlag(key, value, updated_at) 持久化 kill switch、halted 旗標,以及每日權益基準(鍵 equity_baseline:<UTC日期>),M0.6。

marketdata/ — 行情接縫

檔案內容
fx.pyFxConverter(base_currency, rates):以設定驅動的靜態匯率把金額折算為基準幣;to_base(amount, ccy) 同幣別直接回傳、缺率 fail loud(ValueError);from_settings() 由設定建立;MARKET_QUOTE_CURRENCY/quote_currency_for 提供 市場→計價幣(crypto→USDT、tw_stock→TWD、us_stock→USD)。Phase-0 最小接縫,M1.1 在同介面換成即時來源 (M0.6)
calendar.pyis_market_open(market, dt):純函式、決定性。tw_stock 09:00–13:30 Asia/Taipei、us_stock 09:30–16:00 America/New_York(皆排除週末與內建假日集),crypto 永遠開盤。dt 契約:tz-aware 依其時區、naive 視為 UTC,內部轉成市場時區。假日為可匯入/可變的 TW_STOCK_HOLIDAYS/US_STOCK_HOLIDAYS 集合,add_holidays(market, dates) 可擴充。用 zoneinfo,無新依賴 (M1.4)

brokers/ — 券商抽象(核心接縫)

檔案內容
base.pyBroker ABC,所有市場/模式的統一介面
crypto_ccxt.pyCcxtBroker:Binance(含 testnet)。公開行情免金鑰;create_order 無金鑰時 fail loud。get_positions() 由現貨餘額合成 Position(非計價幣別資產;avg_price=0 因為餘額快照無成本基礎),讓現貨部位上限生效(M0.5)
paper.pyPaperBroker:包一個資料來源 broker 取真實價,模擬撮合,記憶體追蹤現金/部位(加權平均成本)
yuanta.pyYuantaBroker:台股 元大(及美股 元大複委託)live 骨架,所有方法 fail loud 並說明所需金鑰/SDK
firstrade.pyFirstradeBroker:美股 Firstrade live 骨架;明確標示無官方 API、依賴非官方函式庫
market_data.py記憶體 OHLCV 倉儲(依 market+symbol)+ parse_csv;讓台股/美股可離線匯入資料
csv_data.pyCsvDataBroker:以匯入的 CSV 歷史供應 ticker/ohlcv,作為 PaperBroker 的資料來源
registry.pyget_data_broker(crypto→ccxt;台股/美股→有匯入資料則 CsvDataBroker,否則 fail loud)、get_live_broker(crypto→ccxt;台股→元大;美股→Firstrade)、get_broker(market, mode)

strategies/ — 指標與策略

strategies.mdregistry.py 集中 4 種策略,供工作流與回測共用。

ai/ — Claude 訊號

檔案內容
claude_client.py延遲建立 anthropic.Anthropic,無 ANTHROPIC_API_KEY 時 fail loud
signal_agent.py在程式端算精簡行情摘要(收盤、變化%、RSI)→ Claude messages.parse 結構化輸出 → Signal。預設 claude-opus-4-8

設計:機械性計算留在程式端(CLAUDE.md「Keep Deterministic Work out of AI」), 只把摘要交給模型,控制 token。

trading/ — 風控、組合、下單

檔案內容
risk.pyRiskGuard.check(req, fill_price, held, current_price):單筆金額上限、部位總值上限;部位上限改以現價市值判斷(現有持倉 × current_price + 新單 × fill_price),違規拋 RiskError(M0.5)。PortfolioGuard.check(req, fill_price, market, broker, session):以基準幣 (TWD)FxConverter 判斷總曝險、當日虧損(觸發設 halted)、每日下單數、kill switch;任一觸發只擋進場(買入),永遠放行出場(減倉賣出),並發 notify(M0.6)
runtime_state.py隔離 RuntimeFlag 的持久化:get/set kill switch、get/set haltedget_or_snapshot_day_start_equity(依 UTC 日快照一次)、count_orders_today(OrderRecord.created_at >= 今日 UTC 起點)(M0.6)
portfolio.pybuild_portfolio(broker):部位帶入即時價、未實現損益、權益總值;取價失敗退回成本價並標記
execution.pyexecute_order(...):手動與工作流共用的唯一下單路徑(冪等檢查→價→風控→撮合→存檔→通知)。可帶 client_order_id(M0.5):若該鍵已有 OrderRecord跳過下單並回傳既有結果(info.idempotent_skip=True);手動下單預設 None,行為不變
paper_store.pyPaperStore:把紙上帳戶現金/部位持久化到 DB,讓 PaperBroker 重啟後仍保留狀態
ledger.pyFIFO 已實現損益帳本(M1.3)。FifoLedger.record_buy(...) 開一個 Lot(數量、價、買進手續費計入成本基礎);record_sell(...) 依時間最舊先出消耗 Lot,每筆消耗產生一筆 RealizedPnL:gross_pnl = (賣價−買價)×數量realized_net = gross − 分攤買進手續費 − 分攤賣出手續費 − 證交稅(費用/稅一律取自 M0.1 CostModel,crypto 稅=0、tw_stock 賣出課證交稅)。record_fill(...) 是接到 execute_order 的接縫:買進開 Lot、賣出 FIFO 消耗;賣出量超過已記錄 Lot 時消耗現有部分並對缺口發 warning 通知(永不擋出場)。execute_order 只在實際成交且非冪等跳過時才記帳,故不會重複計算

workflow/ — 節點圖引擎

workflow.mdschema.py(圖/節點/結果模型)、nodes.py(節點執行器)、 engine.py(拓撲排序、循環偵測、逐節點 fail-loud)。

backtest/ — 回測與最佳化

backtesting.mdengine.py(逐根回測)、optimize.py(網格搜尋)。

notifications/ — 通知

service.py:record_notification(寫入站內通知)、dispatch_webhook(best-effort 外送,失敗不影響交易)、 notify(兩者合一)。execute_order 成交後會發出 success 通知。Notification 資料表保存站內動態。

scheduler/ — 自動執行

service.py:APScheduler BackgroundSchedulerSchedule 對應一個 job;觸發時跑工作流、 寫 RunLog、更新排程狀態。啟動時還原已啟用排程。 M1.4:job 設 max_instances=1coalesce=Truemisfire_grace_time=30;Schedule.cron 有值時 以 CronTrigger 取代 interval。respect_market_hours=True(預設)時,觸發當下市場收盤則 SKIP (last_status="skipped: market closed",非錯誤、不寫 RunLog)。排程市場取自工作流第一個 data_source 節點的 params["market"](預設 crypto,與引擎一致),無 data_source(如純 logger)視為永遠開盤。

api/ — HTTP 路由

markets.pyorders.pyai.pyworkflows.pybacktest.pyschedules.pyledger.py (GET /api/ledger/realized 已實現損益報表,可依 market/symbol/start/end 篩選並回傳合計; GET /api/ledger/realized.csv 匯出 CSV 供報稅)。 見 api-reference.md

main.py — 入口

建立 FastAPI app、CORS、lifespan(啟動 init_db + start_scheduler,關閉 shutdown_scheduler)、 掛載所有路由、/health/api/config