Overall Statistics |
Total Trades 117 Average Win 0.07% Average Loss -0.07% Compounding Annual Return 134.556% Drawdown 0.000% Expectancy 0.424 Net Profit 2.603% Sharpe Ratio 11.481 Probabilistic Sharpe Ratio 99.824% Loss Rate 31% Win Rate 69% Profit-Loss Ratio 1.06 Alpha 1.253 Beta -0.332 Annual Standard Deviation 0.092 Annual Variance 0.009 Information Ratio 4.161 Tracking Error 0.116 Treynor Ratio -3.195 Total Fees $117.30 Estimated Strategy Capacity $34000000.00 Lowest Capacity Asset MKL R735QTJ8XC9X |
import pandas as pd class WellDressedSkyBlueSardine(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 12, 21) self.SetEndDate(2020, 12, 31) self.SetCash(1000000) self.SPY = self.AddEquity('SPY', Resolution.Daily).Symbol self.AddUniverse(self.CoarseFilter, self.FineFilter) self.UniverseSettings.Resolution = Resolution.Daily self.UniverseSettings.SetDataNormalizationMode = DataNormalizationMode.SplitAdjusted self.SCTRuniverse = [] self.potential = [] self.Data = {} self.indicators = {} self.percentagebuy = 0.05 self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 30), self.daily_check) 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][:1000] def FineFilter(self, fine): sortedByPE = sorted(fine, key=lambda x: x.MarketCap) self.universe = [x.Symbol for x in sortedByPE if x.MarketCap > 10e9][:500] # self.Debug(self.universe) return self.universe 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] = SymbolData(self, x.Symbol) SCTR = {} for symboldata in self.Data.values(): if symboldata.IsReady(): SCTR[symboldata.symbol] = symboldata.SCTR() sctr_df = pd.DataFrame.from_dict(SCTR, orient='index') sctr_df['Decile_rank'] = pd.qcut(sctr_df.iloc[:, 0], 10000, labels = False) /100 sctr_df = sctr_df.drop(sctr_df.columns[0], axis=1) sctr_df = sctr_df.loc[sctr_df['Decile_rank'] >= 85] self.SCTRuniverse = sctr_df # self.Debug(SCTR) return self.SCTRuniverse def OnData(self, data): for symboldata in self.SCTRuniverse.itertuples(): symbol = symboldata.Index D_rank = symboldata.Decile_rank if not symbol in self.potential: self.potential.append(symbol) for symbol in self.potential: if not data.ContainsKey(symbol): continue self.indicators[symbol] = Entry_Indicator(self, symbol) if self.IsWarmingUp: continue tradeBars = data.Bars symbolTradeBar = tradeBars[symbol] symbolOpen = symbolTradeBar.Open ## Open price symbolClose = symbolTradeBar.Close ## Close price self.slow_stoch = self.indicators[symbol].get_sto() self.sma = self.indicators[symbol].get_sma() def daily_check(self): for symbol in self.potential: if not self.Securities.ContainsKey(symbol): continue if self.IsWarmingUp: continue symbolClose = float(self.Securities[symbol].Close) self.buyquantity = round((self.percentagebuy*self.Portfolio.TotalPortfolioValue)/symbolClose) if symbolClose != 0 else 0 if (not self.Portfolio[symbol].Invested) and (self.Portfolio.MarginRemaining > 0.9*self.percentagebuy*self.Portfolio.TotalPortfolioValue): if ((symbolClose/self.sma)> 0 and self.slow_stoch<=30): self.MarketOrder(symbol, self.buyquantity) self.Log(str(symbol) + " -sto: " + str(self.slow_stoch)) self.Log(str(symbol) + " -sma: " + str(symbolClose/self.sma)) for kvp in self.Portfolio: holdings = kvp.Value symbol = holdings.Symbol if holdings.Invested: if not symbol in self.potential: self.Liquidate(symbol) self.Log(str(symbol) + " : " + str(holdings.Price)) class SymbolData: def __init__(self, algo, symbol): self.symbol = symbol self.EMA200 = algo.EMA(symbol, 200, Resolution.Daily) self.EMA50 = algo.EMA(symbol, 50, Resolution.Daily) self.ROC125 = algo.ROC(symbol, 125, Resolution.Daily) self.ROC20 = algo.ROC(symbol, 20, Resolution.Daily) self.PPO = algo.PPO(symbol, 12, 26, MovingAverageType.Exponential, Resolution.Daily) self.RSI14 = algo.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily) self.PPOWindow = RollingWindow[float](3) history = algo.History(symbol, 200, Resolution.Daily) for index, bar in history.loc[symbol].iterrows(): self.EMA200.Update(index, bar.close) self.EMA50.Update(index, bar.close) self.ROC125.Update(index, bar.close) self.ROC20.Update(index, bar.close) self.PPO.Update(index, bar.close) self.RSI14.Update(index, bar.close) self.PPOWindow.Add(self.PPO.Current.Value) 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 Entry_Indicator: def __init__(self, algo, symbol): self.SMA200 = algo.SMA(symbol, 200, Resolution.Daily) self.Slow_Stoch5 = algo.STO(symbol, 5, Resolution.Daily) history = algo.History(symbol, 200, Resolution.Daily) for index, bar in history.loc[symbol].iterrows(): self.SMA200.Update(index, bar.close) for bar in history.itertuples(): tradeBar = TradeBar(bar.Index[1], bar.Index[0], bar.open, bar.high, bar.low, bar.close, bar.volume, timedelta(1)) self.Slow_Stoch5.Update(tradeBar) def get_sma(self): return self.SMA200.Current.Value def get_sto(self): return self.Slow_Stoch5.Current.Value