From 7a0866aa4d222da8dae1c8a0026f43d61c41631f Mon Sep 17 00:00:00 2001 From: Romazes Date: Tue, 24 Jun 2025 23:54:35 +0300 Subject: [PATCH 1/2] feat: IDataQueueUniverseProvider --- MyCustomDataProvider.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/MyCustomDataProvider.cs b/MyCustomDataProvider.cs index 2ff770d..724ba7f 100644 --- a/MyCustomDataProvider.cs +++ b/MyCustomDataProvider.cs @@ -28,7 +28,7 @@ namespace QuantConnect.Lean.DataSource.MyCustom /// /// Implementation of Custom Data Provider /// - public class MyCustomDataProvider : SynchronizingHistoryProvider, IDataQueueHandler + public class MyCustomDataProvider : SynchronizingHistoryProvider, IDataQueueHandler, IDataQueueUniverseProvider { /// /// @@ -157,5 +157,20 @@ private bool CanSubscribe(Symbol symbol) throw new NotImplementedException(); } + + public IEnumerable LookupSymbols(Symbol symbol, bool includeExpired, string securityCurrency = null) + { + if (!symbol.SecurityType.IsOption()) + { + throw new ArgumentException($"Unsupported security type: {symbol.SecurityType}", nameof(symbol)); + } + + throw new NotImplementedException(); + } + + public bool CanPerformSelection() + { + throw new NotImplementedException(); + } } } From 1863ef2c74f28a1a82f4bb04be1c73c849906b9d Mon Sep 17 00:00:00 2001 From: Romazes Date: Tue, 24 Jun 2025 23:55:23 +0300 Subject: [PATCH 2/2] test:feat: implement multiple subscription on OptionChain --- tests/MyCustomDataQueueHandlerTests.cs | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/MyCustomDataQueueHandlerTests.cs b/tests/MyCustomDataQueueHandlerTests.cs index 2e0afbd..2bf70ac 100644 --- a/tests/MyCustomDataQueueHandlerTests.cs +++ b/tests/MyCustomDataQueueHandlerTests.cs @@ -25,6 +25,7 @@ using System.Threading.Tasks; using QuantConnect.Data.Market; using System.Collections.Generic; +using System.Collections.Concurrent; using QuantConnect.Lean.DataSource.MyCustom; namespace QuantConnect.DataLibrary.Tests @@ -74,6 +75,57 @@ public void StreamsData(Symbol symbol, Resolution resolution) Thread.Sleep(1_000); } + [TestCaseSource(nameof(TestParameters))] + public void StreamsOptionChainData(Symbol symbol, Resolution resolution) + { + Assert.Pass(); + + var canonicalOption = Symbol.CreateCanonicalOption(symbol); + + var dataProvider = new MyCustomDataProvider(); + + var configs = dataProvider.LookupSymbols(canonicalOption, true).SelectMany(optionContract => GetSubscriptionDataConfigs(optionContract, resolution)).ToList(); + + var symbolOpenInterest = new ConcurrentDictionary>(); + + foreach (var config in configs) + { + symbolOpenInterest[config.Symbol] = new Dictionary + { + [typeof(QuoteBar)] = 0, + [typeof(TradeBar)] = 0 + }; + + ProcessFeed( + dataProvider.Subscribe(config, (s, e) => { }), + (baseData) => + { + if (baseData != null) + { + Log.Trace($"{baseData}"); + switch (baseData) + { + case QuoteBar qb: + symbolOpenInterest[qb.Symbol][typeof(QuoteBar)] += 1; + break; + case TradeBar tb: + symbolOpenInterest[tb.Symbol][typeof(TradeBar)] += 1; + break; + } + } + }); + } + + Thread.Sleep(1_000); + + foreach (var config in configs) + { + dataProvider.Unsubscribe(config); + } + + Thread.Sleep(1_000); + } + private IEnumerable GetSubscriptionDataConfigs(Symbol symbol, Resolution resolution) { if (resolution == Resolution.Tick)