第一章 数据获取与清洗:用yfinance搞定股票/加密货币数据
做量化交易,说白了就是跟数据打交道。我见过太多人一上来就写策略,结果数据源就有问题,折腾半天全白费。今天咱们就从最基础的开始——怎么用yfinance把数据拿下来,再把它收拾得干干净净。
1.1 为什么选yfinance?
市面上能拿金融数据的库不少,但yfinance是我个人最常用的。原因很简单:免费、不用注册、支持股票和加密货币。我刚开始做量化那会儿,为了拿数据还得去各个交易所申请API,麻烦得要命。yfinance直接省掉了这些破事。
核心优势:
- 支持美股、港股、A股(通过特定代码)
- 支持比特币、以太坊等主流加密货币
- 历史数据最多可拉取10年以上
- 自动处理股票分割和股息调整
1.2 安装与基本用法
先装库,这个不用多说:
pip install yfinance pandas numpy
拿数据就几行代码的事:
import yfinance as yf
import pandas as pd
# 获取苹果股票数据
aapl = yf.download('AAPL', start='2020-01-01', end='2023-12-31')
print(aapl.head())
嗯,这里要注意一点。yfinance返回的数据默认是DataFrame,列名是Open、High、Low、Close、Volume、Adj Close。我刚开始用的时候,老是把Adj Close和Close搞混,后来才明白——Adj Close是复权后的收盘价,考虑了分红和拆股。做回测时一定要用Adj Close,不然结果会偏。
1.3 加密货币数据获取
拿加密货币也差不多,就是代码写法有点区别:
# 获取比特币数据
btc = yf.download('BTC-USD', start='2020-01-01', end='2023-12-31')
# 获取以太坊数据
eth = yf.download('ETH-USD', start='2020-01-01', end='2023-12-31')
为什么加个USD?因为yfinance把加密货币当成货币对来处理。BTC-USD就是比特币对美元的价格。我有个朋友一开始写的是'BTC',结果啥也没拿到,折腾了半天才发现少了个后缀。
1.4 处理缺失值——避坑指南
拿到的数据很少是完美的。尤其是加密货币,周末和节假日经常没数据。我曾经因为没处理缺失值,导致策略在回测时出现了奇怪的跳空,差点以为发现了什么套利机会。
常见的缺失值处理方法:
| 方法 | 适用场景 | 代码示例 |
|---|---|---|
| 向前填充 | 非交易时间的数据缺失 | df.fillna(method='ffill') |
| 向后填充 | 数据开头缺失 | df.fillna(method='bfill') |
| 插值法 | 少量缺失且数据平滑 | df.interpolate() |
| 直接删除 | 缺失比例极小 | df.dropna() |
注意:千万别在回测时用未来数据填充!比如用后一天的收盘价填充前一天的缺失值,这属于典型的前瞻偏差。我见过有人因为这个在实盘时亏得底裤都不剩。
1.5 异常值检测与处理
异常值这东西,说白了就是数据里的「刺头」。比如某天比特币突然涨了50%,然后又跌回去,这明显是数据错误。我常用的方法是Z-score和IQR:
from scipy import stats
import numpy as np
# Z-score方法
z_scores = np.abs(stats.zscore(df['Close']))
outliers = df[z_scores > 3] # 超过3个标准差视为异常
# IQR方法
Q1 = df['Close'].quantile(0.25)
Q3 = df['Close'].quantile(0.75)
IQR = Q3 - Q1
outliers = df[(df['Close'] < Q1 - 1.5*IQR) | (df['Close'] > Q3 + 1.5*IQR)]
我个人习惯用IQR方法,因为金融数据往往不是正态分布,Z-score的假设不太成立。处理异常值时,我一般直接删除,或者用前后几天的均值替代。
1.6 重采样与时间对齐
做情绪拐点分析,经常需要把不同频率的数据对齐。比如你有日线数据,但想跟小时级别的情绪指标做对比。这时候就需要重采样:
# 日线转周线
weekly = df.resample('W').agg({
'Open': 'first',
'High': 'max',
'Low': 'min',
'Close': 'last',
'Volume': 'sum'
})
# 日线转小时线(需要先有分钟数据)
hourly = df.resample('H').last()
时间对齐这块,我踩过最大的坑就是时区问题。yfinance默认返回的是UTC时间,但A股是北京时间,美股是美东时间。不对齐的话,你会发现数据对不上。解决办法很简单:
# 统一转换为北京时间
df.index = df.index.tz_convert('Asia/Shanghai')
# 或者去掉时区信息
df.index = df.index.tz_localize(None)
1.7 完整的数据清洗流程
说了这么多,咱们来个完整的例子。这是我做项目时常用的流程:
def clean_data(ticker, start, end):
# 1. 获取数据
df = yf.download(ticker, start=start, end=end)
# 2. 处理缺失值
df = df.fillna(method='ffill').fillna(method='bfill')
# 3. 检测异常值(用IQR方法)
for col in ['Open', 'High', 'Low', 'Close']:
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 3*IQR
upper = Q3 + 3*IQR
df[col] = df[col].clip(lower, upper)
# 4. 只保留交易时间(去掉周末)
df = df[df.index.dayofweek < 5]
return df
# 使用示例
aapl_clean = clean_data('AAPL', '2020-01-01', '2023-12-31')
小技巧:数据清洗完记得保存一份副本。我每次都会把原始数据和清洗后的数据分开存,万一发现清洗逻辑有问题,还能回退重来。
1.8 本章知识体系
下面这张图是我自己总结的数据处理流程,你照着这个思路走,基本不会出大问题:
数据清洗这步看着简单,但真做起来坑不少。我建议你拿到数据后,先画个图看看整体走势,再检查有没有明显的异常点。别急着跑策略,数据干净了,后面的分析才有意义。