Overall Statistics
Total Orders
4
Average Win
0%
Average Loss
-0.68%
Compounding Annual Return
11.545%
Drawdown
5.600%
Expectancy
-0.5
Start Equity
100000
End Equity
120969
Net Profit
20.969%
Sharpe Ratio
0.513
Sortino Ratio
0.628
Probabilistic Sharpe Ratio
77.543%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0
Alpha
-0.034
Beta
0.438
Annual Standard Deviation
0.052
Annual Variance
0.003
Information Ratio
-1.734
Tracking Error
0.064
Treynor Ratio
0.061
Total Fees
$2.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
QQQ RIWIV7K5Z9LX
Portfolio Turnover
0.04%
from AlgorithmImports import *

class VIXShortStraddleAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2023, 1, 1)
        self.SetEndDate(2024, 12, 1)
        self.SetCash(100000)

        # Set proper universe settings for options
        self.UniverseSettings.Resolution = Resolution.Minute
        self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
        
        # Add QQQ with minute resolution
        self.qqq = self.AddEquity("QQQ", Resolution.Minute).Symbol
        
        # Add QQQ Options
        self.qqq_option = self.AddOption("QQQ", Resolution.Minute)
        # Widen the filter to ensure we get contracts
        self.qqq_option.SetFilter(lambda u: u.IncludeWeeklys()
                                          .Strikes(-5, +5)  # Wider strike range
                                          .Expiration(15, 45))

        self.Schedule.On(self.DateRules.EveryDay(self.qqq),
                        self.TimeRules.At(9, 31),
                        self.TradeOptions)

    def TradeOptions(self):
        if self.Portfolio.Invested:
            self.Log("Already invested, skipping trade")
            return

        chain = self.CurrentSlice.OptionChains.get(self.qqq_option.Symbol)
        if chain is None or not chain:
            self.Log(f"No option chain data available")
            return

        self.Log(f"Number of contracts in chain: {chain}")
        
        try:
            # Filter for valid contracts
            contracts = [x for x in chain if 15 <= (x.Expiry - self.Time).days <= 45]
            if not contracts:
                self.Log("No valid contracts found after filtering")
                return

            # Find ATM strike
            underlying_price = chain.Underlying.Price
            self.Log(f"Underlying price: {underlying_price}")
            
            atm_strike = min([x.Strike for x in contracts], 
                           key=lambda s: abs(s - underlying_price))
            self.Log(f"Selected ATM strike: {atm_strike}")

            # Get nearest expiry
            expiry = min(x.Expiry for x in contracts if x.Strike == atm_strike)
            self.Log(f"Selected expiry: {expiry}")
            
            short_straddle = OptionStrategies.ShortStraddle(self.qqq_option.Symbol, atm_strike, expiry)
            
            # Calculate quantity based on notional value
            option_notional = underlying_price * 100  # One contract = 100 shares
            quantity = max(1, int(self.Portfolio.TotalPortfolioValue * 0.02 / option_notional))
            
            self.Log(f"Attempting to sell straddle with quantity: {quantity}")
            self.Sell(short_straddle, quantity)

        except Exception as e:
            self.Log(f"Error in TradeOptions: {str(e)}")