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/

    Pairs Trading example error

    General Code/Help
    pairs trading
    3
    7
    2501
    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.
    • fbleaux
      fbleaux last edited by

      I am exploring backtrader.com and making use of all the examples as you might noticed :').
      Script:

      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=10000,
          )
      
          def log(self, txt, dt=None):
              if self.p.printout:
                  dt = dt or self.data.datetime[0]
                  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 :
              self.transform = btind.OLS_TransformationN(self.data0, self.data1,
                                                         period=self.p.period)
              self.zscore = self.transform.zscore
      
              # 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))
                  print('Data1 len:', len(self.data1))
                  print('Data0 len == Data1 len:',
                        len(self.data0) == len(self.data1))
      
                  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 & place the order
              # Checking the condition for SHORT
              if (self.zscore[0] > self.upper_limit) and (self.status != 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' % ("ABN", self.data0.close[0], x + self.qty1))
                  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' % ("ING", self.data1.close[0], y + self.qty2))
                  self.buy(data=self.data1, size=(y + self.qty2))  # Place an order for selling x + qty1 shares
      
                  # 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 & place the order
                  # Checking the condition for LONG
              elif (self.zscore[0] < self.lower_limit) and (self.status != 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' % ("ABN", self.data0.close[0], x + self.qty1))
                  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' % ("ING", self.data1.close[0], y + self.qty2))
                  self.sell(data=self.data1, size=(y + self.qty2))  # Place an order for selling y + qty2 shares
      
                  # 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' % ("ABN", self.data0.close[0]))
                  self.close(self.data0)
                  self.log('CLOSE LONG %s, price = %.2f' % ("ING", 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(args.fromdate, '%Y%m%d %H:%M:%S')
          todate = datetime.datetime.strptime(args.todate, '%Y%m%d %H:%M:%S')
      
          # Create the 1st data
          class Tickstory(btfeeds.GenericCSVData):
              params = (
                  ('dtformat', '%Y%m%d %H:%M:%S'),
                  ('datetime', 0),
                  ('time', -1),
                  ('open', 1),
                  ('high', 2),
                  ('low', 3),
                  ('close', 4),
                  ('volume', -1),
                  ('openinterest', -1),
                  ('timeframe', bt.TimeFrame.Minutes),
                  ('compression', 1)
              )
          # Develop feed    
          data0 = Tickstory(dataname='/Users/rf/Documents/DSC/Datasets/abn.csv')
      
          # Add the 1st data to cerebro
          cerebro.adddata(data0)
      
          # Create the 1st data
          class Tickstory(btfeeds.GenericCSVData):
              params = (
                  ('dtformat', '%Y%m%d %H:%M:%S'),
                  ('datetime', 0),
                  ('time', -1),
                  ('open', 1),
                  ('high', 2),
                  ('low', 3),
                  ('close', 4),
                  ('volume', -1),
                  ('openinterest', -1),
                  ('timeframe', bt.TimeFrame.Minutes),
                  ('compression', 1)
              )
          # Develop feed    
          data1 = Tickstory(dataname='/Users/rf/Documents/DSC/Datasets/inga.csv')
          # 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='/Users/rf/Documents/DSC/Datasets/abn.csv',
                              help='1st data into the system')
      
          parser.add_argument('--data1', '-d1',
                              default='/Users/rf/Documents/DSC/Datasets/inga.csv',
                              help='2nd data into the system')
      
          parser.add_argument('--fromdate', required=False,
                              default='20170704  09:00:00',
                              help='Starting date in YYYY-MM-DD format')
      
          parser.add_argument('--todate', required=False,
                              default='20170704  09:00:00',
                              help='Starting date in YYYY-MM-DD 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()
      

      Gives the error:

      Traceback (most recent call last):
      
        File "<ipython-input-18-d6922e16d55d>", line 278, in <module>
          runstrategy()
      
        File "<ipython-input-18-d6922e16d55d>", line 219, in runstrategy
          oldsync=args.oldsync)
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/cerebro.py", line 1142, in run
          runstrat = self.runstrategies(iterstrat)
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/cerebro.py", line 1305, in runstrategies
          self._runonce(runstrats)
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/cerebro.py", line 1663, in _runonce
          strat._once()
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/lineiterator.py", line 292, in _once
          indicator._once()
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/lineiterator.py", line 292, in _once
          indicator._once()
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/lineiterator.py", line 313, in _once
          self.once(self._minperiod, self.buflen())
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/indicator.py", line 136, in once_via_next
          self.next()
      
        File "/anaconda/lib/python3.6/site-packages/backtrader/indicators/ols.py", line 52, in next
          slope, intercept = sm.OLS(p0, p1).fit().params
      
      ValueError: not enough values to unpack (expected 2, got 1)
      

      1 feed is correct lined but the other one?

      B 1 Reply Last reply Reply Quote 0
      • fbleaux
        fbleaux last edited by backtrader

        Solved. Backtrader is limited to less than 10000 lines. Very strange. Any idea how to extend this range?

        Well, at least in this script.

        1 Reply Last reply Reply Quote 0
        • B
          backtrader administrators last edited by backtrader

          @fbleaux said in Pairs Trading example error:

          Backtrader is limited to less than 10000 lines. Very strange. Any idea how to extend this range?

          There is NO LIMIT in backtrader. So that's simply a bold statement out of the blue.

          1 Reply Last reply Reply Quote 0
          • M
            Michael172 last edited by

            Just curious to know how did you solve it? I get the same error when I run it. My data is definitely shorter than 10000 lines (even though there is no such limit anyway).. I am also studying paris trading. What is the problem and solution ?

            thanks

            Michael

            1 Reply Last reply Reply Quote 1
            • B
              backtrader administrators @fbleaux last edited by

              @fbleaux said in Pairs Trading example error:

              ValueError: not enough values to unpack (expected 2, got 1)

              Without the actual data it is difficult to say, but an educated guess would hint that a change in the API return values of sm.OLS has happened.

              The Pairs Trading Example was contributed by a user some time ago and just like Pyfolio, those APIs keep changing.

              1 Reply Last reply Reply Quote 1
              • M
                Michael172 last edited by

                Right, I have searched the forum and saw you guys had a lot of discussion on this back in Dec, 2016 and made many changes along the way. I'll keep reading and researching, but is there a github depository or somewhere else where the latest working version of this example reside? That would save a lot of time.

                thanks

                Michael

                1 Reply Last reply Reply Quote 1
                • B
                  backtrader administrators last edited by

                  There was only 1 version of this, which was committed and never touched again. It's the one you find in the sources.

                  1 Reply Last reply Reply Quote 0
                  • 1 / 1
                  • First post
                    Last post
                  Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
                  $(document).ready(function () { app.coldLoad(); }); }