第四章:数据基础设施——数据源的选择、清洗与回测对接
做量化投资,说白了就是跟数据打交道。
我见过太多人,策略逻辑写得漂漂亮亮,一上回测就崩。为什么?数据源选错了,或者数据没洗干净。今天咱们就把这块硬骨头啃下来。
4.1 数据源的选择:行情、财务、另类数据
数据源是量化策略的「原材料」。选错了,后面全白搭。
4.1.1 行情数据
行情数据是最基础的。包括开盘价、收盘价、最高价、最低价、成交量、成交额。我个人习惯把行情数据分成两类:
- 日线数据:适合中低频策略。来源有Tushare、AKShare、Wind、聚宽等。
- 分钟/ tick 数据:适合高频策略。我建议用聚宽或米筐的本地数据包,或者自己从交易所接口拉。
这里有个坑:复权问题。我曾经用未复权的数据跑回测,结果策略收益曲线漂亮得不像话。后来发现是除权缺口造成的假象。记住,回测一定要用后复权数据。
4.1.2 财务数据
财务数据是因子挖掘的「金矿」。包括利润表、资产负债表、现金流量表。常用的字段有:
- 营业收入、净利润、ROE、毛利率
- 资产负债率、流动比率
- 经营活动现金流净额
我推荐用Tushare Pro或者Wind的API。不过要注意,财务数据有滞后性。一季报通常在4月底才出完。你回测时如果用「最新」数据,其实是未来函数。嗯,这里要特别小心。
4.1.3 另类数据
另类数据是超额收益的来源。比如:
- 舆情数据:新闻情感分析、社交媒体热度
- 供应链数据:上下游企业的订单变化
- 卫星数据:停车场车流量、农田面积
我在项目中用过爬虫抓取雪球评论,做情感因子。效果还行,但数据清洗工作量巨大。你想想看,一条「这家公司要完蛋了」和「这家公司要完蛋了?不存在的!」情感完全相反。所以另类数据虽好,但门槛高。
核心建议:初学者先从行情+财务数据入手。等策略稳定了,再考虑另类数据。别一上来就搞花活。
4.2 数据清洗与预处理
数据清洗是量化里最枯燥但最重要的一环。我常说:垃圾进,垃圾出。
4.2.1 常见的数据问题
- 缺失值:某天停牌,数据为空
- 异常值:价格突然跳空,或者成交量暴增
- 重复值:同一时间戳出现多条记录
- 时间戳不对齐:不同数据源的时间格式不一致
4.2.2 清洗流程
我一般按这个顺序处理:
- 去重:按时间戳+股票代码去重
- 处理缺失值:向前填充(ffill)或插值
- 剔除异常值:用3σ原则或IQR方法
- 对齐时间戳:统一为UTC+8,精确到秒
举个例子,处理缺失值的代码:
import pandas as pd
# 假设df是日线数据,索引为日期,列为股票代码
# 向前填充:用前一天的数据填充停牌日
df.fillna(method='ffill', inplace=True)
# 如果连续缺失超过5天,直接剔除该股票
df = df.loc[:, df.isnull().sum() < 5]
小技巧:我曾经遇到过一个坑——除权日的数据会出现价格跳变。清洗时一定要用复权因子把价格统一。否则你的因子计算会出大问题。
4.2.3 因子预处理
因子数据也需要预处理。常用的方法:
- 去极值:MAD方法或分位数截断
- 标准化:Z-score或Min-Max
- 中性化:剔除市值、行业等风格影响
我习惯用MAD去极值。为什么?因为它对异常值更鲁棒。你想想看,如果某天某个因子值突然飙到100倍标准差,用3σ直接截断会丢失信息。MAD就好很多。
4.3 数据存储与回测引擎对接
数据存得好,回测跑得快。这是经验之谈。
4.3.1 存储方案选择
| 存储方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| CSV/Parquet | 小规模数据(<10GB) | 简单、易读 | 查询慢、不支持并发 |
| SQLite/MySQL | 中等规模 | 支持SQL查询 | 写入速度一般 |
| InfluxDB/ClickHouse | 时序数据大规模 | 查询快、压缩率高 | 学习成本高 |
| HDF5/Parquet | 因子数据存储 | 读写快、支持列式存储 | 不适合频繁更新 |
我个人推荐用Parquet格式存储因子数据。为什么?因为它压缩率高,读取速度快。我在项目中用Parquet存储了5年的分钟数据,查询速度比CSV快了10倍不止。
4.3.2 与回测引擎对接
回测引擎需要的数据接口,通常包括:
- get_price:获取行情数据
- get_fundamentals:获取财务数据
- get_factor:获取因子值
我建议把数据访问层封装成一个类。这样换数据源时,只需要改底层实现,上层策略代码不用动。
class DataLoader:
def __init__(self, data_path):
self.data_path = data_path
def load_daily_data(self, start_date, end_date):
# 从Parquet文件读取日线数据
df = pd.read_parquet(f"{self.data_path}/daily.parquet")
return df[(df['date'] >= start_date) & (df['date'] <= end_date)]
def load_factor_data(self, factor_name, start_date, end_date):
# 从数据库读取因子数据
query = f"SELECT * FROM factors WHERE factor='{factor_name}' AND date BETWEEN '{start_date}' AND '{end_date}'"
return pd.read_sql(query, self.conn)
注意:回测引擎对数据的时间戳要求非常严格。我曾经因为时间戳没对齐,导致回测结果偏差了3%。记住,一定要用同一时钟源的时间戳。
4.4 本章知识体系总览
下面这张图,是我自己总结的数据基础设施全流程。你看一眼,心里就有数了。
这张图把数据从源头到回测引擎的完整链路画清楚了。你照着这个流程搭,基本不会出大问题。
我的经验:数据基础设施搭建好了,后面策略开发会顺畅很多。我曾经花了一周时间搭数据管道,后来两年都没换过。前期投入是值得的。
好了,数据这块就聊到这儿。记住:数据是量化投资的「地基」。地基不稳,楼盖得再高也得塌。下一章咱们聊聊因子挖掘的具体方法。