前言
这个系列已经写了两篇了:
终于要完结了,说实话,粗略的走一下 Guidance 基本上收获不大,即使是现在这样稍微细致点的研究,离真正实践还差的远,接下来如何精进,笔者也在探索中,各位同学如果有什么好的建议,欢迎推荐(要免费的啊,程序员学这些东西怎么能花钱呢,多丢人)。
正文
Demo 的代码就不往上贴了,所以以下内容请对照 Quickstart 文档 中的代码同步阅读。
Customizing the Strategy: Parameters
这个 Demo 主要是介绍变量的用法,只要掌握了注入变量的格式(二维元祖)就行了,代码变化也不大,重点如下:
class TestStrategy(bt.Strategy):
params = (
('exitbars', 5),
)
# other code...
else:
# Already in the market ... we might sell
if len(self) >= (self.bar_executed + self.params.exitbars):
# SELL, SELL, SELL!!! (with all possible default parameters)
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
# other code...
# Add a FixedSize sizer according to the stake
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
我们可以看到最开始声明了 exitbars 变量,后面用 self.params.exitbars 就可以直接调用了,很好理解。另外,通过文档和笔者实验,直接使用 self.p.exitbars 也可以成功访问变量。
Sizer
注意 cerebro.addsizer(bt.sizers.FixedSize, stake=10) 这句,这里还偷偷地加入了设置 sizer 的功能,这里要稍微展开讲一下了。sizer 翻译过来是筛选器的意思,但是笔者觉得在这里可以简单理解为设置买卖多少股的功能。
注:当 stake=0 时,只会触发「BUY CREATE」,而不会触发「BUY EXECUTED」。
通过查看 文档,我们可以知道,添加 sizer 有两种方式。
一种是像 Demo 中的一样,直接通过 cerebro.addsizer 添加,这种可以理解为「全局添加」,也就是所有策略都会使用这里面配置的参数。
另一种是为某个策略添加,可以利用 cerebro.addsizer_byidx 的方式,如下:
idx = cerebro.addstrategy(MyStrategy, myparam=myvalue)
cerebro.addsizer_byidx(idx, bt.sizers.SizerFix, stake=5)
也可以在策略中通过 setsizer 主动设置,不过文档中没有对于此用法进行详细说明,笔者实验出了一种用法,仅做参考:
def next(self):
self.setsizer(bt.sizers.FixedSize(stake=100))
最后,除了系统自带的一些 Sizer,如 FixedSize、FixedReverser、AllInSizer、PercentSizer 之外,还可以自定义 Sizer。稍微复杂一些,这里就不展开了,总之就是各种设置买卖股数的逻辑了。
Adding an indicator
这个 Demo 更接近实际策略了,增加了「指标」的逻辑,最常用的指标就是「均线」了。我们来看下主要代码:
class TestStrategy(bt.Strategy):
params = (
('maperiod', 15),
)
def __init__(self):
# other code...
# Add a MovingAverageSimple indicator
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.maperiod)
# other code...
def next(self):
# other code...
# Check if we are in the market
if not self.position:
# Not yet ... we MIGHT BUY if ...
if self.dataclose[0] > self.sma[0]:
# BUY, BUY, BUY!!! (with all possible default parameters)
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
else:
if self.dataclose[0] < self.sma[0]:
# SELL, SELL, SELL!!! (with all possible default parameters)
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
代码很好理解,收盘价高于 15 日均线就买,低于 15 日均线就卖(果然这么操作是赔钱的)。毫无疑问,Indicatior(指标)是做策略最重要的数据,笔者数了一下一共有 138 个 Indicators,这武器库看起来挺强大的,后面应该会单开 Indicator 相关的系列文章,所以这里还是简单看下用法吧。
其实用法也没多少可说的,就一点可能值得一说。Indicator 的文档中提到了,最好在 __init__ 当中做好数据的计算,而不是在 next 中。就像下例中的代码,在 next 中只是使用 buy_sig 这个变量,所有相关的计算都在 __init__ 当中完成了。
class MyStrategy(bt.Strategy):
def __init__(self):
sma1 = btind.SimpleMovingAverage(self.data)
ema1 = btind.ExponentialMovingAverage()
close_over_sma = self.data.close > sma1
close_over_ema = self.data.close > ema1
sma_ema_diff = sma1 - ema1
buy_sig = bt.And(close_over_sma, close_over_ema, sma_ema_diff > 0)
def next(self):
if buy_sig:
self.buy()
这样有诸多好处,其中最大的好处就是「快」。听人劝吃饱饭,我们就按官方推荐的做吧。
Visual Inspection: Plotting
这个 Demo 主要是展示「可视化」视图,笔者在《【Backtrader】Guidance 初探》中有提到:要想用此功能需要安装「backtrader 绘图版」,还要重装一下 matplotlib,安装代码如下:
pip install backtrader-plotting
# Fix Bug, see: https://github.com/mementum/backtrader/pull/418
pip uninstall matplotlib && \
pip install matplotlib==3.2.2
然后来看下重点代码:
class TestStrategy(bt.Strategy):
# other code...
def __init__(self):
# other code...
# Add a MovingAverageSimple indicator
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.maperiod)
# Indicators for the plotting show
bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
bt.indicators.WeightedMovingAverage(self.datas[0], period=25,
subplot=True)
bt.indicators.StochasticSlow(self.datas[0])
bt.indicators.MACDHisto(self.datas[0])
rsi = bt.indicators.RSI(self.datas[0])
bt.indicators.SmoothedMovingAverage(rsi, period=10)
bt.indicators.ATR(self.datas[0], plot=False)
# other code...
if __name__ == '__main__':
# other code...
# Plot the result
cerebro.plot()
显而易见的,最后一句 cerebro.plot() 就是触发可视化的一句,除此之外也没有再跟 plot 有关的代码了。如此看来, __init__ 方法中的那些 bt.indicators.xxx 代码是关键。于是笔者试着将它们都注释了再运行,果然效果是不一样的。具体效果见下图:
原 Demo 代码效果
只保留 sma 的效果
现在基本可以确定了,每使用一个 indicator,视图当中就会渲染出来相应的图形。想想也合理,毕竟我们可视化的目的就是要看代码逻辑是否符合预期,展示出来是很合理的。
但是如果我不想展示呢?解决方法也比较简单,每个 indicator 里都会有 plot 这个参数,默认值是 True,设置为 False 就不会展示了。
Let’s Optimize
神器来了,上例中用 15 日均线亏钱,那用其他均线能不能赚钱呢?能不能批量试一下呢?答案是肯定的,这可是 backtrader 的看家本领啊。我们来看一下重点代码:
class TestStrategy(bt.Strategy):
params = (
('maperiod', 15),
('printlog', False),
)
def log(self, txt, dt=None, doprint=False):
''' Logging function fot this strategy'''
if self.params.printlog or doprint:
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def stop(self):
self.log('(MA Period %2d) Ending Value %.2f' %
(self.params.maperiod, self.broker.getvalue()), doprint=True)
# other code...
if __name__ == '__main__':
# Create a cerebro entity
cerebro = bt.Cerebro()
# Add a strategy
# cerebro.addstrategy(TestStrategy)
strats = cerebro.optstrategy(
TestStrategy,
maperiod=range(10, 31))
# other code...
先来看下运行后的结果:
2000-12-29, (MA Period 10) Ending Value 877.50
2000-12-29, (MA Period 11) Ending Value 878.70
2000-12-29, (MA Period 12) Ending Value 839.80
2000-12-29, (MA Period 13) Ending Value 899.90
2000-12-29, (MA Period 14) Ending Value 902.50
2000-12-29, (MA Period 15) Ending Value 975.60
2000-12-29, (MA Period 16) Ending Value 961.90
2000-12-29, (MA Period 17) Ending Value 952.60
2000-12-29, (MA Period 18) Ending Value 1011.00
2000-12-29, (MA Period 19) Ending Value 1039.40
2000-12-29, (MA Period 20) Ending Value 1073.20
2000-12-29, (MA Period 21) Ending Value 1055.10
2000-12-29, (MA Period 22) Ending Value 1057.60
2000-12-29, (MA Period 23) Ending Value 1021.50
2000-12-29, (MA Period 24) Ending Value 1018.80
2000-12-29, (MA Period 25) Ending Value 1012.40
2000-12-29, (MA Period 26) Ending Value 998.30
2000-12-29, (MA Period 27) Ending Value 983.10
2000-12-29, (MA Period 28) Ending Value 976.90
2000-12-29, (MA Period 29) Ending Value 984.20
2000-12-29, (MA Period 30) Ending Value 980.80
诶?之前的那些 log 呢?刚开始笔者也一脸懵逼,好像没怎么改代码啊,log 怎么全没了?仔细一看才发现:log 方法加了一个 if 判断啊,默认就是不打印的,如果传了 doprint=True 才会打印。怪不得只打印了 stop 方法里的 log 呢,因为它传的 doprint=True。没错,这里又出来了一个钩子函数 stop,有了前面两篇的积累,相信这里就不用多解释了。
最后来看看最关键的一句:
# Add a strategy
# cerebro.addstrategy(TestStrategy)
strats = cerebro.optstrategy(
TestStrategy,
maperiod=range(10, 31))
注意,这里用 optstrategy 方法代替了 addstrategy,而且传参也变成了数组。结合上面的展示结果,我们也能够比较容易的理解,这是在做批量回测。而且从结果来看,20 日均线盈利的效果是最大的。就这样,我们完成了策略的优化,完美~~~
总结
整个 Guidance 的较深入探索完成了,收获明显大了不少,现在有信心用 backtrader 来自己写策略并且测试了。接下来计划用 backtrader 来测试一些比较有名的交易策略,一是熟悉一下 backtrader 的用法,二是真实检验一下这些策略是不是真的好使。
不过数据从哪来是个难题啊,花钱肯定是弄得到,但是这种事情花钱不就对不起「程序员」这三个字了嘛,应该能淘到的,有信息的同学欢迎留言提供哦~
用百分之三百的努力,把你吹的牛逼实现。