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

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
        # 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)
        # Create the monthly rebalance logic. Check SPY 30 minutes after market open to determine the first trading day of the month.
    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
        # 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: