4. 数据获取与清洗
在时间序列动量CTA策略中,数据质量直接决定策略信号的可信度。很多初学者把精力全部放在模型和参数上,却忽略了“垃圾进、垃圾出”这一铁律。本章围绕期货数据的获取与清洗,讲清四个关键环节:数据来源选择、主力合约拼接、复权处理、缺失值与异常值处理。
一、核心原理:为什么CTA数据必须“清洗”
期货与股票最大的不同在于合约有到期日。同一品种在不同时间对应不同合约,价格序列天然存在“跳空”和“换月”问题。如果直接拿单个合约做长周期动量,会因合约到期而断裂;如果随意拼接,又会在换月点引入虚假的跳空收益。因此,数据清洗的目标是构造一条连续、可交易、可复现的价格序列。
二、数据来源:主流渠道与取舍
常见期货数据来源可分为三类:交易所官方数据、第三方数据商、开源/社区数据。选择时需权衡覆盖范围、更新频率、字段完整性和成本。
| 来源类型 | 代表渠道 | 优点 | 局限 |
|---|---|---|---|
| 交易所官方 | 上期所、大商所、郑商所、中金所 | 权威、免费、结算价准确 | 需自行拼接,历史深度有限 |
| 第三方数据商 | Wind、同花顺、聚宽、米筐 | 字段全、复权方便、接口稳定 | 收费,部分历史需授权 |
| 开源/社区 | AkShare、Tushare、GitHub数据集 | 免费、易上手 | 质量参差,需自行校验 |
实战建议:以交易所官方结算价为准,用第三方数据做交叉验证。至少保留开、高、低、收、结算、成交量、持仓量七个字段,动量策略尤其依赖结算价和持仓量。
三、主力合约拼接:规则决定信号稳定性
主力合约拼接的核心是定义“谁是主力”。常见规则有三种:
- 持仓量最大:最常用,反映资金关注度。
- 成交量最大:适合短线,但易受日内投机影响。
- 固定换月规则:如交割月前一个月第一个交易日换月,简单可复现。
拼接时需记录换月点,并在换月日进行价格调整。下面是一个简化的主力合约选择与拼接逻辑:
# 伪代码:按持仓量选择主力并拼接
for each trade_date:
candidates = contracts_of(symbol, trade_date)
main = max(candidates, key=lambda c: c.open_interest)
if main != current_main:
record_roll(trade_date, current_main, main)
current_main = main
append(price_series, main.close)
四、复权处理:价差复权与比例复权
换月时新旧合约存在价差,直接拼接会产生虚假跳空。复权就是消除这种跳空,常用两种方法:
- 价差复权:用新旧合约价差调整历史价格,保持绝对价格连续。
- 比例复权:用新旧合约价格比例调整,保持收益率连续。
对动量策略而言,比例复权更合适,因为动量关注的是收益率而非绝对价格。价差复权在价格接近零时可能产生极端值。
# 比例复权示例
ratio = new_close / old_close
adjusted_old = old_close * ratio
# 或对历史序列整体乘以累积比例因子
五、缺失值与异常值处理
期货数据常见问题包括:节假日缺失、涨跌停导致的无成交、数据源断档、明显错误价格。处理原则是先诊断、再处理、后记录。
- 缺失值:交易日缺失可用前值填充,但需限制连续填充天数;非交易日缺失不应填充。
- 涨跌停:无成交时价格仍存在,应保留结算价,但成交量置零或标记。
- 异常值:用滚动中位数和MAD识别,超过阈值则标记并人工复核。
# 基于MAD的异常值检测
median = rolling_median(price, window=20)
mad = rolling_mad(price, window=20)
is_outlier = abs(price - median) > 5 * mad
所有清洗操作都应生成日志,记录每条被修改的数据及原因。这样在策略表现异常时,才能回溯是信号问题还是数据问题。
六、案例与复盘:一次换月导致的信号失真
某螺纹钢动量策略在回测中表现优异,但实盘连续亏损。排查发现:回测使用价差复权,换月点价格连续,动量信号平稳;实盘使用未复权的主力合约,换月当天出现大幅跳空,动量信号被错误触发。修正为比例复权并统一换月规则后,实盘与回测偏差显著缩小。
总结来看,数据获取与清洗是CTA策略开发的地基。数据来源要权威可验证,主力拼接规则要固定可复现,复权方式要与策略逻辑匹配,缺失与异常处理要留痕可追溯。把这四步做扎实,后续的动量信号和回测结果才值得信任。