第二章:数据获取与清洗——打好量化研究的地基

做量化选股,说白了就是和数据打交道。我见过太多人花大把时间调模型、写策略,结果数据源就有问题——那真是白忙活一场。今天咱们就把数据获取和清洗这块地基打扎实。

核心观点:数据质量决定了策略的上限。模型再牛,也救不了垃圾数据。

2.1 数据获取:选对工具,事半功倍

目前国内做A股数据获取,主流就两个库:aksharetushare。我个人习惯两个都装,因为各有各的脾气。

2.1.1 akshare——免费、轻量、上手快

akshare 的好处是纯免费,不需要token,适合快速验证想法。我在做回测原型时经常用它。

import akshare as ak

# 获取A股日线数据
df = ak.stock_zh_a_hist(symbol="000001", 
                        period="daily", 
                        start_date="20200101", 
                        end_date="20231231",
                        adjust="qfq")  # 前复权
print(df.head())

嗯,这里要注意:akshare 返回的字段名是中文,比如“开盘”、“收盘”。做量化时我习惯统一转成英文,方便后续处理。

2.1.2 tushare——专业、稳定、数据全

tushare 需要注册获取 token,但数据质量更高。我在正式项目中都用它。

import tushare as ts

# 设置token(第一次运行需要)
ts.set_token('your_token_here')
pro = ts.pro_api()

# 获取日线数据
df = pro.daily(ts_code='000001.SZ', 
               start_date='20200101', 
               end_date='20231231')
print(df.head())

我的小技巧:写个函数把两个库封装一下,一个挂了自动切另一个。我在项目中遇到过akshare某天接口调整,幸好有tushare兜底。

2.2 数据清洗:脏数据是量化的大敌

数据拿到手,别急着用。先过三关:去极值、处理缺失值、复权处理。

2.2.1 去极值——别让异常值带偏你的模型

股票数据里经常出现极端值,比如某天涨了20%(新股上市),或者跌停。这些会严重干扰因子计算。

我常用的方法是MAD法(中位数绝对偏差),比3σ更稳健。

import numpy as np
import pandas as pd

def winsorize_mad(series, n=5):
    """
    MAD法去极值
    n: 阈值,默认5倍MAD
    """
    median = series.median()
    mad = np.median(np.abs(series - median))
    upper = median + n * mad
    lower = median - n * mad
    return series.clip(lower, upper)

# 示例:对市盈率去极值
df['pe_ratio'] = winsorize_mad(df['pe_ratio'])

避坑指南:我曾经直接用3σ去极值,结果遇到某只股票连续涨停,标准差被拉得巨大,极值根本没去掉。后来改用MAD法,效果好了很多。

2.2.2 缺失值处理——别让NaN毁了你的回测

缺失值处理没有银弹。我的经验是:

缺失比例 处理方法 适用场景
< 5% 向前填充(ffill) 日线数据、财务数据
5% - 20% 中位数填充 因子数据
> 20% 直接删除该特征 质量太差,留着也没用
# 向前填充(最常用)
df.fillna(method='ffill', inplace=True)

# 中位数填充
df['factor_a'].fillna(df['factor_a'].median(), inplace=True)

# 删除缺失过多的列
df.dropna(thresh=len(df)*0.8, axis=1, inplace=True)

2.2.3 复权处理——还原真实收益率

复权是个大坑。很多新手直接用不复权数据算收益率,结果分红送股后收益率曲线完全走样。

我个人建议:做因子分析用前复权,做回测用后复权

  • 前复权:调整历史价格,让价格连续。适合计算因子。
  • 后复权:调整当前价格,真实反映收益。适合回测。
# akshare直接支持复权参数
df_qfq = ak.stock_zh_a_hist(symbol="000001", adjust="qfq")  # 前复权
df_hfq = ak.stock_zh_a_hist(symbol="000001", adjust="hfq")  # 后复权

# tushare需要手动处理
# 获取复权因子
adj_factor = pro.adj_factor(ts_code='000001.SZ')
# 然后自己算复权价格

我的经验:曾经有个策略回测年化30%,实盘直接腰斩。查了半天,原来是复权方式搞错了。从那以后,我每次都会用复权后的数据算一遍收益率,和原始数据对比验证。

2.3 数据存储:选对格式,效率翻倍

数据清洗完了,得存起来。我推荐两种格式:HDF5 和 Parquet。

2.3.1 HDF5——适合高频读写

HDF5 是二进制格式,读写速度快,支持压缩。我在做分钟级别数据时常用。

# 存储
df.to_hdf('stock_data.h5', key='daily', mode='w')

# 读取
df = pd.read_hdf('stock_data.h5', key='daily')

2.3.2 Parquet——列式存储,查询快

Parquet 是列式存储,适合按列查询。如果你经常只取某几个因子,Parquet 比 HDF5 快很多。

# 存储
df.to_parquet('stock_data.parquet')

# 读取
df = pd.read_parquet('stock_data.parquet')
对比项 HDF5 Parquet
读写速度 整体快 列查询快
压缩率 中等
跨语言支持 一般
我的推荐 分钟数据、高频场景 因子数据、分析场景

总结一下:数据获取用akshare/tushare,清洗过三关(去极值、缺失值、复权),存储选HDF5或Parquet。这套流程我用了三年,基本没出过问题。

数据获取与清洗流程 数据获取 akshare tushare 数据清洗 去极值 缺失值 复权 数据存储 HDF5 Parquet 核心原则:数据质量决定策略上限 关键要点 • 用MAD法去极值,比3σ更稳健 • 缺失值<5%用ffill,>20%直接删 • 因子分析用前复权,回测用后复权 • 分钟数据用HDF5,因子数据用Parquet

专注资料整理