Hello All,
I have been trying to learn how to use rolling windows using Quant Connect's RSI Alpha Model as a starting point. When I try to run the code shown below, I receive the following error:
Runtime Error: AttributeError : 'NoneType' object has no attribute 'Close' AttributeError : 'NoneType' object has no attribute 'Close' (Open Stacktrace)
I believe the error comes from line 35 in my code, but I thought that the preceding "if" statement on line 34 should have prevented this error. If anyone happens to know how to fix this problem, I would greatly appreciate some advice.
As a separate question, I'm not sure how to load my historical data in the rolling window. Any advice on this question would also be appreciated.
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from Risk.MaximumDrawdownPercentPerSecurity import MaximumDrawdownPercentPerSecurity
from Selection.QC500UniverseSelectionModel import QC500UniverseSelectionModel
class SimpleRSITestQC500Universe(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1) # Set Start Date
self.SetEndDate(2010, 2, 28) # Set End Date
self.SetCash(100000) # Set Strategy Cash
self.SetExecution(ImmediateExecutionModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(0.05))
symbols = [ Symbol.Create("SPY", SecurityType.Equity, Market.USA), Symbol.Create("GE", SecurityType.Equity, Market.USA), Symbol.Create("BA", SecurityType.Equity, Market.USA) ]
self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
self.AddAlpha(RsiAlphaModelTest())
class RsiAlphaModelTest(AlphaModel):
def __init__(self, period = 14, resolution = Resolution.Daily):
self.period = period
self.resolution = resolution
self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), period)
self.symbolDataBySymbol = {}
self.closeWindows = {}
resolutionString = Extensions.GetEnumString(resolution, Resolution)
self.Name = '{}({},{})'.format(self.__class__.__name__, period, resolutionString)
def Update(self, algorithm, data):
insights = []
for symbol, symbolData in self.symbolDataBySymbol.items():
if data.ContainsKey(symbol):
self.closeWindows[symbol].Add(data[symbol].Close)
if self.closeWindows[symbol].Count>2:
algorithm.Debug(self.closeWindows[symbol][2])
rsi = symbolData.RSI
previous_state = symbolData.State
state = self.GetState(rsi, previous_state)
if state != previous_state and rsi.IsReady:
if state == State.TrippedLow:
insights.append(Insight.Price(symbol, self.insightPeriod, InsightDirection.Up))
if state == State.TrippedHigh:
insights.append(Insight.Price(symbol, self.insightPeriod, InsightDirection.Down))
symbolData.State = state
return insights
def OnSecuritiesChanged(self, algorithm, changes):
# clean up data for removed securities
symbols = [ x.Symbol for x in changes.RemovedSecurities ]
if len(symbols) > 0:
for subscription in algorithm.SubscriptionManager.Subscriptions:
if subscription.Symbol in symbols:
self.symbolDataBySymbol.pop(subscription.Symbol, None)
subscription.Consolidators.Clear()
# initialize data for added securities
addedSymbols = [ x.Symbol for x in changes.AddedSecurities if x.Symbol not in self.symbolDataBySymbol]
if len(addedSymbols) == 0: return
history = algorithm.History(addedSymbols, self.period, self.resolution)
for symbol in addedSymbols:
rsi = algorithm.RSI(symbol, self.period, MovingAverageType.Wilders, self.resolution)
self.closeWindows[symbol] = RollingWindow[float](4)
# need to figure out how to load historical data into rolling window
if not history.empty:
ticker = SymbolCache.GetTicker(symbol)
if ticker not in history.index.levels[0]:
Log.Trace(f'RsiAlphaModel.OnSecuritiesChanged: {ticker} not found in history data frame.')
continue
for tuple in history.loc[ticker].itertuples():
rsi.Update(tuple.Index, tuple.close)
self.symbolDataBySymbol[symbol] = SymbolData(symbol, rsi)
def GetState(self, rsi, previous):
if rsi.Current.Value > 70:
return State.TrippedHigh
if rsi.Current.Value < 30:
return State.TrippedLow
if previous == State.TrippedLow:
if rsi.Current.Value > 35:
return State.Middle
if previous == State.TrippedHigh:
if rsi.Current.Value < 65:
return State.Middle
return previous
class SymbolData:
def __init__(self, symbol, rsi):
self.Symbol = symbol
self.RSI = rsi
self.State = State.Middle
class State(Enum):
'''Defines the state. This is used to prevent signal spamming and aid in bounce detection.'''
TrippedLow = 0
Middle = 1
TrippedHigh = 2
Garrett Grow
Actually never mind about my second question. I figured that one out.
Rahul Chowdhury
Hi Garret,
Sometimes although the key for a security may be available in the slice data, the bar data for that security may not be available. This may happen when non-price data is available for that symbol while bar data is not. When we access data[symbol].Close, we are accessing the close of the current bar for that symbol. If there is no bar data for that symbol in the current slice, attempting to access the close data will throw an error. We can avoid this by also checking if
if data.ContainsKey(symbol) and data[symbol] is not None: self.closeWindows[symbol].Add(data[symbol].Close)
Best
Rahul
Garrett Grow
Thanks!
Garrett Grow
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!