Metadata-Version: 2.4
Name: chidoribt
Version: 0.4.4
Summary: Multi-asset unified portfolio backtesting framework
License: MIT
Project-URL: Repository, https://github.com/backtestdog/chidoribt.git
Project-URL: Bug Tracker, https://github.com/backtestdog/chidoribt/issues
Keywords: backtesting,finance,trading,portfolio,quantitative
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23.0
Requires-Dist: pandas>=1.5.0
Provides-Extra: numba
Requires-Dist: numba>=0.56; extra == "numba"
Requires-Dist: scipy>=1.10; extra == "numba"
Provides-Extra: postgres
Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
Provides-Extra: tsdb
Requires-Dist: mini-tsdb>=0.1.0; extra == "tsdb"
Provides-Extra: benchmark
Requires-Dist: numba>=0.56; extra == "benchmark"
Requires-Dist: scipy>=1.10; extra == "benchmark"
Requires-Dist: psycopg2-binary>=2.9; extra == "benchmark"
Requires-Dist: python-dotenv>=1.0; extra == "benchmark"
Provides-Extra: full
Requires-Dist: numba>=0.56; extra == "full"
Requires-Dist: scipy>=1.10; extra == "full"
Requires-Dist: mini-tsdb>=0.1.0; extra == "full"
Requires-Dist: psycopg2-binary>=2.9; extra == "full"
Requires-Dist: python-dotenv>=1.0; extra == "full"
Dynamic: license-file

# ChidoriBT ⚡

**High-performance multi-asset backtesting**

[English](#sec-en) · [繁體中文](#sec-zh)

[PyPI version](https://pypi.org/project/chidoribt/)
[Python 3.9+](https://pypi.org/project/chidoribt/)
[License: MIT](https://opensource.org/licenses/MIT)
[Numba](https://numba.pydata.org/)

<p align="center">
  <img src="https://tse2.mm.bing.net/th/id/OIP.tzQLNs50GB5WLS5qufp1gAAAAA?cb=thfc1falcon&rs=1&pid=ImgDetMain&o=7&rm=3" alt="Chidori" width="200"/>
</p>

---

## Quick navigation / 快速導覽

| Section | Description |
|---------|-------------|
| [**English**](#sec-en) | Full documentation in English |
| [**繁體中文**](#sec-zh) | 完整繁體中文說明 |

---

<a id="sec-en"></a>

## English

### Table of contents

- [Project overview](#en-overview)
- [Design philosophy](#en-philosophy)
- [Performance by scenario](#en-performance)
- [0.4.x futures extension](#en-futures)
- [vs vectorbt](#en-vectorbt)
- [Installation](#en-installation)
- [Quick start: parameter scan](#en-quickstart)
- [Custom strategies](#en-strategies)
- [Execution modes](#en-modes)
- [Taiwan equity fees](#en-fees)
- [Package layout](#en-layout)
- [ML integration](#en-ml)

---

<a id="en-overview"></a>

### Project overview

ChidoriBT is a multi-asset portfolio backtesting engine for **self-hosted quant systems**: it holds OHLCV and metadata in a `(T, N)` panel, runs cash-equity, long/short, and futures strategies on a shared capital pool, and supports RL trajectory replay plus large parameter sweeps.

The core design follows [vectorbt](https://github.com/polakowo/vectorbt)'s **vectorized panel + Numba JIT** approach—express signals and target positions in NumPy, then compile hot paths into `@njit` kernels. ChidoriBT extends this for **Taiwan equity practice** (integer shares, unified pool, futures margin) and **system integration** (RL slow path, batch `prange` scans), rather than reinventing the backtest paradigm.

> vectorbt excels at indicator analytics, interactive research, and `Portfolio.from_signals` workflows; ChidoriBT focuses on the pipeline **fixed panel / trajectory → mass parameter scan → embed in your gym / ledger**. Both can coexist; benchmarks are reference-only for comparable scenarios—see [docs/benchmark_paths.md](docs/benchmark_paths.md).

---

<a id="en-philosophy"></a>

### Design philosophy (Chidori)

> **Chidori (千鳥)** — precise and swift, like lightning through darkness.

- **Vectorization first**: signals, targets, and equity curves flow as `(T, N)` / `(T,)` arrays—no per-bar Python loops
- **JIT hot paths**: fill simulation, rebalancing, and batch scans in `numba_kernels.py`
- **Dual paths by scenario**: audit via slow path; parameter sweeps via fast path (see table below)
- **Embeddable**: `fast=False` step callbacks; `mode="target"` for RL and futures accounting

---

<a id="en-performance"></a>

### Performance by scenario

The same panel data can take different execution paths depending on the task—**not "always faster"**, but **the right trade-off for the right job**:

| Scenario | API / Mode | What it does | Best for |
|---------|-----------|---------|------|
| **Parameter screening** | `ParamScanner.scan_fast()` / `run(fast=True)` | Numba kernel computes **`final_equity` only**; no fills, orders, or per-bar log | Hundreds–thousands of grid combinations |
| **Batch parallel scan** | `batch_scan_*` + `prange` | Multiple parameter sets in **Numba `prange`**, shared `(T,N)` panel | Large sweeps on multi-core CPU |
| **Full audit** | `scan_full()` / `run(fast=False)` | Python event-driven; **trades / fills / audit log** | Strategy validation, reconciliation, debugging |
| **RL / ML step inference** | `run(fast=False)` + `MultiAssetStrategy.next()` | Each `t` can call **external model.predict** | PPO and gym-consistent slow path |
| **Equal-weight rebalance** | `mode="rebalance"` + fast | Vectorized weights → Numba rebalance loop | Factor / allocation strategies |
| **Long/short / futures targets** | `mode="target"` + fast / slow | slow: `PortfolioLedger` / `FuturesPortfolioLedger`; fast: `run_target_rebalance_loop` or `run_futures_target_loop` | Gym trajectory replay, margin sweeps |

**Numba threading**: if `NUMBA_NUM_THREADS=1` (common in IDEs), `import chidoribt.benchmark` auto-sets it to CPU core count.

---

<a id="en-futures"></a>

### 0.4.x: Futures extension

For systems with an existing **gym + futures ledger**, ChidoriBT provides a comparable fast path so repeated commission / margin sweeps on a fixed trajectory do not require re-running `env.step()` every round:

| Module | Role |
|------|------|
| `PositionIntent` / `action_codec` | PPO discrete actions → target position (7 / 13 levels) |
| `panel.extra` + `AssetMeta` | `instrument_type`, `multiplier`, `margin_ratio`, `roll_flag`, `close_next_month` |
| `FuturesPortfolioLedger` | Python golden reference (margin, roll semantics) |
| `run_futures_target_loop` | Numba fast path; auto-switches when `instrument_type=1` |
| `batch_scan_target_futures` | Futures batch `prange` scan |
| Parity tests | Numba vs Python day-by-day (rtol 1e-6) |

See `tests/test_futures_numba_parity.py`, `tests/test_chidoribt_parity.py`; example: `examples/futures_target_scan.py`.

---

<a id="en-vectorbt"></a>

### vs vectorbt

Both leverage vectorization and Numba; the difference is **which problem each defaults to solving**:

| Aspect | vectorbt | ChidoriBT |
|------|----------|-----------|
| Strengths | Rich analytics, indicators, interactive research, `from_signals` workflow | Unified pool multi-asset, TW integer shares, built-in `ParamScanner` `prange` scans |
| Batch sweeps | Assemble loops or Portfolio broadcasting yourself | Built-in `scan_fast` / `batch_scan_*`, returns equity matrix only |
| ML / RL | Vectorized signals primarily | `fast=False` event-driven + `mode="target"` trajectory replay |
| Futures margin / roll | Model yourself | `mode="target"` + futures Numba kernel (0.4.x) |

**Same-scenario benchmark** (100 TW stocks × 726 days, child-parent strategy; vectorbt via `Portfolio.from_signals`): ChidoriBT uses less memory and time on **equity-only batch scans**—details in [docs/benchmark_paths.md](docs/benchmark_paths.md). This reflects **different task definitions** (fast skips trades), not that vectorbt is weaker for general analysis.

---

<a id="en-installation"></a>

### Installation

**Python** ≥ 3.9, < 3.13

```bash
pip install "chidoribt[benchmark]"
```

---

<a id="en-quickstart"></a>

### Quick start: Parameter scan

Typical flow: **clone repo → prepare CSV → load panel → JIT warmup → scan parameters**.

> `pip install chidoribt` installs the package only; example scripts live in **`examples/`**—clone the repo first.

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

#### Example files (`examples/`)

| File | In repo | Purpose |
| -------------------------- | ---------- | ---------------------------------------- |
| `chidoribt.prepare_data` | ✅ pip built-in | Prepare test data (CSV / DB / synthetic) |
| `examples/prepare_data.py` | ✅ | Same CLI entry (backward compatible) |
| `examples/test_scan.py` | ✅ | 240-combo batch scan benchmark |
| `examples/test.py` | ✅ | Single fast equity filter benchmark |
| `examples/test.ipynb` | ✅ | Notebook version (Google Colab supported) |
| `examples/futures_target_scan.py` | ✅ | Futures target-position batch scan (`mode="target"`) |
| `examples/data/panel.csv` | ❌ local | Test OHLCV long table (created by `prepare_data.py`) |

#### 1. Prepare test data — `examples/prepare_data.py`

CSV is a stacked long table: `stock_id`, `date`, `Open`, `High`, `Low`, `Close`, `Volume`.

**No external data** (synthetic OHLCV, recommended first run):

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

Synthetic data embeds child-parent / engulfing patterns so `ChildParentStrategy` has entries. If `final_equity` equals initial cash (e.g. 1,000,000), there are no valid signals—regenerate the CSV.

**Existing CSV**:

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

**One-time PostgreSQL export** (optional; set `DB_HOST` etc. or `.env`):

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

#### 2. Run tests

**`examples/test_scan.py`** — 240-combo batch scan:

```bash
python test_scan.py
```

**`examples/test.py`** — single fast filter:

```bash
python test.py
```

**`examples/test.ipynb`** — Notebook ([Open in Colab](https://colab.research.google.com/github/backtestdog/chidoribt/blob/main/examples/test.ipynb))

Both scripts load via `prepare_panel(source="auto")`: uses `data/panel.csv` if present, else DB. Override with env vars:

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

#### 3. Code reference — `examples/test_scan.py`

```python
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. Code reference — `examples/test.py`

```python
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 combos): 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

| Mode | Method | Returns |
| ---- | ----------------------------------- | ----------------------- |
| fast | `scan_fast()` / `run_single_fast()` | `final_equity` only (fast screening) |
| full | `scan_full()` / `run_single_full()` | sharpe, equity_curve, trades, etc. |

---

<a id="en-strategies"></a>

### Custom strategies

Code snippets below—create your own `.py` or use in a notebook.

```python
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]:
        """Return {stock_id: True/False} entry signals per trading day."""
        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
```

Run full backtest with `ChidoriBT`:

```python
from chidoribt import ChidoriBT

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

---

<a id="en-modes"></a>

### Execution modes

| Mode | Parameter | Description |
| ---------- | ------------------ | --------------------------------------------- |
| JIT (default) | `fast=True` | Numba JIT, fastest |
| Event-driven | `fast=False` | Full fills/orders; **supports ML hooks** |
| Equal-weight rebalance | `mode="rebalance"` | Daily equal weight; `ParamScanner(mode="rebalance")` batch scan |
| Target long/short | `mode="target"` | `PositionIntent`; cash & futures fast path; RL docs below |

```python
result = bt.run(mode="portfolio", fast=True)    # default
result = bt.run(mode="portfolio", fast=False)   # audit log + ML
result = bt.run(mode="rebalance", fast=True)    # equal-weight rebalance
result = bt.run(mode="target", fast=False)      # RL / long-short slow path
result = bt.run(mode="target", fast=True)       # long-short / futures Numba fast path
```

#### Long/short / futures (`mode="target"`)

With futures metadata in `panel.extra` (all `(T, N)` matrices), fast path auto-selects the futures margin kernel:

```python
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 = futures, 0 = cash equity
    "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)

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**: `roll_flag[t,i]=True` closes front month; if `close_next_month[t,i]` is valid and cash covers fees, rebuild same size at next month price.
- **Single run**: `ChidoriBT(panel, strategy=..., fast=True).run(mode="target", margin_settings=...)`
- **RL**: `actions_to_intents` maps PPO discrete actions (7 / 13 levels) to `PositionIntent`, or export trajectory for fast parameter sweeps.
- **Parity**: Numba kernel matches `FuturesPortfolioLedger` day-by-day (rtol 1e-6).

See [docs/benchmark_paths.md](docs/benchmark_paths.md) for paths and benchmark notes.

**Return fields:** `equity_curve`, `trades`, `fills` (fast=False), `metrics` (total_return, sharpe, max_drawdown, win_rate, n_trades)

---

<a id="en-fees"></a>

### Taiwan equity fees

| Side | Fee |
| --- | ------------------------------ |
| Buy | Commission **0.1425%** |
| Sell | Commission **0.1425%** + tax **0.3%** |

Current `commission=0.001425` is symmetric (for benchmark parity); **sell-side securities tax is not modeled yet**.

---

<a id="en-layout"></a>

### Package layout

```
chidoribt/
├── benchmark.py             # Numba settings, scan benchmarks (optional DB load)
├── engine.py                # Main engine
├── action_codec.py          # PPO discrete actions → PositionIntent
├── core/
│   ├── panel.py             # PanelData (T×N NumPy panel + extra metadata)
│   ├── numba_kernels.py     # @njit kernels (cash / futures target, batch scan)
│   ├── scanner.py           # ParamScanner (batch prange; mode="target" futures)
│   ├── intent.py            # PositionIntent / Side / HoldIntent
│   ├── asset_meta.py        # Futures metadata from panel.extra
│   ├── portfolio_ledger.py  # Cash long/short ledger (slow path)
│   ├── futures_margin.py    # FuturesMarginSettings
│   └── futures_ledger_ref.py# Futures ledger Python golden reference
├── adapters/
├── strategies/
├── analytics/
└── data/
```

---

<a id="en-ml"></a>

### ML integration (`fast=False`)

Event-driven mode calls `strategy.next()` each step—embed ML inference inside the backtest loop. This complements the Numba fast path that runs the full `(T,N)` array at once.

```python
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()
```

---

<a id="sec-zh"></a>

## 繁體中文

**高效能多資產回測框架**

### 目錄

- [專案定位](#zh-overview)
- [設計哲學（千鳥）](#zh-philosophy)
- [依情境的效能設計](#zh-performance)
- [0.4.x 期貨回測擴展](#zh-futures)
- [與 vectorbt 的定位差異](#zh-vectorbt)
- [安裝](#zh-installation)
- [快速開始：參數掃描](#zh-quickstart)
- [自訂策略](#zh-strategies)
- [執行模式](#zh-modes)
- [台股費率說明](#zh-fees)
- [套件架構](#zh-layout)
- [ML 模型整合](#zh-ml)

---

<a id="zh-overview"></a>

### 專案定位

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

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

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

---

<a id="zh-philosophy"></a>

### 設計哲學（千鳥）

> **千鳥（Chidori）** — 精準、迅速，如閃電貫穿黑暗。

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

---

<a id="zh-performance"></a>

### 依情境的效能設計

同一套 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_loop` 或 `run_futures_target_loop` | 自建 gym 軌跡重放、保證金掃參 |

**Numba 並行**：若環境變數 `NUMBA_NUM_THREADS=1`（IDE 常見），`import chidoribt.benchmark` 會自動改為 CPU 核心數。

---

<a id="zh-futures"></a>

### 0.4.x：期貨回測擴展

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

| 模組 | 職責 |
|------|------|
| `PositionIntent` / `action_codec` | PPO 離散動作 → 目標倉位（7 / 13 檔） |
| `panel.extra` + `AssetMeta` | `instrument_type`、`multiplier`、`margin_ratio`、`roll_flag`、`close_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.py`、`tests/test_chidoribt_parity.py`；範例見 `examples/futures_target_scan.py`。

---

<a id="zh-vectorbt"></a>

### 與 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](docs/benchmark_paths.md)。此結果反映**任務定義不同**（fast 不算 trades），不代表 vectorbt 在通用分析上較弱。

---

<a id="zh-installation"></a>

### 安裝

**Python** ≥ 3.9, < 3.13

```bash
pip install "chidoribt[benchmark]"
```

---

<a id="zh-quickstart"></a>

### 快速開始：參數掃描

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

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

```bash
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，建議首次使用）：

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

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

**已有 CSV**：

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

**從 PostgreSQL 一次性匯出**（可選；需自行設定 `DB_HOST` 等環境變數或 `.env`）：

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

#### 2. 執行測試

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

```bash
python test_scan.py
```

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

```bash
python test.py
```

**`examples/test.ipynb`** — Notebook 版（含 Google Colab）：

[在 Colab 開啟](https://colab.research.google.com/github/backtestdog/chidoribt/blob/main/examples/test.ipynb)

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

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

#### 3–4. 程式碼對照

`test_scan.py` 與 `test.py` 的程式碼範例見上方 [English 快速開始](#en-quickstart) 第 3、4 節（程式碼以英文區塊為準，避免重複）。

`DEFAULT_SCAN_GRID`（240 組合）：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 等完整輸出 |

---

<a id="zh-strategies"></a>

### 自訂策略

以下為程式碼片段範例（需自行建立 `.py` 檔或於 notebook 中使用）。完整程式碼見 [English 自訂策略](#en-strategies) 區塊。

搭配 `ChidoriBT` 執行完整回測時，使用 `bt.run()` 並讀取 `result["metrics"]`。

---

<a id="zh-modes"></a>

### 執行模式

| 模式 | 參數 | 說明 |
| ---------- | ------------------ | --------------------------------------------- |
| JIT 加速（預設） | `fast=True` | Numba JIT，最快 |
| 事件驅動 | `fast=False` | 含完整 fills/orders；**支援 ML 插入** |
| 等權再平衡 | `mode="rebalance"` | 每日等權分配；`ParamScanner(mode="rebalance")` 可批量掃描 |
| 目標倉位多空 | `mode="target"` | `PositionIntent` 多空；現股 / 期貨皆支援 fast path |

`run()` 呼叫範例與 `mode="target"` 期貨 metadata 程式碼見 [English 執行模式](#en-modes) 區塊。

#### 多空／期貨回測（`mode="target"`）

- **換月**：`roll_flag[t,i]=True` 時近月平倉，若 `close_next_month[t,i]` 有效且現金足夠手續費，以次月價同口數重建。
- **單次回測**：`ChidoriBT(panel, strategy=..., fast=True).run(mode="target", margin_settings=...)`
- **RL 整合**：以 `actions_to_intents` 將 PPO 離散動作（7 / 13 檔）轉成 `PositionIntent`，或匯出軌跡後走 fast path 掃參數。
- **Parity 保證**：Numba kernel 與 `FuturesPortfolioLedger` 逐日一致（rtol 1e-6）。

詳細路徑與 benchmark 說明見 [docs/benchmark_paths.md](docs/benchmark_paths.md)。

**回傳欄位：** `equity_curve`、`trades`、`fills`（fast=False）、`metrics`

---

<a id="zh-fees"></a>

### 台股費率說明

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

目前 `commission=0.001425` 為對稱費率（benchmark 對照用），**尚未模擬賣出證交稅**。

---

<a id="zh-layout"></a>

### 套件架構

目錄結構見 [English Package layout](#en-layout) 區塊（與程式碼路徑相同）。

---

<a id="zh-ml"></a>

### ML 模型整合

事件驅動模式（`fast=False`）在每個時步呼叫 `strategy.next()`，適合在回測迴圈內嵌入 ML 推論——這與 Numba fast path「一次跑完整段 `(T,N)` 陣列」的設計互補。

完整 `MLStrategy` 範例見 [English ML integration](#en-ml) 區塊；`fast=False` 必須使用事件驅動模式。
