Overall Statistics
Total Trades
395
Average Win
0.11%
Average Loss
-0.11%
Compounding Annual Return
1.744%
Drawdown
3.700%
Expectancy
-0.063
Net Profit
0.185%
Sharpe Ratio
0.172
Probabilistic Sharpe Ratio
38.487%
Loss Rate
53%
Win Rate
47%
Profit-Loss Ratio
0.98
Alpha
0.515
Beta
-1.017
Annual Standard Deviation
0.14
Annual Variance
0.02
Information Ratio
-2.714
Tracking Error
0.169
Treynor Ratio
-0.024
Total Fees
$395.00
class VerticalOptimizedThrustAssembly(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 10, 12)  # Set Start Date
        self.SetEndDate(2019, 11, 19)
        self.SetCash(100000)  # Set Strategy Cash
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction)
        
        self.symbolDataBySymbol = {}
                
    def CoarseSelectionFunction(self, coarse):
        
        sortedCoarse = sorted(coarse, key=lambda c:c.DollarVolume, reverse=True)
        liquidSymbols = [c.Symbol for c in sortedCoarse][:500]
        symbols = [s for s in liquidSymbols if s not in self.symbolDataBySymbol]
        
        history = self.History(symbols, 252, Resolution.Daily)
        
        if not history.empty:
            history = history.close.unstack(0)
            for symbol in symbols:
                if str(symbol) not in history:
                    continue

                df = history[symbol].dropna()
                if not df.empty:
                    self.symbolDataBySymbol[symbol] = SymbolData(self, symbol, df)
        
        for x in coarse:
            symbol = x.Symbol
            if symbol in self.symbolDataBySymbol:
                self.symbolDataBySymbol[symbol].Update(x.EndTime, x.AdjustedPrice)
        
        selectedSymbols = [symbol for symbol, symbolData in self.symbolDataBySymbol.items() if symbolData.HasNewMax]
        
        return selectedSymbols
        
        
    def OnSecuritiesChanged(self, changes):
        
        for security in changes.AddedSecurities:
            symbol = security.Symbol
            self.MarketOrder(symbol, 100)
            
        for security in changes.RemovedSecurities:
            symbol = security.Symbol
            self.Liquidate(symbol)
                    
                    
class SymbolData:
    
    def __init__(self, algorithm, symbol, history):
        self.symbol = symbol
        
        self.max = Maximum(252)
        
        self.maxWindow = RollingWindow[IndicatorDataPoint](2)
        
        self.max.Updated += self.OnMax
        
        for time, close in history.iteritems():
            self.Update(time, close)

        
    def OnMax(self, sender, updated):
        if self.max.IsReady:
            self.maxWindow.Add(updated)
            
    def Update(self, time, close):
        self.max.Update(time, close)
        
    @property
    def HasNewMax(self):
        if self.maxWindow.IsReady:
            return self.maxWindow[0] > self.maxWindow[1]
        else:
            return False