I have played with the examples in the documentation and I see how to get IV values of a contract within the OnData function. But in this current test project of allocating portfolio based on iv each month, I'm getting 0 returned for every contract. For some reason it is not calculating the IV. The indicator is either None or not Ready, but I don't know why. And neither does Mia.
 

  1. from AlgorithmImports import *
  2. class MonthlyOptionsAllocation(QCAlgorithm):
  3. def Initialize(self):
  4. # Set start and end dates for backtesting
  5. self.SetStartDate(2015, 1, 1)
  6. self.SetEndDate(2024, 12, 1)
  7. self.SetCash(100000)
  8. self.SetWarmUp(timedelta(days=30)) # for IV indicator calculation
  9. # List of stock tickers
  10. self.tickers = [
  11. "AAPL", "MSFT", "GOOG", "AMZN", "TSLA", "META",
  12. "NFLX", "NVDA", "BABA", "AMD", "SPY", "QQQ",
  13. "INTC", "XOM", "BA", "JPM", "V", "MA", "DIS", "COST"
  14. ]
  15. # Add equities and their options
  16. self.symbols = []
  17. for ticker in self.tickers:
  18. equity = self.add_equity(ticker, Resolution.DAILY).Symbol
  19. option = self.add_option(ticker, Resolution.MINUTE).Symbol
  20. self.symbols.append((equity, option))
  21. self._iv = self.iv(self.option)
  22. # Schedule monthly portfolio rebalancing
  23. self.schedule.on(
  24. self.date_rules.month_start(self.tickers[0]),
  25. self.time_rules.at(10, 0),
  26. self.rebalance_portfolio
  27. )
  28. # Keep track of performance metrics
  29. self.sharpe_ratios = {}
  30. def rebalance_portfolio(self):
  31. selected_symbols = []
  32. for equity, option in self.symbols:
  33. history = self.history(equity, 252, Resolution.DAILY)
  34. if len(history) < 252:
  35. continue
  36. daily_returns = history['close'].pct_change().dropna()
  37. if daily_returns.std() == 0:
  38. sharpe_ratio = 0
  39. else:
  40. sharpe_ratio = (252 ** 0.5) * daily_returns.mean() / daily_returns.std()
  41. self.sharpe_ratios[equity] = sharpe_ratio
  42. if sharpe_ratio > 0:
  43. selected_symbols.append(option)
  44. # Allocate capital based on IV
  45. self.set_holdings_based_on_iv(selected_symbols)
  46. def set_holdings_based_on_iv(self, selected_symbols):
  47. """Allocate weights to selected tickers based on IV."""
  48. total_iv = 0
  49. ivs = {}
  50. for option_symbol in selected_symbols:
  51. chain = self.option_chain_provider.get_option_contract_list(option_symbol, self.Time)
  52. if not chain:
  53. self.debug(f"No chain found for: {option_symbol}")
  54. continue
  55. # Filter for monthly contracts expiring in the current month
  56. monthly_contracts = [
  57. c for c in chain
  58. if c.ID.Date.month == self.Time.month and c.ID.Date.year == self.Time.year
  59. ]
  60. if not monthly_contracts:
  61. self.debug(f"No monthly contracts: {option_symbol}")
  62. continue
  63. # Select the ATM contract
  64. underlying_price = self.Securities[option_symbol.Underlying].Price
  65. atm_contract = min(
  66. monthly_contracts,
  67. key=lambda x: abs(x.ID.StrikePrice - underlying_price),
  68. default=None
  69. )
  70. if atm_contract is None:
  71. self.debug(f"No ATM contract found for: {option_symbol}")
  72. continue
  73. # Add the contract and calculate IV
  74. self.AddOptionContract(atm_contract, Resolution.Minute)
  75. iv_indicator = self.iv(atm_contract, option_model=OptionPricingModelType.BLACK_SCHOLES)
  76. if iv_indicator is None or not iv_indicator.IsReady:
  77. self.debug(f"IV indicator not ready for contract: {atm_contract}")
  78. continue
  79. iv = iv_indicator.Current.Value
  80. if iv <= 0:
  81. self.debug(f"Invalid IV value for contract: {atm_contract}")
  82. continue
  83. self.debug(f"Contract {atm_contract} IV: {iv}")
  84. ivs[option_symbol] = iv
  85. total_iv += iv
  86. # Allocate capital proportionally based on IV
  87. if total_iv > 0:
  88. for option_symbol, iv in ivs.items():
  89. weight = iv / total_iv
  90. self.SetHoldings(option_symbol.Underlying, weight)
  91. else:
  92. self.debug("Total implied volatility is zero, no holdings set.")
  93. def OnData(self, data):
  94. """Handles incoming data."""
  95. pass
+ Expand

Author

Matt

December 2024