Overall Statistics
Total Trades
131
Average Win
0.07%
Average Loss
-0.03%
Compounding Annual Return
24.460%
Drawdown
0.900%
Expectancy
0.422
Net Profit
0.662%
Sharpe Ratio
2.764
Probabilistic Sharpe Ratio
59.869%
Loss Rate
52%
Win Rate
48%
Profit-Loss Ratio
1.94
Alpha
0.134
Beta
0.124
Annual Standard Deviation
0.074
Annual Variance
0.006
Information Ratio
-4.213
Tracking Error
0.088
Treynor Ratio
1.664
Total Fees
$131.65
Estimated Strategy Capacity
$16000000.00
Lowest Capacity Asset
ARGX WKMH0DXMBVXH
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.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 30), self.daily_check)
        
        self.Data = {}
        self.SCTRuniverse = []
        
        self.percentagebuy = 0.05
        
    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)
        universe =  [x.Symbol for x in sortedByPE if x.MarketCap > 10e9][:500]
        return 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)
        
    def OnData(self, data):
        
        if self.IsWarmingUp: return
            
        SCTR = {}
        
        for symboldata in self.Data.values():
            if symboldata.IsReady():
                SCTR[symboldata.symbol] = symboldata.SCTR()

        self.SCTRuniverse = [pair[0] for pair in sorted(SCTR.items(), key=lambda kv: kv[1], reverse=True)[:int(len(SCTR)*0.15)]]
        
        for symbol in self.SCTRuniverse:
            if not data.ContainsKey(symbol): continue
            
            self.Data[symbol].Update()
            
            # won't make a difference in setting specific time to trade if you use daily resolution
            symbolClose = float(self.Securities[symbol].Close)
            
            if (not self.Securities[symbol].Invested) & (self.Portfolio.MarginRemaining > 0.9*self.percentagebuy*self.Portfolio.TotalPortfolioValue):
                sma = self.Data[symbol].get_sma()
                sto = self.Data[symbol].get_sto()
                
                if (symbolClose > sma) & (sto <= 30):
                    self.SetHoldings(symbol, self.percentagebuy)
                    self.Log(str(symbol) + " -sto: " + str(sto))
                    self.Log(str(symbol) + " -sma: " + str(sma) + " -price: " + str(symbolClose))

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.SMA200 = algo.SMA(symbol, 200, Resolution.Daily)
        self.Slow_Stoch5 = algo.STO(symbol, 5, 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.SMA200.Update(index, bar.close)
            
            self.PPOWindow.Add(self.PPO.Current.Value)
            
        for bar in history.iloc[-5:].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 Update(self):
        self.PPOWindow.Add(self.PPO.Current.Value)
        
    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 and self.PPOWindow.IsReady
                    
    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 get_sma(self):
        return self.SMA200.Current.Value
        
    def get_sto(self):
        return self.Slow_Stoch5.Current.Value