第二章:金融数据获取——从零开始搭建你的数据管道
做金融量化研究,第一步永远绕不开数据。我见过太多人花大把时间调模型,结果数据源就出了问题——嗯,那真是灾难。今天咱们就聊聊怎么用Python把股票数据稳稳当当地抓下来。
2.1 为什么数据获取是量化研究的基石
说白了,没有数据,再牛的策略也是空中楼阁。我个人习惯把数据获取比作「做饭前的买菜」——菜不新鲜,厨艺再好也白搭。金融数据有几个特点:
- 时效性:昨天的数据今天可能就失效了
- 完整性:缺失值、停牌、复权,坑特别多
- 一致性:不同数据源对同一只股票可能给出不同价格
我在项目中遇到过最头疼的事:用两个不同库拉同一只股票的历史数据,收盘价差了0.3%。后来查了半天,发现是复权方式不同。所以,数据获取不是简单的「pip install + 一行代码」,你得知道背后发生了什么。
2.2 使用pandas-datareader获取A股数据
pandas-datareader是Pandas官方维护的数据接口库。它支持多个数据源,包括雅虎财经、FRED、世界银行等。不过要注意,雅虎财经的国内数据有时不太稳定。
⚠️ 避坑指南:我曾经因为雅虎财经接口变动,导致整个回测系统数据断流。建议至少准备两个数据源作为备份。
2.2.1 安装与基本使用
# 安装
pip install pandas-datareader
# 基本用法
import pandas_datareader as pdr
import datetime
# 获取贵州茅台(600519.SH)的历史数据
start = datetime.datetime(2023, 1, 1)
end = datetime.datetime(2023, 12, 31)
# 注意:A股代码需要加上后缀
df = pdr.get_data_yahoo('600519.SS', start, end)
print(df.head())
这里有个细节:A股在上海交易所上市的后缀是.SS,深圳是.SZ。我刚开始做的时候老记混,后来干脆写了个映射表。
2.2.2 数据清洗第一步
拿到的数据通常长这样:
| 字段 | 说明 | 常见问题 |
|---|---|---|
| Open | 开盘价 | 可能为0(停牌) |
| High | 最高价 | 异常值(数据错误) |
| Low | 最低价 | 可能高于收盘价(需校验) |
| Close | 收盘价 | 核心字段,注意复权 |
| Volume | 成交量 | 缺失值常见 |
💡 我的习惯:拿到数据后先做三件事——检查缺失值、看数据范围是否合理、确认时间索引是否连续。这三步能过滤掉80%的数据问题。
2.3 使用yfinance获取美股数据
说到美股数据,yfinance是我用得最多的库。它比pandas-datareader更稳定,而且支持更多功能。你想想看,苹果、特斯拉、亚马逊,这些代码直接就能用。
2.3.1 安装与快速上手
pip install yfinance
import yfinance as yf
# 获取苹果公司数据
aapl = yf.download('AAPL', start='2023-01-01', end='2023-12-31')
print(aapl.describe())
为什么推荐yfinance?因为它会自动处理一些麻烦事:
- 自动调整股票分割
- 支持股息数据
- 可以获取实时行情
- 数据格式更规范
2.3.2 批量获取多只股票
做量化研究很少只盯一只股票。我通常这样批量拉数据:
import yfinance as yf
import pandas as pd
# 定义股票池
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']
# 批量下载
data = yf.download(tickers, start='2023-01-01', end='2023-12-31')
# 只取收盘价
close_prices = data['Close']
print(close_prices.head())
🔑 关键点:批量下载时,yfinance返回的是MultiIndex DataFrame。列名是两层——第一层是字段(Open/Close/Volume等),第二层是股票代码。这个结构在后续分析中特别方便。
2.4 数据清洗与预处理基础
数据拿到手,别急着跑模型。我见过最惨的案例:有人用含缺失值的数据训练了三个月,结果模型在实盘时直接崩了。数据清洗,说白了就是给你的数据「洗澡」。
2.4.1 常见数据问题及处理
我总结了几类高频问题:
- 缺失值:停牌日、节假日、数据源问题
- 异常值:价格突然跳变、成交量异常放大
- 重复值:同一时间戳出现多条记录
- 时间索引不连续:缺少交易日数据
2.4.2 实战:完整的数据清洗流程
import pandas as pd
import numpy as np
def clean_stock_data(df):
"""
我的标准清洗流程
"""
# 1. 检查缺失值
print(f"缺失值统计:\n{df.isnull().sum()}")
# 2. 处理缺失值——前向填充
# 停牌日的价格用上一个交易日填充
df = df.fillna(method='ffill')
# 3. 检查异常值——用3倍标准差法
for col in ['Open', 'High', 'Low', 'Close']:
mean = df[col].mean()
std = df[col].std()
# 标记异常值
df[f'{col}_outlier'] = np.abs(df[col] - mean) > 3 * std
# 4. 确保时间索引连续
# 生成完整的交易日历
full_dates = pd.date_range(start=df.index.min(),
end=df.index.max(),
freq='B') # B代表工作日
df = df.reindex(full_dates)
# 5. 删除重复索引
df = df[~df.index.duplicated(keep='first')]
return df
# 使用示例
clean_data = clean_stock_data(df)
⚠️ 我曾经踩过的坑:前向填充(ffill)虽然方便,但连续停牌超过5天时,填充的数据会严重失真。建议设置一个阈值,比如连续缺失超过3天就标记出来人工处理。
2.5 本章知识体系总览
为了让你更直观地理解数据获取的完整流程,我画了张图:
2.6 避坑指南与最佳实践
最后,分享几个我这些年总结的经验:
🎯 最佳实践:
- 永远保留原始数据副本,清洗操作在副本上进行
- 每次清洗操作都记录日志,方便回溯
- 数据获取脚本要支持断点续传,防止网络中断
- 定期校验数据源,确保接口没有变化
嗯,数据获取这部分就聊到这儿。记住一句话:数据质量决定研究上限。花80%的时间在数据上,剩下20%的时间建模,这个比例我觉得刚刚好。