1. 课程导入

在金融研究尤其是量化策略开发中,数据是策略的“燃料”,但也是最容易被误用的环节。许多回测曲线漂亮、实盘却迅速失效的策略,根源往往不在模型本身,而在于研究阶段引入了未来信息,或训练数据与验证数据之间发生了泄漏。本课程围绕“防未来函数”与“防数据泄漏”两条主线,帮助研究者建立一套可落地、可审计的数据使用规范。

一、学习目标

完成本课程后,你应当能够独立完成以下任务:

  • 识别金融时间序列中常见的未来函数形态,包括前视偏差、幸存者偏差、财报披露时点错配等;
  • 理解数据泄漏在特征工程、标准化、缺失值填充、超参数搜索等环节的发生机制;
  • 掌握基于时间切分的训练集、验证集、测试集划分方法,并能正确使用滚动窗口与扩展窗口;
  • 能够为因子计算、标签构造、回测撮合编写符合“当时可得信息”约束的代码;
  • 建立数据血缘与实验记录意识,使研究过程可复现、可复盘、可审计。

核心判断标准:在任意一个历史时点 t,策略只能使用 t 时刻及之前已经真实可获得的数据。任何依赖 t 之后信息的计算,无论多隐蔽,都属于未来函数或数据泄漏。

二、核心概念

本课程反复出现的关键概念需要先建立清晰边界。

概念 含义 典型后果
未来函数 在 t 时刻的计算中使用了 t 之后才产生或才披露的数据 回测虚高,实盘无法复现
数据泄漏 训练阶段接触到了验证集或测试集的信息 模型评估过于乐观,泛化能力差
前视偏差 使用当前成分股、当前财务口径回溯历史 幸存者偏差、指数成分漂移失真
时点对齐 确保特征、标签、交易信号在时间轴上严格对齐 避免“用收盘价预测开盘价”类错误
滚动回测 按时间推进反复训练与验证,模拟真实研究节奏 更接近实盘,但计算成本更高

其中,未来函数与数据泄漏经常同时出现,但来源不同:前者多发生在单条样本内部的时间逻辑,后者多发生在样本集合之间的信息隔离。二者都需要在代码层面显式约束。

三、整体框架

课程按照“认知—识别—防御—验证—复盘”的路径展开,共 30 章,可划分为五个模块。

  1. 基础认知模块:建立时间语义、数据可得性、回测可信度的基本判断框架。
  2. 未来函数识别模块:覆盖行情、财报、公告、指数成分、宏观数据等场景中的典型陷阱。
  3. 数据泄漏防御模块:讲解特征工程、标准化、缺失值处理、交叉验证中的隔离原则。
  4. 工程实现模块:通过代码模板、配置约束、日志审计,把规范固化到研究流程中。
  5. 案例复盘模块:用真实策略片段演示错误如何产生、如何定位、如何修复。

学习建议:每章至少准备一份自己的历史研究代码,边学边对照检查。防未来函数不是理论问题,而是代码审查问题。

四、典型场景与误区

先看一个常见场景。研究者用日频数据构造标签,代码如下:

# 错误示例:用当日收盘价计算未来收益,却把当日收盘特征也放进去
df['future_return'] = df['close'].shift(-1) / df['close'] - 1
df['ma5'] = df['close'].rolling(5).mean()
X = df[['ma5', 'volume', 'pe']]
y = df['future_return']

这段代码本身没有直接错误,但若 pe 使用的是当日收盘后才知道的财报数据,或者 ma5 在 t 日盘中就使用了 t 日收盘价,就会产生未来函数。更隐蔽的是,若在标准化时使用全样本均值方差,则验证集信息泄漏进训练集。

高频误区:“只要用了 shift 就没有未来函数”是错误认知。shift 只解决单列错位,不解决数据披露时点、样本外标准化、超参数选择泄漏等问题。

另一个典型误区是幸存者偏差。若今天获取股票池,再回溯过去五年数据,已经退市或曾被 ST 的股票会被系统性剔除,回测收益会被高估。正确做法是使用历史时点的成分股名单或全市场历史快照。

研究时点 t 的信息边界 时间 历史可得数据 t 时刻 未来信息(禁用) 信息边界 允许:t 及之前已披露的行情、财报、公告、宏观数据 禁止:t 之后的价格、未披露财报、未来成分股名单、全样本统计量

五、方法流程与工程约束

为了把上述原则落到代码中,建议采用统一的研究流程:

  1. 明确每个字段的“可得时间戳”,而不仅是“数据时间戳”;
  2. 按时间切分训练、验证、测试集,禁止随机打乱;
  3. 所有统计量只在训练窗口内拟合,再应用到验证与测试;
  4. 标签构造与特征构造分别检查是否跨越信息边界;
  5. 回测撮合使用下一可交易时点,避免用当日收盘信号当日成交;
  6. 记录每次实验的数据版本、时间窗口与参数,便于复盘。
# 推荐的时间切分示意
train = df[df['date'] < '2021-01-01']
valid = df[(df['date'] >= '2021-01-01') & (df['date'] < '2022-01-01')]
test  = df[df['date'] >= '2022-01-01']

# 标准化只在训练集拟合
scaler.fit(train[features])
train_scaled = scaler.transform(train[features])
valid_scaled = scaler.transform(valid[features])

一句话原则:任何在 t 时刻不可获得的信息,都不能进入 t 时刻的决策路径;任何验证集或测试集的信息,都不能进入训练过程。