数据采集与清洗:金融数据源介绍、API数据获取、数据清洗与预处理

做金融网络建模,第一步就是搞数据。这活儿我干了快十年,说句实话,80%的时间都花在数据上。不是采集就是清洗,真正建模的时间反而没那么多。但这一步要是偷懒,后面模型跑得再漂亮也是白搭。

今天咱们就聊聊,金融数据到底从哪来,怎么拿,拿到手之后怎么收拾干净。

金融数据源:你总得知道去哪找

金融数据源五花八门,我按自己的经验分了三大类。你想想看,做网络建模,节点和边都需要数据支撑,来源不对,后面全错。

数据源类型 典型代表 适合场景 注意点
公开免费API Yahoo Finance、Alpha Vantage、Tushare 个人研究、原型验证 频率限制、数据质量参差
商业数据终端 Wind、Bloomberg、东方财富Choice 机构级、高频交易 贵,但数据干净
爬虫获取 新浪财经、巨潮资讯、公司年报 非结构化数据、舆情 法律风险、反爬机制

我个人习惯,做原型阶段先用免费API。等模型跑通了,再换商业数据源。别一上来就买几万块的终端,万一模型不work呢?

小提示: 国内做金融研究,Tushare是个好东西。我2017年第一次用它,那时候还叫TuShare,现在生态已经很成熟了。注册后拿个token,基本覆盖A股、基金、期货。

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())

你看,三行代码搞定。但别高兴太早——

警告: Yahoo Finance的数据有时会延迟15分钟,而且美股和A股的代码格式不一样。A股是"代码.SS"(上海)或"代码.SZ"(深圳)。我曾经因为代码写错,跑了一晚上拿了一堆空数据,第二天才发现。

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)]
重要: 别盲目删除异常值!我曾经在分析2015年股灾数据时,把暴跌的异常值全删了,结果模型完全看不出风险。异常值可能是信号,不是噪声。先搞清楚原因再动手。

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数据获取 频率控制 / 重试机制 / 缓存 原始数据 DataFrame格式 数据清洗与预处理 缺失值处理 异常值检测 复权处理 时间戳对齐 清洗后的干净数据 可用于网络建模 / 特征工程 发现问题,返回清洗

这张图里有个反馈循环,我特意画出来了。数据清洗不是一次性的活儿。你清洗完发现收益率曲线有异常,可能得回去重新检查API获取的逻辑。我经常要迭代三四轮,数据才能用。

经验之谈: 每次清洗完数据,我都会保存一份原始数据的副本。万一洗坏了,还能回滚。这个习惯救过我很多次。

写在最后

数据采集与清洗,说白了就是「脏活累活」。但金融网络建模的根基就在这。数据不对,模型再花哨也是垃圾进垃圾出。

我个人建议,刚开始做的时候,先拿两三只股票练手。把API调用、缺失值处理、复权、对齐这一套流程跑通。别一上来就搞几百只,出了问题你都不知道从哪查起。

嗯,今天就到这。数据准备好了,下一章咱们就可以开始建网络了。


公众号:蓝海资料掘金营,微信deep3321