Overall Statistics
Total Orders
238
Average Win
0.00%
Average Loss
-0.01%
Compounding Annual Return
-0.170%
Drawdown
1.000%
Expectancy
-0.487
Start Equity
1000000
End Equity
991520.1
Net Profit
-0.848%
Sharpe Ratio
-5.589
Sortino Ratio
-3.692
Probabilistic Sharpe Ratio
0.029%
Loss Rate
60%
Win Rate
40%
Profit-Loss Ratio
0.29
Alpha
-0.025
Beta
0.004
Annual Standard Deviation
0.004
Annual Variance
0
Information Ratio
-0.717
Tracking Error
0.173
Treynor Ratio
-6.19
Total Fees
$293.93
Estimated Strategy Capacity
$0
Lowest Capacity Asset
VX YHPO8JXZLPT5
Portfolio Turnover
0.14%
# region imports
from AlgorithmImports import *
# endregion

class StrategyTwoSellPutAndHedge(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2019, 6, 1)
        self.set_end_date(2024, 6, 1)
        self.set_cash(1000000)
        self.set_warmup(timedelta(100))
        self.settings.daily_precise_end_time = True

        # Initialize asset data immediately.
        self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))

        # Adds the VIX chain
        # The default mapping mode for the continous contract is OPEN_INTEREST, but we will set it explicitly
        # The default data normalization mode for the continous contract is RAW, but we will set it explicitly
        # For other options, see https://www.quantconnect.com/docs/v2/writing-algorithms/universes/futures#12-Continous-Contracts
        self.future = self.add_future(Futures.Indices.VIX,  # SP_500_E_MINI # VIX
            extended_market_hours=False,
            data_mapping_mode=DataMappingMode.OPEN_INTEREST,
            data_normalization_mode=DataNormalizationMode.RAW)

        # We don't have VIX Future Options data; but used VIX Cash Options as a hedge instead
        vix = self.add_index("VIX").symbol
        self.index_option_symbol = Symbol.create_canonical_option(vix, "VIXW", Market.USA, "?VIXW")       

        self.mapped_future = None
        self.schedule.on(self.date_rules.every_day(self.future.symbol), self.time_rules.after_market_open(self.future.symbol, 5), self.trade)

    def trade(self):

        if self.is_warming_up:
            self.mapped_future = self.future.mapped
            return

        # Contract is rolling.
        if self.future.mapped != self.mapped_future:

            #Clean up open positions in old future and old hedge.
            self.liquidate()

            # Open short in old VIX future contract that is no longer mapped
            self.market_order(self.mapped_future, -1)
            self.mapped_future = self.future.mapped

            # Refresh our hedge
            option_contract_list = self.option_chain_provider.get_option_contract_list(self.index_option_symbol, self.time)
            # Price of the VIX Index: 
            underlying_price = self.securities[self.index_option_symbol.underlying].price
            # Find right expiry date close to 90 days
            expiry = min(option_contract_list, key=lambda symbol: abs((symbol.id.date - self.time).days - 90)).id.date
            # Filter the strike prices on above expiry.
            calls = filter(lambda symbol: symbol.id.option_right == OptionRight.CALL and symbol.id.date == expiry and symbol.id.strike_price > underlying_price, option_contract_list)
 
            # Add the contract to calculate the delta
            deltas_by_symbol = {}
            for call in calls:
                put = Symbol.create_option(call.id.underlying.symbol, "VIXW", call.id.market, call.id.option_style, OptionRight.PUT, call.id.strike_price, call.id.date)
                deltas = self.indicator_history(Delta(option=call, mirror_option=put), [call, put, call.Underlying], 24, Resolution.HOUR)
                if deltas.count == 0:
                    continue
                deltas_by_symbol[call] = list(deltas)[-1].current.value

            call = sorted(deltas_by_symbol.items(), key=lambda x: abs(x[1]-.10))[0][0]
            self.add_option_contract(call)
            self.market_order(call, -1, tag=f'Delta={deltas_by_symbol[call]:.3f}')