I'm looking to backtest a strategy using futures options strangles but it has been a long time since I coded anything using Lean. When trying to examine the option chain for options that are within the delta range I am looking for, I can't get it to stop complaining that there is no data in OptionsChain. I presume I'm initializing something incorrectly but I can't quite figure out what. Any help would be appreciated….

  1. namespace QuantConnect.Algorithm.CSharp
  2. {
  3. public class FuturesStranglesTrader : QCAlgorithm
  4. {
  5. DateTime startDate = new(month: 1, day: 1, year: 2022);
  6. //TODO: make deltas optimization variables, for now, just way out of the money
  7. decimal deltaMin = 2; //absolute value
  8. decimal deltaMax = 3; //absolute value
  9. decimal startingCash = 100000;
  10. BrokerageName brokerageName = BrokerageName.Default;
  11. string symbol = Futures.Metals.Gold;
  12. Resolution resolution = Resolution.Hour;
  13. //Intentionally a single DTE
  14. TimeSpan minDTE = TimeSpan.FromDays(7);
  15. TimeSpan maxDTE = TimeSpan.FromDays(7);
  16. Future future;
  17. public override void Initialize()
  18. {
  19. SetBenchmark("QQQ");
  20. SetStartDate(startDate);
  21. SetCash(startingCash);
  22. SetBrokerageModel(brokerageName, AccountType.Margin);
  23. // Chart - Master Container for the Chart:
  24. var stockPlot = new Chart("Trade Plot");
  25. // On the Trade Plotter Chart we want 3 series: trades and price:
  26. var buyOrders = new Series("Buy", SeriesType.Scatter, 0);
  27. var sellOrders = new Series("Sell", SeriesType.Scatter, 0);
  28. var assetPrice = new Series("Price", SeriesType.Line, 0);
  29. // future =
  30. // AddFuture
  31. // (
  32. // symbol,
  33. // Resolution.Hour, //TODO: switch to minute or tick when things generally working
  34. // dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
  35. // dataMappingMode: DataMappingMode.OpenInterest,
  36. // contractDepthOffset: 0
  37. // );
  38. future = AddFuture(symbol);
  39. //TODO: SHOULDN'T need to set filter anymore due to new continuous contract mode
  40. //https://www.quantconnect.com/forum/discussion/12644/continuous-futures-support/p1
  41. future.SetFilter(0, 90); //TODO: What's the minimum I need to get 7 DTE options working?
  42. AddFutureOption(future.Symbol, FuturesOptionsSelector);
  43. SetWarmup(TimeSpan.FromDays(8), resolution); //TODO: do I need a warmup if I'm not using any indicators?
  44. }
  45. //TODO: Can filter to abs(delta) be applied here to speedup test?
  46. //Filter down to options that have a specific DTE
  47. private OptionFilterUniverse FuturesOptionsSelector(OptionFilterUniverse filter) =>
  48. filter.IncludeWeeklys().Expiration(minDTE, maxDTE);
  49. public override void OnData(Slice data)
  50. {
  51. if (!IsReady(data))
  52. return;
  53. Plot("Price Plot", "Price", data.Bars[future.Symbol].Price);
  54. foreach (var changedEvent in data.SymbolChangedEvents.Values)
  55. Log($"Underlying futures contract changed: {changedEvent.OldSymbol} -> {changedEvent.NewSymbol}"); //log futures contract rollovers
  56. //TODO: check for options assignments and close position if assigned
  57. if (!Portfolio.Invested)
  58. EnterPosition(data);
  59. else
  60. ExitPosition(data);
  61. }
  62. private bool IsReady(Slice data) =>
  63. data.HasData && !IsWarmingUp;
  64. private void EnterPosition(Slice data)
  65. {
  66. if (Portfolio.Invested)
  67. return; //Only allow one position at a time
  68. if (!data.OptionChains.Any() && !data.FutureChains.Any())
  69. return; //don't have anything to work with
  70. //Select the calls and puts that are within the delta range
  71. //TODO: THIS LINE ALWAYS FAILS THE BACKTEST AND BREAKPOINT ON IT DOESN'T WORK
  72. var availableContracts = data.OptionChains[future.Symbol].Where(contract => Math.Abs(contract.Greeks.Delta) >= deltaMin && Math.Abs(contract.Greeks.Delta) < deltaMax);
  73. //TODO: make sure I have these +/- deltas right for identifying contract type or use a different method
  74. //TODO: Preferrable to order available contracts by volume and/or open interest?
  75. var availablePuts = availableContracts.Where(contract => contract.Greeks.Delta < 0).OrderByDescending(contract => Math.Abs(contract.Greeks.Delta));
  76. var availableCalls = availableContracts.Where(contract => contract.Greeks.Delta > 0).OrderByDescending(contract => Math.Abs(contract.Greeks.Delta));
  77. var callLeg = availableCalls.FirstOrDefault();
  78. var putLeg = availablePuts.FirstOrDefault();
  79. if (callLeg is not null && putLeg is not null)
  80. {
  81. var optionStrategy = OptionStrategies.Strangle(future.Symbol, callLeg.Strike, putLeg.Strike, putLeg.Expiry);
  82. var order = Sell(optionStrategy, 1); //TODO: how to set as a limit order? How do I do max size with a specified margin buffer?
  83. Debug($"Sold strangle: {order}");
  84. }
  85. }
  86. private void ExitPosition(Slice data)
  87. {
  88. //For now, just letting it expire. I suspect a sell will only trigger every other week because we effectively look to open on the day one is expiring
  89. }
  90. }
  91. }
+ Expand

Author

Tb

November 2022