本文是量化策略解析系列的第2篇。系列连载见:
量化策略解析—系列连载
在上一篇文章《均线择时策略全攻略(上)》中,我们介绍了均线择时策略的思路,本篇将用一个具体的例子来介绍如何用Python来实现均线择时策略。
一、获取基础数据
我们以上证指数为例,讲解如何用Python实现均线择时。
- 导入需要用到的库
#导入需要使用的库
import akshare as ak
import pandas as pd
import numpy as np
import pandas_ta as ta
#在matplotlib绘图中显示中文和负号
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams[‘font.family’] = ‘STKAITI’
plt.rcParams[‘axes.unicode_minus’] = False
#关闭警告信息
import warnings
warnings.filterwarnings(‘ignore’)
- 从AKShare数据源获取上证指数的数据
关于AKShare的使用,可以参看后附的文章《如何用AKShare获取金融数据》。AKShare的接口有时会有变动,如果获取数据出错请参考AKShare的官网解决。
#获取上证指数数据
index_code = ‘sh000001’
start_date = pd.to_datetime(‘2014-01-01’)
end_date = pd.to_datetime(‘2023-12-31’)
price_df = ak.stock_zh_index_daily(symbol=index_code)
price_df[‘date’] = pd.to_datetime(price_df[‘date’]).dt.date
price_df = price_df[(price_df[‘date’]>=start_date) & (price_df[‘date’]<=end_date)]
price_df = price_df.sort_values(‘date’).set_index(‘date’)
上述代码从AKShare的stock_zh_index_daily接口获取上证指数的行情数据保存到一个名为”price_df”的DataFrame中,并选取’2014-01-01’至’2023-12-31’这10年的数据作为本例的样本数据。
- 计算每日的收益率
#计算每日的收益率
price_df[‘returns’] = price_df[‘close’].pct_change().shift(-1).fillna(0)
上述代码用pct_change()函数计算每日的收益率。
price_df的格式如下:

二、单均线择时策略
- 计算移动平均线
移动平均线的种类很多,这里用简单移动平均线作为示例。我们使用pandas_ta库来计算移动平均线,关于pandas_ta库的使用可以参看后附的文章《量化宝藏工具箱:技术指标库 Pandas TA 教程》。
#计算移动平均线值
days = 20
price_df[f’ma_{days}’] = ta.sma(price_df[‘close’], length=days)
上述代码的功能是调用pandas_ta库来计算20日简单移动平均线(SMA)。
- 计算择时信号
开仓信号:当日的均线值大于昨日的均线值时开仓,值为1;
清仓信号:当日的均线值小于昨日的均线值时清仓,值为0。
#择时信号:当日均线值大于昨日则开仓,否则清仓
timing_df = (price_df[[f’ma_{days}’]].diff()>0) * 1.
timing_df[‘不择时’] = 1.
上述代码中,price_df[[f’ma_{days}’]].diff()计算20日均线值的变化量,即当天的均线值与前一天的均线值的差。若当天的均线值大于前一天的,则diff()>0的结果为True,乘以1.后变为1.0,表示发出买入信号;若不大于,则结果为False,乘以1.后变为0.0,表示发出卖出信号。在timing_df中还添加了一个名为不择时的列,其值均为1.0,代表不进行择时操作时的基准情况。
- 计算择时的效果
#计算每日收益率
timing_ret = timing_df.mul(price_df[‘returns’], axis=0)
timing_ret[‘超额收益’] = (1+timing_ret[f’ma_{days}’]).div(1+timing_ret[‘不择时’], axis=0) – 1.
#计算累计收益率
cumul_ret = (1 + timing_ret.fillna(0)).cumprod() – 1.
这段代码先计算每日收益率(包括择时后的每日收益和不择时的每日收益),用mul()函数将择时信号与前面计算的日收益率相乘,如果择时信号为1则保留当日收益率,如果择时信号为0则当日收益率为0;然后计算超额收益,即将择时的收益与不择时的收益比较;最后用cumprod()函数计算累计收益。
关于各种收益的计算,可以参看后附的文章《一文讲清7种收益率的python实现》和《计算超额收益,你用减法还是除法》。
- 将择时的效果可视化输出
#可视化输出
cumul_ret.plot(figsize=(10, 6), title=’单均线择时’)
结果如下:

三、双均线择时策略
双均线择时策略需要两条均线:长均线和短均线。
- 计算长短周期的均线值
days_s = 5
days_l = 60
price_df[‘ma_s’] = ta.sma(price_df[‘close’], length=days_s)
price_df[‘ma_l’] = ta.sma(price_df[‘close’], length=days_l)
上述代码中,days_s 为短周期(5天),days_l 为长周期(60天)。然后使用pandas_ta库来计算长短两个周期的移动平均线。 - 计算择时信号
开仓信号:当短均线值大于长均线值时开仓,值为1;
清仓信号:当短均线值小于长均线值时清仓,值为0。
#择时信号:当日短期均线值大于长期均线值则开仓,否则清仓
timing_df = pd.DataFrame()
timing_df[‘择时’] = (price_df[‘ma_s’]>price_df[‘ma_l’]) * 1.
timing_df[‘不择时’] = 1.
- 计算择时的效果
#计算择时后的每日收益率
timing_ret = timing_df.mul(price_df[‘returns’], axis=0)
timing_ret[‘超额收益’] = (1+timing_ret[‘择时’]).div(1+timing_ret[‘不择时’], axis=0) – 1.
#计算累计收益率
cumul_ret = (1 + timing_ret.fillna(0)).cumprod() – 1.
- 将择时的效果可视化输出
#可视化输出
cumul_ret.plot(figsize=(10, 6), title=’双均线择时’)
结果如下:

四、均线择时的分析
- 均线择时的效果分析
(1)从择时的收益曲线可以看到,在本例中,不管是单均线择时还是双均线择时,都比不择时的效果要好。
(2)分析择时的超额曲线可以看到,择时的收益主要来源于指数大跌时的止损。而由于均线的滞后性,在指数由跌转涨时,由于未能及时开仓,此时的超额收益是负的(表现为超额收益曲线的下跌),尤其在震荡行情中,均线择时容易反复打脸。 - 均线择时的参数优化
均线择时主要有两个参数:
(1)均线的类型
均线的类型有多种,本例中使用了简单移动平均线,我们还可以使用其他的均线来观察择时效果。关于各种均线的介绍,可以参看后附的文章《均线解密:如何有效利用移动平均线》。利用pandas_ta库,很容易就能计算各种不同的均线。
(2)均线的周期
在本例中,我们在单均线策略中使用了20日均线,在双均线策略中使用了5日均线和60日均线。我们还可以尝试其他的均线周期,观察不同周期的择时效果。但要注意的是,如果我们对所有的均线周期进行遍历,总能够找到一个最好的周期值,但这个周期值可能是过拟合的结果,即过度拟合了样本数据,但在样本外可能是失效的。为了防止这个问题,我们可以将数据分为样本内和样本外两份,在样本内寻找最佳参数,然后在样本外验证这个参数是否有效。
均线择时是一种简单但常用的择时策略。尽管存在一定的局限性和挑战,但通过合理的策略设计和优化,均线择时仍然是量化投资领域中一个有价值的工具。
发布者:爱吃肉的小猫,转载请注明出处:https://www.95sca.cn/archives/40014
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!