数据获取与预处理:金融时间序列的源头活水

做市场关联性网络,第一步就是搞数据。这活儿看着简单,其实坑特别多。我刚开始做量化那会儿,觉得数据嘛,拉下来就能用,结果被各种奇葩问题折磨得够呛。今天咱们就把这块掰开揉碎了讲清楚。

2.1 数据来源:去哪儿拿靠谱的数据?

金融数据源,说白了就两类:免费的跟付费的。咱们做研究,免费的基本够用。

2.1.1 Yahoo Finance

这个大家应该不陌生。Yahoo Finance 的数据覆盖面广,全球主要市场的股票、ETF、指数都有。我习惯用 yfinance 这个 Python 库来拉数据,简单粗暴。

import yfinance as yf

# 下载贵州茅台和五粮液的历史数据
tickers = ["600519.SS", "000858.SZ"]
data = yf.download(tickers, start="2020-01-01", end="2023-12-31")
print(data.head())
小提示:Yahoo Finance 对 A 股的支持其实一般,代码后缀要写对。上海交易所是 .SS,深圳是 .SZ。我遇到过好几次因为后缀写错,拉回来一堆空数据的情况。

2.1.2 Tushare

做 A 股研究,Tushare 是绕不开的。数据质量高,字段全,就是需要注册拿 token。我个人觉得,花几分钟注册一下,绝对值。

import tushare as ts

# 设置 token
ts.set_token('你的token')

pro = ts.pro_api()

# 获取平安银行日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20200101', end_date='20231231')
print(df.head())
注意:Tushare 有调用频率限制。我曾经写了个循环批量下载几百只股票,结果直接被封了半小时。建议加个 time.sleep() 控制节奏。

2.1.3 其他数据源

除了上面两个,还有几个我偶尔会用:

  • Quandl:宏观数据很全,适合做因子研究
  • Alpha Vantage:免费但有次数限制
  • Wind / Choice:机构用的多,数据最干净,但贵

2.2 数据清洗:脏数据是模型的毒药

数据拉下来,千万别直接算收益率。你想想看,如果原始数据里有个跌停价被记成了涨停价,那算出来的相关系数全歪了。我吃过这个亏,现在每次拿到数据第一件事就是清洗。

2.2.1 缺失值处理

金融时间序列的缺失值,常见原因有:停牌、节假日、数据源抽风。处理方式分几种:

方法 适用场景 我的建议
向前填充 停牌导致的价格缺失 最常用,用上一个交易日的价格
插值法 日内数据的小缺口 线性插值简单有效
直接删除 缺失比例超过 20% 这种股票建议直接剔除
# 向前填充缺失值
df.fillna(method='ffill', inplace=True)

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

2.2.2 异常值处理

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

  • 3σ 原则:超过均值±3倍标准差的值,标记为异常
  • 百分位法:低于 0.5% 或高于 99.5% 分位数的值,需要检查
避坑指南:我曾经处理过一只股票,某天收益率突然变成 -50%。一开始以为是异常值,后来一查,原来是那天除权除息。所以遇到极端值,先别急着删,去查一下公告。

2.3 收益率计算:简单还是对数?

收益率计算,是构建关联性网络的核心输入。这里有两个选择:

2.3.1 简单收益率

公式:R_t = (P_t - P_{t-1}) / P_{t-1}

优点:直观,容易理解。缺点:时间不可加,多期收益率不能直接相加。

2.3.2 对数收益率

公式:r_t = ln(P_t / P_{t-1})

优点:时间可加性,多期收益率可以直接相加。而且对数收益率近似正态分布,很多统计模型都喜欢它。

我的习惯:做关联性网络,我几乎只用对数收益率。原因很简单——当我们要计算多只股票之间的相关性时,对数收益率的统计性质更稳定。而且,你想想看,对数收益率可以避免「收益率不能为负」这种尴尬。
import numpy as np

# 计算对数收益率
df['log_return'] = np.log(df['close'] / df['close'].shift(1))

# 去掉第一行的 NaN
df.dropna(inplace=True)

2.4 数据对齐与频率统一

做关联性网络,最怕什么?时间轴对不上。比如 A 股和港股,节假日不一样,交易时间也不一样。如果不做对齐,算出来的相关系数就是错的。

2.4.1 时间对齐

我常用的方法是:以某个基准时间序列为准,其他序列按这个时间轴对齐。缺失的用 NaN 填充,然后统一处理。

# 假设 df1 是基准,df2 和 df3 需要对齐
aligned_data = pd.concat([df1, df2, df3], axis=1, join='inner')
# join='inner' 只保留所有数据都存在的日期

2.4.2 频率统一

不同数据源的频率可能不一样。Yahoo Finance 默认是日频,Tushare 可以选日、周、月。做关联性网络,我建议统一用日频。为什么?

  • 日频数据量够大,统计显著性好
  • 周频和月频会丢失太多信息
  • 高频数据(分钟级)噪声太大,不适合做网络分析
注意:如果你要混合不同市场的股票(比如 A 股 + 港股),一定要考虑时区差异。A 股下午 3 点收盘,港股下午 4 点收盘。直接用收盘价算相关性,其实是有偏差的。我一般会统一用 UTC 时间对齐。

知识体系总览

下面这张图,把数据获取与预处理的整个流程串起来了。你可以把它当作一个检查清单,每次做数据准备时对照着来。

数据获取与预处理流程 数据源 数据清洗 收益率计算 数据对齐与频率统一 干净的时间序列 Yahoo Finance Tushare 其他数据源 缺失值处理 异常值检测

嗯,数据准备这块就讲这么多。记住一句话:数据质量决定了你模型的上限。花 80% 的时间在数据上,一点都不夸张。

专注资料整理