(9条消息) backtrader量化平台教程(四)对策略进行优化
对策略进行优化
多数策略实际上依赖于指标,指标又依赖一下预设的数值。那么预设的数值是否合理?
光凭脑袋想肯定是不行的,既然我们用了量化的方法。可以教给计算机来计算,找到最优值。
经典的28轮动策略
300、500、国债指数轮动,300和500的20天涨幅哪个大持有哪个,两个都为负数持有国债.
28轮动的收益到底怎么样?我们通过回测程序看下。
我们还是回测2012-1-1到2019-12-31的历史数据。(主要时baostock的国债指数在2011年有部分缺失)
对策略进行优化
策略里的20天,究竟为什么?有没有更好的选择?
可以通过optstrategy设置策略参数的测试范围,backtrader会对这些参数进行测试验证。
这块验证了1~60天的收益率情况。
periods = range(1, 60)
...
cerebro.optstrategy(TestStrategy, period=periods)
我们可以通过优化,进行下测试,测试结果如下(最高的5个):
21211.89%12.05%
20176.95%10.72%
8174.41%10.62%
19163.45%10.17%
22163.34%10.17%
怎么设置交易费率(不可忽视的交易成本)
短周期的轮动策略,通常会导致大量换手。不同交易费率,究竟在回测结果中又多大影响?
我们可以通过setcommission设置交易费率进行下测试。
可以看出来不同费率下总的收益和每年平均收益,差距还是挺大的。
cerebro.broker.setcommission(0.0005)
手续费为0.0005时的收益率情况:
21211.89%12.05%
20176.95%10.72%
8174.41%10.62%
19163.45%10.17%
22163.34%10.17%
手续费为0.0001时的收益率情况:
periodTotal ROIAnnual ROI
21275.69%14.15%
8269.06%13.95%
20233.37%12.8%
19219.36%12.31%
12219.02%12.3%
总结
28轮动的策略,总体来说回报还是不错的,远远战胜了通胀。
在28轮动策略里,我们需要重视交易费率的问题,尽量采用费率更低的轮动方式。(比如
从券商处获得更大的优惠,或者通过同一家基金公司的基金的方式转换节省交易费用。)
完整回测代码
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
"""计算28轮动某个标的池的盈利情况"""
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
import backtrader.feeds as btfeed
# Create a Stratey
class TestStrategy(bt.Strategy):
params = (
('period', 20),
)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders
self.order = None
self.month = -1
self.mom = [bt.indicators.MomentumOscillator(i, period=self.params.period) for i in self.datas]
def next(self):
# Simply log the closing price of the series from the reference
buy_id = 0
c = [i.momosc[0] for i in self.mom]
c[0] = 0
index, value = c.index(max(c)), max(c)
if value > 100:
buy_id = index
# print("buy_id ", buy_id)
for i in range(0, len(c)):
if i != buy_id:
position_size = self.broker.getposition(data=self.datas[i]).size
if position_size != 0:
self.order_target_percent(data=self.datas[i], target=0)
position_size = self.broker.getposition(data=self.datas[buy_id]).size
if position_size == 0:
self.order_target_percent(data=self.datas[buy_id], target=0.98)
def stop(self):
# print(self.broker.getvalue())
return_all = self.broker.getvalue() / 1200000.0
print('{0},{1}%,{2}%'.format(self.params.period,
round((return_all - 1.0) * 100, 2),
round((pow(return_all, 1.0 / 8) - 1.0) * 100, 2)
))
class TSCSVData(btfeed.GenericCSVData):
params = (
("fromdate", datetime.datetime(2012, 1, 1)),
("todate", datetime.datetime(2019, 12, 31)),
('nullvalue', 0.0),
('dtformat', ('%Y-%m-%d')),
('openinterest', -1)
)
def backtest(cash, files, periods):
files = files
# Create a cerebro entity
cerebro = bt.Cerebro()
# Add a strategy
cerebro.optstrategy(TestStrategy, period=periods)
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
for i in files:
datapath = os.path.join(modpath, 'datas/{0}'.format(i))
# Create a Data Feed
data = TSCSVData(dataname=datapath)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# Set our desired cash start
cerebro.broker.setcash(cash)
cerebro.broker.setcommission(0.0005)
# Run over everything
print("period,Total ROI,Annual ROI")
cerebro.run()
if __name__ == '__main__':
"""第0个标的是默认标的"""
files = ['bs_sh.000012.csv', 'bs_sh.000300.csv', 'bs_sh.000905.csv']
cash = 1200000.0
periods = range(0, 60)
backtest(cash, files, periods)
赞 (0)