第四章:数据基础设施——数据源的选择、清洗与回测对接

做量化投资,说白了就是跟数据打交道。

我见过太多人,策略逻辑写得漂漂亮亮,一上回测就崩。为什么?数据源选错了,或者数据没洗干净。今天咱们就把这块硬骨头啃下来。

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 清洗流程

我一般按这个顺序处理:

  1. 去重:按时间戳+股票代码去重
  2. 处理缺失值:向前填充(ffill)或插值
  3. 剔除异常值:用3σ原则或IQR方法
  4. 对齐时间戳:统一为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 本章知识体系总览

下面这张图,是我自己总结的数据基础设施全流程。你看一眼,心里就有数了。

数据基础设施全流程 数据源层 行情数据 财务数据 另类数据 舆情/供应链 数据清洗与预处理层 去重 缺失值处理 异常值剔除 时间对齐 因子预处理 数据存储层 CSV/Parquet SQLite/MySQL InfluxDB/ClickHouse HDF5 回测引擎对接层 get_price() get_fundamentals() get_factor()

这张图把数据从源头到回测引擎的完整链路画清楚了。你照着这个流程搭,基本不会出大问题。

我的经验:数据基础设施搭建好了,后面策略开发会顺畅很多。我曾经花了一周时间搭数据管道,后来两年都没换过。前期投入是值得的。

好了,数据这块就聊到这儿。记住:数据是量化投资的「地基」。地基不稳,楼盖得再高也得塌。下一章咱们聊聊因子挖掘的具体方法。

专注资料整理