Overall Statistics
Total Orders
49
Average Win
14.99%
Average Loss
-9.60%
Compounding Annual Return
31.718%
Drawdown
46.800%
Expectancy
0.601
Start Equity
100000
End Equity
396784.22
Net Profit
296.784%
Sharpe Ratio
0.676
Sortino Ratio
0.638
Probabilistic Sharpe Ratio
18.562%
Loss Rate
38%
Win Rate
62%
Profit-Loss Ratio
1.56
Alpha
0.193
Beta
1.283
Annual Standard Deviation
0.417
Annual Variance
0.174
Information Ratio
0.563
Tracking Error
0.378
Treynor Ratio
0.22
Total Fees
$1290.18
Estimated Strategy Capacity
$240000.00
Lowest Capacity Asset
SVXY V0H08FY38ZFP
Portfolio Turnover
2.59%
Drawdown Recovery
511
# region imports
from AlgorithmImports import *
# endregion


class VolailityTradingAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(self.end_date - timedelta(5*365))
        self.set_cash(100_000)
        self.settings.automatic_indicator_warm_up = True
        self.settings.seed_initial_prices = True
        # Add the assets we'll trade.
        self._uvxy = self.add_equity("UVXY")
        self._svxy = self.add_equity("SVXY")
        self._tqqq = self.add_equity("TQQQ")
        # Add SPY and some indicators to inform our trading decisions.
        self._spy = self.add_equity("SPY")
        self._spy.sma = self.sma(self._spy, 25, Resolution.DAILY, Field.OPEN)
        self._spy.macd = self.macd(self._spy, 12, 26, 6, MovingAverageType.EXPONENTIAL, Resolution.DAILY, Field.OPEN)
        self._spy.session.size = 3
        for bar in self.history[TradeBar](self._spy, 3, Resolution.DAILY):
            self._spy.session.update(bar)
        # Add VIX data to inform our trading decisions.
        self._vix = self.add_data(CBOE, "VIX", Resolution.DAILY)
        self._vix3m = self.add_data(CBOE, "VIX3M", Resolution.DAILY)
        self._vix9d = self.add_data(CBOE, "VIX9D", Resolution.DAILY)
        # Add a Scheduled Event to rebalance the portfolio every morning.
        self._target = None
        self.schedule.on(
            self.date_rules.every_day(self._spy), 
            self.time_rules.after_market_open(self._spy), 
            self._rebalance
        )

    def on_data(self, data):
        # Only trade on new VIX data.
        if self._vix not in data.bars:
            return 
        # Calculate some trading factors.
        vix_ratio = self._vix.open / self._vix3m.open
        vix_cross_up = self._vix9d.open > self._vix.open
        # Scan for entries.
        short_entry = (
            vix_ratio < 0.95 and 
            not vix_cross_up and 
            not (
                self._spy.session[1].open <= self._spy.sma.current.value and 
                self._spy.session[2].open > self._spy.sma.previous.value
            )
        )
        long_entry = (
            vix_ratio > 1.05 and 
            vix_cross_up and 
            self._spy.macd.current.value < self._spy.macd.signal.current.value
        )
        # Scan for exits.
        in_long = self._uvxy.holdings.is_long
        in_short = self._svxy.holdings.is_short
        in_rest = self._tqqq.holdings.is_long
        short_exit = in_short and (not short_entry)
        long_exit = in_long and (not long_entry)
        # Set the target asset.
        if (short_exit or long_exit) and not self._tqqq.invested:
            self._target = self._tqqq
        elif short_entry and not self._svxy.invested:
            self._target = self._svxy
        elif long_entry and not self._uvxy.invested:
            self._target = self._uvxy

    def _rebalance(self):
        # Place trades to rebalance the portfolio.
        if self._target:
            self.set_holdings(self._target, 1, liquidate_existing_holdings=True)
            self._target = None