写一个双均线策略,使用backtrader进行回测,并选出双均线的最佳参数
先说单均线策略,单均线策略是量化交易策略里面比较典型的一种。单均线的交易策略逻辑比较简单:价格上穿均线时买入,价格下穿均线时卖出。
而双均线策略,顾名思义,就是两根均线:短期均线和长期均线。当短线均线上穿长期均线(金叉)时买入,当短期均线下穿长期均线(死叉)时卖出,这就是双均线策略的核心思想。
可参考:股票python量化交易020-双均线策略(上)
对于均线应该是众多投资者经常会用到的技术指标,但是大家也熟知在股价原有趋势发生反转时,由于均线的追踪趋势的特性,均线的行动往往过于迟缓,调头速度落后于大趋势。这也是是均线的一个极大的弱点。在市场中,单独依靠均线获取超额收益的投资者非常之少,虽然都知道金叉买入,死叉卖出,但执行起来却非常困难。
双均线策略要优于单均线策略,在股市震荡中,双均线策略的波动要小很多。
逻辑很简单,直接上代码:
import backtrader as bt
from backtrader import cerebro
import data.stock as st
import pandas as pd
class MyStrategy(bt.Strategy):
params = (
('short_ma', 10),
('long_ma', 120),
)
def log(self, txt, dt=None):
dt = dt or self.datas[0].datetime.date(0)
print('%s %s ' % (dt.isoformat(), txt))
def __init__(self):
self.order = None
self.ma1 = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.short_ma)
self.ma2 = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.long_ma)
# print(self.ma1, self.ma2)
def next(self):
if self.order:
return
if not self.position:
if self.ma1[0] > self.ma2[0]:
print('==买入:买入时短期均线价格%.2f,长期均线价格%.2f' % (self.ma1[0], self.ma2[0]))
self.order = self.buy(size=200)
else:
if self.ma1[0] < self.ma2[0]:
print('==卖出:卖出时短期均线价格%.2f,长期均线价格%.2f' % (self.ma1[0], self.ma2[0]))
self.order = self.sell(size=200)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# 做多/做空 订单 已提交/已执行 到/被代理 - 无事可做
return
# 检查订单是否已经完成
# 注意:如果没有足够资金,代理可能拒绝订单
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else: # 做空
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected===可能资金不足!!!')
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm))
def feed_data(code, startDate, endDate):
'''
自定义方法:backtrader喂数据
:param code: 股票代码
:param startDate: 开始日期
:param endDate: 结束日期
:return: bt.feeddata
'''
# 1.1 拿到数据
data = st.get_csv_price(code, startDate, endDate)
data['date'] = pd.to_datetime(data.index)
print(data.head())
# 1.2 把数据传入bt.feeds
data = bt.feeds.PandasData(
dataname=data,
fromdate=pd.to_datetime(startDate),
todate=pd.to_datetime(endDate),
datetime='date',
open='open',
high='high',
low='low',
close='close',
volume='volume',
openinterest=-1
)
return data
# 入口函数
if __name__ == '__main__':
'''1.喂数据'''
code = '000001.XSHE'
# code = '688981.XSHG'
# code = '000799.XSHE'
data = feed_data(code, '2016-01-01', '2022-05-26')
'''2.构建自己的策略(见上MyStrategy)'''
'''3.加入数据'''
cerebro = bt.Cerebro()
cerebro.adddata(data)
'''4.加入策略'''
cerebro.addstrategy(MyStrategy)
'''5.设置初始资金'''
startCash = 100000
cerebro.broker.setcash(startCash)
'''6.设置佣金'''
cerebro.broker.setcommission(0.0002)
'''5.运行cerebro'''
cerebro.run()
''''''
endCash = cerebro.broker.getvalue()
print(f'剩余总资金:{round(endCash, 2)}')
netIncome = round(endCash - startCash, 2)
# print(f'净收益:{round(endCash-startCash,2)} ')
print(f'净收益:{netIncome}')
print('净收益率:%.6f' % (netIncome / startCash))
cerebro.plot()
打印结果:
==买入:买入时短期均线价格13.48,长期均线价格13.46
2020-08-10 BUY EXECUTED, Price: 13.56, Cost: 2712.00, Comm 0.54
==卖出:卖出时短期均线价格22.29,长期均线价格22.50
2021-07-12 SELL EXECUTED, Price: 21.50, Cost: 2712.00, Comm 0.86
2021-07-12 OPERATION PROFIT, GROSS 1588.00, NET 1586.60
剩余总资金:102574.72
净收益:2574.72
净收益率:0.025747
在上面双均线策略代码基础上运行策略优化方案,cerebro.optstrategy(xx)
函数,通过optstrategy方法,给策略设置范围值,让策略逐个执行,对比结果。
# 普通方法添加策略
# cerebro.addstrategy(MyStrategy)
# 均线参数容器
short_ma_params = [5, 10, 20, 30, 60, 120]
long_ma_params = [10, 20, 30, 60, 120, 250]
# 带优化参数的形式添加策略
cerebro.optstrategy(
MyStrategy,
short_ma=short_ma_params,
long_ma=long_ma_params
)
添加打印代码:
添加结果到策略回测stop函数中,由于双均线策略,短期均线是要小于长期均线参数的,我这里是简单做了一次过滤,后面可优化。
def stop(self):
if self.params.short_ma < self.params.long_ma:
self.final_profit = (self.broker.getvalue() / self.startCash) - 1
self.log('=====(短期均线%d,长期均线%d)的初始资金:%f ,剩余资金:%f ,净收益:%f ,最终收益率: %.4f' %
(self.params.short_ma, self.params.long_ma, self.startCash, self.broker.getvalue(),
(self.broker.getvalue() - self.startCash), self.final_profit))
打印结果:
2022-05-26 =====(短期均线5,长期均线10)的初始资金:100000.000000 ,剩余资金:99378.534400 ,净收益:-621.465600 ,最终收益率: -0.0062
2022-05-26 =====(短期均线5,长期均线20)的初始资金:100000.000000 ,剩余资金:98290.735200 ,净收益:-1709.264800 ,最终收益率: -0.0171
2022-05-26 =====(短期均线5,长期均线30)的初始资金:100000.000000 ,剩余资金:99452.603200 ,净收益:-547.396800 ,最终收益率: -0.0055
2022-05-26 =====(短期均线5,长期均线60)的初始资金:100000.000000 ,剩余资金:102611.463200 ,净收益:2611.463200 ,最终收益率: 0.0261
2022-05-26 =====(短期均线5,长期均线120)的初始资金:100000.000000 ,剩余资金:103953.973600 ,净收益:3953.973600 ,最终收益率: 0.0395
2022-05-26 =====(短期均线5,长期均线250)的初始资金:100000.000000 ,剩余资金:100995.843200 ,净收益:995.843200 ,最终收益率: 0.0100
2022-05-26 =====(短期均线10,长期均线20)的初始资金:100000.000000 ,剩余资金:100495.216000 ,净收益:495.216000 ,最终收益率: 0.0050
2022-05-26 =====(短期均线10,长期均线30)的初始资金:100000.000000 ,剩余资金:97436.436800 ,净收益:-2563.563200 ,最终收益率: -0.0256
2022-05-26 =====(短期均线10,长期均线60)的初始资金:100000.000000 ,剩余资金:101794.530400 ,净收益:1794.530400 ,最终收益率: 0.0179
2022-05-26 =====(短期均线10,长期均线120)的初始资金:100000.000000 ,剩余资金:105149.440800 ,净收益:5149.440800 ,最终收益率: 0.0515
2022-05-26 =====(短期均线10,长期均线250)的初始资金:100000.000000 ,剩余资金:102120.692000 ,净收益:2120.692000 ,最终收益率: 0.0212
可以看到,平安银行这只股票,如果执行10均线与120日均线的双均线策略,达到的收益是最高的。
Backtrader issue - optstrategy AttributeError: module ‘collections’ has no attribute ‘Iterable’
解决办法:把 python 3.10版本降到 python 3.9 或 3.9以下版本,可解决问题
以上就是用backtrader双均线策略择优参数的方法。
之后可以自行实现遍历所有A股并分别进行双均线的择优参数回测,列出每支票的最优短期均线和长期均线参数,看是否能找到一些规律。