Overall Statistics
Total Trades
10893
Average Win
0.00%
Average Loss
0.00%
Compounding Annual Return
0.561%
Drawdown
11.200%
Expectancy
0.445
Net Profit
0.367%
Sharpe Ratio
0.097
Probabilistic Sharpe Ratio
21.210%
Loss Rate
61%
Win Rate
39%
Profit-Loss Ratio
2.68
Alpha
0.01
Beta
-0.002
Annual Standard Deviation
0.094
Annual Variance
0.009
Information Ratio
-1.848
Tracking Error
0.149
Treynor Ratio
-4.163
Total Fees
$10883.59
Estimated Strategy Capacity
$6000.00
Lowest Capacity Asset
CAND R735QTJ8XC9X
from QuantConnect.Indicators import *

class MomentumEffectAlgorithm(QCAlgorithm):
    def Initialize(self):

        self.SetStartDate(2021, 1, 1) 

        self.month = -1
        self.rebalance = False
        
        self.hull_dict = {}
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        self.SetWarmup(40)

    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.month == self.Time.month:
            return Universe.Unchanged

        self.rebalance = True
        self.month = self.Time.month

        for s in list(filter(lambda x: (x.HasFundamentalData), coarse)):
            if s.Symbol not in self.hull_dict:
                
                history = self.History(s.Symbol, 20, Resolution.Daily)
                self.hull_dict[s.Symbol] = Hull(s.Symbol,history)

            avg = self.hull_dict[s.Symbol]
            avg.update(s.EndTime, s.AdjustedPrice)
            
        selected = []
        
        for x in self.hull_dict.values():
            current = x.hull.Current.Value
            self.Debug("HMA - {symbol} - {hma}".format(symbol=x.symbol.Value,hma=x.hull.Current.Value))
            # Check hull momentum is positive.
            selected.append(x.symbol)

        return selected


    def FineSelectionFunction(self, fine):
        
        self.selected = [x.Symbol for x in fine]

        return self.selected


    def OnData(self, data):
        
        if not self.rebalance:
            return
        
        
        for i in self.selected:
            self.SetHoldings(i, 1/len(self.selected))

        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.hull_dict.pop(symbol,None) is not None:
                self.Liquidate(symbol, 'Removed from universe')
                    
            
class Hull(object):
    def __init__(self, symbol, history):
        self.symbol = symbol
        self.hull = HullMovingAverage(30)
        
        for bar in history.itertuples():
            self.hull.Update(bar.Index[1],bar.close)
            
    def update(self, time, value):
        self.hull.Update(time, value)