Overall Statistics |
Total Trades 430 Average Win 0.64% Average Loss -0.36% Compounding Annual Return -33.818% Drawdown 20.100% Expectancy 0.025 Net Profit -3.445% Sharpe Ratio -0.203 Probabilistic Sharpe Ratio 34.078% Loss Rate 63% Win Rate 37% Profit-Loss Ratio 1.77 Alpha -0.377 Beta 1.065 Annual Standard Deviation 0.591 Annual Variance 0.349 Information Ratio -0.616 Tracking Error 0.586 Treynor Ratio -0.112 Total Fees $1118.78 Estimated Strategy Capacity $23000000.00 Lowest Capacity Asset GME SC72NCBXXAHX |
class WellDressedSkyBlueSardine(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 12, 1) self.SetEndDate(2020, 12, 31) self.SetCash(100000) self.AddUniverse(self.CoarseFilter, self.FineFilter) self.UniverseSettings.Resolution = Resolution.Hour self.Data = {} def CoarseFilter(self, coarse): sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in sortedByDollarVolume if x.Price > 10 and x.HasFundamentalData][:200] def FineFilter(self, fine): sortedByPE = sorted(fine, key=lambda x: x.MarketCap) return [x.Symbol for x in sortedByPE if x.MarketCap > 0][:10] def OnSecuritiesChanged(self, changes): # close positions in removed securities for x in changes.RemovedSecurities: self.Liquidate() if x.Symbol in self.Data: del self.Data[x.Symbol] # can't open positions here since data might not be added correctly yet for x in changes.AddedSecurities: self.Data[x.Symbol] = SCTR("SCTR",x.Symbol) self.RegisterIndicator(x.Symbol,self.Data[x.Symbol],Resolution.Daily) history = self.History(x.Symbol, 200, Resolution.Daily) if history.empty or 'close' not in history.columns: continue for bar in history.loc[x.Symbol, :].itertuples(): tradebar = TradeBar(bar.Index, x.Symbol, bar.open, bar.high, bar.low, bar.close, bar.volume) self.Data[x.Symbol].Update(tradebar) def OnData(self, data): SCTR = {} for symboldata in self.Data.values(): if symboldata.IsReady: self.Debug(f'{symboldata.symbol}\'s SCTR = {symboldata.Value} in {self.Time}') SCTR[symboldata.symbol] = symboldata.Value [self.SetHoldings(symbol, sctr/sum(list(SCTR.values()))) for symbol, sctr in SCTR.items()] class SCTR(PythonIndicator): def __init__(self, name,symbol): self.Name = name self.symbol = symbol self.Value = 0 self.EMA200 = ExponentialMovingAverage(200) self.EMA50 = ExponentialMovingAverage(50) self.ROC125 = RateOfChange(125) self.ROC20 = RateOfChange(20) self.PPO = PercentagePriceOscillator(12,26,MovingAverageType.Exponential) self.RSI14 = RelativeStrengthIndex(14) self.PPOWindow = RollingWindow[float](3) def Update(self, bar): self.EMA200.Update(bar.Time, bar.Close) self.EMA50.Update(bar.Time, bar.Close) self.ROC125.Update(bar.Time, bar.Close) self.ROC20.Update(bar.Time, bar.Close) self.PPO.Update(bar.Time, bar.Close) self.RSI14.Update(bar.Time, bar.Close) self.PPOWindow.Add(self.PPO.Current.Value) if self.isReady(): self.Value = self.SCTR() return True self.Value = 0 return False def SCTR(self): return self.EMA200.Current.Value*0.3 + self.EMA50.Current.Value*0.15 + self.ROC125.Current.Value*0.3 + self.ROC20.Current.Value*0.15 \ + (self.PPOWindow[0] - self.PPOWindow[2])/(3*self.PPOWindow[2]) *0.05 + self.RSI14.Current.Value*0.05 def isReady(self): return self.EMA200.IsReady and self.EMA50.IsReady \ and self.ROC125.IsReady and self.ROC20.IsReady\ and self.PPO.IsReady and self.RSI14.IsReady
class StockChartsTechnicalRank: """ Custom indicator class for the StockCharts Technical Rank indicator. REF: https://school.stockcharts.com/doku.php?id=technical_indicators:sctr By: Aaron Eller aaron@excelintrading.com """ # Can customize each input, but will default to the settings from link def __init__( self, name, lt_ema_period=200, lt_ema_weight=0.30, lt_roc_period=125, lt_roc_weight=0.30, mt_ema_period=50, mt_ema_weight=0.15, mt_roc_period=20, mt_roc_weight=0.15, ppo_fast=12, ppo_slow=26, ppo_signal=9, ppo_histogram_slope_period=3, ppo_histogram_slope_weight=0.05, rsi_period=14, rsi_weight=0.05, ): """Initialize the indicator.""" self.Name = name self.Time = datetime.min self.Value = 0 self.IsReady = False # Create required indicators self.lt_ema = ExponentialMovingAverage(lt_ema_period) self.lt_roc = RateOfChange(lt_roc_period) self.mt_ema = ExponentialMovingAverage(mt_ema_period) self.mt_roc = RateOfChange(mt_roc_period) self.ppo = PercentagePriceOscillator( ppo_fast, ppo_slow, MovingAverageType.Exponential ) self.ppo_signal = ExponentialMovingAverage(ppo_signal) # ppo_histogram = self.ppo-self.ppo_signal self.ppo_histogram_window = \ RollingWindow[float](ppo_histogram_slope_period) self.rsi = RelativeStrengthIndex(rsi_period, MovingAverageType.Wilders) # Constants for the indicator self.lt_ema_weight = lt_ema_weight self.lt_roc_weight = lt_roc_weight self.mt_ema_weight = mt_ema_weight self.mt_roc_weight = mt_roc_weight self.ppo_histogram_slope_weight = ppo_histogram_slope_weight self.rsi_weight = rsi_weight # Let's verify that the weights total 1.0 if sum( [lt_ema_weight, lt_roc_weight, mt_ema_weight, mt_roc_weight, ppo_histogram_slope_weight, rsi_weight] ) != 1.0: raise ValueError("Invalid weights for StockChartsTechnicalRank " "class. Sum must equal 1.0") # Let's calculate the minimum number of bars required to initialize self.min_bars = max([ lt_ema_period, lt_roc_period, mt_ema_period, mt_roc_period, ppo_slow + ppo_signal + ppo_histogram_slope_period, rsi_period ]) #------------------------------------------------------------------------------- def __repr__(self): """ Returns the object representation in string format. Called via repr() on the object. """ return "{0} -> IsReady: {1}. Time: {2}. Value: {3}".format( self.Name, self.IsReady, self.Time, self.Value) #------------------------------------------------------------------------------- def is_ready(self): """ Check if the indicator is warmed up and ready to be calculated. """ return self.lt_ema.IsReady and self.mt_ema.IsReady and \ self.lt_roc.IsReady and self.mt_roc.IsReady and \ self.rsi.IsReady and self.ppo_histogram_window.IsReady #------------------------------------------------------------------------------- def Update(self, input): """ Update the indicator with the input. This is a required function for custom indicators! """ # Update EMAs self.lt_ema.Update(input.Time, input.Close) # Check if long-term EMA is ready if self.lt_ema.IsReady: # Calculate the percent above/below the current EMA lt_ema = self.lt_ema.Current.Value self.lt_ema_pct = (input.Close-lt_ema)/lt_ema self.mt_ema.Update(input.Time, input.Close) # Check if medium-term EMA is ready if self.mt_ema.IsReady: # Calculate the percent above/below the current EMA mt_ema = self.mt_ema.Current.Value self.mt_ema_pct = (input.Close-mt_ema)/mt_ema # Update ROCs self.lt_roc.Update(input.Time, input.Close) self.mt_roc.Update(input.Time, input.Close) # Update the RSI self.rsi.Update(input.Time, input.Close) # Update the PPO self.ppo.Update(input.Time, input.Close) # Check if ready if self.ppo.IsReady: # Update the PPO signal ppo = self.ppo.Current.Value self.ppo_signal.Update(input.Time, ppo) # Check if PPO signal is ready if self.ppo_signal.IsReady: # Update the PPO Histogram ppo_signal = self.ppo_signal.Current.Value ppo_histogram = ppo-ppo_signal # Update the PPO Histogram RollingWindow self.ppo_histogram_window.Add(ppo_histogram) # Calculate the PPO Histogram Slope if self.ppo_histogram_window.IsReady: ppo_hist_current = self.ppo_histogram_window[0] ppo_hist_last = self.ppo_histogram_window[ self.ppo_histogram_window.Count-1] # Slope is degree of change in PPO histogram over x days # = rise/run slope = (ppo_hist_current-ppo_hist_last) \ /self.ppo_histogram_window.Count # Get slope component value # Slope greater than +1 (ie +45degrees), set value to 100 if slope >= 1: self.ppo_histogram_slope_value = 100 # Slope less than -1 (ie -45degrees), set value to 0 elif slope <= -1: self.ppo_histogram_slope_value = 0 # Otherwise value is (slope+1)*50 else: self.ppo_histogram_slope_value = (slope+1)*50.0 # e.g. slope=0.9 returns 1.9*50=95 # Update IsReady if not self.IsReady: if self.is_ready(): self.IsReady = True # Update self.Value when ready if self.IsReady: self.Value = \ 100.0*self.lt_ema_pct*self.lt_ema_weight + \ 100.0*self.lt_roc.Current.Value*self.lt_roc_weight + \ 100.0*self.mt_ema_pct*self.mt_ema_weight + \ 100.0*self.mt_roc.Current.Value*self.mt_roc_weight + \ self.rsi.Current.Value*self.rsi_weight + \ self.ppo_histogram_slope_value*self.ppo_histogram_slope_weight # NOTE: use 100.0* for values that are decimal floats # e.g. if a stock is 5% above it's long-term ema, lt_ema_pct=0.05 # RSI value is 0-100