Overall Statistics
Total Trades
59
Average Win
0.86%
Average Loss
-1.27%
Compounding Annual Return
25.120%
Drawdown
7.500%
Expectancy
0.227
Net Profit
15.524%
Sharpe Ratio
1.414
Probabilistic Sharpe Ratio
63.898%
Loss Rate
27%
Win Rate
73%
Profit-Loss Ratio
0.68
Alpha
0.2
Beta
-0.043
Annual Standard Deviation
0.136
Annual Variance
0.018
Information Ratio
0.046
Tracking Error
0.177
Treynor Ratio
-4.515
Total Fees
$60.04
# https://quantpedia.com/Screener/Details/14
class MomentumEffectAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2019, 7, 1)  # Set Start Date
        self.SetEndDate(2020, 7, 1)    # Set Start Date       
        self.SetCash(100000)           # Set Strategy Cash

        self.UniverseSettings.Resolution = Resolution.Daily

        self.mom = {}           # Dict of Momentum indicator keyed by Symbol
        self.lookback = 40     # Momentum indicator lookback period
        self.num_coarse = 100   # Number of symbols selected at Coarse Selection
        self.num_fine = 50      # Number of symbols selected at Fine Selection
        self.num_long = 10       # Number of symbols with open positions
        
        self.month = -1
        self.rebalance = False

        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)


    def CoarseSelectionFunction(self, coarse):
        '''Drop securities which have no fundamental data or have too low prices.
        Select those with highest by dollar volume'''

        if self.Time.month < self.month or (self.Time.month >= 11 and self.month <= 2):
            return Universe.Unchanged

           
        self.rebalance = True
        
    
        return [x.Symbol for x in coarse if x.HasFundamentalData and x.Price > 2]
    
        


    def FineSelectionFunction(self, fine):
        
        self.month = (self.Time.month + 2) % 12
    
        filteredfine = [f for f in fine if f.CompanyProfile.MarketCap > 1000000000]
    
        selected = sorted(filteredfine, key=lambda f: f.ValuationRatios.EarningYield + 
                        f.OperationRatios.ROIC.OneYear +
                        (f.FinancialStatements.BalanceSheet.TangibleBookValue.OneMonth / f.Price) +
                        f.OperationRatios.AVG5YrsROIC.FiveYears
                        ,reverse=True)
    
        return [x.Symbol for x in selected][:100]


    def OnData(self, data):

        # Update the indicator
        for symbol, mom in self.mom.items():
            mom.Update(self.Time, self.Securities[symbol].Close)

        if not self.rebalance:
            return

        # Selects the securities with highest momentum
        sorted_mom = sorted([k for k,v in self.mom.items() if v.IsReady],
            key=lambda x: self.mom[x].Current.Value, reverse=True)
        selected = sorted_mom[:self.num_long]

        # Liquidate securities that are not in the list
        for symbol, mom in self.mom.items():
            if symbol not in selected:
                self.Liquidate(symbol, 'Not selected')

        # Buy selected securities
        for symbol in selected:
            self.SetHoldings(symbol, 1/self.num_long)

        self.rebalance = False


    def OnSecuritiesChanged(self, changes):

        # Clean up data for removed securities and Liquidate
        for security in changes.RemovedSecurities:
            symbol = security.Symbol
            if self.mom.pop(symbol, None) is not None:
                self.Liquidate(symbol, 'Removed from universe')

        for security in changes.AddedSecurities:
            if security.Symbol not in self.mom:
                self.mom[security.Symbol] = Momentum(self.lookback)

        # Warm up the indicator with history price if it is not ready
        addedSymbols = [k for k,v in self.mom.items() if not v.IsReady]

        history = self.History(addedSymbols, 1 + self.lookback, Resolution.Daily)
        history = history.close.unstack(level=0)

        for symbol in addedSymbols:
            ticker = str(symbol)
            if ticker in history:
                for time, value in history[ticker].items():
                    item = IndicatorDataPoint(symbol, time, value)
                    self.mom[symbol].Update(item)