战略选择
原文: https://www.backtrader.com/blog/posts/2016-10-29-strategy-selection/strategy-selection/
休斯顿我们有一个问题:
- 大脑并不意味着要运行多次。这不是第 1次次,与其认为用户做得不对,不如说这是一个用例。
这个有趣的用例是通过票证 177提出的。在这种情况下,大脑被多次用于评估从外部数据源获取的不同策略。
backtrader仍然可以支持此用例,但不是以尝试过的直接方式。
优化选择
backtrader中的内置优化已经完成了所需的工作:
- 实例化几个策略实例并收集结果
是唯一一个实例都属于同一类的东西。这就是 Python 帮助我们控制对象创建的地方。
首先,让我们使用反向交易者内置的信号技术为脚本添加快速策略
class St0(bt.SignalStrategy):
    def __init__(self):
        sma1, sma2 = bt.ind.SMA(period=10), bt.ind.SMA(period=30)
        crossover = bt.ind.CrossOver(sma1, sma2)
        self.signal_add(bt.SIGNAL_LONG, crossover)
class St1(bt.SignalStrategy):
    def __init__(self):
        sma1 = bt.ind.SMA(period=10)
        crossover = bt.ind.CrossOver(self.data.close, sma1)
        self.signal_add(bt.SIGNAL_LONG, crossover) 
这再容易不过了。
现在,让我们来展示这两种策略的魔力。
class StFetcher(object):
    _STRATS = [St0, St1]
    def __new__(cls, *args, **kwargs):
        idx = kwargs.pop('idx')
        obj = cls._STRATS[idx](*args, **kwargs)
        return obj 
瞧!在实例化类StFetcher时,方法__new__控制实例的创建。在这种情况下:
- 
获取传递给它的 idx参数
- 
使用此参数从 _STRATS列表中获取策略,我们之前的示例策略已存储在该列表中笔记 没有什么可以阻止使用这个 idx值从服务器和/或数据库获取策略。
- 
实例化并返回fecthed策略 
主持演出
 cerebro.addanalyzer(bt.analyzers.Returns)
    cerebro.optstrategy(StFetcher, idx=[0, 1])
    results = cerebro.run(maxcpus=args.maxcpus, optreturn=args.optreturn) 
的确这就是优化!我们使用optstrategy而不是addstrategy,并为idx传递一个值数组。优化引擎将迭代这些值。
因为cerebro可以在每个优化过程中托管多个策略,所以结果将包含一个列表列表。每个子列表是每个优化过程的结果。
在我们的例子中,每个过程只有一个策略,我们可以快速展平结果并提取我们添加的分析器的值。
 strats = [x[0] for x in results]  # flatten the result
    for i, strat in enumerate(strats):
        rets = strat.analyzers.returns.get_analysis()
        print('Strat {} Name {}:\n - analyzer: {}\n'.format(
            i, strat.__class__.__name__, rets)) 
试运行
./strategy-selection.py
Strat 0 Name St0:
  - analyzer: OrderedDict([(u'rtot', 0.04847392369449283), (u'ravg', 9.467563221580632e-05), (u'rnorm', 0.02414514457151587), (u'rnorm100', 2.414514457151587)])
Strat 1 Name St1:
  - analyzer: OrderedDict([(u'rtot', 0.05124714332260593), (u'ravg', 0.00010009207680196471), (u'rnorm', 0.025543999840699633), (u'rnorm100', 2.5543999840699634)]) 
我们的两个战略已经运行并(如预期)产生了不同的结果。
笔记
该示例非常小,但已使用所有可用的 CPU 运行。用--maxpcpus=1执行会更快。对于更复杂的场景,使用所有 CPU 将非常有用。
结论
策略选择用例是可能的,不需要绕过backtrader或Python本身的任何内置功能。
样本使用
$ ./strategy-selection.py --help
usage: strategy-selection.py [-h] [--data DATA] [--maxcpus MAXCPUS]
                             [--optreturn]
Sample for strategy selection
optional arguments:
  -h, --help         show this help message and exit
  --data DATA        Data to be read in (default:
                     ../../datas/2005-2006-day-001.txt)
  --maxcpus MAXCPUS  Limit the numer of CPUs to use (default: None)
  --optreturn        Return reduced/mocked strategy object (default: False) 
代码
已包含在 backtrader 的来源中
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
import backtrader as bt
class St0(bt.SignalStrategy):
    def __init__(self):
        sma1, sma2 = bt.ind.SMA(period=10), bt.ind.SMA(period=30)
        crossover = bt.ind.CrossOver(sma1, sma2)
        self.signal_add(bt.SIGNAL_LONG, crossover)
class St1(bt.SignalStrategy):
    def __init__(self):
        sma1 = bt.ind.SMA(period=10)
        crossover = bt.ind.CrossOver(self.data.close, sma1)
        self.signal_add(bt.SIGNAL_LONG, crossover)
class StFetcher(object):
    _STRATS = [St0, St1]
    def __new__(cls, *args, **kwargs):
        idx = kwargs.pop('idx')
        obj = cls._STRATS[idx](*args, **kwargs)
        return obj
def runstrat(pargs=None):
    args = parse_args(pargs)
    cerebro = bt.Cerebro()
    data = bt.feeds.BacktraderCSVData(dataname=args.data)
    cerebro.adddata(data)
    cerebro.addanalyzer(bt.analyzers.Returns)
    cerebro.optstrategy(StFetcher, idx=[0, 1])
    results = cerebro.run(maxcpus=args.maxcpus, optreturn=args.optreturn)
    strats = [x[0] for x in results]  # flatten the result
    for i, strat in enumerate(strats):
        rets = strat.analyzers.returns.get_analysis()
        print('Strat {} Name {}:\n - analyzer: {}\n'.format(
            i, strat.__class__.__name__, rets))
def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Sample for strategy selection')
    parser.add_argument('--data', required=False,
                        default='../../datas/2005-2006-day-001.txt',
                        help='Data to be read in')
    parser.add_argument('--maxcpus', required=False, action='store',
                        default=None, type=int,
                        help='Limit the numer of CPUs to use')
    parser.add_argument('--optreturn', required=False, action='store_true',
                        help='Return reduced/mocked strategy object')
    return parser.parse_args(pargs)
if __name__ == '__main__':
    runstrat() 

