第二章:金融数据获取——从零开始搭建你的数据管道

做金融量化研究,第一步永远绕不开数据。我见过太多人花大把时间调模型,结果数据源就出了问题——嗯,那真是灾难。今天咱们就聊聊怎么用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 常见数据问题及处理

我总结了几类高频问题:

  1. 缺失值:停牌日、节假日、数据源问题
  2. 异常值:价格突然跳变、成交量异常放大
  3. 重复值:同一时间戳出现多条记录
  4. 时间索引不连续:缺少交易日数据

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 本章知识体系总览

为了让你更直观地理解数据获取的完整流程,我画了张图:

金融数据获取与清洗流程 数据源 pandas-datareader / yfinance 数据获取 批量下载 / 单只股票 数据清洗 缺失值 / 异常值 预处理 时间索引对齐 / 复权处理 / 格式统一 输出:干净、标准化的金融时间序列数据 图2-1:金融数据获取与清洗标准流程

2.6 避坑指南与最佳实践

最后,分享几个我这些年总结的经验:

🎯 最佳实践
  • 永远保留原始数据副本,清洗操作在副本上进行
  • 每次清洗操作都记录日志,方便回溯
  • 数据获取脚本要支持断点续传,防止网络中断
  • 定期校验数据源,确保接口没有变化

嗯,数据获取这部分就聊到这儿。记住一句话:数据质量决定研究上限。花80%的时间在数据上,剩下20%的时间建模,这个比例我觉得刚刚好。

蓝海数据掘金营,专注资料整理