broker.addcommissioninfo: how to add two classes Dividends and Commision?
I'm testing portfolios on different markets (single or global, for example France, US). For each market I have class with commisions logic, for example:
import backtrader as bt class CommIB(bt.CommInfoBase): params = ( ('commission', 0.0035), ('stocklike', True), # Stocks ('commtype', bt.CommInfoBase.COMM_FIXED), # Apply fixed Commission ('min_comm', 0.35), # USD ('max_comm', 0.005), ('on_slippage', 0.001), ) def _getcommission(self, size, price, pseudoexec): commis = abs(size) * self.p.commission trade_value = abs(size) * price max_commis = trade_value * self.p.max_comm slipp_value = trade_value * self.p.on_slippage if self._commtype == self.COMM_PERC: return trade_value * self.p.commission if commis < self.p.min_comm: return self.p.min_comm + trade_value * self.p.on_slippage elif commis > max_commis: return max_commis + slipp_value else: return commis + slipp_value
Also there is a unified class, that apply accrues dividends on holding shares:
import backtrader as bt import numpy as np class Dividends(bt.CommissionInfo): params = ( ('interest_long', True), ('path_data', '..\\Data\\DIV Data') ) def __init__(self): super(Dividends, self).__init__() self.div_data = div_loader(self.p.path_data) def _get_credit_interest(self, data, size, price, days, dt0, dt1): amount_total = 0 if (dt1 in self.div_data.index) & (data._name in self.div_data.columns): amount = self.div_data.loc[dt1, data._name] if ~np.isnan(amount): amount_total = -amount * size print('Dividend found:', data._name, 'Held shares:', size, 'Price:', price, # 'Days:', days, 'dt0,dt1:', dt0, dt1, 'DIV Amount:', amount, 'DIV Total:', -amount_total) return amount_total
If to test strategy only on the US market, it's obviously, that the next syntax work's well only for the Dividend accruals, (CommIB() overriden):
Solving of this is creating a single class with the CommIB and the Dividends logic that could works in one market (although it is inconvenient). But if to test strategy on Global market, with the different commissions for the different markets, then the task seems impossible.
It would great if the method addcommissioninfo would supports the syntax like this (doesn't overriding instance self.comminfo[name] in broker.py every time, but adding):
for stock in stock_list_US + stock_list_France: cerebro.broker.addcommissioninfo(Dividends(), name=stock) for stock in stock_list_US: cerebro.broker.addcommissioninfo(CommIB(), name=stock) for stock in stock_list_France: cerebro.broker.addcommissioninfo(CommIB_France(), name=stock)
Backtrader allows you to connect commission scheme to data feed. Check the docs -
@ab_trader yes, it's clear, thanks.
To the question above how to use two classes CommIB and Dividends I found simple solution with inheritance like this:
class Dividends(bt.CommissionInfo): params = (...) def _get_credit_interest(self, data, size, price, days, dt0, dt1): ''' some logic ''' class CommIB_US(Dividends): params = (...) def _getcommission(self, size, price, pseudoexec): ''' some logic ''' class CommIB_France(Dividends): params = (...) def _getcommission(self, size, price, pseudoexec): ''' some logic '''
And then set commission scheme for each datafeed:
The problem with this approach is around edge cases: if we buy on the ex-div date, the function will run and return dividend, even though it shouldn't. Likewise, if we sell on ex-div date the function won't run and the dividend won't be calculated.