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/

    Curious on Negative Balance Occurring in My Pair Trading Strategy Algo

    Indicators/Strategies/Analyzers
    3
    8
    2772
    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.
    • S
      samk last edited by samk

      Hello,

      I am attempting to create a pair trading algorithm between DG and DLTR as tickers. While the algorithm is running, there seems to be some odd error I have not been able to figure out. While I start balance at 100,000, ending balance is - 3,513,102; which obviously does not make sense.

      Wondering if anyone can please take a quick glance at my code and see where the problem could be lying here. Very much appreciate the assistance and thanks in advance. Full code below:

      from __future__ import (absolute_import, division, print_function,
                              unicode_literals)
      
      import argparse
      import datetime
      
      # The above could be sent to an independent module
      import backtrader as bt
      import backtrader.feeds as btfeeds
      import backtrader.indicators as btind
      
      
      class PairTradingStrategy(bt.Strategy):
          params = dict(
              period=10,
              stake=10,
              qty1=0,
              qty2=0,
              printout=True,
              upper=2.1,
              lower=-2.1,
              up_medium=0.5,
              low_medium=-0.5,
              status=0,
              portfolio_value=100000,
          )
      
          def log(self, txt, dt=None):
              if self.p.printout:
                  dt = dt or self.data.datetime[0] # set date time
                  dt = bt.num2date(dt)
                  print('%s, %s' % (dt.isoformat(), txt))
      
          def notify_order(self, order):
              if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
                  return  # Await further notifications
      
              if order.status == order.Completed:
                  if order.isbuy():
                      buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                      self.log(buytxt, order.executed.dt)
                  else:
                      selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                      self.log(selltxt, order.executed.dt)
      
              elif order.status in [order.Expired, order.Canceled, order.Margin]:
                  self.log('%s ,' % order.Status[order.status])
                  pass  # Simply log
      
              # Allow new orders
              self.orderid = None
      
          def __init__(self):
              # To control operation entries
              self.orderid = None
              self.qty1 = self.p.qty1
              self.qty2 = self.p.qty2
              self.upper_limit = self.p.upper
              self.lower_limit = self.p.lower
              self.up_medium = self.p.up_medium
              self.low_medium = self.p.low_medium
              self.status = self.p.status
              self.portfolio_value = self.p.portfolio_value
      
              # Signals performed with PD.OLS :                                   # signal performed using ordinary least regression
              self.transform = btind.OLS_TransformationN(self.data0, self.data1,
                                                         period=self.p.period)
              self.zscore = self.transform.zscore         # calculate z score ( - mean / std deviation)
      
              # Checking signals built with StatsModel.API :
              # self.ols_transfo = btind.OLS_Transformation(self.data0, self.data1,
              #                                             period=self.p.period,
              #                                             plot=True)
      
          def next(self):
      
              if self.orderid:
                  return  # if an order is active, no new orders are allowed
      
              if self.p.printout:
                  print('Self  len:', len(self))
                  print('Data0 len:', len(self.data0)) # length of time series for data 0
                  print('Data1 len:', len(self.data1)) # length of time series for data 1
                  print('Data0 len == Data1 len:',
                        len(self.data0) == len(self.data1)) # set lengths equal to each other (ensure they are)
      
                  print('Data0 dt:', self.data0.datetime.datetime())
                  print('Data1 dt:', self.data1.datetime.datetime())
      
              print('status is', self.status)
              print('zscore is', self.zscore[0])
      
              # Step 2: Check conditions for SHORT the spread & place the order
              # Checking the condition for SHORT
              if (self.zscore[0] > self.upper_limit) and (self.status != 1):  # get short if z score is above upper limit and not short the spread already (!=1)
      
                  # Calculating the number of shares for each stock
                  value = 0.5 * self.portfolio_value  # Divide the cash equally
                  x = int(value / (self.data0.close))  # Find the number of shares for Stock1
                  y = int(value / (self.data1.close))  # Find the number of shares for Stock2
                  print('x + self.qty1 is', x + self.qty1)
                  print('y + self.qty2 is', y + self.qty2)
      
                  # Placing the order
                  self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("DG", self.data0.close[0], x + self.qty1))    # sell DG and assign to x
                  self.sell(data=self.data0, size=(x + self.qty1))  # Place an order for buying y + qty2 shares
                  self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("DLTR", self.data1.close[0], y + self.qty2))    # buy DLTR and assign to y
                  self.buy(data=self.data1, size=(y + self.qty2))  # Place an order for selling x + qty1 shares (DG)
      
                  # Updating the counters with new value
                  self.qty1 = x  # The new open position quantity for Stock1 is x shares
                  self.qty2 = y  # The new open position quantity for Stock2 is y shares
      
                  self.status = 1  # The current status is "short the spread"
      
                  # Step 3: Check conditions for LONG the spread & place the order
                  # Checking the condition for LONG
              elif (self.zscore[0] < self.lower_limit) and (self.status != 2): # get long if z score is below lower limit and not long the spread already (!=2)
      
                  # Calculating the number of shares for each stock
                  value = 0.5 * self.portfolio_value  # Divide the cash equally
                  x = int(value / (self.data0.close))  # Find the number of shares for Stock1
                  y = int(value / (self.data1.close))  # Find the number of shares for Stock2
                  print('x + self.qty1 is', x + self.qty1)
                  print('y + self.qty2 is', y + self.qty2)
      
                  # Place the order
                  self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("DG", self.data0.close[0], x + self.qty1)) # buy DG and assigned to x
                  self.buy(data=self.data0, size=(x + self.qty1))  # Place an order for buying x + qty1 shares
                  self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("DLTR", self.data1.close[0], y + self.qty2)) # sell DLTR and assign to y
                  self.sell(data=self.data1, size=(y + self.qty2))  # Place an order for selling y + qty2
      
                  # Updating the counters with new value
                  self.qty1 = x  # The new open position quantity for Stock1 is x shares
                  self.qty2 = y  # The new open position quantity for Stock2 is y shares
                  self.status = 2  # The current status is "long the spread"
      
      
                  # Step 4: Check conditions for No Trade
                  # If the z-score is within the two bounds, close all
      
              elif (self.zscore[0] < self.up_medium and self.zscore[0] > self.low_medium):
                  self.log('CLOSE LONG %s, price = %.2f' % ("DG", self.data0.close[0]))
                  self.close(self.data0)
                  self.log('CLOSE LONG %s, price = %.2f' % ("DLTR", self.data1.close[0]))
                  self.close(self.data1)
      
      
          def stop(self):
              print('==================================================')
              print('Starting Value - %.2f' % self.broker.startingcash)
              print('Ending   Value - %.2f' % self.broker.getvalue())
              print('==================================================')
      
      
      def runstrategy():
          args = parse_args()
      
          # Create a cerebro
          cerebro = bt.Cerebro()
      
      
          # Get the dates from the args
          fromdate = datetime.datetime.strptime("1/4/2010", "%m/%d/%Y")
          todate = datetime.datetime.strptime("10/17/2017", "%m/%d/%Y")
      
          # Create the 1st data
          data0 = btfeeds.GenericCSVData( # DG Data
              dataname='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DG.csv',
      
              fromdate=fromdate,
              todate=todate,
      
              nullvalue=0.0,  # missing values to be replaced with 0
      
              dtformat=('%m/%d/%Y'),
      
              datetime=0,
              time=-1,
              open=-1,
              high=-1,
              low=-1,
              close=4,
              adjclose=-1,
              volume=-1,
              openinterest=-1,
      
          )
      
          # Add the 1st data to cerebro
          cerebro.adddata(data0)
      
          # Create the 2nd data
          data1 = btfeeds.GenericCSVData(  # DLTR Data
              dataname='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DLTR.csv',
      
              fromdate=fromdate,
              todate=todate,
      
              nullvalue=0.0,  # missing values to be replaced with 0
      
              dtformat=('%m/%d/%Y'),
      
              datetime=0,
              time=-1,
              open=-1,
              high=-1,
              low=-1,
              close=4,
              adjclose=-1,
              volume=-1,
              openinterest=-1,
      
          )
      
          # Add the 2nd data to cerebro
          cerebro.adddata(data1)
      
          # Add the strategy
          cerebro.addstrategy(PairTradingStrategy,
                              period=args.period,
                              stake=args.stake)
      
          # Add the commission - only stocks like a for each operation
          cerebro.broker.setcash(args.cash)
      
          # Add the commission - only stocks like a for each operation
          cerebro.broker.setcommission(commission=args.commperc)
      
          # And run it
          cerebro.run(runonce=not args.runnext,
                      preload=not args.nopreload,
                      oldsync=args.oldsync)
      
          # Plot if requested
          if args.plot:
              cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)
      
      
      
      def parse_args():
          parser = argparse.ArgumentParser(description='MultiData Strategy')
      
          parser.add_argument('--data0', '-d0',
                              default='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DG.csv',
                              help='1st data into the system')
      
          parser.add_argument('--data1', '-d1',
                              default='C:\\Users\\Sam\PycharmProjects\\Test\\.ipynb_checkpoints\\DLTR.csv',
                              help='2nd data into the system')
      
          parser.add_argument('--fromdate', '-f',
                              default='01/04/2010',
                              help='Starting date in %m/%d/%Y format')
      
          parser.add_argument('--todate', '-t',
                              default='10/17/2017',
                              help='Starting date in %m/%d/%Y format')
      
          parser.add_argument('--period', default=10, type=int,
                              help='Period to apply to the Simple Moving Average')
      
          parser.add_argument('--cash', default=100000, type=int,
                              help='Starting Cash')
      
          parser.add_argument('--runnext', action='store_true',
                              help='Use next by next instead of runonce')
      
          parser.add_argument('--nopreload', action='store_true',
                              help='Do not preload the data')
      
          parser.add_argument('--oldsync', action='store_true',
                              help='Use old data synchronization method')
      
          parser.add_argument('--commperc', default=0.005, type=float,
                              help='Percentage commission (0.005 is 0.5%%')
      
          parser.add_argument('--stake', default=10, type=int,
                              help='Stake to apply in each operation')
      
          parser.add_argument('--plot', '-p', default=True, action='store_true',
                              help='Plot the read data')
      
          parser.add_argument('--numfigs', '-n', default=1,
                              help='Plot using numfigs figures')
      
          return parser.parse_args()
      
      
      if __name__ == '__main__':
          runstrategy()
      
      1 Reply Last reply Reply Quote 0
      • S
        samk last edited by

        @backtrader @administrators

        Any idea what is going on here by any chance?

        A 1 Reply Last reply Reply Quote 0
        • A
          ab_trader @samk last edited by

          @samk why don't you want to print all related to your system conditions such as prices, indicator values, order statuses, positions etc on every next() call and go thru the whole test and check out what is going on?

          it can be faster then waiting for somebody to do this work for you. By the way @backtrader will not answer any question according to his statement on top of the page.

          • If my answer helped, hit reputation up arrow at lower right corner of the post.
          • Python Debugging With Pdb
          • New to python and bt - check this out
          1 Reply Last reply Reply Quote 0
          • S
            samk last edited by

            Hi @ab_trader ,

            Thank you very much for the response. I have created multiple print statements as you suggested, however just am having a hard time seeing where it is flushing in negative balance territory, or why it is for that matter. Any help in terms of what I can be missing here would be greatly appreciated. I have pasted new adjusted code with print statements below. Thanks for the help. I am very new to backtrader still, so could jut be missing something very silly here....

            from __future__ import (absolute_import, division, print_function,
                                    unicode_literals)
            
            import argparse
            import datetime
            
            # The above could be sent to an independent module
            import backtrader as bt
            import backtrader.feeds as btfeeds
            import backtrader.indicators as btind
            import backtrader.analyzers as btan
            
            
            class PairTradingStrategy(bt.Strategy):
                params = dict(
                    period=10,
                    stake=10,
                    qty1=0,
                    qty2=0,
                    printout=True,
                    upper=2.1,
                    lower=-2.1,
                    up_medium=0.5,
                    low_medium=-0.5,
                    status=0,
                    portfolio_value=100000,
                )
            
                def log(self, txt, dt=None):
                    if self.p.printout:
                        dt = dt or self.data.datetime[0] # set date time
                        dt = bt.num2date(dt)
                        print('%s, %s' % (dt.isoformat(), txt))
            
                def notify_order(self, order):
                    if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
                        return  # Await further notifications
            
                    if order.status == order.Completed:
                        if order.isbuy():
                            buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                            self.log(buytxt, order.executed.dt)
                        else:
                            selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                            self.log(selltxt, order.executed.dt)
            
                    elif order.status in [order.Expired, order.Canceled, order.Margin]:
                        self.log('%s ,' % order.Status[order.status])
                        pass  # Simply log
            
                    # Allow new orders
                    self.orderid = None
            
                def __init__(self):
                    # To control operation entries
                    self.orderid = None
                    self.qty1 = self.p.qty1
                    self.qty2 = self.p.qty2
                    self.upper_limit = self.p.upper
                    self.lower_limit = self.p.lower
                    self.up_medium = self.p.up_medium
                    self.low_medium = self.p.low_medium
                    self.status = self.p.status
                    self.portfolio_value = self.p.portfolio_value
            
                    # Signals performed with PD.OLS :                                   # signal performed using ordinary least regression
                    self.transform = btind.OLS_TransformationN(self.data0, self.data1,
                                                               period=self.p.period)
                    self.zscore = self.transform.zscore         # calculate z score ( - mean / std deviation)
            
                    # Checking signals built with StatsModel.API :
                    # self.ols_transfo = btind.OLS_Transformation(self.data0, self.data1,
                    #                                             period=self.p.period,
                    #                                             plot=True)
            
                def next(self):
            
                    if self.orderid:
                        return  # if an order is active, no new orders are allowed
            
                    if self.p.printout:
                        print('Self  len:', len(self))
                        print('Data0 len:', len(self.data0)) # length of time series for data 0
                        print('Data1 len:', len(self.data1)) # length of time series for data 1
                        print('Data0 len == Data1 len:',
                              len(self.data0) == len(self.data1)) # set lengths equal to each other (ensure they are)
            
                        print('Data0 dt:', self.data0.datetime.datetime())
                        print('Data1 dt:', self.data1.datetime.datetime())
            
                    print('status is', self.status)
                    print('zscore is', self.zscore[0])
            
                    # Step 2: Check conditions for SHORT the spread & place the order
                    # Checking the condition for SHORT
                    if (self.zscore[0] > self.upper_limit) and (self.status != 1):  # get short if z score is above upper limit and not short the spread already (!=1)
            
                        # Calculating the number of shares for each stock
                        value = 0.5 * self.portfolio_value  # Divide the cash equally
                        x = int(value / (self.data0.close))  # Find the number of shares for Stock1
                        y = int(value / (self.data1.close))  # Find the number of shares for Stock2
                        print('x + self.qty1 is', x + self.qty1)
                        print('y + self.qty2 is', y + self.qty2)
            
                        # Placing the order
                        self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("DG", self.data0.close[0], x + self.qty1))    # sell DG and assign to x
                        self.sell(data=self.data0, size=(x + self.qty1))  # Place an order for buying y + qty2 shares
                        self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("DLTR", self.data1.close[0], y + self.qty2))    # buy DLTR and assign to y
                        self.buy(data=self.data1, size=(y + self.qty2))  # Place an order for selling x + qty1 shares (DG)
            
                        # Updating the counters with new value
                        self.qty1 = x  # The new open position quantity for Stock1 is x shares
                        self.qty2 = y  # The new open position quantity for Stock2 is y shares
            
                        self.status = 1  # The current status is "short the spread"
            
                        # Step 3: Check conditions for LONG the spread & place the order
                        # Checking the condition for LONG
                    elif (self.zscore[0] < self.lower_limit) and (self.status != 2): # get long if z score is below lower limit and not long the spread already (!=2)
            
                        # Calculating the number of shares for each stock
                        value = 0.5 * self.portfolio_value  # Divide the cash equally
                        x = int(value / (self.data0.close))  # Find the number of shares for Stock1
                        y = int(value / (self.data1.close))  # Find the number of shares for Stock2
                        print('x + self.qty1 is', x + self.qty1)
                        print('y + self.qty2 is', y + self.qty2)
            
                        # Place the order
                        self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("DG", self.data0.close[0], x + self.qty1)) # buy DG and assigned to x
                        self.buy(data=self.data0, size=(x + self.qty1))  # Place an order for buying x + qty1 shares
                        self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("DLTR", self.data1.close[0], y + self.qty2)) # sell DLTR and assign to y
                        self.sell(data=self.data1, size=(y + self.qty2))  # Place an order for selling y + qty2
            
                        # Updating the counters with new value
                        self.qty1 = x  # The new open position quantity for Stock1 is x shares
                        self.qty2 = y  # The new open position quantity for Stock2 is y shares
                        self.status = 2  # The current status is "long the spread"
            
            
                        # Step 4: Check conditions for No Trade
                        # If the z-score is within the two bounds, close all
            
                    elif (self.zscore[0] < self.up_medium and self.zscore[0] > self.low_medium):
                        self.log('CLOSE LONG %s, price = %.2f' % ("DG", self.data0.close[0]))
                        self.close(self.data0)
                        self.log('CLOSE LONG %s, price = %.2f' % ("DLTR", self.data1.close[0]))
                        self.close(self.data1)
            
            
                def stop(self):
                    print('==================================================')
                    print('Starting Value - %.2f' % self.broker.startingcash)
                    print('Ending   Value - %.2f' % self.broker.getvalue())
                    print('==================================================')
            
            
            def runstrategy():
                args = parse_args()
            
                # Create a cerebro
                cerebro = bt.Cerebro()
            
            
                # Get the dates from the args
                fromdate = datetime.datetime.strptime("1/4/2010", "%m/%d/%Y")
                todate = datetime.datetime.strptime("10/17/2017", "%m/%d/%Y")
            
                # Create the 1st data
                data0 = btfeeds.GenericCSVData( # DG Data
                    dataname='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DG.csv',
            
                    fromdate=fromdate,
                    todate=todate,
            
                    nullvalue=0.0,  # missing values to be replaced with 0
            
                    dtformat=('%m/%d/%Y'),
            
                    datetime=0,
                    time=-1,
                    open=-1,
                    high=-1,
                    low=-1,
                    close=4,
                    adjclose=-1,
                    volume=-1,
                    openinterest=-1,
            
                )
            
                # Add the 1st data to cerebro
                cerebro.adddata(data0)
            
                # Create the 2nd data
                data1 = btfeeds.GenericCSVData(  # DLTR Data
                    dataname='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DLTR.csv',
            
                    fromdate=fromdate,
                    todate=todate,
            
                    nullvalue=0.0,  # missing values to be replaced with 0
            
                    dtformat=('%m/%d/%Y'),
            
                    datetime=0,
                    time=-1,
                    open=-1,
                    high=-1,
                    low=-1,
                    close=4,
                    adjclose=-1,
                    volume=-1,
                    openinterest=-1,
            
                )
            
                # Add the 2nd data to cerebro
                cerebro.adddata(data1)
            
                # Add the strategy
                cerebro.addstrategy(PairTradingStrategy,
                                    period=args.period,
                                    stake=args.stake)
            
                # Add the commission - only stocks like a for each operation
                cerebro.broker.setcash(args.cash)
            
                # Add the commission - only stocks like a for each operation
                cerebro.broker.setcommission(commission=args.commperc)
            
                # And run it
                cerebro.run(runonce=not args.runnext,
                            preload=not args.nopreload,
                            oldsync=args.oldsync)
            
                # Plot if requested
                if args.plot:
                    cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)
            
            
            
            def parse_args():
                parser = argparse.ArgumentParser(description='MultiData Strategy')
            
                parser.add_argument('--data0', '-d0',
                                    default='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DG.csv',
                                    help='1st data into the system')
            
                parser.add_argument('--data1', '-d1',
                                    default='C:\\Users\\Sam\PycharmProjects\\Test\\.ipynb_checkpoints\\DLTR.csv',
                                    help='2nd data into the system')
            
                parser.add_argument('--fromdate', '-f',
                                    default='01/04/2010',
                                    help='Starting date in %m/%d/%Y format')
            
                parser.add_argument('--todate', '-t',
                                    default='10/17/2017',
                                    help='Starting date in %m/%d/%Y format')
            
                parser.add_argument('--period', default=10, type=int,
                                    help='Period to apply to the Simple Moving Average')
            
                parser.add_argument('--cash', default=100000, type=int,
                                    help='Starting Cash')
            
                parser.add_argument('--runnext', action='store_true',
                                    help='Use next by next instead of runonce')
            
                parser.add_argument('--nopreload', action='store_true',
                                    help='Do not preload the data')
            
                parser.add_argument('--oldsync', action='store_true',
                                    help='Use old data synchronization method')
            
                parser.add_argument('--commperc', default=0.005, type=float,
                                    help='Percentage commission (0.005 is 0.5%%')
            
                parser.add_argument('--stake', default=10, type=int,
                                    help='Stake to apply in each operation')
            
                parser.add_argument('--plot', '-p', default=True, action='store_true',
                                    help='Plot the read data')
            
                parser.add_argument('--numfigs', '-n', default=1,
                                    help='Plot using numfigs figures')
            
                return parser.parse_args()
            
            
            if __name__ == '__main__':
                runstrategy()
            
            1 Reply Last reply Reply Quote 0
            • S
              samk last edited by

              @ab_trader

              Sorry sent the wrong revised code. The correct one with print statements shown below.....

              from __future__ import (absolute_import, division, print_function,
                                      unicode_literals)
              
              import argparse
              import datetime
              
              # The above could be sent to an independent module
              import backtrader as bt
              import backtrader.feeds as btfeeds
              import backtrader.indicators as btind
              import backtrader.analyzers as btan
              
              
              class PairTradingStrategy(bt.Strategy):
                  params = dict(
                      period=10,
                      stake=10,
                      qty1=0,
                      qty2=0,
                      printout=True,
                      upper=2.1,
                      lower=-2.1,
                      up_medium=0.5,
                      low_medium=-0.5,
                      status=0,
                      portfolio_value=100000,
                  )
              
                  def log(self, txt, dt=None):
                      if self.p.printout:
                          dt = dt or self.data.datetime[0] # set date time
                          dt = bt.num2date(dt)
                          print('%s, %s' % (dt.isoformat(), txt))
              
                  def notify_order(self, order):
                      if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
                          return  # Await further notifications
              
                      if order.status == order.Completed:
                          if order.isbuy():
                              buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                              self.log(buytxt, order.executed.dt)
                          else:
                              selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                              self.log(selltxt, order.executed.dt)
              
                      elif order.status in [order.Expired, order.Canceled, order.Margin]:
                          self.log('%s ,' % order.Status[order.status])
                          pass  # Simply log
              
                      # Allow new orders
                      self.orderid = None
              
                  def __init__(self):
                      # To control operation entries
                      self.orderid = None
                      self.qty1 = self.p.qty1
                      self.qty2 = self.p.qty2
                      self.upper_limit = self.p.upper
                      self.lower_limit = self.p.lower
                      self.up_medium = self.p.up_medium
                      self.low_medium = self.p.low_medium
                      self.status = self.p.status
                      self.portfolio_value = self.p.portfolio_value
              
                      # Signals performed with PD.OLS :                                   # signal performed using ordinary least regression
                      self.transform = btind.OLS_TransformationN(self.data0, self.data1,
                                                                 period=self.p.period)
                      self.zscore = self.transform.zscore         # calculate z score ( - mean / std deviation)
              
                      # Checking signals built with StatsModel.API :
                      # self.ols_transfo = btind.OLS_Transformation(self.data0, self.data1,
                      #                                             period=self.p.period,
                      #                                             plot=True)
              
                  def next(self):
              
                      if self.orderid:
                          return  # if an order is active, no new orders are allowed
              
                      if self.p.printout:
                          print('Self  len:', len(self))
                          print('Data0 len:', len(self.data0)) # length of time series for data 0
                          print('Data1 len:', len(self.data1)) # length of time series for data 1
                          print('Data0 len == Data1 len:',
                                len(self.data0) == len(self.data1)) # set lengths equal to each other (ensure they are)
              
                          print('Data0 dt:', self.data0.datetime.datetime())
                          print('Data1 dt:', self.data1.datetime.datetime())
              
                      print('status is', self.status)
                      print('zscore is', self.zscore[0])
              
                      # Step 2: Check conditions for SHORT the spread & place the order
                      # Checking the condition for SHORT
                      if (self.zscore[0] > self.upper_limit) and (self.status != 1):  # get short if z score is above upper limit and not short the spread already (!=1)
              
                          # Calculating the number of shares for each stock
                          value = 0.5 * self.portfolio_value  # Divide the cash equally
                          x = int(value / (self.data0.close))  # Find the number of shares for Stock1
                          y = int(value / (self.data1.close))  # Find the number of shares for Stock2
                          print('x + self.qty1 is', x + self.qty1)
                          print('y + self.qty2 is', y + self.qty2)
              
                          # Placing the order
                          self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("DG", self.data0.close[0], x + self.qty1))    # sell DG and assign to x
                          self.sell(data=self.data0, size=(x + self.qty1))  # Place an order for buying y + qty2 shares
                          self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("DLTR", self.data1.close[0], y + self.qty2))    # buy DLTR and assign to y
                          self.buy(data=self.data1, size=(y + self.qty2))  # Place an order for selling x + qty1 shares (DG)
              
                          # Updating the counters with new value
                          self.qty1 = x  # The new open position quantity for Stock1 is x shares
                          self.qty2 = y  # The new open position quantity for Stock2 is y shares
              
                          self.status = 1  # The current status is "short the spread"
              
                          # Step 3: Check conditions for LONG the spread & place the order
                          # Checking the condition for LONG
                      elif (self.zscore[0] < self.lower_limit) and (self.status != 2): # get long if z score is below lower limit and not long the spread already (!=2)
              
                          # Calculating the number of shares for each stock
                          value = 0.5 * self.portfolio_value  # Divide the cash equally
                          x = int(value / (self.data0.close))  # Find the number of shares for Stock1
                          y = int(value / (self.data1.close))  # Find the number of shares for Stock2
                          print('x + self.qty1 is', x + self.qty1)
                          print('y + self.qty2 is', y + self.qty2)
              
                          # Place the order
                          self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("DG", self.data0.close[0], x + self.qty1)) # buy DG and assigned to x
                          self.buy(data=self.data0, size=(x + self.qty1))  # Place an order for buying x + qty1 shares
                          self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("DLTR", self.data1.close[0], y + self.qty2)) # sell DLTR and assign to y
                          self.sell(data=self.data1, size=(y + self.qty2))  # Place an order for selling y + qty2
              
                          # Updating the counters with new value
                          self.qty1 = x  # The new open position quantity for Stock1 is x shares
                          self.qty2 = y  # The new open position quantity for Stock2 is y shares
                          self.status = 2  # The current status is "long the spread"
              
              
                          # Step 4: Check conditions for No Trade
                          # If the z-score is within the two bounds, close all
              
                      elif (self.zscore[0] < self.up_medium and self.zscore[0] > self.low_medium):
                          self.log('CLOSE LONG %s, price = %.2f' % ("DG", self.data0.close[0]))
                          self.close(self.data0)
                          self.log('CLOSE SHORT %s, price = %.2f' % ("DLTR", self.data1.close[0]))
                          self.close(self.data1)
              
                      elif (self.zscore[0] < self.up_medium and self.zscore[0] > self.low_medium):
                          self.log('CLOSE SHORT %s, price = %.2f' % ("DG", self.data0.close[0]))
                          self.close(self.data0)
                          self.log('CLOSE LONG %s, price = %.2f' % ("DLTR", self.data1.close[0]))
                          self.close(self.data1)
              
              
                  def stop(self):
                      print('==================================================')
                      print('Starting Value - %.2f' % self.broker.startingcash)
                      print('Ending   Value - %.2f' % self.broker.getvalue())
                      print('==================================================')
              
              
              def runstrategy():
                  args = parse_args()
              
                  # Create a cerebro
                  cerebro = bt.Cerebro()
              
              
                  # Get the dates from the args
                  fromdate = datetime.datetime.strptime("1/4/2010", "%m/%d/%Y")
                  todate = datetime.datetime.strptime("10/17/2017", "%m/%d/%Y")
              
                  # Create the 1st data
                  data0 = btfeeds.GenericCSVData( # DG Data
                      dataname='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DG.csv',
              
                      fromdate=fromdate,
                      todate=todate,
              
                      nullvalue=0.0,  # missing values to be replaced with 0
              
                      dtformat=('%m/%d/%Y'),
              
                      datetime=0,
                      time=-1,
                      open=-1,
                      high=-1,
                      low=-1,
                      close=4,
                      adjclose=-1,
                      volume=-1,
                      openinterest=-1,
              
                  )
              
                  # Add the 1st data to cerebro
                  cerebro.adddata(data0)
              
                  # Create the 2nd data
                  data1 = btfeeds.GenericCSVData(  # DLTR Data
                      dataname='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DLTR.csv',
              
                      fromdate=fromdate,
                      todate=todate,
              
                      nullvalue=0.0,  # missing values to be replaced with 0
              
                      dtformat=('%m/%d/%Y'),
              
                      datetime=0,
                      time=-1,
                      open=-1,
                      high=-1,
                      low=-1,
                      close=4,
                      adjclose=-1,
                      volume=-1,
                      openinterest=-1,
              
                  )
              
                  # Add the 2nd data to cerebro
                  cerebro.adddata(data1)
              
                  # Add the strategy
                  cerebro.addstrategy(PairTradingStrategy,
                                      period=args.period,
                                      stake=args.stake)
              
                  # Add the commission - only stocks like a for each operation
                  cerebro.broker.setcash(args.cash)
              
                  # Add the commission - only stocks like a for each operation
                  cerebro.broker.setcommission(commission=args.commperc)
              
                  # And run it
                  cerebro.run(runonce=not args.runnext,
                              preload=not args.nopreload,
                              oldsync=args.oldsync)
              
                  # Plot if requested
                  if args.plot:
                      cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)
              
              
              
              def parse_args():
                  parser = argparse.ArgumentParser(description='MultiData Strategy')
              
                  parser.add_argument('--data0', '-d0',
                                      default='C:\\Users\\Sam\\PycharmProjects\\Test\\.ipynb_checkpoints\\DG.csv',
                                      help='1st data into the system')
              
                  parser.add_argument('--data1', '-d1',
                                      default='C:\\Users\\Sam\PycharmProjects\\Test\\.ipynb_checkpoints\\DLTR.csv',
                                      help='2nd data into the system')
              
                  parser.add_argument('--fromdate', '-f',
                                      default='01/04/2010',
                                      help='Starting date in %m/%d/%Y format')
              
                  parser.add_argument('--todate', '-t',
                                      default='10/17/2017',
                                      help='Starting date in %m/%d/%Y format')
              
                  parser.add_argument('--period', default=10, type=int,
                                      help='Period to apply to the Simple Moving Average')
              
                  parser.add_argument('--cash', default=100000, type=int,
                                      help='Starting Cash')
              
                  parser.add_argument('--runnext', action='store_true',
                                      help='Use next by next instead of runonce')
              
                  parser.add_argument('--nopreload', action='store_true',
                                      help='Do not preload the data')
              
                  parser.add_argument('--oldsync', action='store_true',
                                      help='Use old data synchronization method')
              
                  parser.add_argument('--commperc', default=0.005, type=float,
                                      help='Percentage commission (0.005 is 0.5%%')
              
                  parser.add_argument('--stake', default=10, type=int,
                                      help='Stake to apply in each operation')
              
                  parser.add_argument('--plot', '-p', default=True, action='store_true',
                                      help='Plot the read data')
              
                  parser.add_argument('--numfigs', '-n', default=1,
                                      help='Plot using numfigs figures')
              
                  return parser.parse_args()
              
              
              if __name__ == '__main__':
                  runstrategy()
              
              1 Reply Last reply Reply Quote 0
              • A
                ab_trader last edited by

                Great! Now you can run your script, go thru logs and identify if your orders were issued correctly, trades were opened and closed correctly, maybe do some hand calcs etc. Finally I believe you will find what is wrong.

                You have negative balance, my guess that your trades were not closed correctly and you might have number of opened trades with the loss.

                • If my answer helped, hit reputation up arrow at lower right corner of the post.
                • Python Debugging With Pdb
                • New to python and bt - check this out
                1 Reply Last reply Reply Quote 0
                • M
                  mpskowron last edited by

                  Seems like you are short selling something which than raises in value by a lot. To simplify analyzing I think, that you should add writer and analyzer (pyfolio analyzer adds a lot of useful output):

                  cerebro.addwriter(bt.WriterFile, out='OUTPUT_FILE_PATH')
                  cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
                  

                  After you add those 2 you will have all positions written to your file, which should be quite easy to analyze when the value went below 0 and what trades did you make before.

                  Hope that it will help.

                  1 Reply Last reply Reply Quote 0
                  • S
                    samk last edited by

                    @mpskowron

                    Thanks a lot. I was not aware of this option at all! It definitely helped me. Looks like I was not allocating the shares equally amongst the two stocks (calculation was off). Anyhow, what you mentioned helped me see what was going on a lot and will definitely be using it for more algo. Thanks!

                    Sam

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