Metadata-Version: 2.4
Name: kestrel-fbt
Version: 0.1.0
Summary: 国内期货事件驱动回测框架（L1/L5 Tick + Bar，多合约，可配置撮合策略）
License-Expression: MIT
Project-URL: Homepage, https://github.com/yourusername/KestrelFBT
Project-URL: Source, https://github.com/yourusername/KestrelFBT
Project-URL: Bug Tracker, https://github.com/yourusername/KestrelFBT/issues
Keywords: futures,backtest,quant,trading,tick,china
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas>=2.0.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: pyarrow>=12.0.0
Dynamic: license-file

# KestrelFBT — 国内期货回测框架

基于 Python 的事件驱动期货回测系统，支持 L1（普通行情）和 L5（五档深度）数据，
覆盖国内全交易所（SHFE / DCE / CZCE / CFFEX / INE / GFEX）。

---

## 快速开始

### 安装依赖

```bash
pip install -r requirements.txt
```

### 运行示例

```bash
# Bar 策略（双均线）
python examples/demo_strategy.py

# Tick 策略（L1 ask1 动量）
python examples/demo_tick_strategy.py

# 完整仓位管理（止盈/止损/移动止损）
python examples/example_position_mgmt.py

# 多合约价差策略（rb + hc）
python examples/example_multi_contract.py

# L5 walk_book 冲击成本对比
python examples/example_l5_walkbook.py
```

---

## 数据源

### 目录结构

```
{data_root}/
├── FUTURES_L1_TICK_ALL/              # L1 行情，覆盖全交易所
│   └── {YYYYMMDD}/
│       ├── DAY/{instrument}_day.csv
│       └── NIGHT/{instrument}_night.csv
├── FUTURES_L5_TICK_SHFE/             # L5 深度行情，按交易所分目录
├── FUTURES_L5_TICK_DCE/
├── FUTURES_L5_TICK_CZCE/
├── FUTURES_L5_TICK_CFFEX/
├── FUTURES_L5_TICK_INE/
└── FUTURES_L5_TICK_GFEX/
```

### CSV 格式（30 列，无表头）

| 列 | 字段 | 说明 |
|----|------|------|
| 0 | instrument | 合约代码 |
| 1 | date | 自然日 YYYYMMDD |
| 2 | time | HHMMSSMMM（如 090000000） |
| 3 | turnover | 当日累计成交额 |
| 4 | volume | 当日累计成交量（session 内重置）|
| 5 | last_price | 最新价 |
| 6 | open_interest | 持仓量 |
| 7 | high_price | 当日最高 |
| 8 | low_price | 当日最低 |
| 9 | timestamp | Unix 微秒时间戳 |
| 10-14 | bid1-5 | 买一到买五价格 |
| 15-19 | ask1-5 | 卖一到卖五价格 |
| 20-24 | bid1-5_vol | 买一到买五挂量 |
| 25-29 | ask1-5_vol | 卖一到卖五挂量 |

> L1 文件中 bid2-5 / ask2-5 列恒为 0

### 日期约定

```
文件夹日期 = 自然日（数据物理发生的日期）

交易日 T 的数据 =
  夜盘：{T 的上一个自然日}/NIGHT/   ← 物理发生在上一自然日晚
  日盘：{T}/DAY/

示例：交易日 20250103 = 20250102/NIGHT/ + 20250103/DAY/
```

---

## 基本用法

### 1. 编写策略

继承 `BaseStrategy`，重写需要的回调：

```python
from kestrel.strategy.base import BaseStrategy
from kestrel.data.schema import TickData, BarData

class MyStrategy(BaseStrategy):
    name = "MyStrategy"

    def on_start(self): ...              # 回测开始，初始化
    def on_day_start(self, date): ...    # 每日开始
    def on_tick(self, tick: TickData): ... # 每条 tick
    def on_bar(self, bar: BarData): ...  # 每根 K 线（需配置 bar_periods）
    def on_fill(self, fill_data): ...    # 成交回报
    def on_reject(self, reject_data): ...# 拒单通知
    def on_day_end(self, date): ...      # 每日结束
    def on_stop(self): ...               # 回测结束
```

### 2. 下单接口

```python
# 开仓（均返回 order_id）
order_id = self.buy_open(instrument, price, volume, order_type="LIMIT")
order_id = self.sell_open(instrument, price, volume, order_type="MARKET")

# 平仓
order_id = self.sell_close(instrument, price, volume, order_type="LIMIT")
order_id = self.buy_close(instrument, price, volume, close_today=False)

# 撤单
self.cancel(order_id)

# order_type: "LIMIT"（限价）或 "MARKET"（市价/对手价）
```

### 3. 查询接口

```python
tick  = self.get_last_tick("rb2501")   # 最新 tick
pos   = self.get_position("rb2501")    # 持仓（含多空手数、均价）
eq    = self.get_equity()              # 总权益
cash  = self.get_cash()                # 可用资金
```

### 4. 配置回测

```python
from kestrel.engine.backtest import BacktestEngine, BacktestConfig
from kestrel.engine.matching import ContractInfo, FillPolicy

config = BacktestConfig(
    instruments     = ["rb2501"],           # 订阅合约列表
    start_date      = 20250102,             # 回测起始交易日
    end_date        = 20250228,             # 回测结束交易日
    initial_capital = 500_000.0,            # 初始资金（元）
    contracts       = [ContractInfo(...)],  # 合约规格（见下方）
    use_l5          = False,                # True=L5数据，False=L1数据
    fill_policy     = FillPolicy(...),      # 撮合策略（见下方）
    bar_periods     = [1, 5],              # K线周期（分钟），None=不合成K线
    data_root       = "/mnt/nvme1n1",       # 数据根目录
    output_path     = "./results/out.json", # 结果输出路径（None=不输出）
)

engine = BacktestEngine(config, MyStrategy())
report = engine.run()
```

### 5. 合约规格

```python
ContractInfo(
    instrument        = "rb2501",   # 合约代码
    exchange          = "SHFE",     # 交易所
    multiplier        = 10,         # 合约乘数（元/点）
    price_tick        = 1.0,        # 最小变动价位
    margin_rate       = 0.08,       # 保证金率（按合约价值比例）
    commission_per_lot = 10.0,      # 手续费（元/手），与 commission_rate 二选一
    commission_rate   = 0.0,        # 手续费率（按成交额比例）
)
```

常见合约规格参考 `config/contract_specs.json`（含乘数和最小变动价，不含保证金/手续费）。

### 6. 撮合策略

```python
FillPolicy(
    # 限价单成交模式
    limit_fill_mode           = "next_tick",  # 默认：下一tick才成交（最保守）
                                              # "price_cross"：价格穿越即成交
                                              # "volume_participation"：按成交量参与比例

    volume_participation_rate = 0.3,          # 仅 volume_participation 模式有效

    # 市价单 / 对手价成交模式
    market_fill_mode          = "next_tick",  # 默认：下一tick对手盘价格成交
                                              # "current_tick"：当前快照价格（有预见性偏差）

    market_slippage_ticks     = 1,            # 额外滑点跳数（>=0）

    # L5 模式：按五档深度逐档消耗，量化市场冲击成本
    # L1 模式下此参数自动忽略
    walk_book                 = True,
)
```

---

## 数据模型

### TickData

```python
tick.instrument     # 合约代码
tick.trading_date   # 交易日 YYYYMMDD
tick.time           # HHMMSSMMM
tick.timestamp      # Unix 微秒
tick.last_price     # 最新价
tick.delta_volume   # 本 tick 新增成交量（差分后）
tick.bid1           # 买一价（L1/L5 均有）
tick.ask1           # 卖一价
tick.bid1_vol       # 买一量
tick.ask1_vol       # 卖一量
tick.bid2-5         # 买二到买五（仅 L5 有效）
tick.ask2-5         # 卖二到卖五（仅 L5 有效）
tick.is_l5()        # 是否包含五档深度
tick.ask_prices()   # [ask1, ask2, ..., ask5]
tick.ask_vols()     # [ask1_vol, ..., ask5_vol]
```

### BarData

```python
bar.instrument      # 合约代码
bar.trading_date    # 交易日
bar.bar_time        # Bar 开始时间 HHMMSSMMM
bar.period_min      # K 线周期（分钟）
bar.open / high / low / close
bar.volume          # 该 Bar 内成交量
```

### InstrumentPosition

```python
pos = self.get_position("rb2501")
pos.long_volume     # 多头持仓手数
pos.short_volume    # 空头持仓手数
pos.long_avg_price() # 多头均价
pos.short_avg_price()# 空头均价
pos.floating_pnl()  # 浮动盈亏
```

---

## 回测报告

`engine.run()` 返回 dict，同时（若配置了 `output_path`）写入 JSON 文件：

```json
{
  "config": { "instruments": [...], "start_date": ..., ... },
  "metrics": {
    "total_return": 0.0512,
    "annual_return": 0.1823,
    "sharpe_ratio": 1.42,
    "calmar_ratio": 2.31,
    "max_drawdown": 12500.0,
    "max_drawdown_pct": 0.025,
    "win_rate": 0.54,
    "profit_factor": 1.8,
    "total_commission": 1200.0,
    "total_trades": 120,
    "total_turnover": 8500000.0
  },
  "equity_curve": [
    { "date": 20250102, "equity": 502000, "daily_pnl": 2000, ... },
    ...
  ]
}
```

控制台打印摘要：

```python
from kestrel.analytics.report import print_summary
print_summary(report)
```

---

## 架构概览

```
BacktestConfig
    └─ BacktestEngine
          ├─ DataFeed          ← 多合约 tick 归并回放 + Bar 合成
          ├─ EventEngine        ← 同步事件总线
          ├─ MatchingEngine     ← 撮合引擎（FillPolicy 可配置）
          │    └─ ContractInfo  ← 合约规格
          ├─ Portfolio          ← 持仓 + 逐日盯市结算
          └─ BaseStrategy       ← 用户策略（可插拔）
                                   ↑ on_tick / on_bar / on_fill
```

**事件流（每个 tick）**：

```
DataFeed → TickData
  → MatchingEngine.on_tick()    先撮合上一 tick 的挂单
  → Portfolio.on_tick()         更新浮动盈亏
  → Strategy.on_tick()          触发策略逻辑
  → Strategy.on_bar()           触发已完成的 Bar
  → EventEngine.process_all()   处理 ORDER / FILL / REJECT
```

---

## 注意事项

1. **L1 vs L5 互斥**：两者代表不同的实盘账户类型，不可混用。
   用 L5 数据回测但实盘只有 L1 会高估信息优势。

2. **撮合近似性**：所有基于快照数据的成交模拟都是近似。
   `FillPolicy` 的默认参数（`next_tick` + 1 跳滑点）偏保守。
   建议用实盘成交数据校准滑点参数。

3. **成交量累计**：CSV 中的 `volume` 是 session 内累计值。
   框架内部已自动做差分，`tick.delta_volume` 即为本 tick 新增成交量。
   DAY/NIGHT session 切换时差分基准自动重置。

4. **交易日归属**：夜盘数据文件夹日期为自然日，但在框架内已映射为下一交易日。
   策略代码中的 `tick.trading_date` 和 `on_day_start(date)` 始终是交易日。

5. **多合约时间对齐**：多合约 tick 按 `timestamp`（微秒）全局排序后串行回放，
   保证跨合约策略（套利）的时间一致性和结果确定性。

6. **参数优化**：如需跑多组参数，使用 `multiprocessing.Pool` 在多进程中
   各自创建独立的 `BacktestEngine`，不要在引擎内部使用多线程。
