KestrelFBT — 国内期货回测框架
基于 Python 的事件驱动期货回测系统,支持 L1(普通行情)和 L5(五档深度)数据, 覆盖国内全交易所(SHFE / DCE / CZCE / CFFEX / INE / GFEX)。
快速开始
安装依赖
pip install -r requirements.txt
运行示例
# 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,重写需要的回调:
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. 下单接口
# 开仓(均返回 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. 查询接口
tick = self.get_last_tick("rb2501") # 最新 tick
pos = self.get_position("rb2501") # 持仓(含多空手数、均价)
eq = self.get_equity() # 总权益
cash = self.get_cash() # 可用资金
4. 配置回测
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. 合约规格
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. 撮合策略
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
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
bar.instrument # 合约代码
bar.trading_date # 交易日
bar.bar_time # Bar 开始时间 HHMMSSMMM
bar.period_min # K 线周期(分钟)
bar.open / high / low / close
bar.volume # 该 Bar 内成交量
InstrumentPosition
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 文件:
{
"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, ... },
...
]
}
控制台打印摘要:
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
注意事项
-
L1 vs L5 互斥:两者代表不同的实盘账户类型,不可混用。 用 L5 数据回测但实盘只有 L1 会高估信息优势。
-
撮合近似性:所有基于快照数据的成交模拟都是近似。
FillPolicy的默认参数(next_tick+ 1 跳滑点)偏保守。 建议用实盘成交数据校准滑点参数。 -
成交量累计:CSV 中的
volume是 session 内累计值。 框架内部已自动做差分,tick.delta_volume即为本 tick 新增成交量。 DAY/NIGHT session 切换时差分基准自动重置。 -
交易日归属:夜盘数据文件夹日期为自然日,但在框架内已映射为下一交易日。 策略代码中的
tick.trading_date和on_day_start(date)始终是交易日。 -
多合约时间对齐:多合约 tick 按
timestamp(微秒)全局排序后串行回放, 保证跨合约策略(套利)的时间一致性和结果确定性。 -
参数优化:如需跑多组参数,使用
multiprocessing.Pool在多进程中 各自创建独立的BacktestEngine,不要在引擎内部使用多线程。
Metadata
Release files for kestrel-fbt 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kestrel_fbt-0.1.0.tar.gz | 34.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kestrel_fbt-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 71.7 kB
Release files / kestrel_fbt-0.1.0.tar.gz
| Download URL | kestrel_fbt-0.1.0.tar.gz |
|---|---|
| Size | 34.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3076ceeb98d1959c8c495fbc9029a279e305b227512574ab04732e46e8c9ce96
|
|
BLAKE2b-256 checksum How to use checksums |
b2c06880b65242021b28321e6d33875d8dc8ef965196ff0309d272f2d58c0d5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / kestrel_fbt-0.1.0-py3-none-any.whl
| Download URL | kestrel_fbt-0.1.0-py3-none-any.whl |
|---|---|
| Size | 37.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
51bb1dc2e51ce750093e4ea274c4b428cbecca3b7965a91df29e950dd5673ce9
|
|
BLAKE2b-256 checksum How to use checksums |
c3974ec69338c7fdcf1d83585bab6046ce67b1c8de345cecce75163cbad3ae77
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|