Hi,
I seem to have some trouble using algorithm.Consolidate with coarse-fine Universe Selection model. The algorithm basically works by rebalancing my portfolio every month based on some fundamentals/growth criteria. What this means is that every month I will have new securities to keep track of, and thus will need to subscribe to a set of consolidators every month. From my understanding, the universe selection model will automatically subscribe to data for all the symbols returned by the fine selection method. However, that does not seem to be the case. I made sure to subscribe to the consolidators for the symbols that I need only upon securities changes in the OnSecuritiesChanged function, but the following error still persisted:
Please register to receive data for symbol 'AAME 0' using the AddSecurity() function.
Here's the code. Thanks!
from QuantConnect import *
from QuantConnect.Parameters import *
from QuantConnect.Benchmarks import *
from QuantConnect.Brokerages import *
from QuantConnect.Util import *
from QuantConnect.Interfaces import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Custom import *
from QuantConnect.Data.Fundamental import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Notifications import *
from QuantConnect.Orders import *
from QuantConnect.Orders.Fees import *
from QuantConnect.Orders.Fills import *
from QuantConnect.Orders.Slippage import *
from QuantConnect.Scheduling import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Equity import *
from QuantConnect.Securities.Forex import *
from QuantConnect.Securities.Interfaces import *
from datetime import date, datetime, timedelta
from QuantConnect.Python import *
from QuantConnect.Storage import *
QCAlgorithmFramework = QCAlgorithm
QCAlgorithmFrameworkBridge = QCAlgorithm
import math
import numpy as np
import pandas as pd
import scipy as sp
class MicroGrowth(QCAlgorithm):
def Initialize(self):
#self.SetStartDate(2020, 2, 12) # Set Start Date
self.SetStartDate(2019, 1,1)
#self.SetEndDate(2021, 3, 1)
self.SetCash(100000) # Set Strategy Cash
#self.Settings.FreePortfolioValuePercentage = 0.6
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)
self.UniverseSettings.Resolution = Resolution.Hour
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
self.lasttime = None
self.monthinterval = 1
self.periods = [30,60,180]
self.rebalance_interval = timedelta(days = min(self.periods))
self.tohold = []
self.toliquidate = []
self.newlyadded = []
self.numsecurities = 25
self.SetWarmUp(timedelta(hours= 4))
self.debug_execution = False
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
self.history_dict = {}
self.rebalance = False
#self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("TSLA", 0), self.ClosingBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(minutes=10)), self.RebalancePortfolio)
self.universe_securities = []
def RebalancePortfolio(self):
if self.IsWarmingUp: return
if self.rebalance == True:
self.rebalance = False
#self.Debug('\n\n\n'+f'========== NEW CYCLE ==========')
#self.Debug(f'New Securities Added: {self.newlyadded}')
#self.Debug(f'Securities Removed{self.toliquidate}')
#self.Debug(f'PORTFOLIO CASH BEFORE LIQUIDATION: {self.Portfolio.Cash}')
#self.Debug(f'PORTFOLIO UNSETTLED CASH BEFORE LIQUIDATION: {self.Portfolio.UnsettledCash}')
for sym in self.toliquidate:
self.Liquidate(sym)
#self.Debug(f'PRE-LIQUIDATION: {[[sym.Value, self.Portfolio[sym].Quantity] for sym in self.toliquidate]}')
for sym in self.tohold:
self.SetHoldings(str(sym),1/self.numsecurities)
#self.Debug(f'PRE-SET-QUANTITY: {[[sym.Value,self.Portfolio[sym].Quantity] for sym in self.tohold]}')
#self.Debug(f'EXPECTED CURRENT STATE: {sorted([sym.Value for sym in self.tohold])}')
if self.debug_execution == False:
self.debug_execution = True
elif self.debug_execution == True:
self.debug_execution = False
#self.Debug(f'========== {(self.Time - self.lasttime).seconds / 3600} HOURS AFTER ORDER IS EXECUTED ==========')
#self.Debug(f'POST-LIQUIDATION: {[[sym.Value, self.Portfolio[sym].Quantity] for sym in self.toliquidate]}')
#self.Debug(f'POST-SET-QUANTITY: {[[sym.Value,self.Portfolio[sym].Quantity] for sym in self.tohold]}')
#self.Debug(f'PORTFOLIO CASH AFTER REBALANCING: {self.Portfolio.Cash}')
#self.Debug(f'PORTFOLIO UNSETTLED CASH AFTER REBALANCING: {self.Portfolio.UnsettledCash}')
#self.Debug(f'ACTUAL CURRENT STATE: {sorted([x.Key.Value for x in self.Portfolio if x.Value.Invested])}')
#self.Debug(f'PORTFOLIO TOTAL HOLDINGS VALUE: {self.Portfolio.TotalHoldingsValue}')
#self.Debug(f'PORTFOLIO TOTAL EQUITY VALUE: {self.Portfolio.TotalPortfolioValue}')
#self.Debug(f'PORTFOLIO TOTAL (HOLDINGS - EQUITY) VALUE: {self.Portfolio.TotalHoldingsValue - self.Portfolio.TotalPortfolioValue}')
def OnData(self, data):
return
def CoarseSelectionFunction(self,coarse):
if self.IsWarmingUp: return
if self.lasttime == None or self.Time-self.lasttime >= self.rebalance_interval:
self.lasttime = self.Time
self.rebalance = True
return [x.Symbol for x in coarse if x.HasFundamentalData]
else:
return Universe.Unchanged
def FineSelectionFunction(self,fine):
security_momo_list = []
MKTCAP_dict = {}
#exclude delisted and TOPS (due to split value issue)
excluded_delisted = [i for i in fine if isinstance(i.SecurityReference.DelistingDate.date(),datetime) == False and i.Symbol.Value != "TOPS"]
#filter by mkt_cap
for i in fine:
if isinstance(i.MarketCap,(float,int)) and i.MarketCap != 0:
MKTCAP_dict[i]=i.MarketCap
microcap = [i for i in excluded_delisted if isinstance(MKTCAP_dict.get(i),(int,float)) and MKTCAP_dict.get(i)>25e6 and MKTCAP_dict.get(i)<250e6]
#filter by Price-to-Sales Ratio < 1 (defined to be null if result <= 0)
micro_PSR = [i for i in microcap if isinstance(i.ValuationRatios.PSRatio,(float,int)) and i.ValuationRatios.PSRatio < 1 and i.ValuationRatios.PSRatio > 0]
micro_PSR_symbols = [i.Symbol for i in micro_PSR]
#sorting by momentum using rolling window
list_to_remove = [sym for sym in self.history_dict.keys() if symbol not in micro_PSR_symbols]
for sym in list_to_remove:
del self.history_dict[sym]
for sym in micro_PSR_symbols:
if sym not in self.history_dict.keys():
self.history_dict[sym] = hist_window(self,sym)
security_momo_list = [[sym, self.history_dict[sym].get_crit_momo()] for sym in self.history_dict.keys() if self.history_dict[sym].eval_self_momo() == True]
security_momo_list_sorted = sorted(security_momo_list,key = lambda i : i[1],reverse = True)
new_tohold = [f[0] for f in security_momo_list_sorted[:self.numsecurities]]
self.toliquidate = [sym for sym in self.tohold if sym not in new_tohold]
self.newlyadded = [sym for sym in new_tohold if sym not in self.tohold]
self.tohold = new_tohold
#self.Debug(f'{[f.Value for f in output]}')
#output = [f[0] for f in security_momo_list]
return micro_PSR_symbols
def OnSecuritiesChanged(self, changes):
#for security in changes.AddedSecurities:
# if security.Symbol not in self.universe_securities:
# self.universe_securities.append(security.Symbol)
#for security in changes.RemovedSecurities:
# self.universe_securities.remove(security.Symbol)
#self.Debug(f'CURRENT UNIVERSE STATE: {self.universe_securities}')
for security in changes.AddedSecurities:
#ticker = str(self.Symbol.Value).replace(" 0","")
#problem is that the securities have yet to be subscribed
sym = security.Symbol
ticker = sym.Value.replace(" 0","")
self.Consolidate(ticker, timedelta(days=1), lambda x: self.history_dict[sym].Window.Add(x))
class hist_window():
def __init__(self,algorithm,symbol,criterion_period = None):
self.Symbol = symbol
#self.Ticker = self.Symbol.Value
self.periods=sorted(algorithm.periods)
self.maxperiod = max(self.periods)
self.Window = RollingWindow[TradeBar](self.maxperiod + 1)
#periods quantified in days
#period sorted in increasing length of period
self.maxperiod = max(self.periods)
if criterion_period == None:
self.criterion_period = self.maxperiod
else:
self.criterion_period = criterion_period
#in order of increasing recency, last element being most recent tradebar
hist_bars = algorithm.History(self.Symbol, self.maxperiod+1, Resolution.Daily)
for bar in hist_bars.itertuples():
tradebar = TradeBar(bar.Index[1], self.Symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)
self.Window.Add(tradebar)
def get_momo(self, period):
if self.Window.Count >= period:
return self.Window[0].Close/self.Window[period].Close/period
else:
return None
def get_crit_momo(self):
return self.get_momo(self.criterion_period)
def eval_self_momo(self):
if self.get_momo(self.maxperiod) == None:
return False
for i in range(0,len(self.periods)-1):
if self.get_momo(self.periods[i]) < self.get_momo(self.periods[i+1]):
return False
return True
Â
Derek Melchin
Hi Han,
Refer to this related thread. In the future, please avoid creating duplicate threads.
Best,
Derek Melchin
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.
Han Ruobin
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!