(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)

相关推荐