不可知论
在继续之前,让我们记住, backtrader
者试图对数据代表什么保持不可知论。不同的佣金方案可以应用于相同的数据集。
让我们看看它是如何做到的。
使用代理快捷方式
这使最终用户远离CommissionInfo
对象,因为可以通过单个函数调用创建/设置佣金方案。在常规的cerebro
创建/设置过程中,只需在broker
成员属性上添加对setcommission
的调用。以下调用设置了与盈透证券合作时Eurostoxx50期货的常规佣金计划:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
由于大多数用户通常只测试一个仪器,这就是它的全部。如果您已为数据馈送name
,因为图表上同时考虑了多个工具,此调用可以稍微扩展为如下所示:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0, name='Eurostoxxx50')
在这种情况下,这种即时佣金计划将仅适用于名称与Eurostoxx50
匹配的工具。
setcommission参数的含义
commission
(默认值:0.0
)以绝对或百分比计算的货币单位每项行动的成本。
在上面的例子中,每份
buy
合约 2.0 欧元,sell
每份合约 2.0 欧元。这里的重要问题是何时使用绝对值或百分比值。
如果
margin
评估为False
(例如为False 、0 或 None),则认为commission
表示price
乘以size
操作值的百分比如果
margin
是其他东西,则认为该操作是在诸如工具之类的futures
上进行的,而commission
是size
合约的固定价格
margin
(默认值:None
)使用诸如工具之类的
futures
进行操作时需要保证金。如上所述如果设置了无
margin
,则commission
将被理解为以百分比表示并应用于buy
或sell
操作的price * size
组成部分如果设置了
margin
,commission
将被理解为乘以buy
或sell
操作的size
分量的固定值
mult
(默认值:1.0)对于
future
类似的工具,这决定了应用于损益计算的乘数。这就是使期货同时具有吸引力和风险的原因。
name
(默认:无)将佣金计划的应用限制在与
name
匹配的工具上这可以在创建数据馈送期间进行设置。
如果未设置,该方案将适用于系统中存在的任何数据。
现在举两个例子:股票与期货
上面的期货示例:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
股票的一个例子:
cerebro.broker.setcommission(commission=0.005) # 0.5% of the operation value
笔记
第二种语法不设置保证金,并且 mult 和 backtrader尝试一种聪明的方法,将佣金考虑为基于%
。
要完全指定佣金方案,需要创建CommissionInfo
的子类
创建永久佣金计划
可以通过直接使用CommissionInfo
类来创建更永久的佣金方案。用户可以选择在某处有这个定义:
import backtrader as bt commEurostoxx50 = bt.CommissionInfo(commission=2.0, margin=2000.0, mult=10.0)
稍后使用addcommissioninfo
将其应用到另一个 Python 模块中:
from mycomm import commEurostoxx50 ... cerebro.broker.addcommissioninfo(commEuroStoxx50, name='Eurostoxxx50')
CommissionInfo
是一个使用params
声明的对象,就像backtrader
环境中的其他对像一样。因此,上述也可以表示为:
import backtrader as bt class CommEurostoxx50(bt.CommissionInfo): params = dict(commission=2.0, margin=2000.0, mult=10.0)
然后:
from mycomm import CommEurostoxx50 ... cerebro.broker.addcommissioninfoCommEuroStoxx50(), name='Eurostoxxx50')
现在与 SMA Crossover 进行“真实”比较
使用 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
类似方案一起运行。
添加了一些日志代码来评估不同佣金方案的影响。让我们只关注前两个操作。
对于期货:
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
第一次操作的价格如下:
买入(运行)-> 3754.13 / 卖出(运行)-> 3786.93
期货盈亏(含佣金):324.0
股票损益(含佣金):-4.91
嘿!!委员会已经完全吞噬了stocks
业务的任何利润,但对futures
业务的影响只是很小的一部分。
第二次操作:
买入(运行)->
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()
参考
类反向交易者 .CommInfoBase ()
佣金计划的基类。
参数:
commission
(定义:0.0
):以百分比或货币单位表示的基本佣金值mult
(def1.0
): 应用于资产价值/利润的乘数margin
(定义:None
):打开/持有操作所需的货币单位数量。仅当类中的最终_stocklike
属性设置为False
时才适用automargin
(def:False
): 由方法get_margin
用于自动计算以下策略所需的保证金/保证如果 param
automargin
评估为False
,则使用 parammargin
如果
automargin < 0
,则使用 parammult
并使用mult * price
使用参数
automargin
并使用automargin * price
如果automargin > 0
commtype
(def:None
): 支持的值为CommInfoBase.COMM_PERC
(佣金被理解为 %)和CommInfoBase.COMM_FIXED
(佣金被理解为货币单位)None
的默认值是支持的值,以保持与旧CommissionInfo
对象的兼容性。如果commtype
设置为 None,则以下内容适用:margin
为None
:内部_commtype
设置为COMM_PERC
并且_stocklike
设置为True
(使用股票操作 %-wise)margin
不是None
:_commtype
设置为COMM_FIXED
和_stocklike
设置为False
(使用期货固定往返佣金操作)
如果此参数设置为
None
以外的其他值,则它将传递给内部_commtype
属性,并且对参数stocklike
和内部属性_stocklike
stocklike
(def:False
): 指示该工具是 Stock-like 还是 Futures-like (参见上面的commtype
讨论)percabs
(def:False
):commtype
设置为 COMM_PERC 时,参数commission
是否必须理解为 XX% 或 0.XX如果此参数为
True
:0.XX 如果此参数为False
:XX%interest
(def:0.0
)如果该值非零,则为持有卖空头寸收取的年利息。这主要用于股票卖空
公式:
days * price * abs(size) * (interest / 365)
必须以绝对值指定:0.05 -> 5%
笔记
可以通过覆盖方法来更改行为:
_get_credit_interest
interest_long
(def:False
)某些产品(例如 ETF)会收取空头和多头头寸的利息。如果 ths 为
True
且interest
不为零,则将在两个方向上收取利息leverage
(def:1.0
)与所需现金相关的资产杠杆率
- ``_stocklike``()
用于股票类/期货类行为的最终值
-``_commtype``()
用于 PERC 与 FIXED 佣金的最终价值
这两个在内部使用而不是声明的参数来激活()
上述对遗留 ``CommissionInfo``() 的兼容性检查
目的()
类反向交易者 .CommissionInfo ()
实际佣金计划的基类。
创建 CommInfoBase 是为了支持backtrader提供的原始、不完整的支持。新的佣金计划派生自此类,它是CommInfoBase
的子类。
percabs
的默认值也更改为True
参数:
percabs
(def: True ):commtype
设置为 COMM_PERC 时,参数commission
是否必须理解为 XX% 或 0.XX如果此参数为真:0.XX 如果此参数为假:XX%
获取杠杆()
返回此佣金计划允许的杠杆水平
getsize(价格,现金)
返回在给定价格下满足现金操作所需的大小
获取运营成本(大小,价格)
返回操作所需的现金金额
getvaluesize(大小,价格)
返回给定价格的大小值。对于类似未来的对象,它固定为size * margin
获取价值(位置,价格)
返回给定价格的头寸值。对于类似未来的对象,它固定为size * margin
get_margin(价格)
返回给定价格的单个资产项目所需的实际保证金/担保。默认实现具有此策略:
如果 param
automargin
评估为False
,则使用 parammargin
使用参数
mult
,即mult * price
ifautomargin < 0
使用参数
automargin
,即automargin * price
ifautomargin > 0
获得佣金(尺寸,价格)
计算给定价格的操作佣金
_getcommission(大小,价格,伪运行)
计算给定价格的操作佣金
pseudoexec:如果为True ,则操作尚未运行
盈亏(大小、价格、新价格)
返回头寸的实际盈亏
现金调整(大小,价格,新价格)
计算给定价格差异的现金调整
get_credit_interest(数据,pos,dt)
计算卖空或特定产品的信用额度
_get_credit_interest(数据、大小、价格、天数、dt0、dt1)
此方法以经纪人收取的信用利息返回成本。
在size > 0
的情况下,仅当类interest_long
的参数为True
时才会调用此方法
信贷利率的计算公式为:
公式: days * price * abs(size) * (interest / 365)
参数:
* `data`: data feed for which interest is charged * `size`: current position size. > 0 for long positions and < 0 for short positions (this parameter will not be `0`) * `price`: current position price * `days`: number of days elapsed since last credit calculation (this is (dt0 - dt1).days) * `dt0`: (datetime.datetime) current datetime * `dt1`: (datetime.datetime) datetime of previous calculation
dt0
和dt1
在默认实现中不使用,并作为覆盖方法的额外输入提供