Pairs Trading example error
-
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?
-
Solved.
Backtrader is limited to less than 10000 lines. Very strange. Any idea how to extend this range?Well, at least in this script.
-
@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. -
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
-
@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. -
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
-
There was only 1 version of this, which was committed and never touched again. It's the one you find in the sources.