第二章 金融数据基础:股票数据获取、清洗与指标计算

各位同学,欢迎来到第二章。说实话,这一章是整个课程的地基。你想想看,算法再牛,模型再强,数据要是烂了,一切都白搭。我在量化交易这行摸爬滚打这些年,见过太多人把精力全花在调参上,结果数据源都没搞干净——嗯,这种亏我吃过,所以今天咱们把基础打扎实。

2.1 用 yfinance 获取股票数据

我个人习惯用 yfinance 这个库。为什么?因为它免费、简单,而且数据质量对于入门和策略回测来说完全够用。你不需要去申请什么 API Key,一行代码就能拉数据。

先装库:

pip install yfinance

然后拉取某只股票的历史数据。比如我经常拿贵州茅台(A股代码 600519.SS)做演示:

import yfinance as yf

# 下载贵州茅台近1年数据
df = yf.download('600519.SS', start='2023-01-01', end='2024-01-01')
print(df.head())

这里要注意,A股代码后面要加 .SS(上海)或 .SZ(深圳)。港股是 .HK,美股直接写代码就行。我曾经在项目里因为忘记加后缀,拉了一堆空数据回来,排查了半天才发现——这种低级错误,大家引以为戒。

yfinance 返回的是 Pandas DataFrame,包含 Open、High、Low、Close、Volume、Adj Close 这几列。我们做策略,最常用的是收盘价(Close)和成交量(Volume)。

小技巧: 如果你只想拉最近 N 个交易日的数据,可以用 period='1y' 参数,不用写起止日期。比如 yf.download('AAPL', period='6mo') 拉苹果半年的数据。

2.2 数据清洗与预处理

数据拉下来,别急着算指标。先看看有没有脏数据。我遇到过的情况包括:

  • 停牌日导致数据缺失(NaN)
  • 除权除息日价格跳空
  • 节假日数据为空
  • 偶尔有异常值(比如成交量突然少个零)

我的标准流程是这样的:

# 1. 检查缺失值
print(df.isnull().sum())

# 2. 向前填充(用上一个交易日的数据填充)
df.fillna(method='ffill', inplace=True)

# 3. 如果开头还有缺失,向后填充
df.fillna(method='bfill', inplace=True)

# 4. 删除剩余的空值(极少情况)
df.dropna(inplace=True)

为什么用向前填充?因为金融数据有连续性。今天停牌,明天开盘价大概率参考昨天收盘价。这是行业惯例。

注意: 千万不要直接 df.dropna() 了事。如果连续几天停牌,你会丢掉一大段数据,回测结果会失真。我曾经在回测一只小盘股时,因为没处理好停牌数据,策略信号全乱了,亏了不少模拟资金。

另外,检查一下有没有价格小于等于0的情况。理论上不可能,但数据源偶尔抽风:

# 检查异常值
print(df[df['Close'] <= 0])

如果有,直接删掉那行。别犹豫。

2.3 技术指标计算:SMA、RSI、MACD

技术指标是量化策略的「眼睛」。我个人最常用的三个:SMA(简单移动平均)、RSI(相对强弱指标)、MACD(指数平滑异同移动平均线)。

2.3.1 SMA(简单移动平均)

SMA 就是过去 N 天的收盘价平均值。说白了,它帮你过滤掉短期噪音,看清趋势方向。

# 计算 20 日均线
df['SMA_20'] = df['Close'].rolling(window=20).mean()

# 计算 60 日均线
df['SMA_60'] = df['Close'].rolling(window=60).mean()

这里有个坑:rolling() 计算出来的前 N-1 天是 NaN。我建议用 min_periods 参数控制最少需要多少有效数据:

df['SMA_20'] = df['Close'].rolling(window=20, min_periods=10).mean()

这样前10天也能算出值,虽然精度差一点,但不会让策略在初期无信号。

2.3.2 RSI(相对强弱指标)

RSI 衡量价格变动的速度和幅度,范围0-100。一般认为超过70超买,低于30超卖。但说实话,这个阈值在A股经常失效,我一般会动态调整。

计算公式:

def compute_rsi(data, window=14):
    delta = data.diff()
    gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
    rs = gain / loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

df['RSI_14'] = compute_rsi(df['Close'])

注意:当 loss 为0时,RSI 会变成100。这在连续上涨行情中常见。我曾经在牛市中用 RSI 做空信号,结果被连续打脸——因为 RSI 在强势行情中会长期维持在70以上,超买之后还能更超买。

核心经验: RSI 在震荡市中效果最好,趋势行情里容易失效。建议结合趋势指标(比如 SMA)一起用。

2.3.3 MACD(指数平滑异同移动平均线)

MACD 由三部分组成:快线(DIF)、慢线(DEA)、柱状图(MACD柱)。它本质上是两条指数移动平均线的差值。

# 计算 MACD
exp12 = df['Close'].ewm(span=12, adjust=False).mean()
exp26 = df['Close'].ewm(span=26, adjust=False).mean()
df['MACD_DIF'] = exp12 - exp26
df['MACD_DEA'] = df['MACD_DIF'].ewm(span=9, adjust=False).mean()
df['MACD_HIST'] = 2 * (df['MACD_DIF'] - df['MACD_DEA'])

MACD 的金叉(DIF 上穿 DEA)是买入信号,死叉(DIF 下穿 DEA)是卖出信号。但这里有个细节:金叉的位置很重要。如果在零轴上方金叉,说明多头趋势强劲;如果在零轴下方金叉,可能只是反弹。

我个人习惯把 MACD 和成交量配合使用。金叉当天如果成交量放大,信号可靠性更高。

2.4 本章知识体系总览

下面这张图,是我自己梳理的本章知识结构。你把它存下来,学完这章后对照着检查自己是否都掌握了。

第二章:金融数据基础 数据获取 yfinance 库 股票代码规则 历史数据下载 DataFrame 结构 数据清洗 缺失值处理 向前/向后填充 异常值检测 停牌数据处理 指标计算 SMA 移动平均 RSI 相对强弱 MACD 趋势指标 金叉死叉判断 数据质量决定策略上限 → 清洗比建模更重要 本章关键点 ✓ 掌握 yfinance 数据获取 ✓ 学会缺失值处理流程 ✓ 能独立计算 SMA/RSI/MACD ✓ 理解指标的使用场景与局限

2.5 实战小贴士

最后,分享几个我在实战中总结的经验:

指标 常用参数 我的经验
SMA 20日、60日 短线用10/20,中线用60/120
RSI 14日 震荡市用30/70,趋势市放宽到20/80
MACD 12/26/9 零轴上方金叉更可靠,配合成交量确认

嗯,这一章的内容就到这里。数据基础打好了,后面讲策略回测、强化学习环境搭建,你才不会手忙脚乱。记住:好的数据,是成功策略的一半。


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