I was able to get this working, but in a rather hacky way. I'm hoping someone will be kind enough to reply with a better solution than this.
Python is not my forte, and I'm not trained in data science, so probably this is not the best solution, and I wonder about the memory impact. All the example code I could find for rolling z-score, involved pandas, so I just imported it and used a pandas series object.
import pandas as pd
import math
asset_class = 'stocks'
pairs_list = {
'stocks': [
('CVX', 'XOM'),
('EGBN', 'FMBI')
],
'forex': [
('EURUSD', 'GBPUSD')
]
}
(symbol1, symbol2) = pairs_list[asset_class][0]
class MeanReversionResearch(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 12, 3)
self.SetEndDate(2020, 12, 5)
self.SetCash(100_000)
self.ZScoreSeries = Series("Z Score", SeriesType.Line, 0)
chart = Chart("Z Score")
chart.AddSeries(self.ZScoreSeries)
self.AddChart(chart)
self.Ratios = pd.Series()
if asset_class == 'stocks':
self.AddEquity(symbol1, Resolution.Minute)
self.AddEquity(symbol2, Resolution.Minute)
elif asset_class == 'forex':
self.AddForex(symbol1, Resolution.Minute)
self.AddForex(symbol2, Resolution.Minute)
def OnData(self, data):
if data.ContainsKey(symbol1) and data.ContainsKey(symbol2):
price1 = data[symbol1].Close
price2 = data[symbol2].Close
self.ZScore(price1 / price2)
def ZScore(self, ratio):
self.Debug(f'ratio: {ratio}')
self.Ratios = self.Ratios.append(pd.Series([ratio]))
x = self.Ratios.rolling(window = 1).mean()
w = self.Ratios.rolling(window = 20)
score = (x - w.mean()) / w.std()
if score.size > 0:
s = score.iat[-1]
if math.isnan(s):
return
else:
self.ZScoreSeries.AddPoint(self.Time, s)
Â