from datetime import timedelta
import numpy as np
from scipy import stats
from collections import deque
class ModulatedMultidimensionalReplicator(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 6, 1)
#self.SetEndDate(2020, 1, 1)
self.SetCash(50000)
tickers = ["QQQ","IYC","IYK","IGV","SMH","GLD","TLH","TLT"]
self.sym=[]
for x in tickers:
self.sym.append(Symbol.Create(x, SecurityType.Equity, Market.USA))
self.SetUniverseSelection( ManualUniverseSelectionModel(self.sym) )
self.UniverseSettings.Resolution = Resolution.Daily
self.AddAlpha(MOMAlphaModel())
self.Settings.RebalancePortfolioOnInsightChanges = False
self.Settings.RebalancePortfolioOnSecurityChanges = False
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(self.DateRules.Every(DayOfWeek.Monday)))
self.SetExecution(ImmediateExecutionModel())
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
# if not self.Portfolio.Invested:
# self.SetHoldings("SPY", 1)
pass
class MOMAlphaModel(AlphaModel):
def __init__(self):
self.indi = {}
self.securities = []
def OnSecuritiesChanged(self, algorithm, changes):
'''
Description:
Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm
'''
# add new securities
for security in changes.AddedSecurities:
self.securities.append(security)
symbol = security.Symbol
# Call history to get an array of 200 days of history data
history = algorithm.History(symbol, 100, Resolution.Daily)
# Adjust SelectionData to pass in the history result
self.indi[symbol] = SelectionData(history, 100)
# RegisterIndicator
#algorithm.RegisterIndicator(symbol, self.indi, Resolution.Daily)
# remove securities
for security in changes.RemovedSecurities:
if security in self.securities:
self.securities.remove(security)
def Update(self, algorithm, data):
insights = []
# momentum
ordered = sorted(self.indi.items(), key=lambda x: x[1].Value, reverse=True)[3]
for x in ordered:
symbol = x[0]
insights.append( Insight.Price(symbol, timedelta(1), InsightDirection.Up) )
return insights
class SelectionData():
# Update the constructor to accept a history array
def __init__(self,history, period):
self.IsReady = False
self.Value = 0
self.ex_reg = CustomSlope('CSlope', period)
#4. Loop over the history data and update the indicators
for bar in history.itertuples():
self.ex_reg.Update(bar.Index[1], bar.close)
def is_ready(self):
return self.ex_reg.IsReady
def Update(self, time, price):
self.ex_reg.Update(time, price)
class CustomSlope:
def __init__(self, name, period):
self.Name = name
self.Time = datetime.min
self.IsReady = False
self.Value = 0
self.Slope = 0
self.Corr = 0
self.queue = deque(maxlen=period)
def __repr__(self):
return "{0} -> IsReady: {1}. Time: {2}. Value: {3}".format(self.Name, self.IsReady, self.Time, self.Value)
def Update(self, time, value):
self.queue.appendleft(value)
count = len(self.queue)
self.Time = time
self.IsReady = count == self.queue.maxlen
#### start here the indicator calulation
if self.IsReady:
y = np.log(self.queue)
x = [range(len(y))]
slope, corr = stats.linregress(x, y)[0], stats.linregress(x, y)[2]
self.Slope = slope
self.Corr = corr
self.Value = slope * corr
#### finish the custom indicator
return self.IsReady