Overall Statistics
Total Trades
92
Average Win
1.11%
Average Loss
-0.89%
Compounding Annual Return
8.772%
Drawdown
16.000%
Expectancy
0.194
Net Profit
6.858%
Sharpe Ratio
0.464
Probabilistic Sharpe Ratio
27.750%
Loss Rate
47%
Win Rate
53%
Profit-Loss Ratio
1.24
Alpha
0.006
Beta
0.367
Annual Standard Deviation
0.158
Annual Variance
0.025
Information Ratio
-0.663
Tracking Error
0.167
Treynor Ratio
0.199
Total Fees
$563.19
Estimated Strategy Capacity
$88000000.00
Lowest Capacity Asset
NFLX SEWJWLJNHZDX
# MACD Alpha Model


class FrameworkAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2021, 1, 1)     
        self.SetEndDate(2021, 10, 15)       
        self.SetCash(1000000)
        
        tickers = ["AAPL", "MSFT", "TSLA", "NVDA", "DIS", "AMD", "NFLX"]
        symbols = [Symbol.Create(t, SecurityType.Equity, Market.USA) for t in tickers]
        self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))  
        
        self.UniverseSettings.Resolution = Resolution.Daily 
        self.AddAlpha(MacdAlphaModel())
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(self.DateRules.Every(DayOfWeek.Monday)))
        self.SetExecution(ImmediateExecutionModel())


class MacdAlphaModel(AlphaModel):

    def __init__(self,
                 fastPeriod = 12,
                 slowPeriod = 26,
                 signalPeriod = 9,
                 movingAverageType = MovingAverageType.Exponential,
                 resolution = Resolution.Daily):

        self.fastPeriod = fastPeriod
        self.slowPeriod = slowPeriod
        self.signalPeriod = signalPeriod
        self.movingAverageType = movingAverageType
        self.resolution = resolution
        self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), fastPeriod)
        self.bounceThresholdPercent = 0.01
        self.symbolData = {}

        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        movingAverageTypeString = Extensions.GetEnumString(movingAverageType, MovingAverageType)
        self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, signalPeriod, movingAverageTypeString, resolutionString)


    def Update(self, algorithm, data):
        insights = []

        for key, sd in self.symbolData.items():
            if sd.Security.Price == 0:
                continue

            direction = InsightDirection.Flat
            normalized_signal = sd.MACD.Signal.Current.Value / sd.Security.Price

            if normalized_signal > self.bounceThresholdPercent:
                direction = InsightDirection.Up
            elif normalized_signal < -self.bounceThresholdPercent:
                direction = InsightDirection.Flat

            if direction == sd.PreviousDirection:
                continue

            insight = Insight.Price(sd.Security.Symbol, self.insightPeriod, direction)
            sd.PreviousDirection = insight.Direction
            insights.append(insight)

        return insights


    def OnSecuritiesChanged(self, algorithm, changes):

        for added in changes.AddedSecurities:
            self.symbolData[added.Symbol] = SymbolData(algorithm, added, self.fastPeriod, self.slowPeriod, self.signalPeriod, self.movingAverageType, self.resolution)

        for removed in changes.RemovedSecurities:
            data = self.symbolData.pop(removed.Symbol, None)
            if data is not None:
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator)


class SymbolData:
    def __init__(self, algorithm, security, fastPeriod, slowPeriod, signalPeriod, movingAverageType, resolution):
        self.Security = security
        self.MACD = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType)

        self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.MACD, self.Consolidator)

        self.PreviousDirection = None