I'm working my first monthly rebalance strategy and I added a regime change condition to rebalance into the top 20 stocks in fine selection if SPY>200SMA, or into AGG if SPY<200SMA. And it's giving me a "name 'data' is not defined" error, I suspect it's this spy>200sma logic I put in the def(rebalance) section.
I couldn't attach my backtest in the dropdown, so here is the code:
from clr import AddReference
AddReference("System.Core")
AddReference("System.Collections")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
from System import *
from System.Collections.Generic import List
from QuantConnect import *
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Data.UniverseSelection import *
class MonthlyROERebalanceAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015,1,1) #Set Start Date
self.SetEndDate(2020,8,1) #Set End Date
self.SetCash(100000) #Set Strategy Cash
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
self.agg = self.AddEquity("AGG", Resolution.Daily).Symbol
#self.SetBenchmark("QQQ")
# what resolution should the data *added* to the universe be?
self.UniverseSettings.Resolution = Resolution.Daily
# this add universe method accepts two parameters:
# - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
# - fine selection function: accepts an IEnumerable<FineFundamental> and returns an IEnumerable<Symbol>
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
self.numberOfSymbols = 250
self.numberOfSymbolsFine = 20
self._changes = None
self.fineFilteredSymbols = []
self.sma200 = self.SMA("SPY", 200)
self.SetWarmUp(200)
# Create the monthly rebalance logic. Check SPY 30 minutes after market open to determine the first trading day of the month.
self.Schedule.On(self.DateRules.MonthStart(self.spy),
self.TimeRules.AfterMarketOpen(self.spy,30),
self.rebalance)
def OnData(self, data):
#make sure SPY and AGG has data to prevent missing data errors
if data.ContainsKey("SPY") == False: return
if data.ContainsKey("AGG") == False: return
if not self.sma200.IsReady: return
pass
# Free cash buffer, increase in case buy orders are rejected due to insufficient funds
self.Settings.FreePortfolioValuePercentage = 0.05
# Coarse selection by sorting the symbols by daily dollar volume and define this list as "numbersOfSymbol"
def CoarseSelectionFunction(self, coarse):
# sort descending by daily dollar volume and only those that have fundamental data
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume and x.HasFundamentalData, reverse=True)
# return the symbol objects of the top entries from our sorted collection
return [ x.Symbol for x in sortedByDollarVolume[:self.numberOfSymbols] ]
# sort the data by Return on Equity and take the top ranked symbols as 'NumberOfSymbolsFine'
def FineSelectionFunction(self, fine):
# sort descending by Return On Equity
sortedByROE = sorted(fine, key=lambda x: x.OperationRatios.ROE.Value, reverse=True)
# take the top entries from our sorted collection
self.fineFilteredSymbols = [ x.Symbol for x in sortedByROE[:self.numberOfSymbolsFine] ]
return self.fineFilteredSymbols
def rebalance(self):
# if SPY is above 200 SMA then sell and AGG and go long stocks.
if data[self.spy.Symbol].Low>self.sma200.Current.Value:
for symbol in self.fineFilteredSymbols:
self.SetHoldings("AGG", 0)
self.SetHoldings(symbol, 1 / self.numberOfSymbolsFine )
# if SPY is below 200 SMA then go long AGG, no stocks.
if data[self.spy.Symbol].Low<self.sma200.Current.Value:
self.SetHoldings("AGG", 1)
def OnSecuritiesChanged(self, changes):
# Liquidate securities no longer in universe
for security in changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)