回测引擎设计
一个可信引擎的四个模块
Section titled “一个可信引擎的四个模块”回测引擎的可信度由四个显式建模的模块决定:成交、成本、延迟、资金。任何一块的简化都会让回测结果系统性偏向乐观——而偏乐观的回测比没有回测更危险。
| 假设 | 乐观程度 | 说明 |
|---|---|---|
| 收盘价成交 | 最乐观 | 信号在收盘才可得,实际只能次日成交——系统性前视 |
| 次日开盘价 | 基准 | 最小可行假设 |
| 次日 VWAP | 更真实 | 对容量较大的策略应使用 |
| 撮合限价单 | 最真实 | 需要订单簿数据,见限价订单簿 |
最低标准:信号时刻与成交时刻分离。用收盘价给"收盘信号"成交,是几乎所有新手回测的共同 bug。
成本不是常数,应随成交量占比非线性增长:
冲击项的常用形式为平方根律:, 为订单量, 为日均成交量。要点:
- 成交量占比 > 1% 时,冲击成本进入非线性区间,容量上限由此而来;
- 价差是时变的:刚暴跌的股票价差扩大数倍,见短期反转;
- A 股还有印花税等制度性成本,见A 股:T+1、涨跌停与融券。
- 延迟:信号计算 → 下单 → 成交的链路延迟,最小可行实现是"下一根 bar 成交 + 固定延迟惩罚";
- 资金与保证金:期货/杠杆策略必须显式跟踪保证金占用、可用资金与强平线,见期货:保证金、换月与限仓;
- 停牌与涨跌停:无法成交的日子要真实地"无法成交",见回测中的典型偏差。
import numpy as np
def fill_price(side: str, next_open: float, spread: float, q: float, adv: float, sigma: float, impact_coef: float = 0.5) -> float: """最小可行成交模型:次日开盘 + 半价差 + 平方根冲击""" half_spread = spread / 2 impact = impact_coef * sigma * np.sqrt(q / adv) return next_open * (1 + half_spread + impact) if side == "buy" \ else next_open * (1 - half_spread - impact)- 每笔模拟成交记录:时间、价格、信号价、假设的成本——事后审计与滑点分析的数据源,见监控与事故复盘;
- 回测输出应能回答:"这笔成交用了什么假设?"
量化中的用法
Section titled “量化中的用法”- 默认成交假设:次日开盘/VWAP + 半价差 + 平方根冲击;
- 容量上限按成交量占比 1–5% 设定,随策略频率调整;
- 每笔模拟成交可审计,成本假设可配置且记录在案。
- 用收盘价成交是最乐观的假设,应至少用次日开盘或 VWAP
- 成本模型应随成交量占比非线性增长(平方根律)
- 记录每笔模拟成交以便事后审计