Hi!
I've been working on a strategy lately but no matter what I try, the symbols I have in the universe are not the ones I'm expecting. I tried using self.ActiveSecurities.Keys, self.changes.AddedSecurities…but no matter what I try the securities are more than the ones I would expect. I tried reducing the output from the CoarseSelectionFunction, too. But no matter what I do…it seems that the filtering is broken.
class MeanReversionAlgo(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1) # Set Start Date
self.initialcash = 100000
self.SetCash(self.initialcash) # Set Strategy Cash
# Add SPY to set scheduled events
self.AddEquity("SPY", Resolution.Daily)
# Setting Universe
self.UniverseSettings.Resolution = Resolution.Daily
self.SetUniverseSelection(QC500UniverseSelectionModel())
# 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.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction)
self.__numberOfSymbolsFine = 2
self._changes = None
self.AutomaticIndicatorWarmUp=True
self.SetWarmup(200, Resolution.Daily)
self.dataDict = {}
self.verboseLogging = False
# schedule an event to fire every trading day for a security the
# time rule here tells it to fire 10 minutes after SPY's market open
self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 10), self.EveryDayAfterMarketOpen)
def EveryDayAfterMarketOpen(self):
'''
self.Debug(f"EveryDay.SPY 10 min after open: Fired at: {self.Time}")
# if we have no changes, do nothing
if self._changes is None: return
# liquidate removed securities
for security in self._changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)
# we want 20% allocation in each security in our universe
for security in self._changes.AddedSecurities:
self.SetHoldings(security.Symbol, 0.01)
self._changes = None
'''
for i in self.ActiveSecurities.Keys:
self.Debug(i)
# sort the data by daily dollar volume and take the top 'NumberOfSymbols'
def CoarseSelectionFunction(self, coarse):
CoarseWithFundamental = [x for x in coarse if x.HasFundamentalData and x.Price > 5]
for i in CoarseWithFundamental:
if i.Symbol not in self.dataDict:
self.dataDict[i.Symbol] = SymbolData(i.Symbol)
self.dataDict[i.Symbol].update(i.EndTime, i.AdjustedPrice)
values = [x for x in self.dataDict.values()]
values_sharpe_ready = [x for x in values if x.SharpeOK]
values_sharpe_ready.sort(key=lambda x: x.sharpe , reverse=True)
top_100_sharpe = values_sharpe_ready[:100]
if self.verboseLogging:
self.Debug("First filter --------> top 100 by Sharpe")
for i in top_100_sharpe:
self.Debug(str(i.symbol)+" Current Sharpe "+str(i.sharpe))
sma_filtered = [x for x in top_100_sharpe if x.SMA_OK]
if self.verboseLogging:
self.Debug("Second filter --------> SMA filter")
for i in sma_filtered:
self.Debug(str(i.symbol)+" Current SMA Value "+str(i.SMA)+" Current Price "+str(i.currentPrice))
rsi_filtered = [x for x in sma_filtered if x.RSI_OK]
if self.verboseLogging:
self.Debug("Third filter --------> RSI filter")
for i in rsi_filtered:
self.Debug(str(i.symbol)+" Current 2 period RSI "+str(i.RSI))
rsi_filtered.sort(key=lambda x: x.STD, reverse=True)
finally_filtered = rsi_filtered[:10]
if self.verboseLogging:
self.Debug("Fourth filter --------> Top 10 by Standard Deviation")
for i in finally_filtered:
self.Debug(str(i.symbol)+" Current Standard Deviation "+str(i.STD))
# return the symbol objects of the top entries from our sorted collection
return [ x.symbol for x in finally_filtered[:10] ]
'''
def FineSelectionFunction(self, fine):
filteredByMktCap = [x for x in fine if x.MarketCap>500000000]
isPrimaryShare = [x for x in filteredByMktCap if x.SecurityReference.IsPrimaryShare]
# take the top entries from our sorted collection
return [ x.Symbol for x in isPrimaryShare]
'''
def OnData(self, slice):
'''
self.Debug("###### Instruments we are going to buy")
for i in self.ActiveSecurities.Keys:
self.Debug(i.Value)
self.SetHoldings(i.Value,0.1)
#Take profit logic
if self.Portfolio.Invested:
if self.Portfolio.TotalPortfolioValue > self.initialcash* 1.05:# means a 5% take profit target, if the initial portfolio value is 100000 with 1.05 you will take profit when the value of portfolio is greater than 105 000 $.
self.Liquidate()
'''
#Stop loss logic
if self.Portfolio.Invested:
if self.Portfolio.TotalPortfolioValue < self.initialcash*0.90: # means a 10% stop loss. In this case 0.9 means that the portfolio is valued a 90% of the original value, so if the initial value is 100 000 $ it means 90 000$, a 10% stop loss. if you set self.initialcash*0.5 means a 50% stop loss and so on.
self.Liquidate()
# this event fires whenever we have changes to our universe
def OnSecuritiesChanged(self, changes):
self._changes = changes
'''
# this event fires whenever we have changes to our universe
def OnSecuritiesChanged(self, changes):
# liquidate removed securities
for security in changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)
# we want 20% allocation in each security in our universe
for security in changes.AddedSecurities:
self.Debug(security)
self.SetHoldings(security.Symbol, 0.1)
self._changes = changes
self.Log(f"OnSecuritiesChanged({self.UtcTime}):: {changes}")
'''
class SymbolData(object):
def __init__(self, symbol):
self.symbol = symbol
self.History = RollingWindow[float](126)# you can't change this
self.STD = StandardDeviation(126)
self.SMA = SimpleMovingAverage(200)
self.SMA_OK = False
self.riskFreeRate=0.1
self.RSI = RelativeStrengthIndex(2)
self.RSI_OK = False
self.sharpe = 0
self.SharpeOK = False
self.currentPrice = 0
def update(self, time, value):
self.SMA.Update(time,value)
self.History.Add(float(value))
self.STD.Update(time,value)
self.RSI.Update(time, value)
self.currentPrice=value
if self.History.IsReady and self.STD.IsReady and self.STD.Current.Value != 0:
totalReturn = (self.History[10]-self.History[125])/self.History[10]
sharpeRatio = (totalReturn-self.riskFreeRate)/self.STD.Current.Value
self.sharpe = sharpeRatio
self.SharpeOK = sharpeRatio != 0
self.SMA_OK = self.SMA.IsReady and value > self.SMA.Current.Value
self.RSI_OK = self.RSI.IsReady and self.RSI.Current.Value < 10
Emiliano Fraticelli
Ok, I think to be near a solution after introducing a check to actually see if the returned list by Coarse is empty or not
Louis Szeto
Hi Emiliano
Both methods are not returning the selection of an universe. self.ActiveSecurities.Keys would include the ones your universe has selected as well as your holdings, while self.changes.AddedSecurities will only include those newly added ones, but not the ones already been there on the previous universe update. You may set up a new list to hold your universe's selections and call by that later.
Please check the attached backtest for a brief example.
Best,
Louis Szeto
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.
Emiliano Fraticelli
Thank you very much for your help!
One thing I'm trying to implement and I'm having a bit of problems with is updating the universe only on the first day of the trading month…
I've seen this example here :
And I think to have implemented it using a boolean which is turned true or false in the first trading day of each month…but I'm not sure it works always and would like to have one solution of yours…
Thanks
Emiliano Fraticelli
And Louis…about your code…
If I have an instrument from last universe update and in the next ones it continues to be in my universe because it still meets my universe requirements…
from what I see with this code it would be sold anyway…..
I want to keep it without selling…
Emiliano Fraticelli
And Louis…about your code…
If I have an instrument from last universe update and in the next ones it continues to be in my universe because it still meets my universe requirements…
from what I see with this code it would be sold anyway…..
I want to keep it without selling…
UPDATE: After running a backtest I can confirm your code generates buys and sells immediately afterwards
Vladimir
Emiliano Fraticelli
Try changing line 18 to this
Emiliano Fraticelli
ok, I'm further implementing the strategy but I still have some issues:
1) perform the universe selection, so have the list of securities to buy, just once per month. In order to achieve this I've followed the hints that you can find on this post https://www.quantconnect.com/forum/discussion/5539/schedule-coarse-fine-universe-selection/p1 but without success. For some strange reasons the universe is always empty.
2) I would like to exit a positions in one of the 10 instruments I try to buy every month when the 2-Period RSI is greater than 50. But not always the symbol is updated and can be found by accessing the dataDict. What could be a nice way to keep these instruments updated with their respective RSI and check every day if the current RSI is greater than this threshold and in that case sell the entire position?
Emiliano Fraticelli
issue n1 seems finally solved…after many hours of work
Louis Szeto
Hi Emiliano
May we have the most updated version of your code for further analysis on n2? Please attach a backtest of that, thanks.
Best
Louis Szeto
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.
Emiliano Fraticelli
So I want to have the whole thing simplified, so for now I would like to have this simple strategy…
the strategy “hunts” for candidates every start of the month, by ranking the stocks according to their standard deviation over the past 126 days. I rank these stocks and choose just the top 10. If I buy less than 10 different securities (each one should have 10% of my capital), the rest of the capital is put in SHY.
The next first day of the month I update my universe and choose again my list of securities to buy.
If a security (excluding SHY) during the month shows a 2 period RSI greater than 95 (the former threshold was 55), it is sold.
I am having some difficulties at the moment, for instance I've tried to code this “BUY SHY” logic in different ways, but none seems to be okay.
Emiliano Fraticelli
In the example above SHY in never bought since we always end up buying 10 securities…but in the case that's not true (imagine adding an additional filter)
Varad Kabade
Hi Emiliano Fraticelli,
Note that if we use the SetHoldings method on a security to assign 0.1 percent three times, the percent holdings would still be 10 percent not (30). In the above algorithm inside the buy method, we should calculate the weight of SHY instead of adding it to the list multiple times:
Refer to the attached backtest for reference.
Best,
Varad Kabade
Emiliano Fraticelli
Varad kabade in your solution I don't see any SHY trading happening
Vladimir
Emiliano Fraticelli
-> Varad kabade in your solution I don't see any SHY trading happening
I see some
2020-11-02T14:30:00Z SHY 86.24291362 1196 Market Filled 103146.5247
2020-12-01T14:30:00Z SHY 86.24152151 -1196 Market Filled -103144.8597
2021-02-01T14:30:00Z SHY 86.31626112 1241 Market Filled 107118.48
2021-03-01T14:30:00Z SHY 86.25640149 -1241 Market Filled -107044.1942
2021-03-01T14:30:00Z SHY 86.26639528 961 Market Filled 82902.00586
2021-04-01T13:30:00Z SHY 86.21844975 -961 Market Filled -82855.93021
2021-06-01T13:30:00Z SHY 86.32 401 Market Filled 34614.32
2021-07-01T13:30:00Z SHY 86.15 -401 Market Filled -34546.15
2021-08-02T13:30:00Z SHY 86.29 833 Market Filled 71879.57
Emiliano Fraticelli
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!