第四章:标签与目标变量构建
好,咱们进入第四章。前面几章我们把数据拿来了,也清洗干净了。但有个核心问题没解决——模型到底要学什么?
说白了,就是给模型一个“标准答案”。你想想看,没有标准答案,模型怎么知道自己的预测是对是错?这就是标签和目标变量的构建。
我个人习惯把这一步叫做“给数据注入灵魂”。数据本身只是冰冷的数字,但有了标签,它就有了方向。
4.1 未来N日收益率计算
这是最基础的一步。我们要计算未来N天的收益率,作为模型的预测目标。
公式很简单:
未来N日收益率 = (未来第N日的收盘价 / 当前收盘价) - 1
但这里有个坑——你用的是复权价格还是原始价格?
我在项目中遇到过,有人直接用原始价格算收益率,结果遇到分红除权,收益率直接跳变。嗯,这里要注意,一定要用后复权价格。
代码实现也很直接:
import pandas as pd
import numpy as np
def calculate_future_return(df, n=5):
"""
计算未来N日收益率
df: 包含'close'列的DataFrame
n: 未来天数
"""
df['future_return'] = df['close'].shift(-n) / df['close'] - 1
return df
# 示例
df = pd.DataFrame({'close': [100, 102, 101, 105, 108, 110]})
df = calculate_future_return(df, n=3)
print(df)
输出结果:
close future_return
0 100 0.050000
1 102 0.058824
2 101 0.089109
3 105 NaN
4 108 NaN
5 110 NaN
看到最后三行的NaN了吗?因为未来没有足够的数据了。这就是边界问题,后面会讲怎么处理。
4.2 分类标签构建:涨/跌/平
回归任务直接预测收益率,但很多时候我们只需要知道方向——涨还是跌?
分类标签的构建,说白了就是把连续收益率映射到离散类别。我常用的方法有三种:
| 方法 | 描述 | 适用场景 |
|---|---|---|
| 阈值法 | 收益率 > 阈值 → 涨;< -阈值 → 跌;其余 → 平 | 趋势交易、方向预测 |
| 分位数法 | 按收益率分位数划分,比如前30%为涨,后30%为跌 | 多空策略、排序问题 |
| 符号法 | 收益率 > 0 → 涨;< 0 → 跌;= 0 → 平 | 简单方向预测 |
我个人习惯用阈值法。为什么?因为符号法太敏感了,0.01%的涨幅也算涨,这在实际交易中意义不大。你想想看,手续费都不够。
阈值怎么选?我一般用历史收益率的波动率来定。比如:
threshold = df['future_return'].std() * 0.5
这样阈值会自适应市场波动。市场波动大,阈值就大;波动小,阈值就小。嗯,这个技巧我在实盘项目中用过,效果不错。
代码实现:
def create_classification_labels(df, threshold=None):
"""
构建分类标签
1: 涨, -1: 跌, 0: 平
"""
if threshold is None:
threshold = df['future_return'].std() * 0.5
df['label'] = 0
df.loc[df['future_return'] > threshold, 'label'] = 1
df.loc[df['future_return'] < -threshold, 'label'] = -1
return df
4.3 回归目标构建:收益率与波动率
分类标签适合方向预测,但如果你想做更精细的预测,比如预测具体的收益率,那就需要回归目标。
回归目标有两种常见选择:
- 未来N日收益率:直接预测收益率数值
- 未来N日波动率:预测价格波动的幅度
波动率的计算方式:
未来N日波动率 = 未来N日收益率的标准差
或者用更常用的方法:
未来N日波动率 = np.log(未来N日最高价 / 未来N日最低价)
我个人建议,如果你做的是趋势策略,用收益率作为回归目标;如果你做的是波动率交易或者期权定价,用波动率作为目标。
我曾经犯过一个错误——把收益率和波动率混在一起作为多目标输出。结果模型学得四不像,收益率预测不准,波动率也预测不准。后来我改成单目标,效果反而更好。
代码示例:
def calculate_volatility_target(df, n=5):
"""
计算未来N日波动率
"""
df['future_volatility'] = df['close'].rolling(n).std().shift(-n)
return df
4.4 避免未来信息泄露:防窥探
这是整个章节里最重要的部分。没有之一。
什么叫未来信息泄露?简单说,就是你在构建特征时,不小心用到了未来的数据。
举个例子:
# 错误示范
df['future_return'] = df['close'].shift(-5) / df['close'] - 1
df['ma_5'] = df['close'].rolling(5).mean() # 这里用了当前及过去的数据,没问题
# 但如果你这样做:
df['future_ma'] = df['close'].shift(-5).rolling(5).mean() # 用了未来的数据!
你想想看,在实盘交易中,你只能看到过去和现在的数据,未来的数据你是看不到的。如果你在训练时用了未来数据,模型在回测中表现会非常好,但一到实盘就崩盘。
我曾经接手过一个项目,回测年化收益50%,实盘直接亏了20%。查了三天,发现是特征构建时不小心用了未来数据。嗯,从那以后,我每次构建特征都会做一次“时间对齐检查”。
防窥探的核心原则:
- 特征只能使用当前时刻及之前的数据
- 标签必须使用未来的数据
- 特征和标签之间必须有时间间隔
具体操作时,我习惯用shift()函数来控制时间偏移:
# 正确做法
df['feature_1'] = df['close'].shift(1) # 使用昨天的收盘价
df['feature_2'] = df['volume'].rolling(5).mean().shift(1) # 使用过去5天的平均成交量(昨天及之前)
df['label'] = df['close'].shift(-5) / df['close'] - 1 # 未来5天的收益率
注意看,所有特征都用了shift(1),确保特征只包含历史信息。而标签用了shift(-5),指向未来。
千万不要在特征计算中使用shift(-n)!这是最常见的未来信息泄露来源。
另外,rolling()函数默认是包含当前值的,如果你不小心,也会引入未来信息。
4.5 知识体系总览
说了这么多,咱们用一张图来总结一下本章的核心逻辑:
我每次构建完标签后,都会做一个简单的验证:随机抽取100个样本,手动检查特征和标签的时间对齐。虽然麻烦,但能避免很多坑。
- 未来N日收益率是基础标签,注意使用复权价格
- 分类标签用阈值法,阈值自适应市场波动
- 回归目标分收益率和波动率,根据策略选择
- 未来信息泄露是最大敌人,时刻检查时间对齐
公众号:蓝海资料掘金营,微信deep3321