Overall Statistics
Total Orders
11
Average Win
0%
Average Loss
-2.35%
Compounding Annual Return
-17.803%
Drawdown
36.800%
Expectancy
-0.571
Start Equity
10000
End Equity
8603.52
Net Profit
-13.965%
Sharpe Ratio
0.037
Sortino Ratio
0.036
Probabilistic Sharpe Ratio
17.603%
Loss Rate
57%
Win Rate
43%
Profit-Loss Ratio
0
Alpha
-0.142
Beta
3.757
Annual Standard Deviation
0.656
Annual Variance
0.43
Information Ratio
-0.034
Tracking Error
0.589
Treynor Ratio
0.007
Total Fees
$7.00
Estimated Strategy Capacity
$610000.00
Lowest Capacity Asset
VXXB WRBPJAJZ2Q91
Portfolio Turnover
1.04%
# region imports
from AlgorithmImports import *
# endregion

class VXXPutOnHighVIX(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2024, 6, 1)
        self.set_cash(10000)


        self.vxx = self.add_equity("VXX", Resolution.MINUTE).Symbol

        
        # # Add VIX index as custom data (or use VIXY ETF as proxy if needed)
        self.vixy = self.add_equity("VIXY", Resolution.MINUTE).Symbol

        # Optimization parameters
        self.vixy_threshold = 55 #self.get_parameters("vixy_threshold") or 25
        self.min_expiry_days = 10
        self.max_expiry_days =45
        self.strike_offset = 0*0.05  # 0 = ATM, 0.05 = 5% OTM

        # Add VXX options
        self.option = self.add_option("VXX", Resolution.MINUTE)
        self.option.SetFilter(-10, -1, self.min_expiry_days, self.max_expiry_days)
        self.last_trade_date = None
        self.option_positions = {}
                

    def OnData(self, data):

        if not self.securities[self.vixy].HasData:
            return

        vixy_price = self.securities[self.vixy].Price
        if vixy_price < self.vixy_threshold:
            return


        # Avoid overtrading
        if self.last_trade_date and (self.time - self.last_trade_date).days < 30:
            return

        # Get option chain
        chain = data.OptionChains.get(self.option.Symbol)
        if not chain:
            return

        # Select a put option: 30-45 days to expiry, slightly OTM
        puts = [x for x in chain if x.right == OptionRight.PUT and x.expiry > self.time + timedelta(self.min_expiry_days)]
        if not puts:
            return

        # sorted_puts = sorted(puts, key=lambda x: x.Strike)
        # selected_contract = sorted_puts[0]

       

        # Sort by strike offset
        underlying_price = self.securities[self.vxx].Price
        target_strike = underlying_price * (1 - self.strike_offset)
        selected_contract = sorted(puts, key=lambda x: abs(x.Strike - target_strike))[0]

        self.market_order(selected_contract.Symbol, 1)
        
        self.debug(f"Bought VXX Put: {selected_contract.Symbol} at {self.time}")
        self.option_positions[selected_contract.Symbol] = {
            "entry_time": self.time,
            "entry_price": selected_contract.AskPrice
        }

        self.last_trade_date = self.time

        # Check for exit conditions
        for symbol, info in list(self.option_positions.items()):
            if symbol not in data or not data[symbol]:
                continue

            holding = self.portfolio[symbol]
            if not holding.invested:
                del self.option_positions[symbol]
                continue

            current_price = data[symbol].Price
            self.debug(f"Current Price {current_price}, Entry price {info['entry_price']}")

            entry_price = info["entry_price"]
            holding_days = (self.time - info["entry_time"]).days

            # Exit if held for 7+ days or profit > 100x
            if holding_days >= 7 or (current_price / entry_price) >= 10:
                self.liquidate(symbol)
                self.debug(f"Exited {symbol} on {self.time} | Held {holding_days} days | Return: {current_price / entry_price:.2f}x")
                del self.option_positions[symbol]

# # Custom data class for VIX index
# class CBOE(PythonData):
#     def GetSource(self, config, date, isLiveMode):
#         return SubscriptionDataSource("https://www.cboe.com/us/futures/market_statistics/historical_data/", SubscriptionTransportMedium.RemoteFile)

#     def Reader(self, config, line, date, isLiveMode):
#         # Implement parsing logic if using real VIX data
#         return None