第二章:数据获取与清洗

做量化投资,第一步就是搞数据。这话听起来简单,但我在项目里见过太多人栽在这个环节上。数据不对,后面策略再牛也是白搭。今天咱们就聊聊怎么用Python把数据拿到手,再把它收拾得干干净净。

2.1 用pandas-datareader拉数据

我个人习惯用pandas-datareader来获取股票数据。这玩意儿封装好了,直接调用就行,不用自己写爬虫。

先装库:

pip install pandas-datareader

然后拉数据:

import pandas as pd
import pandas_datareader.data as web
import datetime

start = datetime.datetime(2020, 1, 1)
end = datetime.datetime(2023, 12, 31)

# 获取苹果公司股票数据
df = web.DataReader('AAPL', 'yahoo', start, end)
print(df.head())

跑完这段代码,你会得到一个DataFrame,里面有开盘价、收盘价、最高价、最低价、成交量这些字段。嗯,这里要注意,yahoo这个数据源有时候会抽风,连接不稳定。我在项目中遇到过好几次,解决方案是加个重试机制,或者换个数据源比如stooq

小技巧: 如果网络不好,可以设置一个超时时间,或者用本地缓存。我一般会把拉下来的数据存成CSV,下次直接读本地文件,省得每次都要等网络。

2.2 数据清洗——别让脏数据坑了你

数据拉下来,你以为就能直接用了吗?太天真了。真实的数据往往千疮百孔。我见过最离谱的一次,某只股票某天的收盘价竟然是负数——这明显是数据录入错误。

常见的脏数据问题:

  • 缺失值:某天没有交易,或者数据源没更新
  • 异常值:价格突然跳变,比如从100块蹦到1000块
  • 重复数据:同一天出现了两条记录
  • 格式问题:日期格式不统一,或者数字被存成了字符串

咱们一个一个来处理。

2.2.1 缺失值处理

先看看数据里有没有缺失值:

print(df.isnull().sum())

如果有缺失,怎么补?这得看情况。我个人习惯分三种场景:

场景 处理方法 适用情况
少量缺失(<5%) 向前填充(ffill) 用前一天的数据补上
中间缺失 线性插值 用前后两天的平均值
大量缺失 直接删除 数据质量太差,留着也没用

代码实现:

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

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

# 删除缺失行
df.dropna(inplace=True)
避坑指南: 我曾经用向前填充处理一只停牌股票的数据,结果停牌期间的价格全是停牌前那天的值,导致后续计算收益率时出现了严重偏差。所以,停牌期间的数据最好标记出来,不要简单填充。

2.2.2 异常值检测

异常值怎么找?我常用两种方法:

  • 3σ原则:超出均值±3倍标准差的数据视为异常
  • IQR方法:超出Q1-1.5*IQR或Q3+1.5*IQR的数据视为异常

代码示例:

# 用IQR方法检测异常值
Q1 = df['Close'].quantile(0.25)
Q3 = df['Close'].quantile(0.75)
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 标记异常值
df['is_outlier'] = (df['Close'] < lower_bound) | (df['Close'] > upper_bound)

# 查看异常值
print(df[df['is_outlier']])

你想想看,如果一只股票平时波动在±5%以内,突然某天涨了20%,这大概率是数据错误,而不是真的涨了这么多。我一般会把这些异常值替换成前后两天的平均值,或者直接删除。

2.3 数据标准化——让不同股票站在同一起跑线

不同股票的价格差异很大,茅台2000块一股,农业银行3块一股。如果你直接用价格做分析,茅台会把农行压得死死的。这时候就需要标准化。

常用的标准化方法:

方法 公式 特点
Z-score标准化 (x - μ) / σ 均值为0,标准差为1
Min-Max标准化 (x - min) / (max - min) 缩放到[0,1]区间
百分比变化 (x_t - x_{t-1}) / x_{t-1} 转为收益率序列

我个人最常用的是Z-score标准化,因为它保留了数据的分布形态。代码实现:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
df['Close_scaled'] = scaler.fit_transform(df[['Close']])

print(df[['Close', 'Close_scaled']].head())
核心要点: 标准化一定要在训练集上做fit,然后用同样的参数去transform测试集。否则会造成数据泄露,导致回测结果虚高。这个坑我踩过,后来再也不敢马虎了。

2.4 完整的数据处理流程

说了这么多,咱们把整个流程串起来。下面这张图展示了数据获取到清洗再到标准化的完整链路:

数据获取与清洗流程 数据获取 pandas-datareader 缺失值处理 ffill / interpolate / dropna 异常值检测 3σ / IQR 数据标准化 Z-score / Min-Max 清洗后数据 可直接用于策略开发 策略迭代输入

说白了,数据清洗就是个体力活,但也是最重要的活。我见过太多人花90%的时间写策略,结果数据一塌糊涂,回测结果根本不可信。反过来,把数据搞干净了,哪怕用最简单的均线策略,也能跑出靠谱的结果。

我的习惯: 每次拿到新数据,我都会先画个图看看。把价格曲线画出来,一眼就能看出有没有异常。视觉检查虽然土,但特别有效。

好了,数据这块咱们就聊到这儿。记住一句话:垃圾进,垃圾出。数据质量决定了你策略的天花板。


公众号:蓝海数据掘金营,微信deep3321