随着 1.1.7.88 版本的发布, backtrader有了一个新的补充:作家
这可能早就到期了,应该已经存在了,问题 #14中的讨论也应该已经开始了开发。
但迟到总比没有好。
Writer
实现尝试与backtrader
环境中的其他对象保持一致
通过Cerebro添加
提供最合理的默认值
不要强迫用户做太多事情
当然,更重要的是了解作者实际写了什么。那就是:
- 的 CSV 输出
- `datas` added to the system (can be switched off) - `strategies` (a Strategy can have named lines) - `indicators` inside the strategies (only 1st level) - `observers` inside the strategies (only 1st level) Which `indicators` and `observers` output data to the CSV stream is controlled by the attribute: `csv` in each instance The defaults are: - Observers have `csv = True` - Indicators have `csv = False` The value can be overriden for any instance created inside a strategy
回测阶段结束后, Writers
为Cerebro
实例添加一个新部分,并添加以下子部分:
系统中
datas
的属性(名称、压缩、时间范围)系统中
strategies
的属性(行、参数)策略中
indicators
的属性(行、参数)策略中
observers
的属性(行,参数)具有以下属性的分析器
参数
分析
考虑到所有这些,一个例子可能是展示writers
的力量(或弱点)的最简单方法。
但在如何将它们添加到cerebro之前。
将
writer
参数用于cerebro
:cerebro = bt.Cerebro(writer=True)
这将创建一个默认实例。
具体补充:
cerebro = bt.Cerebro() cerebro.addwriter(bt.WriterFile, csv=False)
添加(现在唯一的writer )一个
WriterFile
类到writer列表,以便稍后用csv= False
实例化(不会在输出中生成 csv 流。
多空策略的长期示例(完整代码见下文),通过运行使用Close -SMA 交叉作为信号:
$ ./writer-test.py
图表:
使用以下输出:
=============================================================================== Cerebro: ----------------------------------------------------------------------------- - Datas: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data0: - Name: 2006-day-001 - Timeframe: Days - Compression: 1 ----------------------------------------------------------------------------- - Strategies: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - LongShortStrategy: ************************************************************************* - Params: - csvcross: False - printout: False - onlylong: False - stake: 1 - period: 15 ************************************************************************* - Indicators: ....................................................................... - SMA: - Lines: sma ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Params: - period: 15 ....................................................................... - CrossOver: - Lines: crossover - Params: None ************************************************************************* - Observers: ....................................................................... - Broker: - Lines: cash, value - Params: None ....................................................................... - BuySell: - Lines: buy, sell - Params: None ....................................................................... - Trades: - Lines: pnlplus, pnlminus - Params: None ************************************************************************* - Analyzers: ....................................................................... - Value: - Begin: 100000 - End: 100826.1 ....................................................................... - SQN: - Params: None ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Analysis: - sqn: 0.05 - trades: 22
运行后,我们对系统的设置方式以及分析人员最后所说的内容进行了完整的总结。在这种情况下,分析仪是
Value
是策略中的一个假分析器,它收集投资组合的开始和结束值由 Van K. Tharp 定义的
SQN
(或 SystemQualityNumber)(除了backtrader
1.1.7.88,它告诉我们它已经看到 22 笔交易并计算出 0.05 的sqn
。这实际上是相当低的。我们可以通过查看一整年后的小额利润来弄清楚(幸运的是系统没有亏损)
测试脚本允许我们将策略调整为long-only :
$ ./writer-test.py --onlylong --plot
图表:
现在的输出是:
=============================================================================== Cerebro: ----------------------------------------------------------------------------- - Datas: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data0: - Name: 2006-day-001 - Timeframe: Days - Compression: 1 ----------------------------------------------------------------------------- - Strategies: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - LongShortStrategy: ************************************************************************* - Params: - csvcross: False - printout: False - onlylong: True - stake: 1 - period: 15 ************************************************************************* - Indicators: ....................................................................... - SMA: - Lines: sma ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Params: - period: 15 ....................................................................... - CrossOver: - Lines: crossover - Params: None ************************************************************************* - Observers: ....................................................................... - Broker: - Lines: cash, value - Params: None ....................................................................... - BuySell: - Lines: buy, sell - Params: None ....................................................................... - Trades: - Lines: pnlplus, pnlminus - Params: None ************************************************************************* - Analyzers: ....................................................................... - Value: - Begin: 100000 - End: 102795.0 ....................................................................... - SQN: - Params: None ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Analysis: - sqn: 0.91 - trades: 11
可以看到策略“参数”的变化(onlylong 已变为True ),分析器讲述了一个不同的故事:
期末价值从 100826.1 提高到 102795.0
SQN 看到的交易从 22 减少到 11
SQN 分数从 0.05 增长到 0.91,这要好得多
但是仍然看不到 CSV 输出。让我们运行脚本来打开它:
$ ./writer-test.py --onlylong --writercsv
更新输出:
=============================================================================== Id,2006-day-001,len,datetime,open,high,low,close,volume,openinterest,LongShortStrategy,len,Broker,len,cash,value,Buy Sell,len,buy,sell,Trades,len,pnlplus,pnlminus 1,2006-day-001,1,2006-01-02 23:59:59+00:00,3578.73,3605.95,3578.73,3604.33,0.0,0.0,LongShortStrategy,1,Broker,1,1000 00.0,100000.0,BuySell,1,,,Trades,1,, 2,2006-day-001,2,2006-01-03 23:59:59+00:00,3604.08,3638.42,3601.84,3614.34,0.0,0.0,LongShortStrategy,2,Broker,2,1000 00.0,100000.0,BuySell,2,,,Trades,2,, ... ... ... 255,2006-day-001,255,2006-12-29 23:59:59+00:00,4130.12,4142.01,4119.94,4119.94,0.0,0.0,LongShortStrategy,255,Broker,255,100795.0,102795.0,BuySell,255,,,Trades,255,, =============================================================================== Cerebro: ----------------------------------------------------------------------------- ... ...
我们可以跳过大部分 csv 流和已经看到的摘要。 CSV 流已打印出以下内容
开头的剖面线分隔符
标题行
对应数据
请注意每个对像如何打印其“长度”。尽管在这种情况下它没有提供太多信息,但如果使用多时间帧数据或重放数据,它会提供。
writer
默认运行以下操作:
没有打印指针(简单移动平均线和交叉点都没有)
观察者被打印出来
让我们使用附加参数运行脚本,以将 CrossOver 指示器添加到 CSV 流中:
$ ./writer-test.py --onlylong --writercsv --csvcross
输出:
=============================================================================== Id,2006-day-001,len,datetime,open,high,low,close,volume,openinterest,LongShortStrategy,len,CrossOver,len,crossover,B roker,len,cash,value,BuySell,len,buy,sell,Trades,len,pnlplus,pnlminus 1,2006-day-001,1,2006-01-02 23:59:59+00:00,3578.73,3605.95,3578.73,3604.33,0.0,0.0,LongShortStrategy,1,CrossOver,1,, Broker,1,100000.0,100000.0,BuySell,1,,,Trades,1,, ... ...
这显示了作家的一些力量。该类的进一步文档仍然是待办事项。
同时,示例中使用的运行可能性和代码。
用法:
$ ./writer-test.py --help usage: writer-test.py [-h] [--data DATA] [--fromdate FROMDATE] [--todate TODATE] [--period PERIOD] [--onlylong] [--writercsv] [--csvcross] [--cash CASH] [--comm COMM] [--mult MULT] [--margin MARGIN] [--stake STAKE] [--plot] [--numfigs NUMFIGS] MultiData Strategy optional arguments: -h, --help show this help message and exit --data DATA, -d DATA data to add to the system --fromdate FROMDATE, -f FROMDATE Starting date in YYYY-MM-DD format --todate TODATE, -t TODATE Starting date in YYYY-MM-DD format --period PERIOD Period to apply to the Simple Moving Average --onlylong, -ol Do only long operations --writercsv, -wcsv Tell the writer to produce a csv stream --csvcross Output the CrossOver signals to CSV --cash CASH Starting Cash --comm COMM Commission for operation --mult MULT Multiplier for futures --margin MARGIN Margin for each future --stake STAKE Stake to apply in each operation --plot, -p Plot the read data --numfigs NUMFIGS, -n NUMFIGS Plot using numfigs figures
和测试脚本。
from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import datetime # The above could be sent to an independent module import backtrader as bt import backtrader.feeds as btfeeds import backtrader.indicators as btind from backtrader.analyzers import SQN class LongShortStrategy(bt.Strategy): '''This strategy buys/sells upong the close price crossing upwards/downwards a Simple Moving Average. It can be a long-only strategy by setting the param "onlylong" to True ''' params = dict( period=15, stake=1, printout=False, onlylong=False, csvcross=False, ) def start(self): pass def stop(self): pass def log(self, txt, dt=None): if self.p.printout: dt = dt or self.data.datetime[0] dt = bt.num2date(dt) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): # To control operation entries self.orderid = None # Create SMA on 2nd data sma = btind.MovAv.SMA(self.data, period=self.p.period) # Create a CrossOver Signal from close an moving average self.signal = btind.CrossOver(self.data.close, sma) self.signal.csv = self.p.csvcross def next(self): if self.orderid: return # if an order is active, no new orders are allowed if self.signal > 0.0: # cross upwards if self.position: self.log('CLOSE SHORT , %.2f' % self.data.close[0]) self.close() self.log('BUY CREATE , %.2f' % self.data.close[0]) self.buy(size=self.p.stake) elif self.signal < 0.0: if self.position: self.log('CLOSE LONG , %.2f' % self.data.close[0]) self.close() if not self.p.onlylong: self.log('SELL CREATE , %.2f' % self.data.close[0]) self.sell(size=self.p.stake) def notify_order(self, order): if order.status in [bt.Order.Submitted, bt.Order.Accepted]: return # Await further notifications if order.status == order.Completed: if order.isbuy(): buytxt = 'BUY COMPLETE, %.2f' % order.executed.price self.log(buytxt, order.executed.dt) else: selltxt = 'SELL COMPLETE, %.2f' % order.executed.price self.log(selltxt, order.executed.dt) elif order.status in [order.Expired, order.Canceled, order.Margin]: self.log('%s ,' % order.Status[order.status]) pass # Simply log # Allow new orders self.orderid = None def notify_trade(self, trade): if trade.isclosed: self.log('TRADE PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm)) elif trade.justopened: self.log('TRADE OPENED, SIZE %2d' % trade.size) def runstrategy(): args = parse_args() # Create a cerebro cerebro = bt.Cerebro() # Get the dates from the args fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d') todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d') # Create the 1st data data = btfeeds.BacktraderCSVData( dataname=args.data, fromdate=fromdate, todate=todate) # Add the 1st data to cerebro cerebro.adddata(data) # Add the strategy cerebro.addstrategy(LongShortStrategy, period=args.period, onlylong=args.onlylong, csvcross=args.csvcross, stake=args.stake) # Add the commission - only stocks like a for each operation cerebro.broker.setcash(args.cash) # Add the commission - only stocks like a for each operation cerebro.broker.setcommission(commission=args.comm, mult=args.mult, margin=args.margin) cerebro.addanalyzer(SQN) cerebro.addwriter(bt.WriterFile, csv=args.writercsv, rounding=2) # And run it cerebro.run() # Plot if requested if args.plot: cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False) def parse_args(): parser = argparse.ArgumentParser(description='MultiData Strategy') parser.add_argument('--data', '-d', default='../../datas/2006-day-001.txt', help='data to add to the system') parser.add_argument('--fromdate', '-f', default='2006-01-01', help='Starting date in YYYY-MM-DD format') parser.add_argument('--todate', '-t', default='2006-12-31', help='Starting date in YYYY-MM-DD format') parser.add_argument('--period', default=15, type=int, help='Period to apply to the Simple Moving Average') parser.add_argument('--onlylong', '-ol', action='store_true', help='Do only long operations') parser.add_argument('--writercsv', '-wcsv', action='store_true', help='Tell the writer to produce a csv stream') parser.add_argument('--csvcross', action='store_true', help='Output the CrossOver signals to CSV') parser.add_argument('--cash', default=100000, type=int, help='Starting Cash') parser.add_argument('--comm', default=2, type=float, help='Commission for operation') parser.add_argument('--mult', default=10, type=int, help='Multiplier for futures') parser.add_argument('--margin', default=2000.0, type=float, help='Margin for each future') parser.add_argument('--stake', default=1, type=int, help='Stake to apply in each operation') parser.add_argument('--plot', '-p', action='store_true', help='Plot the read data') parser.add_argument('--numfigs', '-n', default=1, help='Plot using numfigs figures') return parser.parse_args() if __name__ == '__main__': runstrategy()