Overall Statistics
Total Trades
1724
Average Win
0.56%
Average Loss
-0.36%
Compounding Annual Return
16.897%
Drawdown
35.700%
Expectancy
0.323
Net Profit
163.375%
Sharpe Ratio
0.672
Probabilistic Sharpe Ratio
13.535%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.56
Alpha
0.072
Beta
0.596
Annual Standard Deviation
0.204
Annual Variance
0.042
Information Ratio
0.146
Tracking Error
0.193
Treynor Ratio
0.23
Total Fees
$2297.75
Estimated Strategy Capacity
$29000000.00
Lowest Capacity Asset
QCOM R735QTJ8XC9X
#region imports
from AlgorithmImports import *
#endregion
#region imports
from QuantConnect import Resolution, Extensions
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from itertools import groupby
from datetime import datetime, timedelta
from pytz import utc
UTCMIN = datetime.min.replace(tzinfo=utc)
#endregion
class EqualWeightingPortfolio(PortfolioConstructionModel):


    def __init__(self, initialAllocationPerSecurity = 0.1):
        

        # portfolio exposure per security (as a % of total equity)
        self.initialAllocationPerSecurity = initialAllocationPerSecurity
        
        self.insightCollection = InsightCollection()
        self.removedSymbols = []
        
        self.nextRebalance = None


    def CreateTargets(self, algorithm, insights):

        targets = []

          
        if len(insights) == 0 and len([x.Symbol.Value for x in algorithm.Portfolio.Values if x.Invested]) != 0:
            return targets

        # here we get the new insights and add them to our insight collection
        for insight in insights:
            self.insightCollection.Add(insight)
            
        # create flatten target for each security that was removed from the universe
        if len(self.removedSymbols) > 0:
            universeDeselectionTargets = [ PortfolioTarget(symbol, 0) for symbol in self.removedSymbols ]
            targets.extend(universeDeselectionTargets)
            algorithm.Log('(Portfolio module) liquidating: ' + str([x.Value for x in self.removedSymbols]) + ' if they are active, due to not being in the universe')
            self.removedSymbols = []

        expiredInsights = self.insightCollection.RemoveExpiredInsights(algorithm.UtcTime)

        expiredTargetsLog = []
        expiredTargets = []
        for symbol, f in groupby(expiredInsights, lambda x: x.Symbol):
            if not self.insightCollection.HasActiveInsights(symbol, algorithm.UtcTime):
                expiredTargets.append(PortfolioTarget(symbol, 0))
                expiredTargetsLog.append(symbol)
                continue
        
        algorithm.Log(f'(Portfolio module) sold {expiredTargetsLog} due to insight being expired')
        targets.extend(expiredTargets)

        # get insight that have not expired of each symbol that is still in the universe
        activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime)

        # get the last generated active insight for each symbol
        lastActiveInsights = []
        for symbol, g in groupby(activeInsights, lambda x: x.Symbol):
            lastActiveInsights.append(sorted(g, key = lambda x: x.GeneratedTimeUtc)[-1])
        
        # determine target percent for the given insights
        boughtTargetsLog = []
        for insight in lastActiveInsights:
            allocationPercent = self.initialAllocationPerSecurity * insight.Direction
            target = PortfolioTarget.Percent(algorithm, insight.Symbol, allocationPercent)
            boughtTargetsLog.append(insight.Symbol)
            targets.append(target)
        
        algorithm.Log(f'(Portfolio module) Bought {boughtTargetsLog} stocks, that expires at {Expiry.EndOfMonth}')

        return targets

        
    def OnSecuritiesChanged(self, algorithm, changes):
        
        
        newRemovedSymbols = [x.Symbol for x in changes.RemovedSecurities if x.Symbol not in self.removedSymbols]
        
        # get removed symbol and invalidate them in the insight collection
        self.removedSymbols.extend(newRemovedSymbols)
        self.insightCollection.Clear(self.removedSymbols)
            
        removedList = [x.Value for x in self.removedSymbols]
        algorithm.Log('(Portfolio module) securities removed from Universe: ' + str(removedList))
#region imports
from AlgorithmImports import *
#endregion


class MarketOrderModel(ExecutionModel):
    '''Provides an implementation of IExecutionModel that immediately submits market orders to achieve the desired portfolio targets'''

    def __init__(self):
        '''Initializes a new instance of the ImmediateExecutionModel class'''
        self.targetsCollection = PortfolioTargetCollection()

    def Execute(self, algorithm, targets):

        # for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call
        self.targetsCollection.AddRange(targets)
        if self.targetsCollection.Count > 0:
            for target in self.targetsCollection.OrderByMarginImpact(algorithm):
                security = algorithm.Securities[target.Symbol]
                # calculate remaining quantity to be ordered
                quantity = OrderSizing.GetUnorderedQuantity(algorithm, target, security)
                if quantity != 0:
                    aboveMinimumPortfolio = BuyingPowerModelExtensions.AboveMinimumOrderMarginPortfolioPercentage(security.BuyingPowerModel, security, quantity, algorithm.Portfolio, algorithm.Settings.MinimumOrderMarginPortfolioPercentage)
                    if aboveMinimumPortfolio:
                        algorithm.MarketOrder(security, quantity)

                    if quantity > 0:
                        algorithm.Plot('EMA value and SPY value', 'Buy', 1)
                    if quantity < 0:
                        algorithm.Plot('EMA value and SPY value', 'Sell', 1)

            self.targetsCollection.ClearFulfilled(algorithm)
#region imports
from AlgorithmImports import *
#endregion
class MomentumAlphaModel(AlphaModel):
    def __init__(self, lookback, resolution):
        self.lookback = lookback
        self.resolution = resolution
        self.predictionInterval = Expiry.EndOfMonth
        self.symbolDataBySymbol = {}
        
        self.num_insights = 10
        self.lastMonth = -1

    def Update(self, algorithm, data):
        
        for symbol, symbolData in self.symbolDataBySymbol.items():
            if not algorithm.IsMarketOpen(symbol):
                return []

        if algorithm.Time.month == self.lastMonth:    
            return []
        self.lastMonth = algorithm.Time.month

        insights = []

        for symbol, symbolData in self.symbolDataBySymbol.items():

            if symbolData.CanEmit:
                direction = InsightDirection.Up
                magnitude = symbolData.Return

                insights.append(Insight.Price(symbol, self.predictionInterval, direction, magnitude = magnitude))

        insights1 = sorted([x for x in insights], key = lambda x: x.Magnitude, reverse=True)

        algorithm.Log(f'(Alpha module) Sent {len([x for x in insights1[:self.num_insights]])} insights to the pcm module')

        return [x for x in insights1[:self.num_insights]]

    def OnSecuritiesChanged(self, algorithm, changes):
        
        # clean up data for removed securities
        for removed in changes.RemovedSecurities:
            symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None)
            if symbolData is not None:
                symbolData.RemoveConsolidators(algorithm)

        # initialize data for added securities
        symbols = [ x.Symbol for x in changes.AddedSecurities ]
        history = algorithm.History(symbols, self.lookback, self.resolution)
        if history.empty: return

        tickers = history.index.levels[0]
        for ticker in tickers:
            symbol = SymbolCache.GetSymbol(ticker)
            
            if symbol == "SPY":
                return

            if symbol not in self.symbolDataBySymbol:
                symbolData = SymbolData(symbol, self.lookback)
                self.symbolDataBySymbol[symbol] = symbolData
                symbolData.RegisterIndicators(algorithm, self.resolution)
                symbolData.WarmUpIndicators(history.loc[ticker])


class SymbolData:

    def __init__(self, symbol, lookback):
        
        self.Symbol = symbol
        self.ROC = RateOfChange('{}.ROC({})'.format(symbol, lookback), lookback)
        self.Consolidator = None
        self.previous = 0

    def RegisterIndicators(self, algorithm, resolution):
        self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution)
        algorithm.RegisterIndicator(self.Symbol, self.ROC, self.Consolidator)

    def RemoveConsolidators(self, algorithm):
        if self.Consolidator is not None:
            algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.Consolidator)

    def WarmUpIndicators(self, history):
        for tuple in history.itertuples():
            self.ROC.Update(tuple.Index, tuple.close)

    @property
    def Return(self):
        return float(self.ROC.Current.Value)

    @property
    def CanEmit(self):
        if self.previous == self.ROC.Samples:
            return False

        self.previous = self.ROC.Samples
        return self.ROC.IsReady

    def __str__(self, **kwargs):
        return '{}: {:.2%}'.format(self.ROC.Name, (1 + self.Return)**252 - 1)

#region imports
from AlgorithmImports import *
from datetime import timedelta, time, datetime
#endregion
class RiskModelWithSpy(RiskManagementModel):
    
    def __init__(self, algorithm, spy, lookback,  resolution):
        self.spy = spy
        
        self.lookback = lookback
        self.resolution = resolution
        
        self.symboldata = {}

        self.lastDay = -1
        
        #Flag so we only instanciate it once
        self.symboldata[self.spy.Symbol] = EMASymbolData(algorithm, self.spy, self.lookback, self.resolution)
        
    def ManageRisk(self, algorithm, targets):
        
        targets = []
        
        for symbol, symboldata in self.symboldata.items():
            #logic. If price is below the current value for EMA, we send a portfoliotarget of 0
            spyValue = self.spy.Price
            AlmaValue = symboldata.EMA.Current.Value
            
            for kvp in algorithm.Securities:
                security = kvp.Value
                
                if spyValue <= AlmaValue:
                    targets.append(PortfolioTarget(security.Symbol, 0))

        
        if len(targets) >= 1:
            algorithm.Log('(Risk module) sold the entire portfolio due to SPY being below EMA')
        
        self.PlotCharts(algorithm, symboldata.EMA.Current.Value)

        return targets



    def PlotCharts(self, algorithm, EMAValue):
        if algorithm.Time.day != self.lastDay:
            algorithm.Plot('EMA value and SPY value', 'EMA value', EMAValue)
            algorithm.Plot('EMA value and SPY value', 'SPY value', self.spy.Price)
            self.lastDay = algorithm.Time.day

            
     
class EMASymbolData:
    
    def __init__(self, algorithm, security, lookback, resolution):
        symbol = security.Symbol
        self.Security = symbol
        self.Consolidator = algorithm.ResolveConsolidator(symbol, resolution)
        
        smaName = algorithm.CreateIndicatorName(symbol, f"SMA{lookback}", resolution)
        self.EMA = ExponentialMovingAverage(smaName, lookback)
        algorithm.RegisterIndicator(symbol, self.EMA, self.Consolidator)
        
        history = algorithm.History(symbol, lookback, resolution)
        if 'close' in history:
            history = history.close.unstack(0).squeeze()
            for time, value in history.iteritems():
                self.EMA.Update(time, value)
from AlgorithmImports import *
from datetime import timedelta, time, datetime
from MomentumAlphaModel import MomentumAlphaModel
from EqualWeightingPortfolio import EqualWeightingPortfolio
from RiskModelWithSpy import RiskModelWithSpy
from MarketOrderExecution import MarketOrderModel
from System.Drawing import Color

class MomentumFrameworkAlgo(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2016, 6, 1)
        self.SetCash(100000)  # Set Strategy Cash
        self.UniverseSettings.Resolution = Resolution.Hour
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        seeder = FuncSecuritySeeder(self.GetLastKnownPrices)
        self.SetSecurityInitializer(lambda security: seeder.SeedSecurity(security))

        self.SetWarmup(timedelta(360))
        self.SetBenchmark('SPY')
        
        self.spy = self.AddEquity('SPY', Resolution.Hour)
        
        self.AddUniverse(self.CoarseUniverse)
        self.AddAlpha(MomentumAlphaModel(lookback=203, resolution=Resolution.Daily)) 
        pcm = EqualWeightingPortfolio()
        self.SetPortfolioConstruction(pcm)
        self.SetExecution(MarketOrderModel())
        self.AddRiskManagement(RiskModelWithSpy(self, self.spy, 200, Resolution.Daily))
        
        self.num_coarse = 45
        self.lastMonth = -1


        EMAplot = Chart('EMA value and SPY value')
        EMAplot.AddSeries(Series('EMA value', SeriesType.Line, '$', Color.Blue))
        EMAplot.AddSeries(Series('SPY value', SeriesType.Line, '$', Color.White))
        EMAplot.AddSeries(Series('Buy', SeriesType.Scatter, '$', Color.Green, ScatterMarkerSymbol.Triangle))
        EMAplot.AddSeries(Series('Sell', SeriesType.Scatter, '$', Color.Red, ScatterMarkerSymbol.TriangleDown))
        self.AddChart(EMAplot)


    def CoarseUniverse(self, coarse):
        if self.Time.month == self.lastMonth: 
            return Universe.Unchanged
        self.lastMonth = self.Time.month
        
        selected = sorted([x for x in coarse if x.Price > 10 and x.Price < 5000 and x.HasFundamentalData], key = lambda x: x.DollarVolume, reverse=True)
        
        self.Log(f'(Universe module)Sent {len([x.Symbol for x in selected[:self.num_coarse]])} symbols to the alpha module')

        return [x.Symbol for x in selected[:self.num_coarse]]

    def OnEndOfDay(self):
        self.Plot("Positions", "Num", len([x.Symbol for x in self.Portfolio.Values if self.Portfolio[x.Symbol].Invested]))
        self.Plot(f"Margin", "Used", self.Portfolio.TotalMarginUsed)
        self.Plot(f"Margin", "Remaning", self.Portfolio.MarginRemaining)
        self.Plot(f"Cash", "Remaining", self.Portfolio.Cash)