Overall Statistics
Total Trades
9870
Average Win
0.00%
Average Loss
0.00%
Compounding Annual Return
-7.827%
Drawdown
9.400%
Expectancy
-0.722
Net Profit
-9.418%
Sharpe Ratio
-5.419
Probabilistic Sharpe Ratio
0.000%
Loss Rate
87%
Win Rate
13%
Profit-Loss Ratio
1.12
Alpha
-0.064
Beta
-0.004
Annual Standard Deviation
0.012
Annual Variance
0
Information Ratio
-1.271
Tracking Error
0.136
Treynor Ratio
14.372
Total Fees
$9870.00
Estimated Strategy Capacity
$390000000.00
Lowest Capacity Asset
CSIQ TNII135YAI5H
pass
class MyDonchianChannelAlphaModel(QCAlgorithm):
    
    def __init__(self, upperBand = 55, lowerBand = 55, resolution = Resolution.Daily):
        self.upperBand = upperBand
        self.lowerBand = lowerBand
        self.resolution = resolution
        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(resolution), upperBand)
        self.symbolDataBySymbol = {}
        
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{})'.format(self.__class__.__name__, upperBand, lowerBand, resolutionString)

    def Update(self, algorithm, data):
        insights = []
        
        for symbol, symbolData in self.symbolDataBySymbol.items():
            if symbolData.donchian.IsReady:
                if symbolData.PriceOverUpper: 
                    if symbolData.Close > symbolData.donchian.UpperBand.Current.Value:
                        insights.append(Insight.Price(symbolData.Symbol, self.predictionInterval, InsightDirection.Up))
                    
                elif symbolData.PriceUnderLower:
                    if symbolData.Close < symbolData.donchian.LowerBand.Current.Value:
                        insights.append(Insight.Price(symbolData.symbol, self.predictionInterval, InsightDirection.Down))
                        
            symbolData.PriceOverUpper = symbolData.Close > symbolData.donchian.UpperBand.Current.Value
            symbolData.PriceUnderLower = symbolData.Close < symbolData.donchian.LowerBand.Current.Value
            
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            symbolData = self.symbolDataBySymbol.get(added.Symbol)
            if symbolData is None:
                symbolData = self.SymbolData(added)

                symbolData.donchian = algorithm.DCH(added.Symbol, self.upperBand, self.lowerBand, self.resolution)
                self.SetWarmup(55)
                self.symbolDataBySymbol[added.Symbol] = symbolData
            else:
                symbolData.donchian.Reset()
                
    class SymbolData:
        def __init__(self, security):
            self.Bars = None
            self.Security = security
            self.Symbol = security.Symbol
            self.Close = None
            self.donchian = None
            
            self.PriceOverUpper = False
            self.PriceUnderLower = False
            
            #rolling windows!!
pass
class Donchian3(AlphaModel):
    def __init__(self, upperBand = 55, lowerBand = 55, resolution = Resolution.Daily):
        self.upperBand = upperBand
        self.lowerBand = lowerBand
        self.resolution = resolution
        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(resolution), upperBand)
        self.symbolDataBySymbol = {}
        self.closeWindow = {}
        self.donchianWindow = {}
        
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{})'.format(self.__class__.__name__, upperBand, lowerBand, resolutionString)

def Update(self, algorithm, data):
    # Updates this alpha model with the latest data from the algorithm.
    # This is called each time the algorithm receives data for subscribed securities
    # Generate insights on the securities in the universe.
    insights = []
    for symbol, symbolData in self.symbolDataBySymbol.items():
        if symbolData.closeWindow[0] > symbolData.donchianWindow.UpperBand[1]:
            insights.append(Insight.Price(symbolData.Symbol, self.predicationInterval, InsightDirection.Up))
            
        elif symbolData.closeWindow[0] < symbolData.donchianWindow.LowerBand[1]:
            insights.append(Insight.Price(symbolData.Symbol, self.predicationInterval, InsightDirection.Down))

    return insights

def OnSecuritiesChanged(self, algorithm, changes):
    # Handle security changes in from your universe model.
    
    for added in changes.Securities:
        symbolData = self.symbolDataBySymbol.get(added.Symbol)
        if symbolData is none:
            symbolData = SymbolData(added)
            self.closeWindow.Add(data[added.Symbol])
            self.symbolDataBySymbol[added.Symbol] = symbolData
                
        else:
            symbolData.donchian.Reset()
            

class SymbolData:
    
    def __init__(self, algorithm, security):
        self.algorithm = algorithm
        self.Security = security
        self.Symbol = security.Symbol
        self.donchian = algorithm.DCH(added.Symbol, self.upperBand, self.lowerBand, self.resolution)
        self.donchianWindow = RollingWindow[IndicatorDataPoint](2)
        self.donchian.Updated += self.OnDonchianUpdated
        self.consolidator = TradeBarConsolidator(resolution.Daily)
        #self.closeWindow = RollingWindow[TradeBar](2)
        #self.Consolidate(self.symbol, Resolution.Daily, lambda x: self.closewindow.Add(x))
        #self.closeWindow.Add(Data[self.symbol])
        #init tradebars here for securities also
        
        
    def OnDonchianUpdated(self, sender, updated):
        if self.donchian.IsReady:
            self.donchianWindow.Add(updated)
            
    #def OnCloseWindowUpdated(self, sender, updated):
        #if self.closeWindow.IsReady:
           #self.closeWindow.Add(updated)
            
    @property
    def IsReady(self):
        return self.donchian.IsReady and self.donchianWindow.IsReady
        
    
pass
class DrawdownStops(RiskManagementModel):
    '''Provides an implementation of IRiskManagementModel that limits the drawdown per holding to the specified percentage'''

    def __init__(self, maximumDrawdownPercent = 0.05):
        '''Initializes a new instance of the MaximumDrawdownPercentPerSecurity class
        Args:
            maximumDrawdownPercent: The maximum percentage drawdown allowed for any single security holding'''
        self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)

    def ManageRisk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance
            targets: The current portfolio targets to be assessed for risk'''
        targets = []
        for kvp in algorithm.Securities:
            security = kvp.Value

            if not security.Invested:
                continue

            pnl = security.Holdings.UnrealizedProfitPercent
            if pnl < self.maximumDrawdownPercent:
                # liquidate
                targets.append(PortfolioTarget(security.Symbol, 0))

        return targets
pass
class LiquidUniverseSelection(QCAlgorithm):
    
    def __init__(self, algorithm):
        self.algorithm = algorithm
        self.securities = []
       
    def SelectCoarse(self, coarse):
        # sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True)
        coarseSelection = [x for x in coarse if x.HasFundamentalData and x.DollarVolume > 5000000]
        
        universe = [x.Symbol for x in coarseSelection]
        #self.algorithm.Securities = universe
        #self.Log(universe)
        return universe

    #def OnData(self, data):
       #if self._changes is None: return
    
        #for security in self._changes.RemovedSecurities:
            #if security.Invested:
                #self.securities.remove(security.Symbol)
                
        #for security in self._changes.AddedSecurities:
            #pass
        
        #self._changed = None
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            self.securities.append(added)
            
        for removed in changes.RemovedSecurities:
            if removed in self.securities:
                self.securities.remove(removed)
                
        for invested in self.securities.Invested:
            self.securities.remove(invested)
        
        
        #self.Log(f"OnSecuritiesChanged({self.UtcTime}):: {changes}")
        
class InsightWeightingPortfolioConstructionModel(EqualWeightingPortfolioConstructionModel):
    '''Provides an implementation of IPortfolioConstructionModel that generates percent targets based on the
    Insight.Weight. The target percent holdings of each Symbol is given by the Insight.Weight from the last
    active Insight for that symbol.
    For insights of direction InsightDirection.Up, long targets are returned and for insights of direction
    InsightDirection.Down, short targets are returned.
    If the sum of all the last active Insight per symbol is bigger than 1, it will factor down each target
    percent holdings proportionally so the sum is 1.
    It will ignore Insight that have no Insight.Weight value.'''

    def __init__(self, rebalance = Resolution.Daily, portfolioBias = PortfolioBias.LongShort):
        '''Initialize a new instance of InsightWeightingPortfolioConstructionModel
        Args:
            rebalance: Rebalancing parameter. If it is a timedelta, date rules or Resolution, it will be converted into a function.
                              If None will be ignored.
                              The function returns the next expected rebalance time for a given algorithm UTC DateTime.
                              The function returns null if unknown, in which case the function will be called again in the
                              next loop. Returning current time will trigger rebalance.
            portfolioBias: Specifies the bias of the portfolio (Short, Long/Short, Long)'''
        self.rebalance = rebalance
        self.portfolioBias = portfolioBias

    def ShouldCreateTargetForInsight(self, insight):
        '''Method that will determine if the portfolio construction model should create a
        target for this insight
        Args:
            insight: The insight to create a target for'''
        # Ignore insights that don't have Weight value
        return insight.Weight is not None

    def DetermineTargetPercent(self, activeInsights):
        '''Will determine the target percent for each insight
        Args:
            activeInsights: The active insights to generate a target for'''
        result = {}

        # We will adjust weights proportionally in case the sum is > 1 so it sums to 1.
        weightSums = sum(self.GetValue(insight) for insight in activeInsights if self.RespectPortfolioBias(insight))
        weightFactor = 1.0
        if weightSums > 1:
            weightFactor = 1 / weightSums
        for insight in activeInsights:
            result[insight] = (insight.Direction if self.RespectPortfolioBias(insight) else InsightDirection.Flat) * self.GetValue(insight) * weightFactor
        return result

    def GetValue(self, insight):
        '''Method that will determine which member will be used to compute the weights and gets its value
        Args:
            insight: The insight to create a target for
        Returns:
            The value of the selected insight member'''
        return insight.Weight
pass
class DonchianChannelAlpha(AlphaModel):
    def __init__(self, upperBand = 55, lowerBand = 55, resolution = Resolution.Daily):
        self.upperBand = upperBand
        self.lowerBand = lowerBand
        self.resolution = resolution
        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(resolution), upperBand)
        self.symbolDataBySymbol = {}
        self.closeWindow = None
        
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{})'.format(self.__class__.__name__, upperBand, lowerBand, resolutionString)

    def Update(self, algorithm, data):
        insights = []
        for symbol, symbolData in self.symbolDataBySymbol.items():
            #this is all wrong, you fugged up monkey, its backwars XDDDDD
            previousClose = symbolData.closeWindow[1]
            if previousClose > symbolData.donchian.UpperBand.Current.Value:
                insights.append(Insight.Price(symbolData.Symbol, self, predictionInterval, InsightDirection.Up))
                    
            elif previousClose < symbolData.donchian.LowerBand.Current.Value:
                insights.append(Insight.Price(symbolData.Symbol, self, predictionInterval, InsightDirection.Down))
                    
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            symbolData = self.symbolDataBySymbol.get(added.Symbol)
            if symbolData is None:
                self.donchian = algorithm.DCH(added.Symbol, self.upperBand, self.lowerBand, self.resolution)
                self.donchianWindow = RollingWindow[IndicatorDataPoint](2)
                self.closeWindow = RollingWindow[float](2)
                self.consolidator = TradeBarConsolidator(2)
                self.consolidator.DataConsolidated += self.CloseUpdated
                algorithm.SubscriptionManager.AddConsolidator(Symbol, self.consolidator)
                symbolData = SymbolData(added)
                self.symbolDataBySymbol[added.Symbol] = symbolData
    
            else:
                symbolData.donchian.Reset()
                symbolData.Close.Reset()
                
    def DonchianUpdated(self, sender, updated):
        if self.donchian.IsReady:
            self.donchianWindow.Add(updated)
            
    def CloseUpdated(self, sender, bar):
        self.closeWindow.Add(bar.Close)
        
    @property
    def IsReady(self):
        return self.donchian.IsReady and self.closeWindowIsReady
                
class SymbolData:
    def __init__(self, security):
        self.Security = security
        self.Symbol = security.Symbol
        self.algorithm = algorithm
        self.donchian = algorithm.DCH(symbol,)
        self.donchianWindow = None
        self.closeWindow = None
        self.consolidator = None
        

        
        
        
        
        
        
        
  
class MaximumSectorExposureRiskManagementModel(RiskManagementModel):
    '''Provides an implementation of IRiskManagementModel that that limits the sector exposure to the specified percentage'''

    def __init__(self, maximumSectorExposure = 0.20):
        '''Initializes a new instance of the MaximumSectorExposureRiskManagementModel class
        Args:
            maximumDrawdownPercent: The maximum exposure for any sector, defaults to 20% sector exposure.'''
        if maximumSectorExposure <= 0:
            raise ValueError('MaximumSectorExposureRiskManagementModel: the maximum sector exposure cannot be a non-positive value.')

        self.maximumSectorExposure = maximumSectorExposure
        self.targetsCollection = PortfolioTargetCollection()

    def ManageRisk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance'''
        maximumSectorExposureValue = float(algorithm.Portfolio.TotalPortfolioValue) * self.maximumSectorExposure

        self.targetsCollection.AddRange(targets)

        risk_targets = list()

        # Group the securities by their sector
        filtered = list(filter(lambda x: x.Value.Fundamentals is not None and x.Value.Fundamentals.HasFundamentalData, algorithm.UniverseManager.ActiveSecurities))
        filtered.sort(key = lambda x: x.Value.Fundamentals.CompanyReference.IndustryTemplateCode)
        groupBySector = groupby(filtered, lambda x: x.Value.Fundamentals.CompanyReference.IndustryTemplateCode)

        for code, securities in groupBySector:
            # Compute the sector absolute holdings value
            # If the construction model has created a target, we consider that
            # value to calculate the security absolute holding value
            quantities = {}
            sectorAbsoluteHoldingsValue = 0

            for security in securities:
                symbol = security.Value.Symbol
                quantities[symbol] = security.Value.Holdings.Quantity
                absoluteHoldingsValue = security.Value.Holdings.AbsoluteHoldingsValue

                if self.targetsCollection.ContainsKey(symbol):
                    quantities[symbol] = self.targetsCollection[symbol].Quantity

                    absoluteHoldingsValue = (security.Value.Price * abs(quantities[symbol]) *
                        security.Value.SymbolProperties.ContractMultiplier *
                        security.Value.QuoteCurrency.ConversionRate)

                sectorAbsoluteHoldingsValue += absoluteHoldingsValue

            # If the ratio between the sector absolute holdings value and the maximum sector exposure value
            # exceeds the unity, it means we need to reduce each security of that sector by that ratio
            # Otherwise, it means that the sector exposure is below the maximum and there is nothing to do.
            ratio = float(sectorAbsoluteHoldingsValue) / maximumSectorExposureValue

            if ratio > 1:
                for symbol, quantity in quantities.items():
                    if quantity != 0:
                        risk_targets.append(PortfolioTarget(symbol, float(quantity) / ratio))

        return risk_targets

    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''
        anyFundamentalData = any([
            kvp.Value.Fundamentals is not None and 
            kvp.Value.Fundamentals.HasFundamentalData for kvp in algorithm.ActiveSecurities
            ])

        if not anyFundamentalData:
            raise Exception("MaximumSectorExposureRiskManagementModel.OnSecuritiesChanged: Please select a portfolio selection model that selects securities with fundamental data.")
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from universe import LiquidUniverseSelection
from Alpha8 import Donchian8
from Risk.CompositeRiskManagementModel import CompositeRiskManagementModel
from RiskMaximumDrawdown import DrawdownStops
from RiskSectorExposure import MaximumSectorExposureRiskManagementModel
from RiskTrailingStop import TrailingStopRiskManagementModel
from PortfolioConstruction import InsightWeightingPortfolioConstructionModel

class UpgradedFluorescentYellowBat(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018, 12, 12)
        self.SetEndDate(2021, 1, 1)
        
        self.SetCash(100000)
        self.Settings.FreePortfolioValuePercentage = 0.50
        #self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        #self.SetWarmup(60)
        
        self.SetBenchmark("SPY")
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.CustomUniverseSelectionModel = LiquidUniverseSelection(self)
        self.AddUniverse(self.CustomUniverseSelectionModel.SelectCoarse)
        
        self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel())
        self.Settings.RebalancePortfolioOnInsightChanges = False;
        self.Settings.RebalancePortfolioOnSecurityChanges = False;
        
        self.SetExecution(ImmediateExecutionModel())
        self.SetAlpha(Donchian8())
        
        self.SetRiskManagement(TrailingStopRiskManagementModel())
class Donchian8(AlphaModel):
    
    def __init__(self, lowerBand = 55, upperBand = 55, emaPeriod = 100, momPeriod = 21, resolution = Resolution.Daily):
        self.lowerBand = lowerBand
        self.upperBand = upperBand
        self.resolution = resolution
        self.emaPeriod = emaPeriod
        self.momPeriod = momPeriod
        
        self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), upperBand)
        self.symbolData = {}

        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, lowerBand, upperBand, emaPeriod, momPeriod, resolutionString)
        

    def Update(self, algorithm, data):
        
        insights = []
        for key, sd in self.symbolData.items():
            if sd.donchian.IsReady and \
                sd.donchianWindow.IsReady and \
                sd._donchian["UpperBand"].IsReady and \
                sd._donchian["LowerBand"].IsReady and \
                sd.ema.IsReady and \
                sd.mom.IsReady and\
                sd.momWindow.IsReady:
                
                if sd._donchian["UpperBand"][1] < sd.Security.Close and \
                    sd.Security.Close > sd.ema.Current.Value and \
                    sd.momWindow[1] < sd.momWindow[0]:
                        
                    insights.append(Insight.Price(sd.Security.Symbol, self.insightPeriod, InsightDirection.Up,None, None, None, 0.01))
                
                if sd._donchian["LowerBand"][1] > sd.Security.Close and \
                    sd.Security.Close < sd.ema.Current.Value and \
                    sd.momWindow[1] > sd.momWindow[0]:
                    
                    insights.append(Insight.Price(sd.Security.Symbol, self.insightPeriod, InsightDirection.Down,None, None, None, 0.01))    
            
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            self.symbolData[added.Symbol] = SymbolData(algorithm, added, self.upperBand, self.lowerBand, self.emaPeriod, self.momPeriod, 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)
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.ConsolidatorEMA)
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.ConsolidatorMOM)
class SymbolData:
    def __init__(self, algorithm, security, lowerBand, upperBand, emaPeriod, momPeriod, resolution):
        self.Security = security
        self.donchian = DonchianChannel(upperBand, lowerBand)
        self._donchian = {}
        
        self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.donchian, self.Consolidator)
        
        self.donchian.Updated += self.DonchianUpdated
        
        self.donchianWindow = RollingWindow[IndicatorDataPoint](2)
        self._donchian["UpperBand"] = RollingWindow[float](2)
        self._donchian["LowerBand"] = RollingWindow[float](2)
        
        self.ema = ExponentialMovingAverage(emaPeriod)
        self.ConsolidatorEMA = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.ema, self.ConsolidatorEMA)
        
        self.mom = Momentum(momPeriod)
        self.ConsolidatorMOM = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.mom, self.ConsolidatorMOM)
        
        self.mom.Updated += self.MOMUpdated
        
        self.momWindow = RollingWindow[IndicatorDataPoint](2)
        

        
    def DonchianUpdated(self, sender, updated):
        self.donchianWindow.Add(updated)
        
        self._donchian["UpperBand"].Add(self.donchian.UpperBand.Current.Value)
        self._donchian["LowerBand"].Add(self.donchian.LowerBand.Current.Value)
        
    def MOMUpdated(self, sender, updated):
        self.momWindow.Add(updated)
class Donchian5(AlphaModel):
    
    def __init__(self, period = 55, resolution = Resolution.Daily):
        self.period = period
        self.resolution = resolution
        
        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(resolution), period)
        self.symbolDataBySymbol = {}
        
        #self.donchianWindow = {}
       
        #self._donchian = {}

        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{})'.format(self.__class__.__name__, period, resolutionString)
        
    def Update(self, algorithm, data):
        insights = []
        
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            symbolData = self.symbolDataBySymbol.get(added.Symbol)
            
            if symbolData is None:
                #history = algorithm.History(added.Symbol, self.period, self.resolution)
                self.window = RollingWindow[TradeBar](2)
                symbolData = SymbolData(added)
                symbolData.donchian = algorithm.DCH(added.Symbol, self.period, self.period, self.resolution)
                self.donchianWindow = RollingWindow[IndicatorDataPoint](2)
                symbolData.donchian.Updated += self.DonchianUpdated
                symbolData._donchian["UpperBand"] = RollingWindow[float](2)
                symbolData._donchian["LowerBand"] = RollingWindow[float](2)
                
                self.symbolDataBySymbol[added.Symbol] = symbolData
                
            else:
                symbolData.donchian.Reset()
                self.donchianWindow.Reset()
                symbolData._donchian["UpperBand"].Reset()
                symbolData._donchian["UpperBand"].Reset()
                
    def DonchianUpdated(self, sender, updated):
        self.donchianWindow.Add(updated)
        
        symbolData._donchian["UpperBand"].Add(symbolData.donchian.UpperBand.Current.Value)
        symbolData._donchian["LowerBand"].Add(symbolData.donchian.LowerBand.Current.Value)
                
class SymbolData:
    def __init__ (self, security):
        self.Security = security
        self.Symbol = security.Symbol
        self.donchian = None
        self._donchian = {}
        
class TrailingStopRiskManagementModel(RiskManagementModel):
    '''Provides an implementation of IRiskManagementModel that limits the maximum possible loss
    measured from the highest unrealized profit'''
    def __init__(self, maximumDrawdownPercent = 0.20):
        '''Initializes a new instance of the TrailingStopRiskManagementModel class
        Args:
            maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown'''
        self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
        self.trailingClose = dict()

    def ManageRisk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance
            targets: The current portfolio targets to be assessed for risk'''
        riskAdjustedTargets = list()

        for kvp in algorithm.Securities:
            symbol = kvp.Key
            security = kvp.Value

            # Remove if not invested
            if not security.Invested:
                self.trailingClose.pop(symbol, None)
                continue

            # Add newly invested securities
            if symbol not in self.trailingClose:
                self.trailingClose[symbol] = security.Holdings.AveragePrice   # Set to average holding cost
                continue

            

            # Check for new close high and update - set to tradebar close
            if self.trailingClose[symbol] < security.Close:
                self.trailingClose[symbol] = security.Close
                continue

            # Check for securities past the drawdown limit
            securityClose = self.trailingClose[symbol]
            drawdown = (security.Close / securityClose) - 1

            if drawdown < self.maximumDrawdownPercent:
                # liquidate
                riskAdjustedTargets.append(PortfolioTarget(symbol, 0))

        return riskAdjustedTargets

# Your New Python File
class Donchian6(AlphaModel):
    
    def __init__(self, lowerBand = 55, upperBand = 55, emaPeriod = 100, resolution = Resolution.Daily):
        self.lowerBand = lowerBand
        self.upperBand = upperBand
        self.resolution = resolution
        self.emaPeriod = emaPeriod
        
        self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), upperBand)
        self.symbolData = {}

        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{})'.format(self.__class__.__name__, lowerBand, upperBand, resolutionString)
        

    def Update(self, algorithm, data):
        
        insights = []
        for key, sd in self.symbolData.items():
            if sd.donchian.IsReady and \
                sd.donchianWindow.IsReady and \
                sd._donchian["UpperBand"].IsReady and \
                sd._donchian["LowerBand"].IsReady:
                
                if sd._donchian["UpperBand"][1] < sd.Security.Close and \
                    sd.Security.Close > sd.ema.Current.Value:
                        
                    insights.append(Insight.Price(sd.Security.Symbol, self.insightPeriod, InsightDirection.Up))
                
                if sd._donchian["LowerBand"][1] > sd.Security.Close and \
                    sd.Security.Close < sd.ema.Current.Value:
                    
                    insights.append(Insight.Price(sd.Security.Symbol, self.insightPeriod, InsightDirection.Down))    
            
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            self.symbolData[added.Symbol] = SymbolData(algorithm, added, self.upperBand, self.lowerBand, self.emaPeriod, 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)
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.ConsolidatorEMA)

class SymbolData:
    def __init__(self, algorithm, security, lowerBand, upperBand, emaPeriod, resolution):
        self.Security = security
        self.donchian = DonchianChannel(upperBand, lowerBand)
        self._donchian = {}
        
        self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.donchian, self.Consolidator)
        
        self.donchian.Updated += self.DonchianUpdated
        
        self.donchianWindow = RollingWindow[IndicatorDataPoint](2)
        self._donchian["UpperBand"] = RollingWindow[float](2)
        self._donchian["LowerBand"] = RollingWindow[float](2)
        
        self.ema = ExponentialMovingAverage(emaPeriod)
        self.ConsolidatorEMA = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.ema, self.ConsolidatorEMA)
        
        
    def DonchianUpdated(self, sender, updated):
        self.donchianWindow.Add(updated)
        
        self._donchian["UpperBand"].Add(self.donchian.UpperBand.Current.Value)
        self._donchian["LowerBand"].Add(self.donchian.LowerBand.Current.Value)
class Donchian7(AlphaModel):
    
    def __init__(self, lowerBand = 55, upperBand = 55, emaPeriod = 100, resolution = Resolution.Daily):
        self.lowerBand = lowerBand
        self.upperBand = upperBand
        self.resolution = resolution
        self.emaPeriod = emaPeriod
        
        self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), upperBand)
        self.symbolData = {}

        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{})'.format(self.__class__.__name__, lowerBand, upperBand, resolutionString)
        

    def Update(self, algorithm, data):
        
        insights = []
        for key, sd in self.symbolData.items():
            if sd.donchian.IsReady and \
                sd.donchianWindow.IsReady and \
                sd._donchian["UpperBand"].IsReady and \
                sd._donchian["LowerBand"].IsReady and \
                sd.obv.IsReady and\
                sd.obvWindow.IsReady:
                
                if sd._donchian["UpperBand"][1] < sd.Security.Close and \
                    sd.Security.Close > sd.ema.Current.Value and \
                    sd.obvWindow[1] < sd.obvWindow[0]:
                        
                    insights.append(Insight.Price(sd.Security.Symbol, self.insightPeriod, InsightDirection.Up))
                
                if sd._donchian["LowerBand"][1] > sd.Security.Close and \
                    sd.Security.Close < sd.ema.Current.Value and \
                    sd.obvWindow[1] > sd.obvWindow[0]:
                    
                    insights.append(Insight.Price(sd.Security.Symbol, self.insightPeriod, InsightDirection.Down))    
            
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            self.symbolData[added.Symbol] = SymbolData(algorithm, added, self.upperBand, self.lowerBand, self.emaPeriod, 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)
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.ConsolidatorEMA)
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.ConsolidatorOBV)
class SymbolData:
    def __init__(self, algorithm, security, lowerBand, upperBand, emaPeriod, resolution):
        self.Security = security
        self.donchian = DonchianChannel(upperBand, lowerBand)
        self._donchian = {}
        
        self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.donchian, self.Consolidator)
        
        self.donchian.Updated += self.DonchianUpdated
        
        self.donchianWindow = RollingWindow[IndicatorDataPoint](2)
        self._donchian["UpperBand"] = RollingWindow[float](2)
        self._donchian["LowerBand"] = RollingWindow[float](2)
        
        self.ema = ExponentialMovingAverage(emaPeriod)
        self.ConsolidatorEMA = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.ema, self.ConsolidatorEMA)
        
        self.obv = OnBalanceVolume()
        self.ConsolidatorOBV = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.obv, self.ConsolidatorOBV)
        
        self.obv.Updated += self.OBVUpdated
        
        self.obvWindow = RollingWindow[IndicatorDataPoint](2)
        

        
    def DonchianUpdated(self, sender, updated):
        self.donchianWindow.Add(updated)
        
        self._donchian["UpperBand"].Add(self.donchian.UpperBand.Current.Value)
        self._donchian["LowerBand"].Add(self.donchian.LowerBand.Current.Value)
        
    def OBVUpdated(self, sender, updated):
        self.obvWindow.Add(updated)

# Your New Python File
class Donchian4(AlphaModel):
    
    def __init__(self, period = 55, resolution = Resolution.Daily):
        self.period = period
        self.resolution = resolution
        self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), period)
        self.symbolDataBySymbol = {}
        self.closeWindow = {}
        self.lowWindow = {}
        self.highWindow = {}
        
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{})'.format(self.__class__.__name__, period, resolutionString)
        
    def Update(self, algorithm, data):
        insights = []
        
        for symbol, symbolData in self.symbolDataBySymbol.items():
            if data.ContainsKey(symbol) and data[symbol] is not None:
                self.closeWindow[symbol].Add(data[symbol].Close)
            donchian = symbolData.Donchian
        
        #if donchian.IsReady:
            #pass
        
        #return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        symbols = [x.Symbol for x in changes.RemovedSecurities]
        if len(symbols) > 0:
            for subscription in algorithm.SubscriptionManager.Subscriptions:
                if subscription.Symbol in symbols:
                    self.symbolDataBySymbol.pop(subscription.Symbol, None)
                    subscription.Consolidators.Clear()
                    
        #init data for added securities
        addedSymbols = [x.Symbol for x in changes.AddedSecurities if x.Symbol not in self.symbolDataBySymbol]
        if len(addedSymbols) == 0: return
        
        history = algorithm.History(addedSymbols, self.period, self.resolution)
        
        for symbol in addedSymbols:
            donchian = algorithm.DCH(symbol, self.period, self.period, self.resolution)
            self.closeWindow[symbol] = RollingWindow[float](2)
            if not history.empty:
                ticker = SymbolCache.GetTicker(symbol)
                
                if ticker not in history.index.levels[0]:
                    Log.Trace(f'Donchian4.OnSecuritiesChanged: {ticker} not found in history data frame.')
                    continue
                
                for tuple in history.loc[ticker].itertuples():
                    tradeBar = TradeBar(bar.Index[1], bar.Index[0], bar.open, bar.high, bar.low, bar.close, bar.volume, timedelta(1))
                    donchian.Update(tradeBar)
                    
            self.symbolDataBySymbol[symbol] = SymbolData(symbol, donchian)
            
class SymbolData:
    def __init__(self, symbol, donchian):
        self.Symbol = symbol
        self.Donchian = donchian