I am looking for a way to add a technical indicator, specifically WILR, to my fundamental screener. I was attempting to add it as WILR(x, 14, Resolution.Daily) when I was using the for x in x declaration of the 'fine' screen.
However I kept getting errors; I cannot find much besides scripts that are only using SPY or some other singular symbol for WILR. However, I want to use it to screen my 'universe' for when WILR[-1] < -80 and WILR > -80
Any help is appreciated!
Thanks
Josh
Alexandre Catarino
Hi Josh M ,
Could you please share a backtest with the work you have done so far?
I think that the algorithm should implement a SymbolData helper class with the WilliamsPercentR indicator and a RollingWindow[IndicatorDataPoint]. Since WilliamsPercentR is a BarIndicator, we won't be able to update it only with the data we have in FineFundamental, it requires a Historical data request. However, the design decisions depend on how many symbols the algorithm is filtering in Coarse and Fine.
Josh M
Sorry I'm a noob at python Alexandre Catarino ; just been getting my feet wet the past two days and I think I'm learning rapidly.
Thanks for any help!
Best,
Josh
from QuantConnect.Data.UniverseSelection import * import math import numpy as np import pandas as pd import scipy as sp # import statsmodels.api as sm class CustomSlippageModel: def GetSlippageApproximation(self, asset, order): return np.float( asset.Price ) * 0.002 class FundamentalFactorAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2019, 6, 1) #Set Start Date #self.SetEndDate(2020, 1, 1) #Set Start Date self.SetCash(100000) #Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol self.holding_months = 1 self.num_screener = 100 self.num_stocks = 10 self.formation_days = 200 self.lowmom = False self.month_count = self.holding_months self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), Action(self.monthly_rebalance)) self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(10, 0), Action(self.rebalance)) # rebalance the universe selection once a month self.rebalence_flag = 0 # make sure to run the universe selection at the start of the algorithm even it's not the manth start self.first_month_trade_flag = 1 self.trade_flag = 0 self.symbols = None def CoarseSelectionFunction(self, coarse): if self.rebalence_flag or self.first_month_trade_flag: # drop stocks which have no fundamental data or have too low prices selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > 5)] # rank the stocks by dollar volume filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) return [ x.Symbol for x in filtered[:500]] else: return self.symbols def FineSelectionFunction(self, fine): if self.rebalence_flag or self.first_month_trade_flag: hist = self.History([i.Symbol for i in fine], 1, Resolution.Daily) filtered_fine = [x for x in fine if (x.ValuationRatios.EVToEBITDA > 0) and (x.EarningReports.BasicAverageShares.ThreeMonths > 0) and (WILR[-1](x,14,Resolution.Daily < -80) and WILR(x,14,Resolution.Daily)>-80) and (WILR[-1](x,14,Resolution.Hourly < -80) and WILR(x,14,Resolution.Hourly)>-80)] top = sorted(filtered_fine, key = lambda x: x.ValuationRatios.EVToEBITDA, reverse=True)[:self.num_screener] self.symbols = [x.Symbol for x in top] self.rebalence_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return self.symbols else: return self.symbols def OnData(self, data): pass def monthly_rebalance(self): self.rebalence_flag = 1 def rebalance(self): spy_hist = self.History([self.spy], 120, Resolution.Daily).loc[str(self.spy)]['close'] if self.Securities[self.spy].Price < spy_hist.mean(): for symbol in self.Portfolio.Keys: if symbol.Value != "TLT": self.Liquidate() self.AddEquity("TLT") self.SetHoldings("TLT", 1) return if self.symbols is None: return chosen_df = self.calc_return(self.symbols) chosen_df = chosen_df.iloc[:self.num_stocks] self.existing_pos = 0 add_symbols = [] for symbol in self.Portfolio.Keys: if symbol.Value == 'SPY': continue if (symbol.Value not in chosen_df.index): self.SetHoldings(symbol, 0) elif (symbol.Value in chosen_df.index): self.existing_pos += 1 weight = 0.99/len(chosen_df) for symbol in chosen_df.index: self.AddEquity(symbol) self.SetHoldings(symbol, weight) def calc_return(self, stocks): hist = self.History(stocks, self.formation_days, Resolution.Daily) current = self.History(stocks, 1, Resolution.Minute) self.price = {} ret = {} for symbol in stocks: if str(symbol) in hist.index.levels[0] and str(symbol) in current.index.levels[0]: self.price[symbol.Value] = list(hist.loc[str(symbol)]['close']) self.price[symbol.Value].append(current.loc[str(symbol)]['close'][0]) for symbol in self.price.keys(): ret[symbol] = (self.price[symbol][-1] - self.price[symbol][0]) / self.price[symbol][0] df_ret = pd.DataFrame.from_dict(ret, orient='index') df_ret.columns = ['return'] sort_return = df_ret.sort_values(by = ['return'], ascending = self.lowmom) return sort_return
Josh M
I was trying to implement it in lines 57 and 58 and it runs, but you are right it doesnt ever seem to update
Josh M
Hello Anyone in the future looking for how I solved my problem:
This is how;
I began using the 'Algorithm / Alpha Framework'
I created a fundamental screener in my universe selection and I created an alpha to create insights based on WILR
Here is the module I used
from clr import AddReference AddReference("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Algorithm.Framework") from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * class WILRAlphaModel(AlphaModel): def __init__(self): self.wilr = [] def OnSecuritiesChanged(self, algorithm, changes): for security in changes.AddedSecurities: symbol = security.Symbol self.wilr.append({"symbol":symbol, "indicator":algorithm.WILR(symbol, 14, Resolution.Daily)}) def Update(self, algorithm, data): ordered = sorted(self.wilr, key=lambda kv: kv["indicator"].Current.Value, reverse=False) return Insight.Group([Insight.Price(ordered[0]['symbol'], timedelta(1), InsightDirection.Up), Insight.Price(ordered[1]['symbol'], timedelta(1), InsightDirection.Flat) ])
Alexandre Catarino
Hi Josh M ,
Sorry about the wait.
Good to see that you have found a solution, especially one that uses the Algorithm Framework.
Since WilliamsPercentR is a BarIndicator, we need to make a historical data request for all securities if we want to update it inside a universe selection function. It can be quite computationally expensive if we are talking about hundreds of securities. In this case, the best approach, if possible, is to only create the indicators in OnSecuritiesChanged.
Just one note: in the OnSecuritiesChanged of your alpha model, there is no logic to deal with securities removal. In this case, it needs to remove the indicator from the self.wilr list. I prefer using a dictionary keyed by symbol for indicator implementation in alpha models. You can find examples in Lean repo.
Josh M
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!