Python量化交易开发(五):移动平均线/MACD/RSI/布林带实操教程

打开今日头条查看图片详情

在量化交易的世界里,技术指标是解读市场趋势的“指南针”,但盲目套用现成指标、依赖黑盒工具,往往是新手亏损的核心原因。真正的量化交易者,不仅要会用指标,更要懂其原理、知其边界。今天这篇实操教程,就带大家用Python+akshare从零实现移动平均线、MACD、RSI、布林带这四大主流技术指标,结合真实股票数据拆解应用逻辑,帮你把技术指标从“工具”变成“可控的分析武器”。

在开始之前,先跟大家说清楚这篇教程的核心价值:全程无复杂公式堆砌,每一步代码都有详细解读,每个指标都搭配A股真实案例(以贵州茅台、比亚迪等明星股为例),不仅教你“怎么实现”,更教你“怎么在不同市场环境下用对”。无论你是刚接触量化的新手,还是想深化指标理解的交易者,跟着步骤走都能直接落地应用。

一、前期准备:akshare安装与数据获取核心步骤

技术指标的实现离不开真实的股票数据,akshare作为免费、稳定的财经数据接口,能直接获取A股、港股等市场的历史数据,是量化入门的首选工具。这部分我们先搞定基础环境搭建,确保后续指标实现顺畅。

首先是akshare的安装,打开Python编辑器(推荐PyCharm或Jupyter Notebook),在终端输入以下命令:pip install akshare --upgrade。这里要注意,建议安装最新版本,避免因版本兼容问题导致数据获取失败。如果安装过程中出现依赖包报错,可以补充安装对应的依赖:pip install pandas numpy matplotlib,这三个是数据处理和可视化的基础库,后续绘制指标图形会用到。

接下来是核心的数据获取步骤,我们以获取贵州茅台(600519)2024年1月1日至2024年12月31日的日线数据为例,代码如下:

import akshare as akimport pandas as pdimport matplotlib.pyplot as plt# 设置中文字体,避免绘图时中文乱码plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = False# 获取贵州茅台日线数据stock_data = ak.stock_zh_a_hist(symbol='600519', period='daily', start_date='20240101', end_date='20241231', adjust='qfq')# 查看数据结构,保留核心字段print(stock_data[['日期', '开盘', '最高', '最低', '收盘', '成交量']].head())

打开今日头条查看图片详情

代码解读:首先导入需要的库,akshare用于获取数据,pandas用于数据处理,matplotlib用于绘图;设置中文字体是为了后续绘制K线图和指标时,中文标签能正常显示;stock_zh_a_hist函数中,symbol是股票代码,period设置为“daily”表示日线数据,start_date和end_date是数据的时间范围,adjust='qfq'表示前复权,这样能消除除权除息对股价的影响,更贴近实际交易价格。

运行代码后,我们会得到包含日期、开盘价、最高价、最低价、收盘价等核心字段的数据框。这里要提醒大家,后续所有指标的计算,核心都是基于“收盘价”,所以要确保这一列数据完整,若有缺失值,可以用dropna()方法删除,或用fillna()方法填充。

二、移动平均线(MA):趋势判断的基础工具

1. 数学原理拆解

移动平均线的核心逻辑是“平滑股价波动,凸显趋势方向”,其数学原理非常简单:取连续n个交易日的收盘价的算术平均值,随着时间推移形成的曲线就是n日移动平均线。常见的周期有5日(短期趋势)、10日、20日(中期趋势)、60日(长期趋势)等。公式如下:MA(n) = (C1 + C2 + ... + Cn)/ n,其中Ci是第i个交易日的收盘价。

2. 代码实现与案例演示

基于前面获取的贵州茅台数据,我们实现5日、20日、60日三条移动平均线,代码如下:

# 计算5日、20日、60日移动平均线stock_data['MA5'] = stock_data['收盘'].rolling(window=5).mean()stock_data['MA20'] = stock_data['收盘'].rolling(window=20).mean()stock_data['MA60'] = stock_data['收盘'].rolling(window=60).mean()# 筛选出有完整指标数据的行(前59日无60日均线数据,需剔除)ma_data = stock_data.dropna(subset=['MA60'])# 绘制K线图与移动平均线plt.figure(figsize=(12, 6))plt.plot(ma_data['日期'], ma_data['收盘'], label='收盘价', color='black', linewidth=1)plt.plot(ma_data['日期'], ma_data['MA5'], label='5日均线', color='red', linewidth=1.5)plt.plot(ma_data['日期'], ma_data['MA20'], label='20日均线', color='blue', linewidth=1.5)plt.plot(ma_data['日期'], ma_data['MA60'], label='60日均线', color='green', linewidth=1.5)plt.title('贵州茅台2024年日线图与移动平均线')plt.xlabel('日期')plt.ylabel('价格(元)')plt.legend()plt.xticks(rotation=45)plt.tight_layout()plt.show()

打开今日头条查看图片详情

代码解读:rolling(window=n)表示取连续n个数据的窗口,mean()计算窗口内的平均值,这样就能快速得到不同周期的移动平均线;由于60日均线需要60个交易日的数据,前59行会出现NaN值,用dropna()剔除后,数据更干净;最后通过matplotlib绘制收盘价和三条均线,直观展示趋势。

案例分析:从绘制的图形中可以看到,2024年3-5月,贵州茅台的5日均线始终在20日、60日均线上方运行,且三条均线呈向上发散状态,这是典型的上升趋势信号,期间收盘价从1700元左右上涨至1900元左右,涨幅约11.8%;而在7-8月,5日均线多次下穿20日均线,且三条均线相互缠绕,说明市场进入震荡整理阶段,此时若盲目追涨杀跌,很容易被套牢。这里要提醒大家,移动平均线在趋势明确的行情中有效性较高,在震荡行情中信号会频繁失效,这是其核心适用边界。

三、MACD:多空力量的“晴雨表”

1. 数学原理拆解

MACD(指数平滑异同平均线)的核心是通过两条不同周期的指数移动平均线(EMA)的差值,反映多空力量的变化。其计算分为三步:第一步计算12日EMA和26日EMA,EMA是加权平均,更注重近期股价的影响;第二步计算DIF(离差值)= EMA12 - EMA26;第三步计算DEA(信号线)= DIF的9日EMA;最后MACD柱状线= 2*(DIF - DEA)。当DIF上穿DEA时,视为多头信号;当DIF下穿DEA时,视为空头信号。

2. 代码实现与案例演示

我们继续用贵州茅台2024年的数据实现MACD指标,代码如下:

# 计算12日、26日EMAstock_data['EMA12'] = stock_data['收盘'].ewm(span=12, adjust=False).mean()stock_data['EMA26'] = stock_data['收盘'].ewm(span=26, adjust=False).mean()# 计算DIF、DEA、MACD柱状线stock_data['DIF'] = stock_data['EMA12'] - stock_data['EMA26']stock_data['DEA'] = stock_data['DIF'].ewm(span=9, adjust=False).mean()stock_data['MACD'] = 2 * (stock_data['DIF'] - stock_data['DEA'])# 剔除NaN值macd_data = stock_data.dropna(subset=['MACD'])# 绘制MACD图形fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), gridspec_kw={'height_ratios': [3, 1]})# 上半部分:收盘价与EMAax1.plot(macd_data['日期'], macd_data['收盘'], label='收盘价', color='black', linewidth=1)ax1.plot(macd_data['日期'], macd_data['EMA12'], label='EMA12', color='red', linewidth=1.5)ax1.plot(macd_data['日期'], macd_data['EMA26'], label='EMA26', color='blue', linewidth=1.5)ax1.set_title('贵州茅台2024年收盘价与EMA')ax1.set_ylabel('价格(元)')ax1.legend()ax1.tick_params(axis='x', labelrotation=45)# 下半部分:MACD柱状线ax2.bar(macd_data['日期'], macd_data['MACD'], label='MACD柱状线', color=macd_data['MACD'].apply(lambda x: 'red' if x > 0 else 'green'))ax2.plot(macd_data['日期'], macd_data['DIF'], label='DIF', color='orange', linewidth=1.5)ax2.plot(macd_data['日期'], macd_data['DEA'], label='DEA', color='purple', linewidth=1.5)ax2.axhline(y=0, color='black', linestyle='--', linewidth=1)ax2.set_title('MACD指标')ax2.set_xlabel('日期')ax2.set_ylabel('MACD值')ax2.legend()ax2.tick_params(axis='x', labelrotation=45)plt.tight_layout()plt.show()

打开今日头条查看图片详情

代码解读:ewm(span=n, adjust=False)用于计算EMA,span表示周期,adjust=False表示采用标准的EMA计算方式;通过两次EMA计算得到DIF和DEA,再推导得到MACD柱状线;绘图时采用上下分图的形式,上半部分展示收盘价和EMA,下半部分展示MACD指标,方便对比分析。

案例分析:观察2024年10月的图形,10月15日左右,DIF从下方上穿DEA,同时MACD柱状线从负区间突破至正区间,形成“金叉”信号,此时收盘价为1850元左右,后续1个月内股价上涨至2000元左右,涨幅约8.1%;而在11月20日左右,DIF下穿DEA,MACD柱状线回归负区间,形成“死叉”信号,后续股价出现回调,从2000元跌至1900元左右。这里要注意,MACD指标在趋势行情中信号更可靠,但在震荡行情中会出现多次假信号,比如2024年6月,DIF和DEA多次交叉,此时若依据信号交易,会频繁亏损。另外,当股价创新高但DIF未创新高(顶背离),或股价创新低但DIF未创新低(底背离)时,往往是趋势反转的预警信号,这一点在实际应用中要重点关注。

四、RSI:震荡行情的“精准导航”

1. 数学原理拆解

RSI(相对强弱指数)的核心是通过计算股价上涨幅度和下跌幅度的比值,判断市场的超买超卖状态。计算步骤:第一步计算n日内的平均上涨幅度(UP)和平均下跌幅度(DOWN),上涨幅度=当日收盘价-前一日收盘价(若为负则记为0),下跌幅度=前一日收盘价-当日收盘价(若为负则记为0);第二步计算相对强弱RS=UP/DOWN;第三步计算RSI=100 - (100/(1+RS))。RSI的取值范围是0-100,通常认为RSI>70为超买状态,预示可能下跌;RSI<30为超卖状态,预示可能上涨。

2. 代码实现与案例演示

我们以14日RSI为例,用比亚迪(002594)2024年1月1日至2024年12月31日的数据实现,代码如下:

# 获取比亚迪日线数据byd_data = ak.stock_zh_a_hist(symbol='002594', period='daily', start_date='20240101', end_date='20241231', adjust='qfq')# 计算价格变动byd_data['价格变动'] = byd_data['收盘'].diff()# 计算上涨幅度和下跌幅度byd_data['上涨幅度'] = byd_data['价格变动'].apply(lambda x: x if x > 0 else 0)byd_data['下跌幅度'] = byd_data['价格变动'].apply(lambda x: -x if x < 0 else 0)# 计算14日平均上涨幅度和平均下跌幅度(采用平滑计算方式)byd_data['UP'] = byd_data['上涨幅度'].ewm(span=14, adjust=False).mean()byd_data['DOWN'] = byd_data['下跌幅度'].ewm(span=14, adjust=False).mean()# 计算RS和RSIbyd_data['RS'] = byd_data['UP'] / byd_data['DOWN'].replace(0, 0.0001)  # 避免分母为0byd_data['RSI14'] = 100 - (100 / (1 + byd_data['RS']))# 剔除NaN值rsi_data = byd_data.dropna(subset=['RSI14'])# 绘制RSI图形fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), gridspec_kw={'height_ratios': [3, 1]})# 上半部分:收盘价ax1.plot(rsi_data['日期'], rsi_data['收盘'], label='收盘价', color='black', linewidth=1)ax1.set_title('比亚迪2024年收盘价')ax1.set_ylabel('价格(元)')ax1.legend()ax1.tick_params(axis='x', labelrotation=45)# 下半部分:RSIax2.plot(rsi_data['日期'], rsi_data['RSI14'], label='14日RSI', color='orange', linewidth=1.5)ax2.axhline(y=70, color='red', linestyle='--', label='超买线')ax2.axhline(y=30, color='green', linestyle='--', label='超卖线')ax2.axhline(y=50, color='gray', linestyle='--', label='多空分界线')ax2.set_title('14日RSI指标')ax2.set_xlabel('日期')ax2.set_ylabel('RSI值')ax2.legend()ax2.tick_params(axis='x', labelrotation=45)plt.tight_layout()plt.show()

打开今日头条查看图片详情

代码解读:diff()方法用于计算相邻两个交易日收盘价的差值,即价格变动;通过lambda函数筛选出上涨幅度和下跌幅度;用ewm计算平滑后的平均上涨幅度和平均下跌幅度,避免了简单移动平均的滞后性;在计算RS时,用replace(0, 0.0001)避免分母为0的情况;最后绘制上下分图,清晰展示收盘价与RSI的关系。

案例分析:2024年2月,比亚迪的RSI14多次低于30,进入超卖区间,2月20日左右RSI从超卖区间回升,此时收盘价为23元左右,后续1个月内股价上涨至28元左右,涨幅约21.7%;而在2024年8月,RSI14多次高于70,进入超买区间,8月15日左右RSI从超买区间回落,此时收盘价为32元左右,后续股价回调至29元左右。这里要重点说明,RSI指标在震荡行情中有效性极高,比如2024年4-6月,比亚迪处于震荡区间,RSI的超买超卖信号多次精准对应股价的涨跌;但在趋势明确的行情中,RSI可能会长期处于超买或超卖区间,比如2024年11-12月,比亚迪处于上升趋势,RSI14长期在50以上,甚至多次高于70,但股价仍持续上涨,此时若依据RSI超买信号卖出,会错过后续涨幅。

五、布林带:趋势与震荡的“边界探测器”

1. 数学原理拆解

布林带(Bollinger Bands)的核心是通过移动平均线和标准差,确定股价的合理波动范围,反映市场的趋势强度和波动率。计算步骤:第一步计算n日移动平均线(通常用20日MA,作为中轨);第二步计算n日内收盘价的标准差;第三步计算上轨=中轨+2*标准差,下轨=中轨-2*标准差。当股价突破上轨时,说明趋势强劲(多头);当股价跌破下轨时,说明趋势弱势(空头);当股价在上下轨之间震荡时,说明市场处于整理状态。

2. 代码实现与案例演示

我们以20日布林带为例,用宁德时代(300750)2024年1月1日至2024年12月31日的数据实现,代码如下:

# 获取宁德时代日线数据ndsd_data = ak.stock_zh_a_hist(symbol='300750', period='daily', start_date='20240101', end_date='20241231', adjust='qfq')# 计算20日MA(中轨)ndsd_data['MA20'] = ndsd_data['收盘'].rolling(window=20).mean()# 计算20日标准差ndsd_data['STD20'] = ndsd_data['收盘'].rolling(window=20).std()# 计算上轨和下轨ndsd_data['上轨'] = ndsd_data['MA20'] + 2 * ndsd_data['STD20']ndsd_data['下轨'] = ndsd_data['MA20'] - 2 * ndsd_data['STD20']# 剔除NaN值boll_data = ndsd_data.dropna(subset=['上轨', '下轨'])# 绘制布林带图形plt.figure(figsize=(12, 6))plt.plot(boll_data['日期'], boll_data['收盘'], label='收盘价', color='black', linewidth=1)plt.plot(boll_data['日期'], boll_data['MA20'], label='中轨(20日MA)', color='blue', linewidth=1.5)plt.plot(boll_data['日期'], boll_data['上轨'], label='上轨', color='red', linestyle='--', linewidth=1.5)plt.plot(boll_data['日期'], boll_data['下轨'], label='下轨', color='green', linestyle='--', linewidth=1.5)# 填充上下轨之间的区域plt.fill_between(boll_data['日期'], boll_data['上轨'], boll_data['下轨'], alpha=0.1, color='blue')plt.title('宁德时代2024年日线图与布林带')plt.xlabel('日期')plt.ylabel('价格(元)')plt.legend()plt.xticks(rotation=45)plt.tight_layout()plt.show()

打开今日头条查看图片详情

代码解读:rolling(window=20).std()用于计算20日内收盘价的标准差;上轨和下轨通过中轨加减2倍标准差得到,2倍标准差是统计意义上的大概率波动范围,约95%的股价会落在这个区间内;fill_between()方法用于填充上下轨之间的区域,让布林带图形更直观。

案例分析:2024年5-7月,宁德时代处于上升趋势,股价始终在布林带上轨和中轨之间运行,且布林带呈向上开口状态,说明趋势强劲,期间收盘价从40元左右上涨至50元左右,涨幅约25%;而在2024年9-10月,股价在布林带上下轨之间震荡,布林带开口收窄,说明市场进入整理阶段,此时适合观望,不宜盲目交易;2024年11月,股价突破布林带上轨,且布林带再次向上开口,后续股价继续上涨,从52元涨至58元左右,涨幅约11.5%。这里要提醒大家,布林带的开口方向的变化是判断趋势强弱的关键:向上开口表示趋势增强,向下开口表示趋势减弱,开口收窄表示即将进入震荡或趋势反转。另外,当股价突破上轨或下轨后,若伴随成交量放大,说明突破有效;若成交量萎缩,则可能是假突破。

六、实用技巧:指标组合使用与适用场景总结

通过前面的案例我们能发现,单一技术指标都有其局限性:移动平均线适合趋势行情,RSI适合震荡行情,MACD能反映多空力量变化,布林带能判断波动范围。在实际交易中,将多个指标组合使用,能大幅提高信号的可靠性。比如:趋势行情中,用移动平均线判断趋势方向,结合MACD的金叉死叉确认信号;震荡行情中,用RSI判断超买超卖,结合布林带的上下轨确定交易区间。

这里给大家分享一个实战中常用的组合策略:以20日MA判断趋势,当股价在20日MA上方时,视为上升趋势,此时关注MACD金叉信号,同时结合RSI未进入超买区间,可考虑买入;当股价在20日MA下方时,视为下降趋势,此时关注MACD死叉信号,结合RSI未进入超卖区间,可考虑卖出;当股价在20日MA附近震荡时,用RSI和布林带判断超买超卖和波动范围,区间内低买高卖。

另外,还要注意指标参数的调整:不同市场、不同股票的波动率不同,指标参数也需要灵活调整。比如,对于波动率较高的科技股,可将RSI的周期调整为9日,让信号更灵敏;对于波动率较低的蓝筹股,可将RSI的周期调整为21日,减少假信号。