- After applying a custom commission class by @ThatBlokeDave to forex trading in backtrader, how do I set commission including the multiplier? I am adding the code below but getting an error.
# Set the commission - 0.1% ... divide by 100 to remove the % #cerebro.broker.setcommission(commission=0.001) comm = forexSpreadCommisionScheme(spread=4, acc_counter_currency=False) cerebro.broker.setcommission(leverage=50, stocklike=False, commission=comm, mult=100)
TypeError: unsupported operand type(s) for *: 'int' and 'forexSpreadCommisionScheme'
- I am using the multiplier to account for a lot size of 100 i.e. 1 pip move = $0.01 profit of loss. Is it correct to use a multipier of 100 in this case?
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
# Import the backtrader platform
import backtrader as bt
# Create a Stratey
class TestStrategy(bt.Strategy):
params = (
('exitbars', 12),
('shortemaperiod', 7),
('longemaperiod', 25),
('printlog', False)
)
def log(self, txt, dt=None, doprint=True):
''' Logging function for this strategy'''
if self.params.printlog or doprint:
dt = dt or self.datas[0].datetime.datetime(0)
print(f'{dt.isoformat()}, {txt}')
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders
self.order = None
self.buyprice = None
self.buycomm = None
# Add a MovingAverageSimple indicator
self.shortema = bt.indicators.ExponentialMovingAverage(
self.datas[0], period=self.params.shortemaperiod)
self.longema = bt.indicators.ExponentialMovingAverage(
self.datas[0], period=self.params.longemaperiod)
def notify_order(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 enough cash
if order.status in [order.Completed]:
if order.isbuy():
self.log(
f'Buy EXECUTED: PRICE:{order.executed.price},COST:{order.executed.value:.2f},COMM:{order.executed.comm:.2f}'
)
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else: # SELL
self.log(
f'SELL EXECUTED: PRICE:{order.executed.price},COST:{order.executed.value:.2f},COMM:{order.executed.comm:.2f}'
)
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log(f'OPERATING PROFIT, GROSS:{trade.pnl:.2f}, NET:{trade.pnlcomm:.2f}')
def next(self):
# Simply log the closing price of the series from the reference
self.log(f'Close, {self.dataclose[0]}')
# Check if an order is pending ... if yes we cannot send a 2nd one
if self.order:
return
# Check if we are in the market
if not self.position:
# Not yet we MIGHT BUY if ...
if self.longema < self.shortema:
# BUY, BUY, BUY!!! (with all possible default parameters)
self.log(f'BUY CREATE, {self.dataclose[0]}')
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
else:
# Already in the market ... we might sell
if len(self) >= (self.bar_executed + self.params.exitbars):
# SELL, SELL, SELL!!!(with all possible default parameters)
self.log(f'SELL CREATE: {self.dataclose[0]}')
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
def stop(self):
self.log(
f'ShortEMAPeriod:{self.params.shortemaperiod}, EndingValue:{self.broker.getvalue()}',
doprint=True
)
class forexSpreadCommisionScheme(bt.CommInfoBase):
'''
This commission scheme attempts to calcuate the commission hidden in the
spread by most forex brokers. It assumes a mid point data is being used.
*New Params*
spread: Float, the spread in pips of the instrument
JPY_pair: Bool, states whether the pair being traded is a JPY pair
acc_counter_currency: Bool, states whether the account currency is the same
as the counter currency. If false, it is assumed to be the base currency
'''
params = (
('spread', 2.0),
('stocklike', False),
('JPY_pair', False),
('acc_counter_currency', True),
('commtype', bt.CommInfoBase.COMM_FIXED),
)
def _getcommission(self, size, price, pseudoexec):
'''
This scheme will apply half the commission when buying and half when selling.
If JPY pair change the multiplier accordingly.
If account currency is same as the base currency, change pip value calc.
'''
if self.p.JPY_pair:
pipmultiplier = 0.01
else:
pipmultiplier = 0.0001
if self.p.acc_counter_currency:
comm = abs((self.p.spread * (size * pipmultiplier)/2))
else:
comm = abs((self.p.spread * ((size / price) * pipmultiplier)/2))
return comm
def get_data(file_path):
# Datas are in a subfolder of the samples. Need to find where the script is
# because it could have been called from anywhere
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
datapath = os.path.join(modpath, file_path)
# Create a Data Feed
data = bt.feeds.GenericCSVData(
dataname=datapath,
# Do not pass values before this date Y, M, D
fromdate=datetime.datetime(2016, 1, 1),
# Do not pass values after this date
todate=datetime.datetime(2020, 12, 31),
nullvalue=0.0,
timeframe=bt.TimeFrame.Minutes,
compression=60,
dtformat=('%Y-%m-%d %H:%M:%S UTC'),
datetime=0,
open=1,
high=2,
low=3,
close=4,
volume=5,
openinterest=-1,
reverse=False)
return data
if __name__ == '__main__':
# Create a cerebro entity
cerebro = bt.Cerebro()
# Add a strategy
cerebro.addstrategy(TestStrategy, exitbars=21)
#strats = cerebro.optstrategy(TestStrategy, shortemaperiod=range(3, 28))
# Get data
data = get_data('data/forex/EURUSD_H1.csv')
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# Set our desired cash start
cerebro.broker.setcash(1000000.0)
# Add a FixedSize sizer according to the stake
#cerebro.addsizer(bt.sizers.FixedSize, stake=25000)
# Set the commission - 0.1% ... divide by 100 to remove the %
#cerebro.broker.setcommission(commission=0.001)
comm = forexSpreadCommisionScheme(spread=4, acc_counter_currency=False)
cerebro.broker.setcommission(leverage=50, stocklike=False, commission=comm, mult=100)
# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Run over everything
cerebro.run()
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Plot the result
#cerebro.plot(maxcpus=1)```