Overall Statistics
Total Trades
779
Average Win
3.95%
Average Loss
-2.60%
Compounding Annual Return
98.169%
Drawdown
54.200%
Expectancy
0.361
Net Profit
3120.832%
Sharpe Ratio
1.669
Probabilistic Sharpe Ratio
74.604%
Loss Rate
46%
Win Rate
54%
Profit-Loss Ratio
1.52
Alpha
0.549
Beta
1.314
Annual Standard Deviation
0.471
Annual Variance
0.222
Information Ratio
1.487
Tracking Error
0.407
Treynor Ratio
0.599
Total Fees
$111240.46
Estimated Strategy Capacity
$240000000.00
Lowest Capacity Asset
TQQQ UK280CGTCB51
class PriceActionTQQQ(QCAlgorithm):
    
    def Initialize(self):
        
        self.SetStartDate(2017, 1, 1)  
        self.SetCash(100000)  
        self.TQQQ = self.AddEquity("TQQQ", Resolution.Daily).Symbol  
        self.SetBenchmark("QQQ")
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
        self.SetExecution(ImmediateExecutionModel())
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        
        symbol = [Symbol.Create(self.TQQQ, SecurityType.Equity, Market.USA)]
        self.AddUniverseSelection(ManualUniverseSelectionModel(symbol))
        
        self.AddAlpha(PriceActionTQQQAlphaModel(self, self.TQQQ, 0.05))
        
class PriceActionTQQQAlphaModel(AlphaModel):
    
    def __init__(self, algorithm, tkr, maximumDrawdown):
        self.period = timedelta(days=1)
        self.symbol = tkr
        self.symbolData = SymbolData(algorithm)
        #Set our max drawdown
        self.maximumDrawdown = -abs(maximumDrawdown)
        self.trailingHighs = dict()
    
    def Update(self, algorithm, data):
        insights = []
        #This only gets returned if we have drawdowns appended. This only gets one update pr day, as we have set the resolution to daily
        drawdowns = []
        
        #Iterrate over the securities
        """
        for kvp in algorithm.Securities:
            #Set the value
            security = kvp.Value
            
            #If are flat, we will continue
            if not security.Invested:
                continue
            
            #Set the unrealized profit/loss
            pnl = security.Holdings.UnrealizedProfitPercent
            #If max drawdown is bigger than pnl, we send a flat insights
            if pnl < self.maximumDrawdown:
                drawdowns.append(Insight(security.Symbol, self.period, InsightType.Price, InsightDirection.Flat, 1, None))
        
        if len(drawdowns) != 0:
            return drawdowns
        """
        
        HO = 0.0
        OL = 0.0
        
        if self.symbolData.Updated:
            Open  = self.symbolData.open
            High  = self.symbolData.high
            Low   = self.symbolData.low
            Close = self.symbolData.close
            
            HO = High -  Open
            OL = Open - Low
            HC = High - Close
            CL = Close - Low
            
            if Close > Open:
                if HO > OL:
                    insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Up, 1, None))
                
            if Close < Open:
                if HO < OL:
                    insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Up, 1, None))
                if HO > OL:
                    insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Down, 1, None))
                if HO == OL:
                    insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Up, 1, None))
                    
            if Close == Open:
                if HO < OL:
                    insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Up, 1, None))
                if HO > OL:
                    insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Down, 1, None))
                if HO == OL:
                    insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Flat, 1, None))
                    
            self.symbolData.Updated = False
                
        for kvp in algorithm.Securities:
            symbol = kvp.Key
            security = kvp.Value
            
            if not security.Invested:
                self.trailingHighs.pop(symbol, None)
                continue
            
            if symbol not in self.trailingHighs:
                self.trailingHighs[symbol] = security.Holdings.AveragePrice
                continue
                
            if self.trailingHighs[symbol] < security.High:
                self.trailingHighs[symbol] = security.High
                continue

            # Check for securities past the drawdown limit
            securityHigh = self.trailingHighs[symbol]
            drawdown = (security.Low / securityHigh) - 1

            if drawdown < self.maximumDrawdown:
                # liquidate
                insights.append(Insight(self.symbol, self.period, InsightType.Price, InsightDirection.Flat, 1, None))

            
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        self.changes =  changes
        
    
class SymbolData:
    
    def __init__(self, algorithm):
        algorithm.Consolidate("TQQQ", Resolution.Daily, self.DailyBarHandler)
        
        self.open = 0
        self.close = 0
        self.high = 0
        self.low = 0
        self.Updated = False
        
        
    def DailyBarHandler(self, consolidated):
        self.open = consolidated.Open
        self.close = consolidated.Close
        self.high = consolidated.High
        self.low = consolidated.Low
        self.Updated = True