版权声明:本文为博主原创文章,如需转载请贴上原博文链接:基于Python的量化交易回测框架Backtrader初识记录(二)-CSDN博客
前言:在上一篇文章 基于Python的量化交易回测框架Backtrader初识记录(一)中,已经初步建立了策略框架并进行了简单回测,但是使用的个股数据还是未复权的,导致K线图看上去显得很异常(例如会出现大幅跳空),且如果建立在未复权的数据上进行量化策略回测操作,很可能会造成策略失效或者异常执行,故本篇借助Tushare的复权因子将个股数据进行前复权处理并制定简单的sma量化策略进行回测验证。
目录
一、数据准备
1.1 个股数据获取
1.2 前复权数据处理
二、简单的SMA量化策略回测
2.1 指标定义
2.2 策略制定
2.2.1 入场/出场策略
2.2.2 买入策略
2.2.3 卖出策略
三、实例“000001.SZ 平安银行”的简单SMA量化策略回测
附:多种个股的SMA量化策略回测结果
盈利实例
亏损实例
一、数据准备
1.1 个股数据获取
本篇内容基于《A股行情数据获取&量化交易策略的结构设计》项目完成,故直接使用本地数据库中的数据完成回测。
1.2 前复权数据处理
当完成上述项目中的前两个步骤之后,个股数据就全部存储到本地了,那么接下来就要将复权因子存储到本地,运行`Module/mysql_data_storage.py`文件后,如图1.1所示,就在本地新建了关于复权因子的数据库。
【在进行前复权数据处理过程中遇到一些问题,比如复权因子数量多于个股日线行情数量而并非一一对应(猜测因为个股在不开盘期间是没有数据的,而复权因子是一年365天都有数据所导致的),但此处不是本文重点故作简述】
二、简单的SMA量化策略回测
2.1 指标定义
在策略类的初始化函数中定义需要使用到的指标,如下代码所示,使用到的指标总共就三个,分别是`MovingAverageSimple`、`AccelerationDecelerationOscillator`和`RelativeStrengthIndex`,其中主要通过`MovingAverageSimple`指标来进行买卖点的判断,剩余两个作为辅助指标:
MovingAverageSimple():简单移动平均线
AccelerationDecelerationOscillator():加速/减速振荡器(协助判断趋势变化的量能)
RelativeStrengthIndex():相对强度指数(协助判断股票买卖力量对比)
class MyStrategy(bt.Strategy):
# 设置周期(数字代表天数)
params = (('p5', 5), ('p10', 10), ('p20', 20), ('p30', 30), ('p45', 45), ('p60', 60))
def __init__(self, ):
"""Constructor for MyStrategy"""
# 使用数据的close(收盘价格)
self.dataclose = self.datas[0].close
# 跟踪待处理订单、买价、佣金
self.order = None
self.buyprice = None
self.buycomm = None
# ============ 参数与指标定义 ============
# 添加SMA指标
self.sma_5 = bt.indicators.MovingAverageSimple(period=self.p.p5)
self.sma_10 = bt.indicators.MovingAverageSimple(period=self.p.p10) # 默认均线周期是10天,可自行设置
self.sma_20 = bt.indicators.MovingAverageSimple(period=self.p.p20) # 默认均线周期是20天,可自行设置
self.sma_30 = bt.indicators.MovingAverageSimple(period=self.p.p30) # 默认均线周期是30天,可自行设置
self.sma_45 = bt.indicators.MovingAverageSimple(period=self.p.p45) # 默认均线周期是45天,可自行设置
self.sma_60 = bt.indicators.MovingAverageSimple(period=self.p.p60) # 默认均线周期是60天,可自行设置
# 买卖点标志
self.count = 0
self.sma_bs1 = self.sma_5 - self.sma_10 # >0:5天均线由零值以下上穿10天均线;<0:反之同理
self.sma_bs2 = self.sma_20 - self.sma_30 # >0:20天均线由零值以下上穿30天均线;<0:反之同理
# 添加辅助指标
self.accdeosc = bt.indicators.AccelerationDecelerationOscillator() # 加减速振荡器
self.rsi = bt.indicators.RelativeStrengthIndex() # 相对强弱指标
2.2 策略制定
想要在市场中获利,从趋势角度而言,上升趋势会比在下降趋势中更容易;从收益角度而言,卖出价格一定要高于买入价格(在不考虑佣金等其他费用的情况下,至少卖出价格要不低于买入价格)才不会有亏损,那么根据这三个指标建立的策略如下所示:
def next(self):
"""
# 策略核心:均线和趋势能量买卖策略
:return:
"""
# 显示收盘价
self.log('Close, %.2f' % self.dataclose[0])
# 如果存在待处理订单则不能发起第二个订单
if self.order:
return
# 检查是否在市场(如果已经有仓位则不能再次买入)
if not self.position:
# 买入前提:均线不能呈下降趋势
# 买点要求(需要同时满足):
# 1.20天均线上穿30天均线
# 2.20天均线呈上升趋势
# 3.连续两天下跌的能量变弱或连续两天上涨的能量变强
if not self.sma_30 < self.sma_45 < self.sma_60: # 60天、45天、30天均线依次呈下降趋势,且前一根压着后一根
if (self.sma_bs2[-1] < 0 < self.sma_bs2[0]) and \
(self.sma_20[-1] < self.sma_20[0]) and \
(self.accdeosc[-2] < self.accdeosc[-1] < self.accdeosc[0]):
self.log('BUY CREATE, %.2f' % self.dataclose[0])
self.order = self.buy() # 跟踪创建的订单,避免出现第二个订单
else:
# 卖出前提:上升趋势减弱或被向下突破则卖出
# 卖点要求(需要同时满足):
# 1.连续两天收盘价在20天均线以下
# 2.连续三天上涨的能量变弱或连续下跌的能量变强
# 3.五天均线在10天或20天均线之下
if self.accdeosc[-1] > self.accdeosc[0]:
self.count += 1
if self.dataclose[0] < self.sma_20 and \
self.dataclose[-1] < self.sma_20 and \
self.count >= 3 and \
(self.sma_5 < self.sma_10 or self.sma_5 < self.sma_20):
self.log('SELL CREATED, %.2f' % self.dataclose[0])
self.order = self.sell()
下面简述一下买卖策略的主要思路:
2.2.1 入场/出场策略
首先最重要的就是,在卖出股票之前,你在市场上必须得要有仓位,换句话说就是,你手上一定要有股票才能够卖出(这里仅针对股票,不针对期货等其他金融产品),这里设计的就是最简单的进场/出场策略:空仓才能执行买策略,非空仓(即有仓位)才能执行卖策略。
if not self.position:
xxx
else:
xxx
2.2.2 买入策略
如前面所说,买入是为了盈利的,那么在下降趋势下盈利的可能就没有在上升趋势下盈利的可能性高,所以给出买入之前的大前提:市场必须不能处在下降趋势下:
# 60天、45天、30天均线依次呈下降趋势,且前一根压着后一根
if not self.sma_30 < self.sma_45 < self.sma_60:
xxx
当然,这样定义上升趋势不太严谨 ,有过实操经验的都知道,更加严谨的表达应该是:短期移动均线压制中期移动均线,中期移动均线压制长期移动均线,同时各个时期的均线也要依次上升,简单代码化之后应该写成:
if (self.sma_30 > self.sma_45 > self.sma_60) and \
(self.sma_30[-1] > self.sma_30[0]) and \
(self.sma_45[-1] > self.sma_45[0]) and \
(self.sma_60[-1] > self.sma_60[0]):
但是这样依然会带来影响,过于严苛的策略条件一方面很可能没有符合的标的,另一方面即使有符合的标的,能够处于如此严苛条件下的时期也不会很长,为了追求完美的交易策略只会损失掉更多的收益。如此我们暂且放宽要求与范围,来制定买入策略。
# >0:5天均线由零值以下上穿10天均线;<0:反之同理
self.sma_bs1 = self.sma_5 - self.sma_10
# >0:20天均线由零值以下上穿30天均线;<0:反之同理
self.sma_bs2 = self.sma_20 - self.sma_30=======================================================
① self.sma_bs2[-1] < 0 < self.sma_bs2[0] -> 20日均线上穿30日均线
② self.sma_20[-1] < self.sma_20[0] -> 20日均线呈上升趋势
③ self.accdeosc[-2] < self.accdeosc[-1] < self.accdeosc[0] -> 连续两天下跌的能量变弱或连续两天上涨的能量变强
当满足以下三点,就是进场买入的时机:① 20天均线上穿30天均线;② 20天均线呈上升趋势;③ 连续两天下跌的能量变弱或连续两天上涨的能量变强;
2.2.3 卖出策略
看到这里,如果有小伙伴说,卖出策略就仿照买入策略来制定呗:当行情出现下降趋势就卖出;如果这样操作可要出大事了!真的要等到行情出现下降趋势,很可能股价都已经跌到谷底了,那要如何制定卖出策略呢?在制定卖出策略之前,要清楚我们的目的是什么,很显然是要盈利,要让我们手中的筹码来给我们赚钱,那么如何才能有盈利呢?
正如2.2节刚开始所说的,卖出价格高于买入价格即可(不考虑其他费用),这就是卖出操作的其中一种:止盈;当然第二种卖出操作就是止损,本节主要针对第一种卖出操作进行策略规划。
在卖出之前,先让策略自己记录当前趋势的能量,如下,每当后一天能量小于前一天能量,那么计数器就+1:
if self.accdeosc[-1] > self.accdeosc[0]:
self.count += 1
当同时满足下列四个条件,则卖出;
① self.dataclose[0] < self.sma_20
② self.dataclose[-1] < self.sma_20 -> ①和②:连续两天的收盘价低于20日均线
③ self.count >= 3 -> 连续三天上涨的能量变弱或连续下跌的能量变强
④ self.sma_5 < self.sma_10 or self.sma_5 < self.sma_20 -> 五天均线在10天或20天均线之下
当然,同时满足四个条件再执行卖出操作难免会太过绝对,而且可能会出现第一天刚买入就开始下跌,来不及止损,到第二天再卖出就已经亏损的情况,这时就需要对策略进行相应的优化了。
三、实例“000001.SZ 平安银行”的简单SMA量化策略回测
如图3.1所示为平安银行2022年1月1日至今的SMA量化策略的回测结果,自上而下:
第一栏(‘Broker’——(券商)经纪人/代理人,此处的券商相当于你的代理人,由你的代理人代替你进行交易操作)表示券商手中的现金和股票总价值,红色线表示券商手中的现金余量,蓝色线表示券商手中现金和手中持股的总价值;
第二栏(‘Trades - Net Profit/Loss’——交易净利润/亏损)展示的就是量化交易策略执行的两次买卖操作,蓝色点表示该次操作获得利润,红色点表示该次操作亏损;
第三栏就是K线和成交量主体以及各类移动平均线,绿色^表示买入K线,红色v表示卖出K线;
第四栏是ACCDE(加速/减速振荡器)指标,用来辅助进行策略买卖;
第五栏是RSI(相对强度指数)指标,同样用来辅助进行策略买卖;
经历两年多,该量化策略只进行了两次买卖操作,没有丰厚的盈利也没有造成大幅亏损,从K线图来看,可是存在不少可交易的买卖点的,但是该策略却没有如愿的进行,由此也可见固定的策略依然不能够很好的匹配个股,看来简单的指标还是有局限性,后续会再尝试不同的指标来制定不同的量化策略。至此,简单的SMA量化策略回测结束,以下附带一些其他的个股通过该策略所得的回测结果。
附:多种个股的SMA量化策略回测结果
盈利实例
亏损实例
以上结果均可自行复现,代码见1.1节中的项目。