backtrader 的诞生是出于必要。我自己的...有一种感觉,我控制着自己的回溯测试平台,可以尝试新的想法。但是,在这样做并且从一开始就完全 open 采购它时,很明显它必须有一种方法来满足他人的需求和愿望。
作为未来的交易者,我本可以选择基于点的计算和每轮佣金的固定价格,但这将是一个错误。
注意
7月31, 2015
使用新添加的操作/交易通知跟进帖子,修复交易损益数字的绘图,并避免手动计算,如下面的示例所示
提高佣金:股票与期货
相反,backtrader
它提供了使用常规的基于%大小/价格的方案和固定价格/点方案的可能性。选择权在您手中。
不可知论
在继续之前,让我们记住,backtrader
尝试对数据所代表的内容保持不可知论。不同的佣金方案可以应用于同一数据集。
让我们看看如何做到这一点。
使用代理快捷方式
这使最终用户远离CommissionInfo
对象,因为可以使用单个函数调用创建/设置佣金方案。在常规 cerebro
创建/设置过程中,只需在成员变量上添加对 setcomission
成员变量的 broker
调用即可。以下电话会议为 Eurostoxx50 期货在与交互经纪商合作时设置了通常的佣金方案:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
由于大多数用户通常只测试一台仪器,因此仅此而已。如果您已经对data feed给出了 aname
,因为图表上同时考虑了多个工具,则可以稍微扩展此调用,如下所示:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0, name='Eurostoxxx50')
在这种情况下,此即时佣金计划仅适用于名称匹配的工具Eurostoxx50
。
设置委托参数的含义
-
commission
(默认值:0.0)以绝对或百分比形式表示每个操作 成本的货币单位。
在上面的例子中,a
buy
的每张合约为2.0欧元,每张合约为sell
2.0欧元。这里的重要问题是何时使用绝对值或百分比值。
-
如果
margin
计算结果为False
(例如,它是 False,0或None),则将考虑commission
表示操作值的price
百size
分比 -
如果
margin
是其他东西,则认为操作发生在类似仪器上futures
,并且是commission
每size
张合约的固定价格
-
-
margin
(默认值:无)使用
futures
类似工具操作时所需的保证金。如上文所述-
如果设置了no
margin
,则将commission
理解为以百分比表示,并应用于price \* size
或buy
sell
操作的组件 -
如果设置了 a
margin
,则commission
将理解为一个固定值,该值乘以 或sell
操作的size
buy
分量
-
-
mult
(默认值:1.0)对于
future
类似的工具,这决定了乘法器应用于损益计算。这就是期货同时具有吸引力和风险的原因。
-
name
(默认值:无)将佣金方案的应用限制在工具匹配
name
这可以在创建 data feed期间设置。
如果未设置,该方案将适用于系统中存在的任何数据。
现在有两个例子:股票与期货
上面的期货例子:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
股票示例:
cerebro.broker.setcommission(commission=0.005) # 0.5% of the operation value
创建永久金计划
可以通过直接使用类来创建更永久的CommissionInfo
佣金计划。用户可以选择在以下位置使用此定义:
from bt import CommissionInfo commEurostoxx50 = CommissionInfo(commission=2.0, margin=2000.0, mult=10.0)
稍后在另一个Python模块addcommissioninfo
中应用它:
from mycomm import commEurostoxx50 ... cerebro.broker.addcomissioninfo(commEuroStoxx50, name='Eurostoxxx50')
CommissionInfo
是一个对象,它像环境中backtrader
的其他对象一params
样使用声明。因此,上述内容也可以表示为:
from bt import CommissionInfo class CommEurostoxx50(CommissionInfo): params = dict(commission=2.0, margin=2000.0, mult=10.0)
稍后:
from mycomm import CommEurostoxx50 ... cerebro.broker.addcomissioninfoCommEuroStoxx50(), name='Eurostoxxx50')
现在是与SMA分频器的「真实」比较
使用SimpleMovingAverage交叉作为进入/退出信号,相同的数据集将使用futures
类似的佣金方案进行测试,然后使用类似的佣金方案进行测试 stocks
。
注意
期货头寸不仅可以给出进入/退出行为,还可以在每次情况下给出反转行为。但这个例子是关于比较佣金方案的。
代码(完整策略请参见底部)是相同的,可以在定义策略之前选择方案。
futures_like = True if futures_like: commission, margin, mult = 2.0, 2000.0, 10.0 else: commission, margin, mult = 0.005, None, 1
只需设置为futures_like
false以使用stocks
类似方案运行。
添加了一些 logging 代码来评估不同佣金计划的影响。让我们只关注前 2 个操作。
对于期货:
2006-03-09, BUY CREATE, 3757.59 2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00 2006-04-11, SELL CREATE, 3788.81 2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00 2006-04-12, OPERATION PROFIT, GROSS 328.00, NET 324.00 2006-04-20, BUY CREATE, 3860.00 2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 2000.00, Comm 2.00 2006-04-28, SELL CREATE, 3839.90 2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 2000.00, Comm 2.00 2006-05-02, OPERATION PROFIT, GROSS -243.30, NET -247.30
对于股票:
2006-03-09, BUY CREATE, 3757.59 2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77 2006-04-11, SELL CREATE, 3788.81 2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3786.93, Comm 18.93 2006-04-12, OPERATION PROFIT, GROSS 32.80, NET -4.91 2006-04-20, BUY CREATE, 3860.00 2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 3863.57, Comm 19.32 2006-04-28, SELL CREATE, 3839.90 2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 3839.24, Comm 19.20 2006-05-02, OPERATION PROFIT, GROSS -24.33, NET -62.84
第 1次 操作具有以下价格:
-
买入(运行)-> 3754.13 / 卖出(运行)-> 3786.93
-
期货损益(带佣金):324.0
-
股票盈亏(含佣金):-4.91
嘿!!佣金已经完全吞噬了运营的任何利润,
stocks
但只意味着对运营的futures
一个小小的影响。 -
第 2个 操作:
-
买入(运行)-> 3863.57 / 卖出(运行)-> 3389.24
-
期货损益(含佣金):-247.30
-
股票损益(含佣金):-62.84
对于这种负操作,咬合已经明智地更大了
futures
-
但:
-
期货累计净损益:324.00 + (-247.30) = 76.70
-
股票累计净损益: (-4.91) + (-62.84) = -67.75
累积效应可以在下面的图表上看到,在那里也可以看出,在全年结束时,期货产生了更大的利润,但也遭受了更大的回撤(在水下更深)
但重要的是:是否futures
stocks
... 它可以进行回溯测试。
期货佣金
股票佣金
代码
from __future__ import (absolute_import, division, print_function, unicode_literals) import backtrader as bt import backtrader.feeds as btfeeds import backtrader.indicators as btind futures_like = True if futures_like: commission, margin, mult = 2.0, 2000.0, 10.0 else: commission, margin, mult = 0.005, None, 1 class SMACrossOver(bt.Strategy): def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def notify(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enougth cash if order.status in [order.Completed, order.Canceled, order.Margin]: if order.isbuy(): self.log( 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm self.opsize = order.executed.size else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) gross_pnl = (order.executed.price - self.buyprice) * \ self.opsize if margin: gross_pnl *= mult net_pnl = gross_pnl - self.buycomm - order.executed.comm self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (gross_pnl, net_pnl)) def __init__(self): sma = btind.SMA(self.data) # > 0 crossing up / < 0 crossing down self.buysell_sig = btind.CrossOver(self.data, sma) def next(self): if self.buysell_sig > 0: self.log('BUY CREATE, %.2f' % self.data.close[0]) self.buy() # keep order ref to avoid 2nd orders elif self.position and self.buysell_sig < 0: self.log('SELL CREATE, %.2f' % self.data.close[0]) self.sell() if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.addstrategy(SMACrossOver) # Create a Data Feed datapath = ('../datas/2006-day-001.txt') data = bt.feeds.BacktraderCSVData(dataname=datapath) # Add the Data Feed to Cerebro cerebro.adddata(data) # set commission scheme -- CHANGE HERE TO PLAY cerebro.broker.setcommission( commission=commission, margin=margin, mult=mult) # Run over everything cerebro.run() # Plot the result cerebro.plot()