回测基础概念:回测流程、标签生成逻辑与常见口径

回测(Backtesting)是量化策略研发的核心验证环节,其本质是用历史数据模拟策略执行过程,评估策略的收益、风险与稳定性。标签(Label)则是回测中用于衡量策略表现的基本单元——每一次交易、每一个持仓周期都会产生一个标签,标签的口径定义直接决定了回测结果的可靠性与可复现性。

2.1 回测标准流程

一个完整的回测流程通常包含以下五个阶段,每个阶段都依赖明确的输入与输出,形成闭环迭代:

阶段 核心任务 关键输出
1. 数据准备 清洗、对齐多源行情数据(Tick / 1min / 日线),处理缺失值与复权 标准化 OHLCV 数据集
2. 信号生成 基于因子或规则计算买卖信号,确定开仓/平仓时机 信号时间戳序列
3. 交易模拟 按信号执行虚拟下单,考虑滑点、手续费、涨跌停限制 成交明细表(Trade Log)
4. 标签生成 根据持仓期或事件窗口,为每笔交易计算收益、风险等标签 标签数据集(Label Table)
5. 绩效归因 汇总标签统计指标(夏普、最大回撤、胜率),进行分组对比 回测报告
关键认知: 标签生成是连接“交易模拟”与“绩效归因”的桥梁。口径不一致的标签会导致归因结果失真,甚至掩盖策略的真实缺陷。
回测流程闭环图 回测流程闭环 数据准备 清洗·对齐·复权 信号生成 因子·规则·时机 交易模拟 滑点·手续费·限价 标签生成 口径·计算·存储 绩效归因 夏普·回撤·胜率

2.2 标签生成逻辑

标签生成的核心是将交易记录转化为可统计的指标样本。每笔交易至少包含以下字段:

  • 交易 ID:唯一标识一次开平仓周期。
  • 开仓时间 / 平仓时间:决定持仓时长与事件窗口。
  • 开仓价格 / 平仓价格:用于计算收益率。
  • 持仓方向:多头(Long)或空头(Short)。
  • 成交量:用于计算盈亏金额。

标签计算的基本公式(以多头为例):

收益率 = (平仓价格 - 开仓价格) / 开仓价格
盈亏金额 = 收益率 × 开仓市值
持仓期 = 平仓时间 - 开仓时间(通常以交易日或自然日计)
实践建议: 在生成标签时,务必保留原始交易 ID 与时间戳,以便后续进行口径校准与数据溯源。不要只存储聚合后的统计值。

2.3 常见标签口径

标签口径定义了“如何计算一个标签的数值”。同一笔交易,使用不同口径会得到不同的标签值。以下是三种最常用的口径:

口径类型 定义 适用场景 注意事项
持仓期口径 以开仓到平仓的完整区间计算收益 中长线趋势策略、日频以上策略 需处理隔夜跳空、分红除权
事件窗口口径 以事件发生前后固定窗口(如 T+1 ~ T+5)计算收益 事件驱动策略、因子 IC 测试 窗口长度需统一,避免未来信息
滚动窗口口径 以固定时间长度(如 20 个交易日)滚动计算收益 动量策略、时序因子计算 窗口重叠会导致样本非独立

2.4 持仓期口径 vs 事件窗口口径

这两种口径是回测中最容易混淆的概念,它们的核心差异在于时间锚点:

  • 持仓期口径:锚点是实际交易,标签反映的是策略真实持仓的盈亏。
  • 事件窗口口径:锚点是事件发生时刻,标签反映的是事件发生后市场的平均反应。

举例说明:

策略在 2024-01-05 开仓买入股票 A,2024-01-12 平仓卖出。
- 持仓期口径标签:收益率 = (平仓价 - 开仓价) / 开仓价,持仓期 = 7 天。
- 事件窗口口径标签:以 2024-01-05 为事件日,计算 T+1 到 T+5 的累计收益率,
  即使策略在 T+3 已经平仓,窗口仍计算到 T+5。
常见陷阱: 使用事件窗口口径时,如果窗口内包含平仓后的数据,会导致标签包含“未来信息”,使回测结果虚高。务必在生成标签时截断窗口至实际持仓结束。
持仓期 vs 事件窗口口径对比 持仓期口径 vs 事件窗口口径 T-2 T(开仓) T+3(平仓) T+5 T+7 持仓期口径:T ~ T+3 事件窗口口径:T+1 ~ T+5(含未来数据) 平仓点 事件窗口口径 可能引入未来信息

2.5 口径校准的必要性

在实际量化团队中,不同研究员可能使用不同的口径生成标签,导致以下问题:

  • 绩效不可比:A 研究员用持仓期口径计算夏普 1.5,B 研究员用事件窗口口径计算夏普 2.0,无法直接对比。
  • 因子失效误判:因子 IC 测试使用事件窗口口径,但策略实盘使用持仓期口径,导致因子在回测中有效、实盘中失效。
  • 数据管道混乱:标签生成逻辑分散在多个脚本中,口径定义不统一,维护成本高。
解决方案: 建立统一的标签口径规范,并通过 Kafka 消息管道将标签生成逻辑标准化、可追溯。这正是本课程后续章节的核心内容——用 Kafka 集成实现标签口径的实时校准与版本管理。

2.6 标签口径的元数据管理

为了支持口径校准,每个标签应携带以下元数据:

元数据字段 说明 示例值
label_version 标签口径版本号 v2.1.0
window_type 口径类型 holding_period / event_window
window_length 窗口长度(若适用) 5(交易日)
generated_at 标签生成时间戳 2024-06-15T10:30:00Z
source_trade_id 来源交易 ID trade_20240601_001

通过元数据,团队可以快速回溯某个标签的计算方式,并在口径变更时进行批量重算与对比。

工程落地: 在 Kafka 消息中,将标签元数据放在消息头(Header)中,标签数值放在消息体(Value)中。这样消费者可以根据 Header 中的版本号决定是否处理该消息,实现口径的灰度切换。