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)
Adam W
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.
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.
Mark hatlan
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:"
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.
Adam W
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.
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.
Mark hatlan
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)
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.
Adam W
Made a couple of changes to your code and small design suggestions, marked with ' ### CHANGE '.
Explanations:
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.
Mark hatlan
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.
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!