Hello David,
Thank you for your feedback! I wrote something with the class you provided. I figure my code is really messy and I'd restart it again. However, I am really stuck at line 33 and line 83. The reason why it doesn't run is because line 83 has an error called:
"Runtime exception: AttributeError: 'float' object has no attribute '_data'"
The program ran but it did not generate any trade, perhaps because there was no data. I would be grateful if you can tell me a bit on how I should resolve the problem with line 83.
Terence
Here is the code:
import math
import numpy as np
import scipy as sp
import datetime as dt
import statsmodels.tsa.stattools as ts
import statsmodels.api as sm
window_length = 30
lev = 1.2
def initialize(context):
context.std_multiplier = 1.5
set_commission(commission.PerTrade(cost=1.00))
context.pairs =[ [sid(24), sid(32146)],
[sid(46216), sid(46220)],
[sid(14517), sid(14516)],
[sid(5484), sid(6119)],
[sid(24), sid(5773)],
[sid(46170), sid(46220)],
[sid(46170), sid(46222)],
[sid(46216), sid(46222)],
[sid(46216), sid(46220)]
]
# Set the allocation per stock
pct_per_algo = 1.0 / (2*len(context.pairs))
# Make a separate algo for each stock.
context.algo = [PairTradeSpread(context.pairs[t][0], context.pairs[t][1], pct_per_algo) for t in range (0, (len(context.pairs)))]
def handle_data(context, data):
for algo in context.algo:
algo.handle_data(context, data)
class PairTradeSpread(object):
# Initialize with a single stock and assign a proportion of the account.
def __init__(self, ticker1, ticker2, allocation):
self.ticker1 = ticker1
self.ticker2 = ticker2
self.allocation = allocation
@batch_transform(window_length=30, refresh_period=1)
def avg_ratio(self, price1, price2, datapanel):
price1 = datapanel['price'][self.ticker1]
price2 = datapanel['price'][self.ticker2]
sid1_arr = np.array(price1)
sid2_arr = np.array(price2)
if sid1_arr is None or sid2_arr is None:
return None
return (sid1_arr/sid2_arr).mean()
@batch_transform(window_length=30, refresh_period=1)
def bounds(self, price1, price2, mean, context, datapanel):
price1 = datapanel['price'][self.ticker1]
price2 = datapanel['price'][self.ticker2]
sid1_arr = np.array(price1)
sid2_arr = np.array(price2)
if sid1_arr is None or sid2_arr is None:
return None
std_amt = (sid1_arr/sid2_arr).std()*context.std_multiplier
high_bound = mean + std_amt
low_bound = mean - std_amt
return (high_bound, low_bound)
def handle_data(self, context, data):
cash = context.portfolio.portfolio_value/(2*len(context.pairs))
price1 = data[self.ticker1].price
price2 = data[self.ticker2].price
current_ratio = price1/price2
avgratio = self.avg_ratio(price1, price2, data)
bounds= self.bounds(price1, price2, avgratio, context, data)
high_bound, low_bound = bounds
last_trade = None
if current_ratio > high_bound and last_trade is not "high":
order_value(self.ticker2, +lev*cash)
order_value(self.ticker1, -lev*cash)
last_trade = "high"
elif current_ratio < low_bound and last_trade is not "low":
order_value(self.ticker1, +lev*cash)
order_value(self.ticker2, -lev*cash)
last_trade = "low"
elif last_trade is not "mid" and last_trade is "high" and current_ratio < avgratio:
order_target(self.ticker1, 0)
order_target(self.ticker2, 0)
last_trade = "mid"
elif last_trade is not "mid" and last_trade is "low" and current_ratio > avgratio:
order_target(self.ticker1, 0)
order_target(self.ticker2, 0)
last_trade = "mid"
"""
class PairTradeZscore(object):
def __init__(self, ticker1, ticker2, allocation):
self.ticker1 = ticker1
self.ticker2 = ticker2
self.allocation = allocation
def ols(self, prices, price1, price2):
p0 = prices[price1]
p1 = sm.add_constant(prices[price2], prepend=True)
slope=sm.OLS(p0, p1).fit().params[0]
return slope
def zscore(self, price1, price2, slope, context):
slope = self.ols(prices, price1, price2)
spread = price1 - (slope * price2)
context.spreads.append(spread)
zscore = (spread - np.mean(spreads[-window_length:])) / np.std(spreads[-window_length:])
return zscore
def handle_data(self, context, data):
cash = context.portfolio.portfolio_value/(2*len(context.pairs))
slope = self.ols(prices, price1, price2)
zscore = self.zscore(price1, price2, slope, context)
if zscore > 2:
order_value(self.ticker2, +lev*cash)
order_value(self.ticker1, -lev*cash)
elif zscore < -2:
order_value(self.ticker1, +lev*cash)
order_value(self.ticker2, -lev*cash)
elif zscore < 0.5 and zscore > -0.5:
order_target(self.ticker1, 0)
order_target(self.ticker2, 0)
"""