写下来
原文: https://www.backtrader.com/blog/posts/2015-09-14-write-it-down/write-it-down/
随着 1.1.7.88 版本的发布,backtrader 得到了一个新的添加:writers
这可能是很长一段时间的事了,应该已经存在了,而第 14 期中的讨论也应该已经启动了开发。
但迟做总比不做好。
Writer
实现试图与backtrader
环境中的其他对象保持一致
-
通过大脑得到补充
-
提供最合理的违约
-
不要强迫用户做很多事情
当然,更重要的是理解作者实际写的东西。也就是说:
-
```py
-
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
andobservers
output data to the CSV stream is controlled by the attribute:csv
in each instanceThe defaults are:
-
Observers have
csv = True
-
Indicators have
csv = False
The value can be overriden for any instance created inside a strategy ```
的 CSV 输出
-
一旦回溯测试阶段结束,Writers
为Cerebro
实例添加一个新的部分,并添加以下小节:
-
系统中
datas
的属性(名称、压缩、时间段) -
系统中
strategies
的属性(行、参数)-
策略中
indicators
的属性(行、参数) -
策略中
observers
的属性(行、参数) -
具有以下特性的分析器
-
Params
-
分析
-
考虑到所有这些,一个例子可能是展示力量(或弱点)或writers
的最简单方式。
但在如何将它们添加到大脑之前。
-
将
writer
参数用于cerebro
:py cerebro = bt.Cerebro(writer=True)
这将创建一个默认实例。
-
具体补充:
```py cerebro = bt.Cerebro()
cerebro.addwriter(bt.WriterFile, csv=False) ```
将(目前唯一的编写器)一个
WriterFile
类添加到编写器列表中,稍后将使用csv=False
进行实例化(输出中将不会生成 csv 流)。
具有长-短策略的长到期示例(完整代码见下文),使用闭合 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
(或系统质量编号)(除backtrader
1.1.7.88 外)告诉我们,该公司已经进行了 22 次交易,并计算出sqn
为 0.05。这实际上相当低。我们可以通过一整年后的微利来解决这个问题(幸运的是系统没有亏损)
测试脚本允许我们调整策略,使其变为长:
$ ./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
可以看到策略“参数”的变化(只有 ylong 变为 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
默认值执行以下操作:
-
未打印任何指标(既不打印简单移动平均线,也不打印交叉线)
-
观察结果已打印出来
让我们使用附加参数运行脚本,以将交叉指示符添加到 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()