金融数据基础:股票数据获取与预处理

做量化投资,第一步就是搞数据。没有数据,再牛的模型也是空中楼阁。

我个人习惯把数据获取比作「做饭前的买菜」。菜不新鲜,厨艺再好也白搭。金融数据也一样——脏数据、缺失数据、未来函数,这些坑我全都踩过。今天咱们就聊聊怎么用 Python 拿到干净可用的股票数据。

数据源的选择:Tushare vs AKShare

国内做量化,绕不开两个库:Tushare 和 AKShare。我两个都用过,说说我的感受。

对比维度 Tushare AKShare
数据质量 较高,经过校验 中等,部分接口不稳定
使用门槛 需要注册 token,部分接口积分限制 无需注册,开箱即用
数据范围 A股、期货、基金、宏观等 A股、期货、外汇、加密货币等
更新频率 交易日收盘后更新 实时 + 历史
社区活跃度 较高,文档完善 高,但文档更新较慢

我的建议是:做研究用 Tushare,做快速原型用 AKShare。为什么?Tushare 的数据经过清洗,质量更稳定;AKShare 胜在方便,不用注册就能跑起来。

重要提醒:Tushare 的 pro 接口需要积分。我刚开始用的时候没注意,调日线数据发现返回空,折腾了半天才发现是积分不够。嗯,这个坑你们别踩。

实战:用 Tushare 获取股票数据

先装库:

pip install tushare pandas numpy

然后注册 Tushare,拿到你的 token。我个人习惯把 token 存在环境变量里,而不是硬编码在代码中——你想想看,万一代码传到 GitHub 上,token 就泄露了。

import tushare as ts
import pandas as pd

# 初始化
pro = ts.pro_api('你的token')

# 获取平安银行(000001.SZ)的日线数据
df = pro.daily(
    ts_code='000001.SZ',
    start_date='20230101',
    end_date='20231231'
)

print(df.head())

跑完你会发现,返回的数据是按日期降序排列的。我最开始没注意这个,直接拿去做回测,结果发现「未来数据」混进去了——说白了就是拿明天的数据预测今天,回测曲线漂亮得不像话,实盘直接崩了。

避坑指南:我曾经因为数据排序问题,白白浪费了两周调参。记住:回测前一定要按日期升序排列

# 按日期升序排列
df = df.sort_values('trade_date')

数据清洗:脏数据怎么处理?

拿到原始数据后,你会发现一堆问题:缺失值、异常值、复权问题……我一个个说。

1. 缺失值处理

股票数据缺失的原因很多:停牌、新股上市、数据源本身就没采集到。怎么处理?

  • 前向填充(ffill):用上一个交易日的数据填充。适合停牌的情况。
  • 后向填充(bfill):用下一个交易日的数据填充。适合数据采集延迟。
  • 插值法:线性插值,适合少量缺失。
  • 直接删除:如果缺失比例太高,干脆删掉那几天。
# 前向填充
df['close'].fillna(method='ffill', inplace=True)

# 或者用插值
df['close'].interpolate(method='linear', inplace=True)

我个人习惯:停牌导致的缺失用 ffill,其他情况用插值。为什么?停牌期间价格不变,ffill 最合理;其他缺失用插值更平滑。

2. 异常值检测

你想想看,一只股票突然涨了 1000%,这正常吗?大概率是数据错误。我一般用两种方法:

  • 3σ 原则:超过均值 ± 3 倍标准差的数据视为异常。
  • IQR 方法:超过 Q1 - 1.5*IQR 或 Q3 + 1.5*IQR 的数据视为异常。
# 3σ 方法
mean = df['pct_chg'].mean()
std = df['pct_chg'].std()
df = df[(df['pct_chg'] >= mean - 3*std) & (df['pct_chg'] <= mean + 3*std)]

# IQR 方法
Q1 = df['pct_chg'].quantile(0.25)
Q3 = df['pct_chg'].quantile(0.75)
IQR = Q3 - Q1
df = df[(df['pct_chg'] >= Q1 - 1.5*IQR) & (df['pct_chg'] <= Q3 + 1.5*IQR)]

小技巧:别一上来就删异常值。先看看是不是因为分红、送股导致的「假异常」。我遇到过一只股票因为 10 送 10,价格直接腰斩,但这不是异常,是正常除权。

3. 复权处理

这是新手最容易忽略的点。股票有分红、送股、配股,价格会跳空。不复权的话,技术指标全是错的。

Tushare 提供了三种复权方式:

  • 不复权:原始价格,有跳空。
  • 前复权:调整历史价格,让价格连续。适合回测。
  • 后复权:调整当前价格,适合看真实收益。
# 获取前复权数据
df = pro.daily(
    ts_code='000001.SZ',
    start_date='20230101',
    end_date='20231231',
    adj='qfq'  # 前复权
)

我个人习惯用前复权做回测。为什么?因为前复权保证了价格序列的连续性,技术指标(如均线、MACD)不会因为除权而失真。

数据预处理:为深度学习模型做准备

拿到干净数据后,还要做几件事才能喂给模型。

1. 标准化/归一化

深度学习模型对输入数据的尺度很敏感。股票价格从几块到几百块,不归一化的话,模型会学偏。

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler(feature_range=(0, 1))
df['close_scaled'] = scaler.fit_transform(df[['close']])

2. 构建特征

原始数据只有 OHLCV(开盘、最高、最低、收盘、成交量),但模型需要更多特征。我一般会加这些:

  • 技术指标:MA5、MA20、RSI、MACD、布林带
  • 统计特征:过去 N 天的均值、标准差、最大值、最小值
  • 时间特征:星期几、月份、季度
# 计算移动平均线
df['ma5'] = df['close'].rolling(window=5).mean()
df['ma20'] = df['close'].rolling(window=20).mean()

# 计算收益率
df['return'] = df['close'].pct_change()

# 计算波动率(过去20天标准差)
df['volatility'] = df['return'].rolling(window=20).std()

3. 处理未来函数

这是量化里最致命的错误。所谓未来函数,就是用了「未来才知道」的数据来预测当前。比如用当天的收盘价预测当天的涨跌——你想想看,收盘价出来的时候,当天已经结束了。

避坑指南:我曾经写过一个模型,回测年化收益 80%,实盘亏成狗。查了半天,发现是用了未来数据计算技术指标。记住:计算特征时,只能用过去的数据

# 错误示范:用了当天的数据
df['label'] = (df['close'].shift(-1) > df['close']).astype(int)

# 正确做法:用第二天的开盘价判断涨跌
df['label'] = (df['open'].shift(-1) > df['close']).astype(int)

知识体系总览

下面这张图是我自己总结的,涵盖了本章的核心内容。你可以把它当作一个检查清单:

金融数据获取与预处理流程 数据源选择 Tushare(质量高) AKShare(方便) 数据清洗 缺失值处理 异常值检测 复权处理 数据预处理 标准化/归一化 构建特征 避免未来函数

我的经验:数据清洗和预处理占整个量化项目 70% 的时间。别嫌麻烦,这一步做扎实了,后面的模型训练会非常顺畅。我曾经为了赶进度,数据清洗只花了 20% 的时间,结果模型怎么调都不收敛——最后发现是数据里有空值没处理。

好了,数据获取和预处理就聊到这儿。记住:干净的数据是量化模型的基石。下一章咱们会聊怎么用这些数据构建特征工程,为深度学习模型做准备。

专注资料整理