Hi guys ,
I'm new here.
I discovered BACKTRADER and I'm struggling with PAIR TRADING even if I found out a GitHub repository where we have the full access...but It doesn't work.
Anyway , I tried to apply the code to my .csv files but I get this error :
Traceback (most recent call last):
File "PairTradingStrategy.py", line 174, in <module>
cerebro.run()
File "/opt/anaconda3/lib/python3.7/site-packages/backtrader/cerebro.py", line 1127, in run
runstrat = self.runstrategies(iterstrat)
File "/opt/anaconda3/lib/python3.7/site-packages/backtrader/cerebro.py", line 1212, in runstrategies
data.preload()
File "/opt/anaconda3/lib/python3.7/site-packages/backtrader/feed.py", line 688, in preload
while self.load():
File "/opt/anaconda3/lib/python3.7/site-packages/backtrader/feed.py", line 479, in load
_loadret = self._load()
File "/opt/anaconda3/lib/python3.7/site-packages/backtrader/feed.py", line 710, in _load
return self._loadline(linetokens)
File "/opt/anaconda3/lib/python3.7/site-packages/backtrader/feeds/yahoo.py", line 134, in _loadline
h = float(linetokens[next(i)])
IndexError: list index out of range
and the code is as following :
import backtrader as bt
import backtrader.analyzers as btanalyzers
import backtrader.indicators as btind
import datetime
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 __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.data1, self.data2,
period=self.p.period)
self.zscore = self.transform.zscore
# Checking signals built with StatsModel.API :
# self.ols_transfo = btind.OLS_Transformation(self.data1, self.data2,
# 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('Data1 len:', len(self.data1))
print('Data2 len:', len(self.data2))
print('Data1 len == Data2 len:',
len(self.data1) == len(self.data2))
print('Data1 dt:', self.data1.datetime.datetime())
print('Data2 dt:', self.data2.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.data1.close)) # Find the number of shares for Stock1
y = int(value / (self.data2.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.data1.close[0], x + self.qty1))
self.sell(data=self.data1, size=(x + self.qty1)) # Place an order for buying y + qty2 shares
self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("KO", self.data2.close[0], y + self.qty2))
self.buy(data=self.data2, 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.data1.close)) # Find the number of shares for Stock1
y = int(value / (self.data2.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.data1.close[0], x + self.qty1))
self.buy(data=self.data1, size=(x + self.qty1)) # Place an order for buying x + qty1 shares
self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("AAME", self.data2.close[0], y + self.qty2))
self.sell(data=self.data2, 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' % ("PEP", self.data1.close[0]))
self.close(self.data1)
self.log('CLOSE LONG %s, price = %.2f' % ("KO", self.data2.close[0]))
self.close(self.data2)
def stop(self):
print('==================================================')
print('Starting Value - %.2f' % self.broker.startingcash)
print('Ending Value - %.2f' % self.broker.getvalue())
print('==================================================')
#Instantiate Cerebro engine
cerebro = bt.Cerebro()
#Add data feed to Cerebro
data1 = bt.feeds.YahooFinanceCSVData(dataname='/Users/bibani/Desktop/AACQW.csv',
dtformat=('%Y.%m.%d'),
fromdate = datetime.datetime(2018,1,2),
todate = datetime.datetime(2021,1,15),
datetime=0,
time=-1,
high=-1,
low=-1,
open=-1,
close=1,
volume=-1,
openinterest=-1)
cerebro.adddata(data1)
data2 = bt.feeds.YahooFinanceCSVData(dataname='/Users/bibani/Desktop/AAME.csv',
dtformat=('%Y.%m.%d'),
fromdate = datetime.datetime(2018,1,2),
todate = datetime.datetime(2021,1,15),
datetime=0,
time=-1,
high=-1,
low=-1,
open=-1,
close=1,
volume=-1,
openinterest=-1)
cerebro.adddata(data2)
#Add strategy to Cerebro
cerebro.addstrategy(PairTradingStrategy)
if name == 'main':
# Run Cerebro Engine
start_portfolio_value = cerebro.broker.getvalue()
cerebro.run()
end_portfolio_value = cerebro.broker.getvalue()
pnl = end_portfolio_value - start_portfolio_value
print(f'Starting Portfolio Value: {start_portfolio_value:2f}')
print(f'Final Portfolio Value: {end_portfolio_value:2f}')
print(f'PnL: {pnl:.2f}')
#Run Cerebro Engine
cerebro.run()
Can anyone tell me please why I have an error and if the code is ok to make it run successfully ?
Thank you for your help!