Back

NameError help with monthly rebalance

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)

Update Backtest







0

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.


Yep it's this

if data[self.spy.Symbol].Low>self.sma200.Current.Value:

since data isn't defined anywhere (note that in OnData, LEAN automatically passes the latest stream of data as an argument). 

Bit of a hacky solution:

def Initialize(self):
self.shouldRebalance = False

# Change the scheduling to only turn on/off a boolean flag
self.Schedule.On( # whatever you had before
self.ToggleRebalance
)

def ToggleRebalance(self):
self.shouldRebalance = True

def OnData(self, data):
if self.shouldRebalance:
self.rebalance(data) # Now we can pass in data as an argument
self.shouldRebalance = False # Turn this off until the next ToggleRebalance() call


def rebalance(self, data): # Add the data argument
# whatever you had before

This schedules the function ToggleRebalance() to be called instead of rebalance() every month, then once it's set to True, we can call rebalance within OnData but pass in the data argument.

Cleaner solution would be to wrap the logic up into an AlphaModel or Indicator.

0

Thanks again Adam. I've got all that now but it doesn't like my data as an argument in self.rebalance(data). How do I need to change this statement for the toggle.rebalance flag?

"if data[self.spy.Symbol].Low>self.sma200.Current.Value:"

0

Can you post what you have so far?

In that answer's workaround, the scheduling should only trigger ToggleRebalance (remove the scheduled event call to rebalance if you haven't) and the rebalance function needs to be defined with an additional 'data' argument.

0

Sure, my data argument is this condition i've used on some other strategies that has worked:

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)
        self.shouldRebalance = False
        
        # 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.self.ToggleRebalance)
    
    def ToggleRebalance(self):
        self.shouldRebalance = True
    
    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
 
        if self.shouldRebalance:
            self.rebalance(data)
                data[self.spy.Symbol].Low>self.sma200.Current.Value
            self.shouldRebalance = False    # Turn this off until the next ToggleRebalance() call
            
    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)

0

Made a couple of changes to your code and small design suggestions, marked with ' ### CHANGE '.

Explanations:

  1. CHANGE (1): In general, check for boolean flags in Python with 'if' or 'if not'
  2. CHANGE (2): 'pass' not needed; used as placeholder for empty functions
  3. CHANGE (3): Put this in Initialize, no need to set this everytime data is streamed into OnData()
  4. CHANGE (4): Seems to be in the wrong place. Moved to OnData()
  5. CHANGE (5): Added the 'data' argument into function definition

 

1

class MonthlyROERebalanceAlgorithm(QCAlgorithm):

def Initialize(self):

"""
CHANGE (3): Moved to Initialize
"""
# Free cash buffer, increase in case buy orders are rejected due to insufficient funds
self.Settings.FreePortfolioValuePercentage = 0.05

def OnData(self, data):

"""
CHANGE (1-2): Check boolean with if not and pass not necessary
"""
if not data.ContainsKey('SPY'): return ### data.ContainsKey("SPY") == False: return
if not data.ContainsKey('AGG'): return ### data.ContainsKey("AGG") == False: return
if not self.sma200.IsReady: return
### pass

"""
CHANGE (3) : Delete this

### Free cash buffer, increase in case buy orders are rejected due to insufficient funds
### self.Settings.FreePortfolioValuePercentage = 0.0
"""

"""
CHANGE (4) : Moved to OnData; delete the commented out line
"""
if self.shouldRebalance:
self.rebalance(data)
### Not necessary/delete: data[self.spy.Symbol].Low>self.sma200.Current.Value
self.shouldRebalance = False

# 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


"""
CHANGE (4) : Out of place; Delete this
if self.shouldRebalance:
self.rebalance(data)
data[self.spy.Symbol].Low>self.sma200.Current.Value
self.shouldRebalance = False # Turn this off until the next ToggleRebalance() call
"""

"""
CHANGE (5): Add data argument
"""
def rebalance(self, data): ### def rebalance(self):

# rest is okay
if data[self.spy.Symbol].Low>self.sma200.Current.Value:



 

1

Thanks Adam, that was a huge help.

Once I got all this together it didn't like my regime change condition "if data[self.spy.Symbol].Low>self.sma200.Current.Value:". I changed it to "if data[self.spy.].Low>self.sma200.Current.Value:" and its working.

0

Update Backtest





0

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.


Loading...

This discussion is closed