Overall Statistics
Total Trades
1026
Average Win
0.21%
Average Loss
-0.18%
Compounding Annual Return
-46.471%
Drawdown
13.500%
Expectancy
-0.035
Net Profit
-5.169%
Sharpe Ratio
-0.345
Probabilistic Sharpe Ratio
33.114%
Loss Rate
56%
Win Rate
44%
Profit-Loss Ratio
1.19
Alpha
-0.518
Beta
1.451
Annual Standard Deviation
0.31
Annual Variance
0.096
Information Ratio
-1.335
Tracking Error
0.293
Treynor Ratio
-0.074
Total Fees
$1289.72
Estimated Strategy Capacity
$8700000.00
Lowest Capacity Asset
CURO WQ6CRD6VV1ID
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] = SymbolData(self, x.Symbol)

    def OnData(self, data):
        
        SCTR = {}
        
        for symboldata in self.Data.values():
            if symboldata.IsReady():
                self.Debug(f'{symboldata.symbol}\'s SCTR = {symboldata.SCTR()} in {self.Time}')
                SCTR[symboldata.symbol] = symboldata.SCTR()
                
        [self.SetHoldings(symbol, sctr/sum(list(SCTR.values()))) for symbol, sctr in SCTR.items()]
        
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