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/

    Pair Trading Problem

    General Code/Help
    1
    1
    73
    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.
    • H
      houba last edited by

      Hi to my lovely Community ,

      Where is the error in the code please for implementing the Pair Trading Strategy ?

      Thank you very much

      from __future__ import (absolute_import, division, print_function,
                              unicode_literals)
      import argparse
      import backtrader as bt 
      import backtrader.analyzers as btanalyzers
      import backtrader.feeds as btfeeds
      import backtrader.indicators as btind
      import datetime
      import pandas as pd 
      import matplotlib
      import pandas as pd
      import quantstats
      
      class PairsTradingStrategy(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,
              upper_level = 2.1,
              lower_level = -2.1,
              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 
              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 
      
              self.orderid = None
      
          def __init__(self):
      
              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
      
              self.transform = btind.OLS_TransformationN(self.data0,self.data1,period=self.p.period)
      
              self.zscore = self.transform.zscore
      
      
          def next(self) :
      
              if self.orderid : 
                  return 
      
              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' % ("AACQW", 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' % ("AAME", 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' % ("AACQW", 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' % ("AAME", 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' % ("AACQW", self.data0.close[0]))
                  self.close(self.data0)
                  self.log('CLOSE LONG %s, price = %.2f' % ("AAME", 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 parse_args():
          parser = argparse.ArgumentParser(description='MultiData Strategy')
      
          parser.add_argument('--data0', '-d0',
                              default='/Users/bibani/Desktop/AACQW.csv',
                              help='1st data into the system')
      
          parser.add_argument('--data1', '-d1',
                              default='/Users/bibani/Desktop/AAME.csv',
                              help='2nd data into the system')
      
          parser.add_argument('--fromdate', '-f',
                              default='2018-01-02',
                              help='Starting date in YYYY-MM-DD format')
      
          parser.add_argument('--todate', '-t',
                              default='2021-01-15',
                              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()
      
      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')
          todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
      
          # Create the 1st data
          data0 = btfeeds.GenericCSVData(
              dataname=args.data0,
              dtformat = ('%Y-%m-%d'),
              fromdate=fromdate,
              todate=todate,
              datetime=0, 
              high=-1, 
              low=-1, 
              open=-1,
              close=1, 
              volume=-1, 
              openinterest=-1
      )
      
          # Add the 1st data to cerebro
          cerebro.adddata(data0)
      
          # Create the 2nd data
          data1 = btfeeds.GenericCSVData(
              dataname=args.data1,
              dtformat = ('%Y-%m-%d'),
              fromdate=fromdate,
              todate=todate,
              datetime=0, 
              high=-1, 
              low=-1, 
              open=-1,
              close=1, 
              volume=-1, 
              openinterest=-1
              )
      
          # Add the 2nd data to cerebro
          cerebro.adddata(data1)
      
          # Add the strategy
          cerebro.addstrategy(PairsTradingStrategy,
              period=args.period,
              stake=args.stake)
      
          cerebro.broker.setcash(args.cash)
      
          # Add the commission - only stocks like a for each operation
          cerebro.broker.setcommission(commission=args.commperc)
      
          # And run it
          results = cerebro.run(runonce=not args.runnext,
              preload=not args.nopreload,
              oldsync=args.oldsync)
      
          cerebro.broker.getvalue()
      
          cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='SharpeRatio')
          cerebro.addanalyzer(bt.analyzers.DrawDown, _name='DW')
          end_portfolio_value = cerebro.broker.getvalue()
      
          #print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
          #print('SR:', cerebro.analyzers.SharpeRatio.get_analysis())
          #print('DW:', cerebro.analyzers.DW.get_analysis())
      
          # Plot if requested
          if args.plot:
              cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False,iplot= False)
      
      if __name__ == '__main__':
          runstrategy()
      
      1 Reply Last reply Reply Quote -2
      • 1 / 1
      • First post
        Last post
      Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors