Curious on Negative Balance Occurring in My Pair Trading Strategy Algo
-
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()
-
Any idea what is going on here by any chance?
-
@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.
-
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()
-
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()
-
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.
-
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.
-
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