Defining a Commission Scheme
It involves 1 or 2 steps
- Subclassing CommInfoBase
Simply changing the default parameters may be enough. backtrader already does this with some definitions present in the module
-
Overriding (if needed be) the _getcommission method
def _getcommission(self, size, price, pseudoexec):
'''Calculates the commission of an operation at a given pricepseudoexec: if True the operation has not yet been executed '''
How to apply this to the platform
Once a CommInfoBase subclass is in place the trick is to use broker.addcommissioninfo rather than the usual broker.setcommission. The latter will internally use the legacy CommissionInfoObject.
Easier done than said:
...
comminfo = CommInfo_Stocks_PercAbs(commission=0.005) # 0.5%
cerebro.broker.addcommissioninfo(comminfo)
The addcommissioninfo method is defined as follows:
def addcommissioninfo(self, comminfo, name=None):
self.comminfo[name] = comminfo
Setting name means that the comminfo object will only apply to assets with that name. The default value of None means it applies to all assets in the system.
Explaining pseudoexec
The purpose of the pseudoexec arg may seem obscure but it serves a purpose.
-
The platform may call this method to do precalculation of available cash and some other tasks
-
This means that the method may (and it actually will) be called more than once with the same parameters
pseudoexec indicates whether the call corresponds to the actual execution of an order. (pseudoexec 标记了某一次订单的执行是不是真正的执行,如果不是实际执行的订单,则该变量为True) Although at first sight this may not seem “relevant”, it is if scenarios like the following are considered:
- A broker offers a 50% discount on futures round-trip commission once the amount of negotiated contracts has exceeeded 5000 units
In such case and if pseudoexec was not there, the multiple non-execution calls to the method would quickly trigger the assumption that the discount is in place.
Putting the scenario to work:
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
网友评论