Navigation

    Backtrader Community

    • Register
    • 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/

    Order Margin Call on order_target_percent

    General Code/Help
    1
    2
    220
    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.
    • LeggoMaEggo
      LeggoMaEggo last edited by

      Hi all. I’ve been struggling with this problem for a bit now. The orders keep on failing on margin just a few trades in when a commission scheme is added.

      In attempt to backtest more accurately, I added a commission scheme on the strategy working off of https://teddykoker.com/2019/04/backtesting-a-cross-sectional-mean-reversion-strategy-in-python/:

      Things I have tried:

      1. Fractioned the targets after weight calculation: target=weights[i]/n Reason: instead of taking ~50% and ~50% of the portfolio size for each of the 2 currency pairs in the universe, I scale it way down to not use 100% of cash.
      2. cerebro.broker.set_shortcash(False). Reason: Shorting can count as cash added to an account.
      3. cerebro.broker.set_checksubmit(False) Reason: https://community.backtrader.com/topic/1611/having-margin-problem-when-reversing-the-position-using-order_target_percent/2. Similar problem, but for this strategy, doesn’t even do a partial order. Just straight to order failed because of margin call.
      4. cheat_on_open=True. Weights are calculated at close, so it’ll make sense to try and execute order on open to match.

      None of these solutions have worked for me, still margin called after the first few trades. Perhaps some of the attempts at solving this problem is not completely understanding config settings. It is the configuration that makes the most sense for me at this time.

      Strategy:

      class St(bt.Strategy):
      
          def log(self, arg):
              print("{} {}".format(self.datetime.datetime(), arg))
      
          def __init__(self):
              if self.p.backtest:
                  self.datastatus = 1
              else:
                  self.datastatus = 0
      
          def notify_data(self, data, status, *args, **kwargs):
              print("*" * 5, "DATA NOTIF:", data._getstatusname(status), *args)
              if status == data.LIVE:
                  self.datastatus = 1
      
          def notify_order(self, order):
              """Run on every next iteration. Checks order status and logs accordingly"""
              if order.status in [order.Submitted, order.Accepted]:
                  return
              elif order.status == order.Completed:
                  if order.isbuy():
                      self.log(
                          "BUY EXECUTED price: {}, value: {}, commission: {}, broker: {}, cash: {}".format(
                              order.executed.price,
                              order.executed.value,
                              order.executed.comm,
                              self.broker.get_value(),
                              self.broker.get_cash(),
                          )
                      )
                      self.buyprice = order.executed.price
                      self.buycomm = order.executed.comm
                  else:  # sell
                      self.log(
                          "SELL EXECUTED price: {}, value: {}, commission: {}, broker: {}, cash: {}".format(
                              order.executed.price,
                              order.executed.value,
                              order.executed.comm,
                              self.broker.get_value(),
                              self.broker.get_cash(),
                          )
                      )
      
              elif order.status in [order.Margin, order.Rejected, order.Canceled]:
                  self.log(
                      "ORDER FAILED with status: {}, BROKER VAL: {}, CASH AVAILABLE: {}".format(
                          order.getstatusname(), self.broker.get_value(), self.broker.get_cash()
                      )
                  )
      
              # change order variable back to None to indicate no pending order
              self.order = None
      
          def notify_trade(self, trade):
              """Run on every next iteration. Logs data on every trade when closed."""
              if trade.isclosed:
                  self.log("CLOSE Gross P/L: {}, Net P/L: {}".format(trade.pnl, trade.pnlcomm))
      
          def next(self):
              # only look at data that existed yesterday
              available = list(filter(lambda d: len(d), self.datas))
      
              rets = np.zeros(len(available))
              for i, d in enumerate(available):
                  # calculate individual daily returns
                  rets[i] = (d.close[0] - d.close[-1]) / d.close[-1]
      
              # calculate weights using formula
              market_ret = np.mean(rets)
              weights = -(rets - market_ret)
              weights = weights / np.sum(np.abs(weights))
      
              for i, d in enumerate(available):
                  dt, dn = self.datetime.datetime(), d._name
      
                  if self.datastatus:
                      self.order_target_percent(d, target=weights[i] / 4)
                      self.log(
                          "ORDER CREATED: {:.2f} on {} with broker val: {} cash: {} weights: {} ".format(
                              d.close[0], dn, self.broker.get_value(), self.broker.get_cash(), weights[i] / 4
                          )
                      )
      
       
      

      Commission Scheme:

      class forexSpreadCommisionScheme(bt.CommInfoBase):
          """
          https://backtest-rookies.com/2017/07/13/code-snippet-forex-commission-scheme/
      
          """
      
          params = (
              ("spread", 2.0),
              ("JPY_pair", False),
              ("stocklike", False),
              ("acc_counter_currency", True),
              ("margin", 0.03),  # highest margin requirements for these /USD pairs.
              ("leverage", 20.0),  # 20x cash
              ("commtype", bt.CommInfoBase.COMM_FIXED),
          )
      
          def _getcommission(self, size, price, pseudoexec):
              if self.p.JPY_pair == True:
                  multiplier = 0.01
              else:
                  multiplier = 0.0001
      
              if self.p.acc_counter_currency == True:
                  comm = abs((self.p.spread * (size * multiplier) / 2))
      
              else:
                  comm = abs((self.p.spread * ((size / price) * multiplier) / 2))
      
              return comm
      

      Configuration:

      
      cerebro = bt.Cerebro(quicknotify=True, cheat_on_open=True)
      
      for ticker in ['NZD_USD', 'AUD_USD']:
          # get datafeed here...
          cerebro.adddata(data)
      
      cerebro.broker.setcash(10_000)
      cerebro.addstrategy(St)
      
      cerebro.addsizer(bt.sizers.FixedSize, stake=1)
      
      # Add the new commission scheme
      comminfo = forexSpreadCommisionScheme(spread=2.0, acc_counter_currency=True)
      cerebro.broker.addcommissioninfo(comminfo)
      
      # Set slippage for realistic scenarios. Increase chance of order filling
      cerebro.broker.set_slippage_fixed(0.0005, slip_open=False, slip_match=False, slip_out=False)
      
      cerebro.broker.set_shortcash(False)  # Adds cash when shorting, instead of subtracting
      cerebro.broker.set_checksubmit(False)  # Check margin/cash before accepting an order into the system
      
      results = cerebro.run(tradehistory=True)
      
      pnl = cerebro.broker.get_value() - args.cash
      print("Profit or Loss: {:.2f}".format(pnl))
      

      Logs:

      2010-12-30 22:00:00 ORDER CREATED: 0.77 on NZD_USD with broker val: 10000.0 cash: 10000.0 weights: 0.12500000000000003 
      2010-12-30 22:00:00 ORDER CREATED: 1.02 on AUD_USD with broker val: 10000.0 cash: 10000.0 weights: -0.125 
      2010-12-30 22:00:00 BUY EXECUTED price: 0.77988, value: 24999.6, commission: 83.33200000000001, broker: 23011.875139999738, cash: 14091.601199999866
      2010-12-30 22:00:00 SELL EXECUTED price: 1.02324, value: 24999.6, commission: 83.33200000000001, broker: 23011.875139999738, cash: 14091.601199999866
      2010-12-31 22:00:00 ORDER CREATED: 0.78 on NZD_USD with broker val: 23011.875139999738 cash: 14091.601199999866 weights: 0.125 
      2010-12-31 22:00:00 ORDER CREATED: 1.02 on AUD_USD with broker val: 23011.875139999738 cash: 14091.601199999866 weights: -0.125 
      2010-12-31 22:00:00 CLOSE Gross P/L: -3041.618000000035, Net P/L: -3208.282000000035
      2010-12-31 22:00:00 ORDER FAILED with status: Margin, BROKER VAL: 12298.29655999978, CASH AVAILABLE: 8325.02679999987
      2010-12-31 22:00:00 ORDER FAILED with status: Margin, BROKER VAL: 12298.29655999978, CASH AVAILABLE: 8325.02679999987
      2011-01-01 22:00:00 ORDER CREATED: 0.77 on NZD_USD with broker val: 12298.29655999978 cash: 8325.02679999987 weights: 0.125 
      2011-01-01 22:00:00 ORDER CREATED: 1.02 on AUD_USD with broker val: 12298.29655999978 cash: 8325.02679999987 weights: -0.125 
      2011-01-01 22:00:00 BUY EXECUTED price: 0.77328, value: 30745.199999999997, commission: 102.48400000000001, broker: 18258.873459999806, cash: 9794.544799999883
      2011-01-01 22:00:00 ORDER FAILED with status: Margin, BROKER VAL: 18258.873459999806, CASH AVAILABLE: 9794.544799999883
      2011-01-02 22:00:00 ORDER CREATED: 0.77 on NZD_USD with broker val: 18258.873459999806 cash: 9794.544799999883 weights: -0.12500000000000003 
      2011-01-02 22:00:00 ORDER CREATED: 1.02 on AUD_USD with broker val: 18258.873459999806 cash: 9794.544799999883 weights: 0.12499999999999997 
      2011-01-02 22:00:00 CLOSE Gross P/L: -6866.427999999926, Net P/L: -7071.395999999926
      2011-01-02 22:00:00 ORDER FAILED with status: Margin, BROKER VAL: 32688.961160000075, CASH AVAILABLE: 15336.738800000057
      2011-01-02 22:00:00 ORDER FAILED with status: Margin, BROKER VAL: 32688.961160000075, CASH AVAILABLE: 15336.738800000057
      2011-01-03 22:00:00 ORDER CREATED: 0.76 on NZD_USD with broker val: 32688.961160000075 cash: 15336.738800000057 weights: -0.12499999999999978 
      2011-01-03 22:00:00 ORDER CREATED: 1.00 on AUD_USD with broker val: 32688.961160000075 cash: 15336.738800000057 weights: 0.12500000000000022 
      2011-01-03 22:00:00 SELL EXECUTED price: 0.75709, value: 81722.4, commission: 272.408, broker: 47940.29879999999, cash: 18939.106000000014
      2011-01-03 22:00:00 ORDER FAILED with status: Margin, BROKER VAL: 47940.29879999999, CASH AVAILABLE: 18939.106000000014
      2011-01-04 22:00:00 ORDER CREATED: 0.76 on NZD_USD with broker val: 47940.29879999999 cash: 18939.106000000014 weights: 0.12500000000000003 
      2011-01-04 22:00:00 ORDER CREATED: 1.00 on AUD_USD with broker val: 47940.29879999999 cash: 18939.106000000014 weights: -0.12499999999999997 
      ....
      ....
      ....
      2019-07-25 21:00:00 ORDER CREATED: 0.66 on NZD_USD with broker val: -94102645413.3025 cash: -94102645413.3025 weights: -0.12499999999999993 
      2019-07-25 21:00:00 ORDER CREATED: 0.69 on AUD_USD with broker val: -94102645413.3025 cash: -94102645413.3025 weights: 0.12500000000000008 
      2019-07-25 21:00:00 ORDER FAILED with status: Margin, BROKER VAL: -94102645413.3025, CASH AVAILABLE: -94102645413.3025
      2019-07-25 21:00:00 ORDER FAILED with status: Margin, BROKER VAL: -94102645413.3025, CASH AVAILABLE: -94102645413.3025
      2019-07-28 21:00:00 ORDER CREATED: 0.66 on NZD_USD with broker val: -94102645413.3025 cash: -94102645413.3025 weights: 0.125 
      2019-07-28 21:00:00 ORDER CREATED: 0.69 on AUD_USD with broker val: -94102645413.3025 cash: -94102645413.3025 weights: -0.125 
      2019-07-28 21:00:00 ORDER FAILED with status: Margin, BROKER VAL: -94102645413.3025, CASH AVAILABLE: -94102645413.3025
      2019-07-28 21:00:00 ORDER FAILED with status: Margin, BROKER VAL: -94102645413.3025, CASH AVAILABLE: -94102645413.3025
      2019-07-29 21:00:00 ORDER CREATED: 0.66 on NZD_USD with broker val: -94102645413.3025 cash: -94102645413.3025 weights: -0.125 
      2019-07-29 21:00:00 ORDER CREATED: 0.69 on AUD_USD with broker val: -94102645413.3025 cash: -94102645413.3025 weights: 0.125 
      Strategy run finished with Run ID: 19
      Profit or Loss: -94102655413.30
      

      Is there something I am overlooking/misunderstanding? Only in year 2011 of the logs are a handful of successfully executed orders and closed positions. Any insights as to what may be happening is appreciated and let me know if more information is required. Thank you!

      1 Reply Last reply Reply Quote 0
      • LeggoMaEggo
        LeggoMaEggo last edited by

        Bump. Any ideas?

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