将 CommInfo 对象改造成实际涉及的最重要的部分:
保留原来的
CommissionInfo
类和行为为轻松创建用户定义的佣金打开大门
使格式 xx% 成为新佣金方案的默认值,而不是 0.xx(只是个人喜好问题),保持行为可配置
笔记
请参阅下面的CommInfoBase
文档字符串以获取参数参考
定义佣金计划
它涉及 1 或 2 个步骤
子类
CommInfoBase
只需更改默认参数就足够了。
backtrader
已经使用模块backtrader .commissions
中的一些定义来做到这一点。期货的常规行业标准是每份合约和每轮固定金额。定义可以如下:class CommInfo_Futures_Fixed(CommInfoBase): params = ( ('stocklike', False), ('commtype', CommInfoBase.COMM_FIXED), )
对于股票和 perc-wise 佣金:
class CommInfo_Stocks_Perc(CommInfoBase): params = ( ('stocklike', True), ('commtype', CommInfoBase.COMM_PERC), )
如上所述,此处解释百分比的默认值(作为参数
commission
传递)是: xx% 。如果希望旧的/其他行为0.xx ,可以轻松完成:class CommInfo_Stocks_PercAbs(CommInfoBase): params = ( ('stocklike', True), ('commtype', CommInfoBase.COMM_PERC), ('percabs', True), )
覆盖(如果需要)
_getcommission
方法定义为:
def _getcommission(self, size, price, pseudoexec): '''Calculates the commission of an operation at a given price pseudoexec: if True the operation has not yet been executed '''
下面的实际示例中的更多详细信息
如何将此应用到平台
一旦CommInfoBase
子类就位,诀窍就是使用broker.addcommissioninfo
而不是通常的broker.setcommission
。后者将在内部使用旧的CommissionInfoObject
。
做起来比说的容易:
... comminfo = CommInfo_Stocks_PercAbs(commission=0.005) # 0.5% cerebro.broker.addcommissioninfo(comminfo)
addcommissioninfo
方法定义如下:
def addcommissioninfo(self, comminfo, name=None): self.comminfo[name] = comminfo
设置name
意味着comminfo
对象将仅适用于具有该名称的资产。默认值None
意味着它适用于系统中的所有资产。
一个实际的例子
Ticket #45询问适用于期货的佣金计划,以百分比计算,并使用合约的整个“虚拟”价值的佣金百分比。即:在佣金计算中包括未来乘数。
这应该很容易:
import backtrader as bt class CommInfo_Fut_Perc_Mult(bt.CommInfoBase): params = ( ('stocklike', False), # Futures ('commtype', bt.CommInfoBase.COMM_PERC), # Apply % Commission # ('percabs', False), # pass perc as xx% which is the default ) def _getcommission(self, size, price, pseudoexec): return size * price * self.p.commission * self.p.mult
将其放入系统:
comminfo = CommInfo_Fut_Perc_Mult( commission=0.1, # 0.1% mult=10, margin=2000 # Margin is needed for futures-like instruments ) cerebro.addcommissioninfo(comminfo)
如果首选格式0.xx作为默认值,只需将 param percabs
设置为True
:
class CommInfo_Fut_Perc_Mult(bt.CommInfoBase): params = ( ('stocklike', False), # Futures ('commtype', bt.CommInfoBase.COMM_PERC), # Apply % Commission ('percabs', True), # pass perc as 0.xx ) comminfo = CommInfo_Fut_Perc_Mult( commission=0.001, # 0.1% mult=10, margin=2000 # Margin is needed for futures-like instruments ) cerebro.addcommissioninfo(comminfo)
这一切都应该解决问题。
解释pseudoexec
让我们回顾一下_getcommission
的定义:
def _getcommission(self, size, price, pseudoexec): '''Calculates the commission of an operation at a given price pseudoexec: if True the operation has not yet been executed '''
pseudoexec
arg 的目的可能看起来晦涩难懂,但它是有目的的。
平台可能会调用该方法来进行可用现金的预计算和一些其他任务
这意味着该方法可能(并且实际上会)使用相同的参数多次调用
pseudoexec
指示调用是否对应于订单的实际运行。尽管乍一看这似乎并不“相关”,但如果考虑到以下情况:
一旦谈判合约数量超过5000个单位,经纪人提供期货往返佣金50%的折扣
在这种情况下,如果没有
pseudoexec
,对该方法的多次非运行调用将很快触发折扣到位的假设。
让场景发挥作用:
import backtrader as bt class CommInfo_Fut_Discount(bt.CommInfoBase): params = ( ('stocklike', False), # Futures ('commtype', bt.CommInfoBase.COMM_FIXED), # Apply Commission # Custom params for the discount ('discount_volume', 5000), # minimum contracts to achieve discount ('discount_perc', 50.0), # 50.0% discount ) negotiated_volume = 0 # attribute to keep track of the actual volume def _getcommission(self, size, price, pseudoexec): if self.negotiated_volume > self.p.discount_volume: actual_discount = self.p.discount_perc / 100.0 else: actual_discount = 0.0 commission = self.p.commission * (1.0 - actual_discount) commvalue = size * price * commission if not pseudoexec: # keep track of actual real executed size for future discounts self.negotiated_volume += size return commvalue
pseudoexec
的目的和存在现在希望很清楚。
CommInfoBase 文档字符串和参数
请参阅佣金:股票与期货以供CommInfoBase
参考