编写策略及优化方案的实例 – backtrader中文教程
休斯顿,我们有一个问题:
- cerebro并不意味着要运行多次。这不是第一次,而不是认为用户做错了,这似乎是一个用例。
这个有趣的用例是通过Ticket 177提出的。在这种情况下, cerebro 被多次用于评估从外部数据源获取的不同策略。
backtrader仍然可以支持这个用例,但不是以它尝试过的直接方式。
优化选择
backtrader中的内置优化已经完成了所需的操作:
- 实例化几个策略实例并收集结果
作为实例都属于同一个类的唯一事物。这就是 Python 通过让我们控制对象的创建来提供帮助的地方。
首先,让我们使用backtrader 中内置的Signal技术向脚本添加非常快速的策略
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
值从服务器和/或数据库中获取策略。
- 实例化并返回受感染的策略
运行
cerebro.addanalyzer(bt.analyzers.Returns) cerebro.optstrategy(StFetcher, idx=[0, 1]) results = cerebro.run(maxcpus=args.maxcpus, optreturn=args.optreturn)
的确!优化它!而不是addstrategy
我们使用optstrategy
并传递一个值数组idx
。这些值将由优化引擎迭代。
因为cerebro
可以在每个优化过程中托管多个策略,所以结果将包含一个列表列表。每个子列表是每个优化过程的结果。
在我们的例子中,每次只有 1 个策略,我们可以快速展平结果并提取我们添加的分析器的值。
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)])
我们的 2 个策略已经运行并交付(如预期)不同的结果。
Tips:该示例很小,但已在所有可用 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()
评论被关闭。