From 80f921576d8be6c5337b85ad52d3f9b7d98d8035 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 14 Apr 2026 11:47:48 -0700 Subject: [PATCH 1/2] Add VIX Mini Futures (VXM) support Adds complete LEAN support for VIX Mini Futures (VXM) traded on CBOE: - Add Futures.Indices.VIXMini = "VXM" constant - Add expiry function: 30 days before third Friday of following month - Add symbol properties: multiplier 100, tick 0.01 (USD) - Add 15 test date pairs for 2023-2025 to FuturesExpiryFunctionsTestData.xml - Add [TestCase(VIXMini, EightOClockChicagoTime)] to IndicesExpiryDateFunction test - Add market hours configuration mirroring VX trading hours Closes QuantConnect/Lean#6655 Co-Authored-By: Claude Haiku 4.5 --- Common/Securities/Future/Futures.cs | 6 + .../Future/FuturesExpiryFunctions.cs | 20 +++ Data/market-hours/market-hours-database.json | 118 ++++++++++++++++++ .../symbol-properties-database.csv | 1 + .../Futures/FuturesExpiryFunctionsTests.cs | 1 + .../FuturesExpiryFunctionsTestData.xml | Bin 1312968 -> 1317592 bytes 6 files changed, 146 insertions(+) diff --git a/Common/Securities/Future/Futures.cs b/Common/Securities/Future/Futures.cs index 9227eb4ff3da..2c228068d1a4 100644 --- a/Common/Securities/Future/Futures.cs +++ b/Common/Securities/Future/Futures.cs @@ -1388,6 +1388,12 @@ public static class Indices /// The symbol public const string VIX = "VX"; + /// + /// VIX Mini Futures + /// + /// The symbol + public const string VIXMini = "VXM"; + /// /// E-mini Russell 2000 Futures /// diff --git a/Common/Securities/Future/FuturesExpiryFunctions.cs b/Common/Securities/Future/FuturesExpiryFunctions.cs index c0cbc9fe9d74..84d12743d209 100644 --- a/Common/Securities/Future/FuturesExpiryFunctions.cs +++ b/Common/Securities/Future/FuturesExpiryFunctions.cs @@ -543,6 +543,26 @@ public static Func FuturesExpiryFunction(Symbol symbol) return expiryDate.Add(new TimeSpan(13, 0, 0)); }) }, + // VIX Mini Futures (VXM): https://cdn.cboe.com/resources/futures/VXM_Contract_Specifications.pdf + {Symbol.Create(Futures.Indices.VIXMini, SecurityType.Future, Market.CFE), (time => + { + // Trading occurs 30 days before S&P 500 option expiration (third Friday of contract month). + // Last trading day is the Wednesday 30 days prior to the third Friday of the contract month. + var market = Market.CFE; + var symbol = Futures.Indices.VIXMini; + var nextThirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time.AddMonths(1)); + var expiryDate = nextThirdFriday.AddDays(-30); + var holidays = FuturesExpiryUtilityFunctions.GetExpirationHolidays(market, symbol); + + // If the Wednesday or the third Friday are holidays, then it is moved to the previous day. + if (holidays.Contains(expiryDate) || holidays.Contains(nextThirdFriday)) + { + expiryDate = expiryDate.AddDays(-1); + } + // Trading hours for expiring VXM futures contracts end at 8:00 a.m. Chicago time on the final settlement date. + return expiryDate.Add(new TimeSpan(13, 0, 0)); + }) + }, // Bloomberg Commodity Index (AW): https://www.cmegroup.com/trading/agricultural/commodity-index/bloomberg-commodity-index_contract_specifications.html {Symbol.Create(Futures.Indices.BloombergCommodityIndex, SecurityType.Future, Market.CBOT), (time => { diff --git a/Data/market-hours/market-hours-database.json b/Data/market-hours/market-hours-database.json index 507913acf45a..db8c539cabbb 100644 --- a/Data/market-hours/market-hours-database.json +++ b/Data/market-hours/market-hours-database.json @@ -71632,6 +71632,124 @@ "saturday": [], "holidays": [] }, + "Future-cfe-VXM": { + "dataTimeZone": "UTC", + "exchangeTimeZone": "America/Chicago", + "sunday": [ + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "premarket" + } + ], + "monday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "tuesday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "wednesday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "thursday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "friday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + } + ], + "saturday": [], + "holidays": [] + }, "Future-cme-E3G": { "dataTimeZone": "UTC", "exchangeTimeZone": "America/New_York", diff --git a/Data/symbol-properties/symbol-properties-database.csv b/Data/symbol-properties/symbol-properties-database.csv index ef8d02d02d67..c26555bf7532 100644 --- a/Data/symbol-properties/symbol-properties-database.csv +++ b/Data/symbol-properties/symbol-properties-database.csv @@ -212,6 +212,7 @@ usa,AEX,index,,EUR,1,0.01,1 # for backwards compatibility, order here is important for futures, since we could have the same symbol for more than 1 market in which case Lean will use the first cfe,VX,future,VIX futures ,USD,1000.0,0.05,1.0 +cfe,VXM,future,VIX Mini Futures,USD,100.0,0.01,1.0 cbot,2YY,future,Micro 2-Year Yield Futures,USD,1000,0.001,1 cbot,5YY,future,Micro 5-Year Yield Futures,USD,1000,0.001,1 cbot,10Y,future,Micro 10-Year Yield Futures,USD,1000,0.001,1 diff --git a/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs b/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs index e1522d91b895..4bf4d3959d91 100644 --- a/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs +++ b/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs @@ -394,6 +394,7 @@ public void FinancialsExpiryDateFunction_WithDifferentDates_ShouldFollowContract [TestCase(QuantConnect.Securities.Futures.Indices.Russell2000EMini, NineThirtyEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.Nikkei225Dollar, FiveOClockPMEasternTime)] [TestCase(QuantConnect.Securities.Futures.Indices.VIX, EightOClockChicagoTime)] + [TestCase(QuantConnect.Securities.Futures.Indices.VIXMini, EightOClockChicagoTime)] [TestCase(QuantConnect.Securities.Futures.Indices.Nikkei225Yen, TwoThirtyPM)] [TestCase(QuantConnect.Securities.Futures.Indices.MSCITaiwanIndex, OneFortyFivePM)] [TestCase(QuantConnect.Securities.Futures.Indices.Nifty50, ThreeThirtyPM)] diff --git a/Tests/TestData/FuturesExpiryFunctionsTestData.xml b/Tests/TestData/FuturesExpiryFunctionsTestData.xml index 2e304b3f875e3d3056ef1285b9ec692e2f1e0fe2..7a1f7adec53ba5980f18f3ed50f501039481aa4b 100644 GIT binary patch delta 429 zcmXZVKS%;`6bJBkdfELcH4iGX;o2&YjKV>Pn#>06a*$~1%0%I;!%RwqhK7bF`4O)` z!mb;l$)$2?ZfSRjpe2G9=OE~H9mnT>-}mFa_j_(=-FvNlWyli{p)U*TkYc5=@M0WP zn1d+&bD+Z!>{Hr;9mvBTl<8EV0tF~i&QVU#S# >d52!PiQ1kqPXicsxV0_bSOEa zWjFxCNxHN6@7XDK7M|r&tnqP-(X>}U^V3_wSl46ym#rJVg2PFVjp`R)!@IU@qqgd; zv1HQ1;ETV`@+%Z>`8rBH(H&}MXh*yzSg56ab+n&28(SB?8p~!Z+?*E$TzV6DGz)@> zhYN~(*P9Wm_a=fDQ9 Date: Tue, 14 Apr 2026 13:42:26 -0700 Subject: [PATCH 2/2] Fix VXM expiry holiday validation and add margin file - Replace single `if` holiday check with `while` loop using `IsCommonBusinessDay()` to ensure the computed expiry date is always a valid tradable day (not just one step back) - Fix test data: 2025-03-19 -> 2025-03-18 (April 18 2025 is Good Friday, a CFE holiday, shifting the expiry back) - Add Data/future/cfe/margins/VXM.csv margin file Co-Authored-By: Claude Sonnet 4.6 --- .../Future/FuturesExpiryFunctions.cs | 9 +++++++-- Data/future/cfe/margins/VXM.csv | 3 +++ .../FuturesExpiryFunctionsTestData.xml | Bin 1317592 -> 1317592 bytes 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 Data/future/cfe/margins/VXM.csv diff --git a/Common/Securities/Future/FuturesExpiryFunctions.cs b/Common/Securities/Future/FuturesExpiryFunctions.cs index 84d12743d209..30d93fc2e437 100644 --- a/Common/Securities/Future/FuturesExpiryFunctions.cs +++ b/Common/Securities/Future/FuturesExpiryFunctions.cs @@ -554,8 +554,13 @@ public static Func FuturesExpiryFunction(Symbol symbol) var expiryDate = nextThirdFriday.AddDays(-30); var holidays = FuturesExpiryUtilityFunctions.GetExpirationHolidays(market, symbol); - // If the Wednesday or the third Friday are holidays, then it is moved to the previous day. - if (holidays.Contains(expiryDate) || holidays.Contains(nextThirdFriday)) + // If the reference 3rd Friday is a holiday, shift expiry back one day per spec. + if (holidays.Contains(nextThirdFriday)) + { + expiryDate = expiryDate.AddDays(-1); + } + // Ensure the computed expiry date is itself a valid tradable day. + while (holidays.Contains(expiryDate) || !expiryDate.IsCommonBusinessDay()) { expiryDate = expiryDate.AddDays(-1); } diff --git a/Data/future/cfe/margins/VXM.csv b/Data/future/cfe/margins/VXM.csv new file mode 100644 index 000000000000..18a4efd5ed7e --- /dev/null +++ b/Data/future/cfe/margins/VXM.csv @@ -0,0 +1,3 @@ +# we don't have historical information for this symbol +date,initial,maintenance +19900101,1200,960 diff --git a/Tests/TestData/FuturesExpiryFunctionsTestData.xml b/Tests/TestData/FuturesExpiryFunctionsTestData.xml index 7a1f7adec53ba5980f18f3ed50f501039481aa4b..85ed361251b95475e4fbf089f58878ff036e6562 100644 GIT binary patch delta 98 zcmcbyHQ>h9fDKZ0lVfxuCO51LYd%reexi;Mh?#(x8Hibcm=%cGfS4VKIe?fGh`E56 d8;E&;m=}oofS4bM1%Ox(h=sPFs1p{L0{|%BE++s0 delta 98 zcmcbyHQ>h9fDKZ0lm8?}Om0{g)_kI_{X`uj5HkTWGZ3=?F)I+W0Wmuea{w_X5OV=B dHxTmxF)tAF0Wm)i3jnbo5DRTTQ70@g2LN&6F8lxh