休士頓我們有一個問題:
- cerebro 不應多次運行。這不是第1次 ,而不是認為使用者做錯了,這似乎是一個用例。
這個有趣的用例是通過票證177出現的。在這種情況下, cerebro 被多次用於評估從外部數據源獲取的不同策略。
backtrader 仍然可以支援此用例,但不能以直接嘗試的方式支援。
backtrader中的內置優化已經完成了所需的操作:
- 實例化多個策略實例並收集結果
是唯一一個實例都屬於同一類的東西。這就是Python通過讓我們控制對象的創建來提供説明的地方。
首先,讓我們使用內置的信號技術向腳本中添加非常快速的策略 backtrader
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
Et voilá!實例化類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個策略已經運行,並交付(如預期)不同的結果。
注意
該示例很少,但已使用所有可用的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()