Overall Statistics
Total Orders
542
Average Win
5.64%
Average Loss
-3.02%
Compounding Annual Return
21.479%
Drawdown
53.100%
Expectancy
0.210
Start Equity
100000
End Equity
228674.82
Net Profit
128.675%
Sharpe Ratio
0.511
Sortino Ratio
0.608
Probabilistic Sharpe Ratio
11.831%
Loss Rate
58%
Win Rate
42%
Profit-Loss Ratio
1.87
Alpha
0.154
Beta
0.687
Annual Standard Deviation
0.424
Annual Variance
0.18
Information Ratio
0.442
Tracking Error
0.285
Treynor Ratio
0.315
Total Fees
$1702.48
Estimated Strategy Capacity
$1700000000.00
Lowest Capacity Asset
TSLA UNU3P8Y3WFAD
Portfolio Turnover
34.46%
#region imports
from AlgorithmImports import *
#endregion


# 0. INITIALIZE
from datetime import datetime
import decimal
import numpy as np

class DynamicBreakoutAlgorithm(QCAlgorithm):
    
    # 1.0 INITIALIZATION
    def Initialize(self):
        self.SetStartDate(2021,1,15)
        self.SetEndDate(2025,4,15)
        self._cash = 100000
        self.SetCash(self._cash)
        
        # 2.1. UNIVERSE SELECTION MODEL
        eq = self.AddEquity("TSLA", Resolution.Daily, Market.USA)
        n_eq = self.AddEquity("SPY", Resolution.Daily, Market.USA)
        self.syl = eq.Symbol
        
        # 2.4. EXECUTION MODEL
        self.Schedule.On(self.DateRules.EveryDay(self.syl), self.TimeRules.BeforeMarketClose(self.syl,1), Action(self.SetSignal))
        
        # 3. PARAMETERS
        # 3.1. LOOKBACK FOR BOLLINGERS, LIMITED BY MAXIMUM LOOKBACK AND MINIMUM LOOKBACK
        self.numdays = 20
        self.ceiling = 60
        self.floor = 10
        
        # 3.2. BREAKOUT POINTS
        self.buypoint = None
        self.sellpoint= None
        self.longLiqPoint = None
        self.shortLiqPoint = None
        self.yesterdayclose= None
        
        
        # 4. INFORMATION FOR BENCHMARKING AND PLOTTING
        self.SetBenchmark(self.syl)
        self.reference = self.History(self.syl, 1, Resolution.Daily)['close']
        self._initialValue = self.reference.iloc[0]
        
        
        # 5. DEFINITION OF AN INDICATOR
        self.Bolband = self.BB(self.syl, self.numdays, 0.5, MovingAverageType.Exponential, Resolution.Daily)
        
   
    def SetSignal(self):
        
        # 2.2. ALPHA MODEL
        
        close = self.History(self.syl, 31, Resolution.Daily)['close']
        todayvol = np.std(close[1:31])
        yesterdayvol = np.std(close[0:30])
        
        deltavol = (todayvol - yesterdayvol) / todayvol
        self.numdays = int(round(self.numdays * (1 + deltavol)))

        if self.numdays > self.ceiling:
           self.numdays = self.ceiling
        elif self.numdays < self.floor:
            self.numdays = self.floor
        
        self.high = self.History(self.syl, self.numdays, Resolution.Daily)['high']
        self.low = self.History(self.syl, self.numdays, Resolution.Daily)['low']      

        self.buypoint = max(self.high)
        self.sellpoint = min(self.low)
        
        historyclose = self.History(self.syl, self.numdays, Resolution.Daily)['close'] 
        historyopen = self.History(self.syl, self.numdays, Resolution.Daily)['open'] 
        
        self.longLiqPoint = np.mean(historyclose)
        self.shortLiqPoint = np.mean(historyopen)
        self.yesterdayclose = historyclose.iloc[-1]
        
        
        if not self.Bolband.IsReady: return

        # 2.3. PORTFOLIO CONSTRUCTION MODEL
        holdings = self.Portfolio[self.syl].Quantity
    
        if self.yesterdayclose > self.Bolband.UpperBand.Current.Value and self.Portfolio[self.syl].Price >= self.buypoint:
            self.SetHoldings(self.syl, 2)
        elif self.yesterdayclose < self.Bolband.LowerBand.Current.Value and self.Portfolio[self.syl].Price <= self.sellpoint:
            self.SetHoldings(self.syl, -2)
        else:
            self.SetHoldings(self.syl, 1)
            
            
        #2.4. RISK MANAGEMENT
        if holdings > 0 and self.Portfolio[self.syl].Price <= self.longLiqPoint:
            self.Liquidate(self.syl)
        elif holdings < 0 and self.Portfolio[self.syl].Price >= self.shortLiqPoint:
            self.Liquidate(self.syl)
      
        # 2.5. LOG
        self.Log(str(self.yesterdayclose)+(" # of days ")+(str(self.numdays)))
        
    def OnData(self,data):
        self.Plot("Equity Performance", "Equity", self.Portfolio[self.syl].Price)
        self.Plot("Equity Performance", "Upper", self.Bolband.UpperBand.Current.Value)
        self.Plot("Equity Performance", "Lower", self.Bolband.LowerBand.Current.Value)
        
        self.Plot("Strategy Equity", "Benchmark", self._cash*self.Securities["TSLA"].Close/self._initialValue)