Backtrader Community

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    Forex custom commission and multiplier

    Indicators/Strategies/Analyzers
    2
    2
    470
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • T
      tradersnorth last edited by

      1. 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'

      1. 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)```
      vladisld 1 Reply Last reply Reply Quote 0
      • vladisld
        vladisld @tradersnorth last edited by

        @tradersnorth said in Forex custom commission and multiplier:

        cerebro.broker.setcommission(leverage=50, stocklike=False, commission=comm, mult=100)

        AFAIU setcommision method just creates a generic CommInfoBase object using passed parameters to this method.

        It means the commission parameter is expected to be (quoting from CommInfoBase docs):

        ``commission`` (def: ``0.0``): base commission value in percentage or  monetary units
        

        In your case, using cerebro.addcommissioninfo method is probably more appropriate. It allows passing a custom CommInfoBase inherited commission object.

        1 Reply Last reply Reply Quote 0
        • 1 / 1
        • First post
          Last post
        Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors