不久前,委员会计划的实施进行了重新设计。最重要的是:涉及的部分返工:
-
保留原始的佣金信息类和行为
-
打开大门,轻松创建用户定义的佣金
-
将格式 xx% 作为新佣金方案的默认值,而不是 0.xx(只是一个品味问题),保持行为可配置
扩展委员会概述了基本要素。
注意
请参阅下面的文档字符串CommInfoBase ,以获取参数参考
定义佣金计划
它涉及1或2个步骤
-
子类
CommInfoBase只需更改缺省参数可能就足够了。
backtrader已经使用模块backtrader.commissions中存在的一些定义来运行此操作。期货的常规行业标准是每份合约和每轮的固定金额。该定义可以按如下方式完成:class CommInfo_Futures_Fixed(CommInfoBase): params = ( ('stocklike', False), ('commtype', CommInfoBase.COMM_FIXED), )对于股票和perc明智的佣金:
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 它适用于系统中的所有资产。
一个实际的例子
票证#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.broker.addcommission(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.broker.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
'''
arg的目的pseudoexec 可能看起来很晦涩难懂,但它有一个目的。
-
平台可能会调用此方法对可用现金和其他一些任务进行预计算
-
这意味着该方法可以(实际上也会)使用相同的参数多次调用。
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 docstring and params
在这里:
class CommInfoBase(with_metaclass(MetaParams)):
'''Base Class for the Commission Schemes.
Params:
- commission (def: 0.0): base commission value in percentage or monetary
units
- mult (def 1.0): multiplier applied to the asset for value/profit
- margin (def: None): amount of monetary units needed to open/hold an
operation. It only applies if the final ``_stocklike`` attribute in the
class is set to False
- commtype (def: None): Supported values are CommInfoBase.COMM_PERC
(commission to be understood as %) and CommInfoBase.COMM_FIXED
(commission to be understood as monetary units)
The default value of ``None`` is a supported value to retain
compatibility with the legacy ``CommissionInfo`` object. If
``commtype`` is set to None, then the following applies:
- margin is None: Internal _commtype is set to COMM_PERC and
_stocklike is set to True (Operating %-wise with Stocks)
- margin is not None: _commtype set to COMM_FIXED and _stocklike set
to False (Operating with fixed rount-trip commission with Futures)
If this param is set to something else than None, then it will be
passed to the internal ``_commtype`` attribute and the same will be
done with the param ``stocklike`` and the internal attribute
``_stocklike``
- stocklike (def: False): Indicates if the instrument is Stock-like or
Futures-like (see the ``commtype`` discussion above)
- percabs (def: False): when ``commtype`` is set to COMM_PERC, whether
the parameter ``commission`` has to be understood as XX% or 0.XX
If this param is True: 0.XX
If this param is False: XX%
Attributes:
- _stocklike: Final value to use for Stock-like/Futures-like behavior
- _commtype: Final value to use for PERC vs FIXED commissions
This two are used internally instead of the declared params to enable the
compatibility check described above for the legacy ``CommissionInfo``
object
'''
COMM_PERC, COMM_FIXED = range(2)
params = (
('commission', 0.0), ('mult', 1.0), ('margin', None),
('commtype', None),
('stocklike', False),
('percabs', False),
)