Skip to main content

Multi-asset unified portfolio backtesting framework

Project description

ChidoriBT ⚡

超高速多資產回測框架 / Multi-Asset Backtesting for Self-Hosted Quant Systems

PyPI version Python 3.9+ License: MIT Numba

Chidori — 千鳥


專案定位

ChidoriBT 是面向自建量化交易系統的多資產組合回測引擎:以 (T, N) 面板承載 OHLCV 與 metadata,在同一資金池上跑現股、多空與期貨策略,並支援 RL 軌跡重放與大量參數掃描。

核心思路借鑑 vectorbt 一貫採用的 向量化面板 + Numba JIT 路線——先在 NumPy 上表達訊號與目標倉位,再把熱路徑編譯成 @njit kernel。ChidoriBT 在此基礎上,針對台股實務(整數股、統一資金池、期貨保證金)與自建系統整合(RL slow path、批量 prange 掃描)做專項擴展,而非重新發明回測範式。

vectorbt 在指標分析、互動式研究與 Portfolio.from_signals 等場景非常成熟;ChidoriBT 則專注於「已固定 panel/軌跡 → 大量掃參 → 嵌入自有 gym/ledger」這條管線。兩者可以並用,benchmark 僅供同場景參考,見 docs/benchmark_paths.md


設計哲學(千鳥)

千鳥(Chidori) — 精準、迅速,如閃電貫穿黑暗。

  • 向量化優先:訊號、目標倉位、權益曲線以 (T, N) / (T,) 陣列流動,避免 Python 逐筆迴圈
  • JIT 編譯熱路徑:成交模擬、再平衡、批量掃描走 numba_kernels.py
  • 雙路徑依情境取捨:要審計走 slow path;要掃參走 fast path(見下表)
  • 可嵌入自建系統fast=False 逐步 callback;mode="target" 對接 RL 與期貨帳務

依情境的效能設計

同一套 panel 資料,依任務選不同執行路徑——不是「永遠比較快」,而是在對的場景做對的取捨

應用情境 API / 模式 做了什麼 適合
參數粗篩 ParamScanner.scan_fast() / run(fast=True) Numba kernel 只算 final_equity;不建 fills、orders、逐筆 log 數百~數千組參數 grid search
批量並行掃描 batch_scan_* + prange 多組參數在 Numba prange 內並行,共用同一 (T,N) panel 多核 CPU 上跑大量組合
完整審計 scan_full() / run(fast=False) Python 事件驅動,產出 trades / fills / audit log 策略驗證、對帳、除錯
RL / ML 逐步推論 run(fast=False) + MultiAssetStrategy.next() 每個 t 可呼叫 外部 model.predict PPO 等需與 gym 一致的 slow path
等權再平衡 mode="rebalance" + fast 向量化權重 → Numba 再平衡 loop 因子/配置型策略
多空/期貨目標倉位 mode="target" + fast / slow slow:PortfolioLedger / FuturesPortfolioLedger;fast:run_target_rebalance_looprun_futures_target_loop 自建 gym 軌跡重放、保證金掃參

Numba 並行:若環境變數 NUMBA_NUM_THREADS=1(IDE 常見),import chidoribt.benchmark 會自動改為 CPU 核心數。


0.4.x:期貨回測擴展(自建量化系統)

針對已有 gym + 期貨 ledger 的自建系統,ChidoriBT 提供可對照的 fast path,讓「軌跡已固定、反覆掃 commission / margin 參數」不必每輪重跑 env.step()

模組 職責
PositionIntent / action_codec PPO 離散動作 → 目標倉位(7 / 13 檔)
panel.extra + AssetMeta instrument_typemultipliermargin_ratioroll_flagclose_next_month
FuturesPortfolioLedger Python golden reference(保證金、換月語意)
run_futures_target_loop Numba fast path;instrument_type=1 自動切換
batch_scan_target_futures 期貨批量 prange 掃描
parity 測試 Numba vs Python 逐日一致(rtol 1e-6)

parity 測試見 tests/test_futures_numba_parity.pytests/test_chidoribt_parity.py;範例見 examples/futures_target_scan.py


與 vectorbt 的定位差異

兩者都善用向量化與 Numba;差異在預設解決的問題不同:

面向 vectorbt ChidoriBT
強項 豐富 analytics、指標、互動研究、from_signals 工作流 統一資金池多資產、台股整數股、內建 ParamScanner prange 掃描
批量掃參 需自行組裝迴圈或 Portfolio 廣播 scan_fast / batch_scan_* 內建,只回傳淨值矩陣
ML / RL 以向量化訊號為主 fast=False 事件驅動 + mode="target" 軌跡重放
期貨保證金 / 換月 需自行建模 mode="target" + 期貨 Numba kernel(0.4.x)

同場景 benchmark(100 支台股 × 726 日、子母線策略;vectorbt 以 Portfolio.from_signals 對齊),ChidoriBT 在「只算淨值的批量掃描」上記憶體與耗時較省——詳見 docs/benchmark_paths.md。此結果反映任務定義不同(fast 不算 trades),不代表 vectorbt 在通用分析上較弱。


安裝

Python ≥ 3.9,< 3.13

pip install "chidoribt[benchmark]"

快速開始:參數掃描

典型流程:clone repo → 準備資料 CSV → 載入面板 → JIT 暖機 → 掃描參數

pip install chidoribt 只安裝套件本身;以下範例腳本在 repo 的 **examples/** 目錄,需先 git clone 取得。

git clone https://github.com/backtestdog/chidoribt.git
cd chidoribt
pip install -e ".[benchmark]"
cd examples

範例檔案一覽(examples/

檔案 是否在 repo 內 用途
chidoribt.prepare_data ✅ pip 內建 準備測試資料(CSV / DB / 合成)
examples/prepare_data.py 同上 CLI 入口(向後相容)
examples/test_scan.py 240 組合批量掃描效能測試
examples/test.py 單組 fast 淨值篩選效能測試
examples/test.ipynb 同上流程的 Notebook 版(支援 Google Colab)
examples/futures_target_scan.py 期貨多空目標倉位批量掃描(mode="target"
examples/data/panel.csv ❌ 本地產生 測試用 OHLCV 長表(執行 prepare_data.py 後才會出現)

1. 準備測試資料 — examples/prepare_data.py

CSV 為 stacked 長表,欄位:stock_id, date, Open, High, Low, Close, Volume

無外部資料(產生合成 OHLCV,建議首次使用):

cd examples
python prepare_data.py --source synthetic --export data/panel.csv

合成資料會嵌入母子/吞噬型態,確保 ChildParentStrategy 有進場訊號。若 final_equity 等於初始資金(如 1,000,000),代表資料中無有效訊號,請重新執行上述指令產生 CSV。

已有 CSV

python prepare_data.py --source csv --csv /path/to/your.csv --export data/panel.csv

從 PostgreSQL 一次性匯出(可選;需自行設定 DB_HOST 等環境變數或 .env):

python prepare_data.py --source db --export data/panel.csv

2. 執行測試

**examples/test_scan.py** — 240 組合批量掃描:

python test_scan.py

**examples/test.py** — 單組 fast 篩選:

python test.py

examples/test.ipynb — Notebook 版(含 Google Colab):

在 Colab 開啟(從上而下執行各儲存格;首格會 pip install "chidoribt[benchmark]"

兩支測試腳本皆透過 prepare_panel(source="auto") 載入:有 data/panel.csv 就用 CSV,否則嘗試 DB。也可用環境變數覆寫:

set CHIDORI_DATA_SOURCE=csv
set CHIDORI_DATA_CSV=data/panel.csv
python test_scan.py

3. 程式碼對照 — examples/test_scan.py

# examples/test_scan.py
from chidoribt.benchmark import (
    DEFAULT_SCAN_GRID,
    bench_param_scan,
    print_bench_report,
    print_numba_info,
    warmup_numba_kernels,
)
from chidoribt.prepare_data import prepare_panel

print_numba_info()
panel, meta = prepare_panel(source="auto", stocks=100)
warmup_numba_kernels(panel)

timer, summary = bench_param_scan(
    panel=panel, n=3,
    initial_cash=1_000_000, max_positions=20,
    **DEFAULT_SCAN_GRID,
)
print_bench_report(timer, extra={
    "n_combos": summary["n_combos"],
    "best_final_equity": f"{summary['best_final_equity']:,.0f}",
})

4. 程式碼對照 — examples/test.py

# examples/test.py
from chidoribt.benchmark import (
    bench_single_fast,
    print_bench_report,
    print_numba_info,
    warmup_numba_kernels,
)
from chidoribt.prepare_data import prepare_panel

print_numba_info()
panel, meta = prepare_panel(source="auto", stocks=100)
warmup_numba_kernels(panel, stop_loss=0.05, take_profit=0.10)

timer, summary = bench_single_fast(
    panel=panel, n=3,
    hold_days=5, stop_loss_pct=0.05, take_profit_pct=0.10, trade_size=50_000,
    initial_cash=1_000_000, max_positions=20,
)
print_bench_report(timer, extra={"final_equity": f"{summary['final_equity']:,.0f}"})

DEFAULT_SCAN_GRID(240 組合,test_scan.py 使用):hold [3,5,7,10,15] × sl [0.03…0.10] × tp [0.08…0.20] × trade_size [20k,30k,50k]

scan_fast vs scan_full

模式 方法 回傳
fast scan_fast() / run_single_fast() final_equity(極速篩選)
full scan_full() / run_single_full() 含 sharpe、equity_curve、trades 等完整輸出

自訂策略

以下為程式碼片段範例(需自行建立 .py 檔或於 notebook 中使用)。

# my_strategy.py(範例,需自行建立)
import pandas as pd
from chidoribt.strategies.base import MultiAssetStrategy
from chidoribt.core import PanelData

class MyStrategy(MultiAssetStrategy):
    def init(self):
        pass

    def next(self, timestamp: pd.Timestamp, panel: PanelData, t: int) -> dict[str, bool]:
        """每個交易日回傳 {stock_id: True/False} 進場訊號。"""
        signals = {}
        for i, stock_id in enumerate(panel.stock_ids):
            close = panel.close[t, i]
            ma20  = panel.close[max(0, t-20):t, i].mean() if t >= 20 else close
            if close > ma20:
                signals[stock_id] = True
        return signals

搭配 ChidoriBT 執行完整回測:

# run_backtest.py(範例,需自行建立)
from chidoribt import ChidoriBT

bt = ChidoriBT(data=panel, strategy=MyStrategy, cash=1_000_000, commission=0.001425)
result = bt.run()
print(result["metrics"])

執行模式

模式 參數 說明
JIT 加速(預設) fast=True Numba JIT,最快
事件驅動 fast=False 含完整 fills/orders;支援 ML 插入
等權再平衡 mode="rebalance" 每日等權分配;ParamScanner(mode="rebalance") 可批量掃描
目標倉位多空 mode="target" PositionIntent 多空;現股 / 期貨皆支援 fast path;RL 見下方文件
# 執行模式範例(程式碼片段)
result = bt.run(mode="portfolio", fast=True)   # 預設
result = bt.run(mode="portfolio", fast=False)  # audit log + ML
result = bt.run(mode="rebalance", fast=True)   # 等權再平衡
result = bt.run(mode="target", fast=False)     # RL / 多空 slow path(逐步 audit)
result = bt.run(mode="target", fast=True)      # 多空 / 期貨 Numba fast path

多空/期貨回測(mode="target"

panel.extra 帶入期貨 metadata(皆為 (T, N) 矩陣)後,fast path 自動切換期貨保證金 kernel:

# futures_scan.py(節錄自 examples/futures_target_scan.py)
import numpy as np
from chidoribt import ParamScanner, PanelData
from chidoribt.core.futures_margin import FuturesMarginSettings

extra = {
    "instrument_type":  np.ones((T, N), dtype=np.int8),    # 1 = 期貨,0 = 現股
    "multiplier":       np.full((T, N), 50.0),              # 契約乘數
    "margin_ratio":     np.full((T, N), 0.08),              # 保證金比例
    "roll_flag":        roll_mask,                          # 換月日
    "close_next_month": next_month_close,                   # 次月收盤價
}
panel = PanelData.from_dataframe(df, extra=extra)

# 目標倉位矩陣:target_side (T,N) ∈ {1, -1, 0}、target_pct (T,N) ∈ [0, 1]
scanner = ParamScanner(
    panel, mode="target",
    target_side=target_side, target_pct=target_pct, trade_mask=trade_mask,
    margin_settings=FuturesMarginSettings().to_numba_vector(),
    initial_cash=2_000_000, commission=0.0005,
)
df_result = scanner.scan_fast(pct_scale=[0.4, 0.6, 0.8, 1.0])
  • 換月roll_flag[t,i]=True 時近月平倉,若 close_next_month[t,i] 有效且現金足夠手續費,以次月價同口數重建。
  • 單次回測ChidoriBT(panel, strategy=..., fast=True).run(mode="target", margin_settings=...),策略以 PositionIntent 表達目標倉位。
  • RL 整合:以 actions_to_intents 將 PPO 離散動作(7 / 13 檔)轉成 PositionIntent,或匯出軌跡後走 fast path 掃參數。
  • Parity 保證:Numba kernel 與 FuturesPortfolioLedger(Python golden reference)逐日一致(rtol 1e-6)。

詳細路徑與 benchmark 說明見 docs/benchmark_paths.md

回傳欄位: equity_curvetradesfills(fast=False)、metrics(total_return、sharpe、max_drawdown、win_rate、n_trades)


台股費率說明

方向 費用
買進 手續費 0.1425%
賣出 手續費 0.1425% + 證交稅 0.3%

目前 commission=0.001425 為對稱費率(benchmark 對照用),尚未模擬賣出證交稅


套件架構

chidoribt/
├── benchmark.py             # Numba 設定、掃描基準工具(DB 載入為可選)
├── engine.py                # 主引擎
├── action_codec.py          # PPO 離散動作 → PositionIntent
├── core/
│   ├── panel.py             # PanelData(T×N NumPy 面板 + extra metadata)
│   ├── numba_kernels.py     # @njit kernel(現股 / 期貨 target loop、批量掃描)
│   ├── scanner.py           # ParamScanner(批量 prange 掃描;mode="target" 支援期貨)
│   ├── intent.py            # PositionIntent / Side / HoldIntent
│   ├── asset_meta.py        # 從 panel.extra 解析期貨 metadata
│   ├── portfolio_ledger.py  # 現股多空帳本(slow path)
│   ├── futures_margin.py    # FuturesMarginSettings(保證金常數向量)
│   └── futures_ledger_ref.py# 期貨帳本 Python golden reference
├── adapters/                # ledger / panel_futures / trajectory_export 等
├── strategies/              # MultiAssetStrategy + 範例策略
├── analytics/               # 績效指標
└── data/                    # PostgreSQL / Mini-TSDB 載入

ML 模型整合(fast=False

事件驅動模式(fast=False)在每個時步呼叫 strategy.next(),適合在回測迴圈內嵌入 ML 推論——這與 Numba fast path「一次跑完整段 (T,N) 陣列」的設計互補。以下為程式碼片段範例(需自行建立 .py 檔)。

# ml_strategy.py(範例,需自行建立)
import numpy as np
from chidoribt import ChidoriBT
from chidoribt.strategies.base import MultiAssetStrategy
from chidoribt.core import PanelData

class MLStrategy(MultiAssetStrategy):
    def __init__(self, model, params=None):
        super().__init__(params or {})
        self.model = model

    def init(self):
        self.window = 20

    def next(self, timestamp, panel: PanelData, t: int) -> dict[str, bool]:
        if t < self.window:
            return {}
        window_close = panel.close[t - self.window:t, :]
        returns = np.diff(window_close, axis=0) / (window_close[:-1] + 1e-9)
        X = np.hstack([
            returns.mean(axis=0, keepdims=True).T,
            returns.std(axis=0, keepdims=True).T,
            (returns[-5:].sum(axis=0, keepdims=True)).T,
        ])
        probs = self.model.predict_proba(X)[:, 1]
        return {panel.stock_ids[i]: True for i, p in enumerate(probs) if p > 0.6}

bt = ChidoriBT(
    data=panel,
    strategy=MLStrategy(model=trained_model),
    cash=1_000_000,
    commission=0.001425,
    fast=False,   # 必須使用事件驅動模式
)
result = bt.run()

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chidoribt-0.4.3.tar.gz (109.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chidoribt-0.4.3-py3-none-any.whl (112.2 kB view details)

Uploaded Python 3

File details

Details for the file chidoribt-0.4.3.tar.gz.

File metadata

  • Download URL: chidoribt-0.4.3.tar.gz
  • Upload date:
  • Size: 109.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for chidoribt-0.4.3.tar.gz
Algorithm Hash digest
SHA256 8b99829e75925708e33790b420fe2203d89d5c40a435352ae343a0696f5326a2
MD5 baad84ecf4ada50752329ea3dfdbea7b
BLAKE2b-256 9ca2a526a2682fe91a9f7d00710bd301606729df42f22519f24d750606cd2ab7

See more details on using hashes here.

File details

Details for the file chidoribt-0.4.3-py3-none-any.whl.

File metadata

  • Download URL: chidoribt-0.4.3-py3-none-any.whl
  • Upload date:
  • Size: 112.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for chidoribt-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 84af9edaec274b2a47d23d4b7f80501738911f77d4b72e2a7fab24694a7e2fa8
MD5 e35c23805552feccd49ba9b39fe12b8f
BLAKE2b-256 849f4d79f78045c769874ceaae0a9a59bb6cb4d4ce8c6ed6ef12055a8859dfaf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page