Hi,
I'm creating a Universe where I filter symbols based on a SMA difference (using SymbolData class) like in this code. After this I want to get the price of those symbols but in minute resolution. I don't know how to build the consolidator. Could you help me with a simple code example?
Best regards
Grateful Trading
Hi Eduardo, are you able to attach a backtest? This will make it much easier to help you.
Grateful Trading
This code helped me quite a bit with building consolidators of all sizes.
Derek Melchin
Hi Eduardo,
To change the universe resolution from daily to minute, we need to change
self.UniverseSettings.Resolution = Resolution.Daily
to
self.UniverseSettings.Resolution = Resolution.Minute
Since the algorithm now uses minute resolution data, we should move the order placement logic from OnSecuritiesChanged into the OnData method. Additionally, to ensure the algorithm starts trading immediately, we should manually warm-up the SymbolData indicators.
See the attached backtest for reference. Note that the indicator periods have been reduced to speed up the backtest.
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.
Eduardo Barros Verez
I attach the code here:
import numpy as np # needed for NaN handling import math # ceil and floor are useful for rounding from datetime import timedelta from clr import AddReference AddReference("System") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Indicators") AddReference("QuantConnect.Common") import statistics from System import * from QuantConnect import * import talib class MyAlgorithm(QCAlgorithm): def Initialize(self): self.symbols = None # over simplistic tracking of position age self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage) #self.SetSlippageModel(VolumeShareSlippageModel()) # SMA self.SetStartDate(2019,1,1) #Set Start Date self.SetEndDate(2020,6,28) #Set End Date self.SetCash(1000) #self.SetWarmUp(50, Resolution.Minute) self.SetBenchmark("SPY") self.UniverseSettings.Resolution = Resolution.Minute self.UniverseSettings.Leverage = 1 self.AddSecurity("SPY", Resolution.Minute).Symbol self.Max_Candidates = 100 self.Max_BuyOrdersAtOnce = 20 self.MyLeastPrice = 1.50 self.MyMostPrice = 5.00 self.MyFireSalePrice = self.MyLeastPrice self.MyFireSaleAge = 6 #self.time = { }; self.averages = { }; #self.changes = None #self.index = 0 self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) # esto es para resolution menor de day #self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction)) #BEFORE TRADING START en initialize self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", -10), Action(self.before_trading_start)) # Rebalance EveryThisManyMinutes = 10 TradingDayHours = 6.5 TradingDayMinutes = int(TradingDayHours * 60) #self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.Every(timedelta(minutes=EveryThisManyMinutes)), Action(self.my_rebalance)) for minutz in range(1, TradingDayMinutes, EveryThisManyMinutes): self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", minutz), Action(self.my_rebalance)) # Prevent excessive Logging of canceled orders at market close. self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY", 1), Action(self.cancel_open_orders)) def OnSecuritiesChanged(self, changes): self.Debug("checking") self.changes = changes def CoarseSelectionFunction(self, coarse): sorted(coarse, key = lambda x: x.DollarVolume, reverse = True) # We are going to use a dictionary to refer the object that will keep the moving averages #self.Debug('coarse ' +str(coarse)) for cf in coarse: if cf.HasFundamentalData: if cf.Symbol not in self.averages: self.averages[cf.Symbol] = SymbolData(cf.Symbol) # Updates the SymbolData object with current EOD price avg = self.averages[cf.Symbol] avg.update(cf.EndTime, cf.AdjustedPrice) # Filter the values of the dict: we only want up-trending securities #values = list(filter(lambda x: x.is_downtrend, self.averages.values())) #values.sort(key = lambda x: x.scale , reverse = False) values = sorted(self.averages.values(), key = lambda x: x.scale, reverse = True)[:self.Max_Candidates] self.values = list(values) for x in self.values: self.Debug('values ' + str(x.symbol) + ' price ' + str(x.price) + ' scale ' + str(x.scale)) self.g = [x for x in self.values if (float(x.price) >= self.MyLeastPrice) and (float(x.price) <= self.MyMostPrice)] return [ x.symbol for x in self.g[:self.Max_Candidates] ] def FineSelectionFunction(self, fine): self.filtered = [x for x in fine if (x.SecurityReference.SecurityType == 'ST00000001') and (x.SecurityReference.IsDepositaryReceipt == False) and not (x.CompanyReference.IsLimitedPartnership) and (x.SecurityReference.IsPrimaryShare) and (x.SecurityReference.ExchangeId != 'OTC')] #and x.CompanyReference.StandardName == ".*L[. ]?P.?$"] # Not when-issued equities. LowVar = 0.06 * len(self.filtered) HighVar = 0.4 * len(self.filtered) self.filtered_h = [x.Symbol for x in self.filtered[:int(HighVar)]] self.filtered_l = [x.Symbol for x in self.filtered[-int(LowVar):]] self.symbols = self.filtered_h + self.filtered_l return self.symbols def before_trading_start(self): #update prices of my active securities and do some checks about the time they have been in my portfolio pass def my_rebalance(self): #do my buy and sells checking minute bars prices by comparing it to period daily prices mean self.CurrentPrice = self.Securities[symbol].Price self.PH = self.History(symbol, 20, Resolution.Daily) pass def OnData(self, data): pass class SymbolData(object): def __init__(self, symbol): self.symbol = symbol #SMA (daily resolution) self.fast = SimpleMovingAverage(3) self.mid = SimpleMovingAverage(20) self.slow = SimpleMovingAverage(45) self.price = 0 self.is_downtrend = False self.scale = 0 def update(self, time, value): self.price = value if self.fast.Update(time, value) and self.slow.Update(time, value): fast = self.fast.Current.Value mid = self.fast.Current.Value slow = self.slow.Current.Value self.is_downtrend = fast < slow self.scale = (fast-slow)/slow if self.is_downtrend: self.scale = (fast - slow) / slow
Derek Melchin
Hi Eduardo,
The securities are already subscribed to minute-resolution data in the code above. There is no need to build a consolidator to compute the daily mean because the SymbolData objects are updated with daily values already.
avg.update(cf.EndTime, cf.AdjustedPrice)
We can compare the current minute price of a security with it's daily mean by defining `my_rebalance` as:
data = self.CurrentSlice for symbol, symbol_data in self.averages.items(): if not (data.ContainsKey(symbol) and data[symbol] is not None): continue current_price = data[symbol].Price mean = symbol_data.fast.Current.Value # daily mean
See the attached backtest for reference.
Continuing the development of this algorithm, consider adding a manual warm-up to the SymbolData class as shown in this example.
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.
Eduardo Barros Verez
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!