4、数据获取与清洗:获取股票/期货历史数据、处理缺失值与异常值、数据对齐与重采样

做量化交易,有一句老话我特别认同:「垃圾进,垃圾出」。你策略再牛,模型再花哨,数据一塌糊涂,结果就是白忙活。我自己早期就吃过这个亏——回测曲线漂亮得不行,实盘一跑直接崩。后来查了三天,发现是数据里混了几个「未来函数」的脏数据。

所以这一章,咱们就聊聊数据获取与清洗。说白了,就是怎么把原始数据变成能用的、干净的、对齐好的数据。这是所有回测的地基。

4.1 数据获取:从哪里拿数据?

我个人习惯把数据源分成三类:

  • 免费公开数据:比如 Tushare、AKShare、Yahoo Finance。适合个人研究、小规模回测。
  • 商业数据服务:比如 Wind、Choice、Bloomberg。贵,但数据质量高,字段全。
  • 交易所直连:比如 CTP、FIX 接口。适合高频交易,延迟低,但维护成本高。

嗯,这里要注意:免费数据往往有延迟,而且容易缺字段。我建议你至少准备两个数据源做交叉验证。比如 Tushare 拿日线,AKShare 拿分钟线,互相补漏。

核心原则:数据源要稳定、可复现。别今天用这个接口,明天换那个,回测结果对不上就麻烦了。

4.2 获取股票/期货历史数据

拿股票数据,最常用的就是日线。我一般用 Python 的 akshare 库,几行代码就能搞定:

import akshare as ak

# 获取上证50成分股日线数据
df = ak.stock_zh_a_hist(symbol="600519", period="daily", 
                        start_date="20200101", end_date="20231231")
print(df.head())

期货数据稍微麻烦点。因为期货有主力合约、次主力合约,还有换月的问题。我建议你直接拿「指数合约」或者自己拼接连续合约。

举个例子,螺纹钢期货:

# 获取螺纹钢主力连续合约
df_future = ak.futures_main_sina(symbol="RB0")
print(df_future.tail())

这里有个坑:主力合约换月时会有跳空。你想想看,如果直接拿主力连续,回测里可能莫名其妙多出一大段利润或亏损。我自己的做法是:用「复权」方式处理,或者干脆用「持仓量最大」的合约做滚动。

小技巧:期货数据最好同时拿「前复权」和「后复权」两份。前复权用于回测,后复权用于分析真实价格走势。

4.3 处理缺失值与异常值

数据拿到手,第一件事就是检查缺失值。我见过最离谱的情况:某只股票连续停牌三个月,回测里直接当「零收益」处理了,结果策略在那段时间疯狂开仓……

常见的缺失值处理方法:

  • 向前填充:用上一个交易日的数据填充。适合停牌、节假日。
  • 线性插值:用前后两个值取平均。适合日内分钟线的小缺口。
  • 直接删除:如果缺失比例小于 5%,删掉也无妨。

代码示例:

# 向前填充缺失值
df.fillna(method='ffill', inplace=True)

# 线性插值
df.interpolate(method='linear', inplace=True)

异常值处理更考验经验。什么叫异常?比如某只股票一天涨了 1000%,这明显是数据错误。我一般用「3 倍标准差」或者「百分位截断」来识别:

# 3倍标准差法
mean = df['close'].mean()
std = df['close'].std()
df = df[(df['close'] > mean - 3*std) & (df['close'] < mean + 3*std)]
注意:别一刀切。有些异常是真实的,比如分红、送股导致的跳空。我建议你先标记异常,再人工复核。

4.4 数据对齐与重采样

做多资产回测时,数据对齐是个大问题。比如你有股票 A 和期货 B,它们的交易时间不一样——A 是 9:30 开盘,B 是 9:00 开盘。如果你直接合并,时间戳对不上,回测结果就乱了。

我的做法是:统一时间轴。比如都对齐到「自然时间」的分钟线,或者都对齐到「交易日」的日线。

重采样也很常用。比如把 1 分钟数据重采样成 5 分钟:

# 重采样成5分钟K线
df_5min = df_1min.resample('5T').agg({
    'open': 'first',
    'high': 'max',
    'low': 'min',
    'close': 'last',
    'volume': 'sum'
})

这里有个细节:重采样时要注意「未来数据」。比如你用 5 分钟线的收盘价做信号,但实际交易时你只能拿到 4 分 59 秒的数据。我建议用 shift(1) 把数据往后挪一根,避免未来函数。

避坑指南:我曾经在回测里用了「当日收盘价」作为开仓信号,结果实盘发现根本买不到那个价格。后来我强制所有信号都滞后一根 K 线,回测才变得可信。

4.5 知识体系总览

下面这张图是我自己总结的数据处理流程,你可以参考一下:

数据获取与清洗流程 数据获取 股票/期货/ETF 缺失值处理 向前填充/插值/删除 异常值处理 3σ/百分位/人工复核 数据对齐 统一时间轴/多资产 重采样 1min→5min/日线 输出干净数据 CSV/数据库/内存 每个步骤都需要交叉验证,避免数据污染 迭代优化:发现异常后返回检查数据源

4.6 实战中的几个坑

最后分享几个我踩过的坑:

  1. 除权除息没处理:股票分红后价格会跳空,如果不复权,回测里会出现「假亏损」。我建议用后复权数据做回测。
  2. 期货夜盘数据缺失:很多免费数据不包含夜盘,如果你做日内策略,回测结果会严重失真。
  3. 时间戳时区问题:美股数据用 UTC 时间,A 股用北京时间,合并时一定要转换时区。
我的习惯:每次拿到新数据,先画一张「数据完整性热力图」。横轴是时间,纵轴是股票/合约,颜色代表是否有数据。一眼就能看出哪里缺数据。

好了,数据清洗这块就聊这么多。记住一句话:数据干净了,回测才有意义。下一章咱们会用到这些干净数据,开始真正的策略回测。


公众号:蓝海资料掘金营,微信deep3321