技术指标计算:移动平均线(SMA/EMA)、布林带、RSI、MACD的Python实现
做量化交易的朋友都知道,技术指标是信号生成的基础。说白了,它们就是把价格、成交量这些原始数据,通过数学公式加工一下,变成我们能看懂的交易信号。
我个人习惯把技术指标分成两类:一类是趋势跟踪型,比如移动平均线、MACD;另一类是震荡型,比如RSI、布林带。今天咱们就手把手实现这四个最经典的指标。
核心要点:所有技术指标的计算,本质上都是对历史数据的滑动窗口运算。理解了这个,你就能自己实现任何指标。
3.1 移动平均线:SMA与EMA
移动平均线是最基础的指标。它用来平滑价格数据,帮我们看清趋势方向。
简单移动平均线(SMA)
SMA的计算很简单:取最近N天的收盘价,求个平均值。嗯,就这么简单。
import numpy as np
import pandas as pd
def calculate_sma(data, window=20):
"""
计算简单移动平均线
data: 收盘价序列(Series或list)
window: 窗口大小,默认20天
"""
# 我习惯用rolling方法,代码简洁
sma = data.rolling(window=window).mean()
return sma
# 示例
prices = pd.Series([100, 102, 101, 103, 105, 104, 106, 108, 107, 109])
sma_5 = calculate_sma(prices, window=5)
print("5日SMA:", sma_5.tolist())
小技巧:我在项目中遇到过一个问题——数据量不够时,前N-1个值会是NaN。记得用dropna()处理掉,不然后续计算会出问题。
指数移动平均线(EMA)
EMA和SMA的区别在于:EMA给近期的数据更高的权重。说白了,它更敏感,反应更快。
def calculate_ema(data, window=20, smoothing=2):
"""
计算指数移动平均线
smoothing: 平滑因子,通常取2
"""
# 计算权重系数
alpha = smoothing / (window + 1)
# 初始化EMA序列
ema = data.copy()
# 第一个值用SMA代替
ema.iloc[0] = data.iloc[0]
# 递推计算
for i in range(1, len(data)):
ema.iloc[i] = alpha * data.iloc[i] + (1 - alpha) * ema.iloc[i-1]
return ema
# 或者直接用pandas的ewm方法,更省事
def calculate_ema_fast(data, window=20):
return data.ewm(span=window, adjust=False).mean()
注意:EMA的初始值处理方式会影响前几个值。我曾经因为用了不同的初始化方法,导致回测结果差了0.5%。建议统一用SMA作为初始值。
3.2 布林带(Bollinger Bands)
布林带由三条线组成:中轨(SMA)、上轨(中轨+2倍标准差)、下轨(中轨-2倍标准差)。它用来衡量价格的波动范围。
def calculate_bollinger_bands(data, window=20, num_std=2):
"""
计算布林带
data: 收盘价序列
window: 窗口大小
num_std: 标准差倍数,默认2
"""
# 计算中轨(SMA)
middle_band = data.rolling(window=window).mean()
# 计算标准差
std = data.rolling(window=window).std()
# 计算上下轨
upper_band = middle_band + num_std * std
lower_band = middle_band - num_std * std
# 返回DataFrame,方便后续使用
bands = pd.DataFrame({
'middle': middle_band,
'upper': upper_band,
'lower': lower_band
})
return bands
# 示例
bands = calculate_bollinger_bands(prices, window=5, num_std=2)
print(bands)
实战经验:布林带收窄往往预示着大行情要来了。我在做期货策略时,就专门用布林带宽度小于某个阈值作为入场信号,效果还不错。
3.3 相对强弱指标(RSI)
RSI用来衡量价格变动的速度和幅度。取值范围0-100,一般认为超过70是超买,低于30是超卖。
def calculate_rsi(data, window=14):
"""
计算RSI指标
data: 收盘价序列
window: 周期,默认14
"""
# 计算价格变化
delta = data.diff()
# 分离上涨和下跌
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
# 计算平均涨幅和平均跌幅(使用EMA平滑)
avg_gain = gain.ewm(span=window, adjust=False).mean()
avg_loss = loss.ewm(span=window, adjust=False).mean()
# 计算RS和RSI
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
# 示例
rsi_values = calculate_rsi(prices, window=5)
print("RSI:", rsi_values.tolist())
避坑指南:我曾经用简单平均代替EMA来计算RSI,结果信号频繁出错。记住,标准的RSI用的是指数平滑,不是简单平均。
3.4 MACD指标
MACD由三部分组成:快线(DIF)、慢线(DEA)、柱状图(MACD柱)。它结合了趋势和动量两个维度。
def calculate_macd(data, fast=12, slow=26, signal=9):
"""
计算MACD指标
data: 收盘价序列
fast: 快线周期,默认12
slow: 慢线周期,默认26
signal: 信号线周期,默认9
"""
# 计算快线和慢线的EMA
ema_fast = data.ewm(span=fast, adjust=False).mean()
ema_slow = data.ewm(span=slow, adjust=False).mean()
# DIF = 快线 - 慢线
dif = ema_fast - ema_slow
# DEA = DIF的EMA
dea = dif.ewm(span=signal, adjust=False).mean()
# MACD柱 = 2 * (DIF - DEA)
macd_bar = 2 * (dif - dea)
# 打包返回
macd = pd.DataFrame({
'DIF': dif,
'DEA': dea,
'MACD_BAR': macd_bar
})
return macd
# 示例
macd_values = calculate_macd(prices, fast=5, slow=10, signal=3)
print(macd_values)
核心逻辑:MACD的金叉(DIF上穿DEA)是买入信号,死叉(DIF下穿DEA)是卖出信号。但别只看这一个信号,我一般会结合RSI或成交量来确认。
3.5 知识体系总览
下面这张图展示了四个指标之间的关系和应用场景。你想想看,它们其实可以组合使用,形成更可靠的交易策略。
3.6 实战建议
写到这里,我想分享几点个人经验:
- 参数不是死的:默认参数(20、14、12/26/9)只是参考。我在做不同品种时,都会重新优化参数。比如做比特币,我习惯用更短的周期。
- 指标不是越多越好:我见过有人同时用十几个指标,结果信号互相矛盾。其实2-3个就够了,关键是要理解每个指标在说什么。
- 先验证再实盘:任何指标组合,都要先在历史数据上跑一遍。我曾经因为一个计算bug,在实盘上亏了3%。从那以后,我每次写完代码都会做单元测试。
最后提醒:技术指标只是工具,不是圣杯。它们能帮你发现机会,但不能保证盈利。风险管理才是交易的核心。
公众号:蓝海资料掘金营,微信deep3321