3. 金融市场数据基础:股票数据获取与预处理

做量化投资,第一步就是搞定数据。没有数据,再牛的策略也是空中楼阁。今天我们就来聊聊怎么拿到股票数据,怎么把它洗干净,以及怎么用时间序列的思维去理解它。

我个人习惯把数据获取比作「做饭前的买菜」。菜买得不好,厨艺再高也白搭。数据也一样,源头脏了,后面分析全是错的。

3.1 数据获取:tushare vs akshare

国内做量化,绕不开两个库:tushare 和 akshare。它们都是免费的开源库,但定位不太一样。

对比项 tushare akshare
数据源 自建数据库,稳定 爬取公开网站,更新快
注册门槛 需要 token,部分接口收费 无需注册,直接调用
数据质量 高,经过清洗 中等,偶有异常值
适合场景 正式策略回测 快速验证、研究

我刚开始做量化时,图省事直接用 akshare。结果有一次回测收益特别高,兴奋了半天,后来发现是某天的复权数据算错了。嗯,从那以后,正式策略我都用 tushare。

3.1.1 用 tushare 获取日线数据

import tushare as ts

# 设置 token(需要去官网注册)
ts.set_token('你的token')
pro = ts.pro_api()

# 获取平安银行 2023年日线数据
df = pro.daily(
    ts_code='000001.SZ',
    start_date='20230101',
    end_date='20231231'
)
print(df.head())
小提示:tushare 的 daily 接口返回的是未复权数据。做因子计算时,我建议用复权数据,调用 pro.daily(adj='qfq') 即可。

3.1.2 用 akshare 快速获取数据

import akshare as ak

# 获取上证指数历史数据
df = ak.stock_zh_index_daily(symbol="sh000001")
print(df.head())

akshare 的好处是快,不用注册。但你要自己做好数据校验。我曾经发现某只股票在 akshare 里突然多了一天「涨停」数据,其实是网站爬虫抓错了。

3.2 数据清洗与预处理

数据拿到手,别急着用。先看看有没有坑。我总结了一套「三步清洗法」:

  1. 缺失值处理:股票停牌日会有 NaN,直接 drop 还是 fill?
  2. 异常值检测:涨跌幅超过 20% 的,大概率是数据错误。
  3. 格式统一:日期列转 datetime,价格列转 float。

3.2.1 实战:清洗平安银行数据

import pandas as pd
import numpy as np

# 假设 df 是从 tushare 获取的原始数据
df = df.sort_values('trade_date')  # 按日期排序

# 1. 检查缺失值
print(df.isnull().sum())

# 2. 处理缺失:前向填充(停牌日沿用前一日数据)
df['close'] = df['close'].fillna(method='ffill')

# 3. 异常值检测:涨跌幅超过 ±10% 的标记为异常
df['pct_chg'] = df['close'].pct_change()
df.loc[df['pct_chg'].abs() > 0.1, 'close'] = np.nan
df['close'] = df['close'].fillna(method='ffill')

# 4. 日期格式统一
df['trade_date'] = pd.to_datetime(df['trade_date'])
df.set_index('trade_date', inplace=True)
注意:前向填充(ffill)适合停牌日,但不适合节假日。节假日应该直接删除,而不是填充。我见过有人把春节假期也填充了,结果回测曲线平滑得像假的一样。

3.3 时间序列数据结构

量化数据本质上就是时间序列。每一行是一个时间点,每一列是一个特征。理解这个结构,是后面所有分析的基础。

说白了,时间序列数据就三个要素:

  • 时间戳:唯一标识每一行
  • 观测值:价格、成交量等
  • 频率:日频、分钟频、Tick 级

3.3.1 为什么频率很重要?

你想想看,日频数据和分钟频数据,处理方式完全不同。日频数据可以简单用 pct_change() 算收益率,但分钟频数据要考虑隔夜跳空。

我做过一个高频策略,用 5 分钟数据算动量。结果发现每天开盘第一根 K 线的收益率特别大,差点把策略带偏。后来才意识到,那是隔夜信息的集中释放,不是真正的动量。

3.3.2 构建标准时间序列 DataFrame

# 标准的时间序列 DataFrame
# 索引是时间,列是特征
ts_df = pd.DataFrame({
    'open': [10.0, 10.1, 10.2],
    'high': [10.3, 10.4, 10.5],
    'low': [9.9, 10.0, 10.1],
    'close': [10.2, 10.3, 10.4],
    'volume': [10000, 12000, 11000]
}, index=pd.date_range('2023-01-01', periods=3, freq='D'))

# 检查时间索引是否连续
print(ts_df.index.is_monotonic_increasing)
核心要点:时间序列分析中,索引必须是单调递增的,且频率一致。如果中间有缺失日期,要么填充,要么重采样。

3.4 本章知识体系

下面这张图,是我自己总结的数据处理流程。每次做新策略前,我都会对照着走一遍。

量化数据预处理流程 数据获取 tushare / akshare 数据清洗 缺失值 / 异常值 格式统一 时间序列构建 索引 / 频率 / 对齐 重采样 常见问题与避坑指南 ⚠️ 停牌日数据:用 ffill 填充,不要直接删除 ⚠️ 复权处理:因子计算必须用后复权,回测用前复权 ⚠️ 节假日:直接删除,不要填充任何值 ⚠️ 数据对齐:多只股票必须按同一时间索引对齐

这张图我贴在了工位上。每次数据出问题,我就回来看看是哪一步没做好。你想想看,如果数据获取这一步就错了,后面再花哨的分析都是白费。

我的习惯:每次拿到新数据,先用 df.info()df.describe() 快速扫一眼。如果发现最大值是 99999 这种明显异常,赶紧回去查源头。

好了,数据基础就聊到这里。记住一句话:数据质量决定策略上限。后面我们会基于这些干净数据,开始真正的因子挖掘。


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