数据采集与清洗:金融数据源介绍、API数据获取、数据清洗与预处理
做金融网络建模,第一步就是搞数据。这活儿我干了快十年,说句实话,80%的时间都花在数据上。不是采集就是清洗,真正建模的时间反而没那么多。但这一步要是偷懒,后面模型跑得再漂亮也是白搭。
今天咱们就聊聊,金融数据到底从哪来,怎么拿,拿到手之后怎么收拾干净。
金融数据源:你总得知道去哪找
金融数据源五花八门,我按自己的经验分了三大类。你想想看,做网络建模,节点和边都需要数据支撑,来源不对,后面全错。
| 数据源类型 | 典型代表 | 适合场景 | 注意点 |
|---|---|---|---|
| 公开免费API | Yahoo Finance、Alpha Vantage、Tushare | 个人研究、原型验证 | 频率限制、数据质量参差 |
| 商业数据终端 | Wind、Bloomberg、东方财富Choice | 机构级、高频交易 | 贵,但数据干净 |
| 爬虫获取 | 新浪财经、巨潮资讯、公司年报 | 非结构化数据、舆情 | 法律风险、反爬机制 |
我个人习惯,做原型阶段先用免费API。等模型跑通了,再换商业数据源。别一上来就买几万块的终端,万一模型不work呢?
API数据获取:别光会调接口
拿数据这事儿,看着简单,调个requests就完事了?没那么简单。我在项目中遇到过好几次,接口返回的数据格式跟文档说的不一样,或者突然限流了。嗯,这里要注意几个点。
1. 选对库
Python里做金融API调用,我常用这几个:
- yfinance:Yahoo Finance的非官方封装,简单粗暴
- tushare:国内数据,文档中文,对新手友好
- akshare:开源免费,覆盖广,但偶尔不稳定
举个例子,用yfinance拿股票数据:
import yfinance as yf
# 拿贵州茅台的历史数据
maotai = yf.download('600519.SS', start='2023-01-01', end='2023-12-31')
print(maotai.head())
你看,三行代码搞定。但别高兴太早——
2. 处理频率限制
免费API都有频率限制。比如Alpha Vantage每分钟最多5次请求。你想想看,如果你要拿500只股票的数据,得等到什么时候?
我的做法是加个sleep,或者用异步请求。但更实用的办法是——缓存。
import time
import pandas as pd
def fetch_with_retry(ticker, max_retries=3):
for i in range(max_retries):
try:
data = yf.download(ticker, period='1mo')
if not data.empty:
return data
except Exception as e:
print(f"第{i+1}次尝试失败: {e}")
time.sleep(2 ** i) # 指数退避
return pd.DataFrame()
这段代码里用了指数退避。第一次失败等2秒,第二次等4秒,第三次等8秒。说白了就是给服务器一点喘息时间,别把自己搞封了。
数据清洗与预处理:这才是重头戏
数据拿到手,你以为就能直接建模了?太天真了。金融数据脏得很。我做过一个统计,从公开API拿到的数据,平均有5%-10%是有问题的。缺失值、异常值、复权问题、停牌数据……每一样都能让你头疼。
1. 缺失值处理
金融数据缺失的原因很多:停牌、节假日、数据源没更新。处理方式有三种:
- 直接删除:如果缺失比例小于1%,删了也无所谓
- 向前填充:用上一个交易日的数据填充,适合价格数据
- 插值:线性插值或时间加权插值,适合收益率等连续数据
# 向前填充缺失值
df['close'].fillna(method='ffill', inplace=True)
# 或者用插值
df['close'].interpolate(method='time', inplace=True)
我个人习惯,价格数据用向前填充,收益率数据用插值。为什么?因为价格是累积的,停牌后复牌,价格一般不会突变。但收益率是连续的,插值更合理。
2. 异常值检测
金融数据里的异常值,有时候是数据录入错误,有时候是真实的市场异动。比如某只股票突然涨了1000%,那大概率是数据错了。
我常用的方法是Z-score和IQR:
from scipy import stats
import numpy as np
# Z-score方法
z_scores = np.abs(stats.zscore(df['return']))
df_clean = df[(z_scores < 3)] # 保留3个标准差以内的数据
# IQR方法
Q1 = df['return'].quantile(0.25)
Q3 = df['return'].quantile(0.75)
IQR = Q3 - Q1
df_clean = df[(df['return'] >= Q1 - 1.5*IQR) &
(df['return'] <= Q3 + 1.5*IQR)]
3. 复权处理
做金融网络建模,最容易被忽略的就是复权。股票有分红、送股、配股,价格会跳空。如果你直接用原始价格,计算收益率会失真。
我建议统一用后复权数据。后复权保持了历史价格的真实走势,计算收益率更准确。yfinance默认返回的就是调整后的价格,但Tushare需要手动指定。
# Tushare获取复权数据
import tushare as ts
pro = ts.pro_api('你的token')
df = pro.daily(ts_code='600519.SH',
start_date='20230101',
end_date='20231231',
adj='qfq') # qfq: 前复权, hfq: 后复权
4. 对齐时间戳
金融网络建模经常需要多只股票的数据。但不同股票的交易日期可能不一样——有的停牌了,有的节假日不同。这时候需要对时间戳。
我的做法是取所有股票交易日的并集,然后填充缺失值:
# 假设df_dict是股票代码到DataFrame的映射
all_dates = pd.DatetimeIndex([])
for code, df in df_dict.items():
all_dates = all_dates.union(df.index)
# 重新索引并对齐
for code in df_dict:
df_dict[code] = df_dict[code].reindex(all_dates)
df_dict[code]['return'].fillna(0, inplace=True) # 停牌日收益率为0
知识体系总览
下面这张图,是我做数据采集与清洗的完整流程。每次做新项目,我都会先过一遍这个流程,确保不遗漏。
这张图里有个反馈循环,我特意画出来了。数据清洗不是一次性的活儿。你清洗完发现收益率曲线有异常,可能得回去重新检查API获取的逻辑。我经常要迭代三四轮,数据才能用。
写在最后
数据采集与清洗,说白了就是「脏活累活」。但金融网络建模的根基就在这。数据不对,模型再花哨也是垃圾进垃圾出。
我个人建议,刚开始做的时候,先拿两三只股票练手。把API调用、缺失值处理、复权、对齐这一套流程跑通。别一上来就搞几百只,出了问题你都不知道从哪查起。
嗯,今天就到这。数据准备好了,下一章咱们就可以开始建网络了。
公众号:蓝海资料掘金营,微信deep3321