3. 数据获取与预处理:从零搭建你的量化数据库

做量化交易,尤其是波动率预测,第一步永远不是写模型,而是搞定数据。我见过太多人一上来就调包跑LSTM,结果数据没对齐,回测曲线漂亮得像假的一样——嗯,其实就是假的。今天这章,咱们就踏踏实实把数据这块地基打牢。

3.1 用yfinance获取股票/期权数据

yfinance这个库,说白了就是雅虎财经的非官方Python接口。我最早用的时候它还叫yahoo-finance,后来雅虎把API关了,社区又搞出了yfinance。现在它依然是获取免费行情数据最方便的工具,没有之一。

先装包:

pip install yfinance pandas matplotlib

获取股票数据很简单:

import yfinance as yf

# 获取苹果股票数据
aapl = yf.download('AAPL', start='2023-01-01', end='2024-01-01')
print(aapl.head())

这里有个坑,我踩过好几次:yfinance默认返回的数据是调整后的价格(Adjusted Close)。对于期权定价和波动率计算,我们通常需要原始收盘价。我个人习惯在获取时加上参数 auto_adjust=False

aapl_raw = yf.download('AAPL', start='2023-01-01', end='2024-01-01', auto_adjust=False)
⚠️ 注意: 调整后的价格考虑了分红和拆股,用于计算收益率没问题。但如果你要做期权定价,必须用原始价格。我曾经因为用了调整价,导致期权隐含波动率计算偏差了3%以上。

获取期权数据稍微复杂一点:

# 获取期权链
ticker = yf.Ticker('AAPL')
expirations = ticker.options  # 获取所有到期日
print(expirations[:5])  # 打印前5个到期日

# 获取特定到期日的期权链
opt_chain = ticker.option_chain(expirations[0])
calls = opt_chain.calls
puts = opt_chain.puts
print(calls[['strike', 'lastPrice', 'impliedVolatility']].head())

你看,期权数据里直接包含了隐含波动率(impliedVolatility),这对我们做波动率预测模型来说,简直是现成的标签数据。

3.2 数据清洗与对齐

真实世界的数据从来不会干干净净。我处理过最离谱的一次,某只股票某天成交量是0,但价格却跳涨了20%。你想想看,这明显是数据错误。

常见的脏数据问题:

  • 缺失值:节假日、停牌日导致数据缺失
  • 异常值:价格跳空、成交量异常
  • 时间戳不对齐:不同市场交易时间不同

我的清洗流程是这样的:

import pandas as pd
import numpy as np

def clean_stock_data(df):
    # 1. 处理缺失值
    df = df.dropna(subset=['Close'])  # 删除收盘价为空的记录
    
    # 2. 处理异常值(3倍标准差法)
    for col in ['Close', 'Volume']:
        mean = df[col].mean()
        std = df[col].std()
        df = df[(np.abs(df[col] - mean) <= 3 * std)]
    
    # 3. 向前填充缺失的日期(非交易日)
    df = df.asfreq('D').fillna(method='ffill')
    
    return df

# 对齐多个股票数据
def align_multiple_stocks(tickers, start, end):
    data = {}
    for ticker in tickers:
        df = yf.download(ticker, start=start, end=end)
        data[ticker] = clean_stock_data(df)['Close']
    
    # 合并并对齐
    aligned = pd.DataFrame(data)
    aligned = aligned.dropna()  # 删除任何一只股票缺失的日期
    return aligned

# 使用示例
tickers = ['AAPL', 'MSFT', 'GOOGL']
aligned_prices = align_multiple_stocks(tickers, '2023-01-01', '2024-01-01')
print(aligned_prices.head())
💡 个人经验: 对齐数据时,我建议用inner join(即dropna()),而不是outer join。虽然会损失一些数据点,但能保证所有股票在同一时间点都有价格,这对后续的协方差矩阵计算至关重要。

3.3 计算日收益率与对数收益率

为什么要算收益率而不是直接用价格?因为价格是非平稳的,而收益率(尤其是对数收益率)在统计上更友好。说白了,用价格建模就像在沙滩上盖楼——地基不稳。

两种收益率的计算:

# 简单收益率
df['simple_return'] = df['Close'].pct_change()

# 对数收益率(连续复利收益率)
df['log_return'] = np.log(df['Close'] / df['Close'].shift(1))

# 对比一下
print(df[['Close', 'simple_return', 'log_return']].head())

你可能会问:到底用哪个?我的建议是:

  • 简单收益率:用于计算投资组合的实际盈亏
  • 对数收益率:用于建模和统计分析(因为它的分布更接近正态,且具有时间可加性)

我记得有一次做波动率预测,用简单收益率算出来的波动率总是有偏,换成对数收益率后,模型效果立马提升了一个档次。原因很简单:对数收益率可以跨时间累加,而简单收益率不行。

3.4 数据可视化基础

可视化不是花架子,它是你发现数据问题的第一道防线。我每次拿到新数据,第一件事就是画图——用眼睛看,比任何统计检验都直观。

几个必备的可视化技巧:

import matplotlib.pyplot as plt
import seaborn as sns

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 1. 价格走势图
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 左上:收盘价走势
axes[0, 0].plot(df.index, df['Close'], label='收盘价', color='#2E86AB')
axes[0, 0].set_title('收盘价走势')
axes[0, 0].legend()

# 右上:收益率分布
axes[0, 1].hist(df['log_return'].dropna(), bins=50, color='#A23B72', alpha=0.7)
axes[0, 1].set_title('对数收益率分布')
axes[0, 1].axvline(x=0, color='red', linestyle='--')

# 左下:收益率QQ图(检验正态性)
from scipy import stats
stats.probplot(df['log_return'].dropna(), dist='norm', plot=axes[1, 0])
axes[1, 0].set_title('QQ图(正态性检验)')

# 右下:波动率聚类现象
df['volatility'] = df['log_return'].rolling(20).std() * np.sqrt(252)
axes[1, 1].plot(df.index, df['volatility'], color='#F18F01')
axes[1, 1].set_title('20日滚动年化波动率')

plt.tight_layout()
plt.show()

看到右下角的波动率图了吗?你会发现波动率有聚类现象——高波动率后面跟着高波动率,低波动率后面跟着低波动率。这就是我们做波动率预测的理论基础之一。

🎯 核心要点: 波动率不是随机的,它是有记忆的。GARCH模型、Heston模型,本质上都是在捕捉这种记忆效应。

3.5 本章知识体系

下面这张图,是我做这章内容时画的框架。它把数据获取到预处理的全流程串起来了:

数据获取与预处理流程 步骤1:数据获取 yfinance下载股票/期权数据 步骤2:数据清洗 缺失值处理 · 异常值检测 步骤3:数据对齐 多资产时间戳对齐 步骤4:收益率计算 简单收益率 vs 对数收益率 步骤5:数据可视化 走势图 · 分布图 · QQ图 · 波动率图 输出:干净、对齐、可建模的收益率序列 用于波动率预测模型(GARCH、Heston、深度学习等) 常见坑点 ⚠ 忘记处理调整价格 ⚠ 未对齐导致数据偏差 ⚠ 收益率计算方式选错 ⚠ 忽略波动率聚类现象 ⚠ 可视化只看价格不看分布 —— 我全踩过 😅

这张图把整个流程串起来了。你跟着走一遍,基本不会出大问题。嗯,至少数据这块不会翻车。

3.6 避坑指南

最后分享几个我踩过的坑,希望能帮你省点时间:

  1. 不要直接用调整后的价格做期权分析——我吃过这个亏,算出来的隐含波动率偏差大到没法看。
  2. 对齐数据时,先检查交易时间——美股和A股交易时间不同,直接合并会出问题。
  3. 收益率计算前,先检查是否有0值——如果某天收盘价为0(数据错误),pct_change会返回inf。
  4. 可视化时,记得看分布形态——如果收益率分布严重偏态,你的正态假设可能不成立。
📌 我的习惯: 每次跑新数据前,我都会先画一张四宫格图(价格、收益率分布、QQ图、波动率),花30秒看一眼,就能判断数据质量。这比跑任何统计检验都快。

好了,数据准备就绪。下一章,我们会正式进入波动率模型的世界。不过在那之前,建议你把今天的内容动手跑一遍——数据搞定了,后面的事就顺了。


专注资料整理