Overall Statistics
Total Orders
111
Average Win
0.80%
Average Loss
-1.17%
Compounding Annual Return
0.403%
Drawdown
7.400%
Expectancy
-0.093
Start Equity
1000000
End Equity
1035301
Net Profit
3.530%
Sharpe Ratio
-0.712
Sortino Ratio
-0.451
Probabilistic Sharpe Ratio
0.073%
Loss Rate
46%
Win Rate
54%
Profit-Loss Ratio
0.68
Alpha
-0.015
Beta
-0.045
Annual Standard Deviation
0.027
Annual Variance
0.001
Information Ratio
-0.644
Tracking Error
0.159
Treynor Ratio
0.427
Total Fees
$0.00
Estimated Strategy Capacity
$3600000.00
Lowest Capacity Asset
SPX 32L3DOWEACQY6|SPX 31
Portfolio Turnover
0.03%
# region imports
from AlgorithmImports import *
# endregion


class VolatilityTradingOptionAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2016, 1, 1)
        self.set_cash(1_000_000)
        self._vix = self.add_index('VIX')
        self.settings.automatic_indicator_warm_up = True
        self._std = self.std(self._vix.symbol, 90, resolution=Resolution.DAILY)
        self._sma = self.sma(self._vix.symbol, 90, resolution=Resolution.DAILY)
        self._spx = self.add_index_option('SPX')
        self._spx.set_filter(lambda universe: universe.straddle(30))

        self.schedule.on(self.date_rules.month_start(self._spx.symbol), self.time_rules.after_market_open(self._spx.symbol, 30), self._trade)

    def _trade(self):
        self.plot('VIX', 'Value', self._vix.price)
        self.plot('VIX', 'SMA', self._sma.current.value)
        self.plot('VIX', 'SMA +1 STD', self._sma.current.value + self._std.current.value)
        self.plot('VIX', 'SMA -1 STD', self._sma.current.value - self._std.current.value)
        
        z_score = (self._vix.price - self._sma.current.value) / self._std.current.value
        quantity = int(z_score)
        if quantity == 0:
            self.liquidate()
        else:
            chain = self.current_slice.option_chains.get(self._spx.symbol, None)
            if not chain: return
            chain = [c for c in chain if c.expiry > self.time]
            expiry = min([x.expiry for x in chain])
            strike = sorted([c for c in chain if c.expiry == expiry], key=lambda x: abs(x.strike - self._spx.price))[0].strike
            strategy = OptionStrategies.straddle(self._spx.symbol, strike, expiry)
            self.order(strategy, quantity)

    def on_order_event(self, order_event):
        if order_event.status == OrderStatus.FILLED and order_event.is_assignment:
            self.liquidate()