Overall Statistics
Total Trades
279
Average Win
2.38%
Average Loss
-0.94%
Compounding Annual Return
113.862%
Drawdown
29.900%
Expectancy
0.823
Net Profit
128.601%
Sharpe Ratio
1.536
Probabilistic Sharpe Ratio
51.108%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
2.52
Alpha
1.196
Beta
-0.166
Annual Standard Deviation
0.763
Annual Variance
0.582
Information Ratio
1.325
Tracking Error
0.774
Treynor Ratio
-7.055
Total Fees
$437.02
Estimated Strategy Capacity
$670000000.00
Lowest Capacity Asset
NVDA RHM8UTD8DT2D
from datetime import timedelta

class EMAMomentumUniverse(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2021, 1, 1)
        self.SetEndDate(2022, 2, 1)
        self.SetCash(100000)
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction)
        self.averages = { }
    
    def CoarseSelectionFunction(self, _input):  
        selected = []
        universe = sorted(_input, key=lambda c: c.DollarVolume, reverse=True)  
        universe = [c for c in universe if c.Price > 10][:100]

        for coarse in universe:  
            symbol = coarse.Symbol
            
            if symbol not in self.averages:
                self.averages[symbol] = SelectionData(self)
                
            history = self.History(symbol, 1, Resolution.Daily)
            self.averages[symbol].update(history)
            
            if self.averages[symbol].is_ready():
                selected.append(symbol)
        
        self.Debug(f"Selected: [{','.join(map(str, selected[:5]))}]")
        return selected[:5]
        
    def OnSecuritiesChanged(self, changes):
        for security in changes.RemovedSecurities:
            self.Liquidate(security.Symbol)
       
        for security in changes.AddedSecurities:
            self.SetHoldings(security.Symbol, 0.20)
            
class SelectionData():
    def __init__(self, algo):
        self.obv = OnBalanceVolume()
        self.algo = algo

    def is_ready(self):
        return self.obv.IsReady
    
    def update(self, history):
        for bar in history.itertuples():
            self.algo.Debug(f"Bar: {bar}")
            try:
                tradeBar = TradeBar(bar.Index[1] - timedelta(days=1), bar.Index[0], bar.open, bar.high, bar.low, bar.close, bar.volume, timedelta(days=1))
                self.obv.Update(tradeBar)
            except AttributeError:
                self.algo.Debug("No volume!")