Overall Statistics
Total Orders
5165
Average Win
0.66%
Average Loss
-0.37%
Compounding Annual Return
53.415%
Drawdown
20.700%
Expectancy
0.402
Start Equity
1000000
End Equity
48062947.29
Net Profit
4706.295%
Sharpe Ratio
1.781
Sortino Ratio
2.223
Probabilistic Sharpe Ratio
99.383%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.79
Alpha
0.278
Beta
0.707
Annual Standard Deviation
0.191
Annual Variance
0.036
Information Ratio
1.55
Tracking Error
0.163
Treynor Ratio
0.48
Total Fees
$1457542.85
Estimated Strategy Capacity
$420000.00
Lowest Capacity Asset
UVXY V0H08FY38ZFP
Portfolio Turnover
40.85%
Drawdown Recovery
301
from AlgorithmImports import *
from QuantConnect.Indicators import CommodityChannelIndex

STOCK = "UVXY"
HEDGE = "TQQQ"

PERIOD  = 270
PERIOD2 = 6
PERIOD4 = 70

class IntradayVolatilityRegimeHarvester(QCAlgorithm):

    UVXY_WEIGHT = 0.745

    SIGNAL_CODES = {
        "TRADE_1_LONG": 1,
        "TRADE_2_EXIT": 2,
        "TRADE_3_LONG": 3,
        "TRADE_4_FLATTEN": -4,
        "TRADE_4_EXIT": 4,
        "TRADE_5_LONG": 5,
        "TRADE_6_LONG": 6,
        "TRADE_7_FORCED_EXIT": 9
    }

    def Initialize(self):
        self.SetStartDate(2017, 1, 1)
        self.SetCash(1000000)

        self.uvxy = self.AddEquity(STOCK, Resolution.Minute).Symbol
        self.tqqq = self.AddEquity(HEDGE, Resolution.Minute).Symbol

        self.tqqq_w_default = 0.25
        self.tqqq_w_trade3  = 0.15
        self.tqqq_w_trade5  = 0.05
        self.tqqq_w_trade6  = 0.25

        self.max_high = self.MAX(self.uvxy, PERIOD, Resolution.Minute, Field.High)
        self.min_low  = self.MIN(self.uvxy, PERIOD, Resolution.Minute, Field.Low)

        self.min_open  = self.MIN(self.uvxy, PERIOD, Resolution.Minute, Field.Open)
        self.min_close = self.MIN(self.uvxy, PERIOD, Resolution.Minute, Field.Close)

        self.max_high_short = self.MAX(self.uvxy, PERIOD2, Resolution.Minute, Field.High)
        self.min_low_short  = self.MIN(self.uvxy, PERIOD2, Resolution.Minute, Field.Low)

        self.cci = CommodityChannelIndex(PERIOD4)
        self.RegisterIndicator(self.uvxy, self.cci, Resolution.Minute)

        self.SetWarmUp(PERIOD, Resolution.Minute)

        self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel(lambda _: None))
        self.SetExecution(ImmediateExecutionModel())

        self.AddChart(Chart("Signals"))

        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.AfterMarketOpen(self.uvxy, 7),   self.trade_1)
        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.AfterMarketOpen(self.uvxy, 44),  self.trade_2_exit)
        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.AfterMarketOpen(self.uvxy, 60),  self.trade_3)
        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.AfterMarketOpen(self.uvxy, 100), self.trade_4_flatten)
        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.AfterMarketOpen(self.uvxy, 255), self.trade_4_exit)
        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.AfterMarketOpen(self.uvxy, 255), self.trade_5)
        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.BeforeMarketClose(self.uvxy, 55), self.trade_6)
        self.Schedule.On(self.DateRules.EveryDay(self.uvxy), self.TimeRules.BeforeMarketClose(self.uvxy, 1),  self.trade_7_forced_exit)

        self.SetRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(0.067))

    def _log(self, tag, uvxy_weight, tqqq_weight):
        self.Log(f"{self.Time} | {tag} | UVXY_w={uvxy_weight} | TQQQ_w={tqqq_weight}")
        self.Plot("Signals", "code", self.SIGNAL_CODES[tag])

    def _set_alloc(self, uvxy_weight, tqqq_weight):
        if uvxy_weight == 0:
            self.Liquidate(STOCK)
        else:
            self.SetHoldings(STOCK, uvxy_weight)
        self.SetHoldings(HEDGE, tqqq_weight)

    def trade_1(self):
        if (self.max_high.Current.Value > self.min_low.Current.Value * 1.0845 and
            self.max_high.Current.Value / self.min_low.Current.Value < 1.25 and
            self.min_open.Current.Value * 0.998 < self.min_low_short.Current.Value):
            self._log("TRADE_1_LONG", self.UVXY_WEIGHT, self.tqqq_w_default)
            self._set_alloc(self.UVXY_WEIGHT, self.tqqq_w_default)

    def trade_2_exit(self):
        if self.Portfolio[STOCK].Invested:
            self._log("TRADE_2_EXIT", 0, self.tqqq_w_default)
        self._set_alloc(0, self.tqqq_w_default)

    def trade_3(self):
        if (self.max_high.Current.Value > self.min_low.Current.Value * 1.22 and
            self.max_high.Current.Value / self.min_low.Current.Value < 1.2625 and
            self.min_open.Current.Value * 1.005 < self.min_low_short.Current.Value):
            self._log("TRADE_3_LONG", self.UVXY_WEIGHT, self.tqqq_w_trade3)
            self._set_alloc(self.UVXY_WEIGHT, self.tqqq_w_trade3)

    def trade_4_flatten(self):
        if (self.max_high.Current.Value < self.min_low.Current.Value * 1.0253 and
            self.min_close.Current.Value < self.min_low_short.Current.Value and
            self.cci.Current.Value < 70):
            if self.Portfolio[STOCK].Invested:
                self._log("TRADE_4_FLATTEN", 0, self.tqqq_w_default)
            self._set_alloc(0, self.tqqq_w_default)

    def trade_4_exit(self):
        if self.Portfolio[STOCK].Invested:
            self._log("TRADE_4_EXIT", 0, self.tqqq_w_default)
            self._set_alloc(0, self.tqqq_w_default)

    def trade_5(self):
        if (self.max_high.Current.Value > self.min_low.Current.Value * 1.22 and
            self.max_high.Current.Value / self.min_low.Current.Value < 1.255 and
            self.cci.Current.Value > -80):
            self._log("TRADE_5_LONG", self.UVXY_WEIGHT, self.tqqq_w_trade5)
            self._set_alloc(self.UVXY_WEIGHT, self.tqqq_w_trade5)

    def trade_6(self):
        if (self.max_high.Current.Value < self.min_low.Current.Value * 1.0275 and
            self.min_open.Current.Value < self.min_low_short.Current.Value and
            self.Securities[self.uvxy].Price < self.max_high.Current.Value and
            self.cci.Current.Value > -150):
            self._log("TRADE_6_LONG", self.UVXY_WEIGHT, self.tqqq_w_trade6)
            self._set_alloc(self.UVXY_WEIGHT, self.tqqq_w_trade6)

    def trade_7_forced_exit(self):
        if self.Portfolio[STOCK].Invested:
            self._log("TRADE_7_FORCED_EXIT", 0, self.tqqq_w_default)
        self._set_alloc(0, self.tqqq_w_default)