注:在文章末尾我会分析这个大牛股,002400。
今天介绍下backtrader的策略实现:做的是简单的移动平均策略。
后期我会持续更新其他策略的实现,请关注我的专栏:团子量化
要自定义策略首先需要继承bt.Strategy这个基类,然后覆写里面的方法,其中最主要的是next,和notify_order。
在next中我们可以获取每一天的数据包括:高开低收,注意这里回测是一条数据一条数据的获取,不会获取未来的数据,所以回测是有效果的,不像市面上有些回测框架是在已经知道未来数据来做的策略,这样策略的收益率肯定很高。在next中,如果日线数据上穿15日均线,我们就买入,下穿我们就执行卖出。
在notify_order中,notify_order(self,order):这里面可以获取订单状态,如果是Completed,如果订单是买入,就执行卖出。如果是卖出,就执行买入。
注意:出现交易信号之后都是用第二天的开盘价来买入的。
策略,具体代码实现如下:
# 创建策略
class SimpleMovingStrategy(bt.Strategy):
params = (
('maperiod', 15),
)
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.dataclose = self.datas[0].close
# 保存pending订单的卖价、卖价、手续费。
self.order = None
self.buyprice = None
self.buycomm = None
# 添加一个移动均线的指标
self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# 如果订单是Submitted,Accepted就什么都不做。
return
# 检查一个订单是否提交
# 注意:如果金额不够的话,broker会拒绝执行
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: # Sell
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 next(self):
self.log('Close, %.2f' % self.dataclose[0])
# 如果有订单在pengding中,我们就直接返回
if self.order:
return
# 检测position,我们是否已经在市场中有持仓了。
if not self.position:
# 收盘价上穿均线
if self.dataclose[0] > self.sma[0]:
self.log('BUY CREATE, %.2f' % self.dataclose[0])
self.order = self.buy()
else:
# 有持仓,如果均线下穿就卖出
if self.dataclose[0] < self.sma[0]:
self.log('SELL CREATE, %.2f' % self.dataclose[0])
self.order = self.sell()
通过cerobro来执行策略:
# 创建核心
cerebro = bt.Cerebro()
# 添加策略
cerebro.addstrategy(SimpleMovingStrategy)
# 创建数据datafeed
from data_handler.tusharefeed import TushareData
feed = TushareData(
dataname='002400.SZ',
fromdate=datetime.date(2020, 1, 1),
todate=datetime.date(2020, 8, 4),
)
# 添加数据
cerebro.adddata(feed)
# 设置初始金额
cerebro.broker.setcash(10000.0)
# 添加每次买入的股票数
cerebro.addsizer(bt.sizers.FixedSize, stake=200)
# 设置手续费
cerebro.broker.setcommission(commission=0.0001)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# 运行回测
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
# 画出图形
cerebro.plot()
完整代码如下:
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
# Import the backtrader platform
import backtrader as bt
# 创建策略
class SimpleMovingStrategy(bt.Strategy):
params = (
('maperiod', 15),
)
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.dataclose = self.datas[0].close
# 保存pending订单的卖价、卖价、手续费。
self.order = None
self.buyprice = None
self.buycomm = None
# 添加一个移动均线的指标
self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# 如果订单是Submitted,Accepted就什么都不做。
return
# 检查一个订单是否提交
# 注意:如果金额不够的话,broker会拒绝执行
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: # Sell
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 next(self):
self.log('Close, %.2f' % self.dataclose[0])
# 如果有订单在pengding中,我们就直接返回
if self.order:
return
# 检测position,我们是否已经在市场中有持仓了。
if not self.position:
# 收盘价上穿均线
if self.dataclose[0] > self.sma[0]:
self.log('BUY CREATE, %.2f' % self.dataclose[0])
self.order = self.buy()
else:
# 有持仓,如果均线下穿就卖出
if self.dataclose[0] < self.sma[0]:
self.log('SELL CREATE, %.2f' % self.dataclose[0])
self.order = self.sell()
if __name__ == '__main__':
# 创建核心
cerebro = bt.Cerebro()
# 添加策略
cerebro.addstrategy(SimpleMovingStrategy)
# 创建数据datafeed
from data_handler.tusharefeed import TushareData
feed = TushareData(
dataname='002400.SZ',
fromdate=datetime.date(2020, 1, 1),
todate=datetime.date(2020, 8, 4),
)
# 添加数据
cerebro.adddata(feed)
# 设置初始金额
cerebro.broker.setcash(10000.0)
# 添加每次买入的股票数
cerebro.addsizer(bt.sizers.FixedSize, stake=200)
# 设置手续费
cerebro.broker.setcommission(commission=0.0001)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# 运行回测
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
# 画出图形
cerebro.plot()
回测结果如下:在万一的费率下,每次买入2手:
注意这里要使用我上一篇文章中使用的tushare_datafeed。如果没有添加的请回去看我的上一篇文章。0005-量化回测框架backtrader介绍,定义datafeed是用tushare。
对于省广集团的分析如下:
首先该股票是日线,macd,成交量都出现了金叉。而且今天还在头条上面露了一手。所以无论是指标还是行情,这个股注定是大牛股。附上我今天的战绩
感兴趣的朋友可以关注下我,后面我会持续更新我的炒股历程。