第三章:数据获取——用yfinance搞定纳斯达克历史数据

做量化交易,第一步就是拿数据。没有数据,再牛的策略也是空中楼阁。

我个人习惯用yfinance来获取纳斯达克的历史数据。为什么选它?因为简单、免费、够用。你想想看,一个pip install就能搞定的事,何必折腾复杂的API?

3.1 安装与初始化

先装包,这步没什么好说的:

pip install yfinance pandas numpy

嗯,这里要注意。yfinance依赖pandas,所以一起装上。我建议用虚拟环境,别污染全局包。

导入的时候,我习惯这样写:

import yfinance as yf
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

为什么要导入datetime?因为后面要算时间范围。你总不想每次都手动输入起止日期吧?

3.2 获取纳斯达克指数数据

纳斯达克指数的代码是^IXIC。注意那个脱字符,这是雅虎财经的特殊标记。

# 获取纳斯达克近5年数据
nasdaq = yf.download('^IXIC', start='2019-01-01', end='2024-01-01')

# 看一眼数据长什么样
print(nasdaq.head())
print(nasdaq.info())

跑完这段代码,你会看到类似这样的输出:

Date Open High Low Close Volume Adj Close
2019-01-02 6635.27 6665.95 6569.80 6635.27 0 6635.27

看到Volume那一列了吗?全是0。这是雅虎财经的老毛病了。指数数据不提供成交量,但没关系,我们做策略分析主要用价格。

避坑提醒: 我曾经遇到过一个坑——yfinance默认返回的数据是按日期降序排列的。最新的数据在最上面。如果你习惯从旧到新处理数据,记得用sort_index()反转一下。

3.3 获取个股数据

光有指数不够,我们还得拿个股数据。比如苹果、微软、谷歌这些纳斯达克权重股。

# 定义股票列表
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META']

# 批量下载
data = yf.download(tickers, start='2019-01-01', end='2024-01-01')

# 看看数据结构
print(data.columns.names)
print(data.head())

这里有个小技巧。yf.download返回的是MultiIndex DataFrame。列名是两层:第一层是字段(Open, Close...),第二层是股票代码。我个人习惯把它展平:

# 展平列名
data.columns = ['_'.join(col).strip() for col in data.columns.values]
print(data.head())

展平之后,列名就变成了'AAPL_Open'、'MSFT_Close'这种格式。操作起来方便多了。

3.4 数据清洗与预处理

数据拿到手,别急着用。先洗一洗。我见过太多人直接拿原始数据跑策略,结果回测漂亮,实盘一塌糊涂。

清洗步骤其实就三件事:

  1. 处理缺失值——检查有没有NaN
  2. 处理异常值——比如某天价格突然跳空100%
  3. 对齐时间轴——确保所有股票日期一致
# 检查缺失值
print(data.isnull().sum())

# 前向填充(用前一天的数据填充缺失)
data.fillna(method='ffill', inplace=True)

# 再检查一遍
print(data.isnull().sum().sum())
我的经验: 前向填充(ffill)是处理金融时间序列最常用的方法。为什么?因为股票停牌、节假日这些情况,用前一天的数据是合理的。千万别用均值填充,那会引入未来信息。

异常值怎么处理?我一般用Z-score方法:

from scipy import stats

# 计算每只股票的Z-score
z_scores = np.abs(stats.zscore(data['AAPL_Close']))
threshold = 3

# 标记异常值
outliers = np.where(z_scores > threshold)
print(f'发现 {len(outliers[0])} 个异常值')

# 用中位数替换异常值
median_val = data['AAPL_Close'].median()
data.loc[data['AAPL_Close'].index[outliers], 'AAPL_Close'] = median_val

阈值设3倍标准差,这是统计学上的常用做法。当然,你也可以根据实际情况调整。

3.5 数据存储

清洗完的数据,得存起来。下次用的时候直接读,不用再跑一遍下载。

我提供两种存储方式:CSV和HDF5。

3.5.1 CSV存储

CSV是最通用的格式,任何工具都能打开。但缺点也很明显——大文件读写慢,而且不支持数据类型保留。

# 保存为CSV
data.to_csv('nasdaq_data.csv')

# 读取CSV
df_csv = pd.read_csv('nasdaq_data.csv', index_col=0, parse_dates=True)

注意那个parse_dates=True。如果不加,日期列会被当成字符串读进来。到时候你还要手动转格式,多麻烦。

3.5.2 HDF5存储

HDF5是我个人更推荐的格式。读写速度快,支持压缩,还能保留数据类型。对于量化交易这种高频读写场景,HDF5是首选。

# 保存为HDF5
data.to_hdf('nasdaq_data.h5', key='nasdaq', mode='w')

# 读取HDF5
df_h5 = pd.read_hdf('nasdaq_data.h5', key='nasdaq')

HDF5还有一个好处——可以存多个数据集。比如你可以把指数数据、个股数据、因子数据都放在同一个文件里,用不同的key区分:

# 存储多个数据集
with pd.HDFStore('quant_data.h5') as store:
    store['nasdaq_index'] = nasdaq
    store['stocks'] = data
    store['factors'] = factor_data
核心建议: 日常分析用CSV,方便查看。生产环境用HDF5,性能更好。我自己的做法是:开发阶段用CSV,部署到服务器后切到HDF5。

3.6 本章知识体系

下面这张图,是我梳理的本章核心逻辑。你看一眼就能明白整个数据获取流程:

数据获取与处理流程 数据获取 yfinance.download() 数据清洗 缺失值 → 异常值 → 对齐 数据存储 CSV / HDF5 获取内容 • 纳斯达克指数 ^IXIC • 个股数据 AAPL等 • 批量下载 MultiIndex 清洗方法 • ffill 前向填充 • Z-score 异常检测 • 中位数替换 存储对比 • CSV:通用易读 • HDF5:高性能 • 支持多数据集 数据质量决定策略质量 —— 清洗比建模更重要

这张图把整个流程串起来了。从yfinance获取原始数据,到清洗预处理,最后落地存储。每一步都有坑,但每一步也都有解法。

避坑指南: 我曾经因为没检查数据对齐,导致策略信号错位了三天。回测时收益曲线漂亮得不行,实盘一跑直接亏钱。从那以后,我每次清洗完数据都会做一次对齐检查——确保所有股票的时间索引完全一致。

好了,数据获取这块就讲到这里。记住一个原则:数据质量决定策略质量。花80%的时间在数据上,20%的时间在策略上,这才是量化交易的正确姿势。


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