From 4e8ee7151f40199db3b3196b7590e9be663968bb Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Thu, 8 Jan 2026 16:34:05 -0500 Subject: [PATCH 1/5] Fix NullReferenceException in SecurityService --- Common/Securities/SecurityService.cs | 2 +- .../RandomDataGeneratorTests.cs | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Common/Securities/SecurityService.cs b/Common/Securities/SecurityService.cs index aab216a7f11b..1cfd4e77cc7a 100644 --- a/Common/Securities/SecurityService.cs +++ b/Common/Securities/SecurityService.cs @@ -343,7 +343,7 @@ private void InitializeSecurity(bool initializeSecurity, Security security, bool { if (initializeSecurity && !security.IsInitialized) { - if (seedSecurity && _algorithm.Settings.SeedInitialPrices) + if (seedSecurity && _algorithm != null && _algorithm.Settings.SeedInitialPrices) { AlgorithmUtils.SeedSecurities([security], _algorithm); } diff --git a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs index 099fe2b1cf92..c2e557e5b241 100644 --- a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs +++ b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs @@ -162,6 +162,61 @@ public void GetProgressAsPercentageShouldLogWhenProgressExceedsThreshold(Resolut Assert.IsTrue(logs.All(p => p >= 0 && p <= 100)); } + [Test] + public void SecurityServiceShouldNotThrowWhenAlgorithmIsNullInDataGenerationContext() + { + var settings = RandomDataGeneratorSettings.FromCommandLineArguments( + "20240101", + "20240102", + "1", + "usa", + "Equity", + "Minute", + "Dense", + "true", + "1", + null, + "5.0", + "30.0", + "15.0", + "60.0", + "30.0", + "BaroneAdesiWhaleyApproximationEngine", + "Daily", + "1", + new List() + ); + + var securityManager = new SecurityManager(new TimeKeeper(settings.Start, new[] { TimeZones.Utc })); + + var securityService = new SecurityService( + new CashBook(), + MarketHoursDatabase.FromDataFolder(), + SymbolPropertiesDatabase.FromDataFolder(), + new SecurityInitializerProvider(new FuncSecurityInitializer(security => { })), + RegisteredSecurityDataTypesProvider.Null, + new SecurityCacheProvider( + new SecurityPortfolioManager(securityManager, + new SecurityTransactionManager(null, securityManager), + new AlgorithmSettings())), + new MapFilePrimaryExchangeProvider( + Composer.Instance.GetExportedValueByTypeName( + Config.Get("map-file-provider", "LocalDiskMapFileProvider"))), + algorithm: null + ); + + securityManager.SetSecurityService(securityService); + + Assert.DoesNotThrow(() => + { + var symbol = Symbol.Create("TEST", SecurityType.Equity, Market.USA); + var security = securityManager.CreateSecurity( + symbol, + new List(), + underlying: null); + }); + } + private static readonly IRiskFreeInterestRateModel _interestRateProvider = new InterestRateProvider(); private static SecurityService GetSecurityService(RandomDataGeneratorSettings settings, SecurityManager securityManager) From 4093e7ec0a7e49bab3083a9ca21d7bc0d7cb584c Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Thu, 8 Jan 2026 16:47:08 -0500 Subject: [PATCH 2/5] Simplify the unit test --- .../RandomDataGeneratorTests.cs | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs index c2e557e5b241..f5f5ba041bab 100644 --- a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs +++ b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs @@ -163,31 +163,10 @@ public void GetProgressAsPercentageShouldLogWhenProgressExceedsThreshold(Resolut } [Test] - public void SecurityServiceShouldNotThrowWhenAlgorithmIsNullInDataGenerationContext() + public void SecurityServiceShouldNotThrowWhenAlgorithmIsNull() { - var settings = RandomDataGeneratorSettings.FromCommandLineArguments( - "20240101", - "20240102", - "1", - "usa", - "Equity", - "Minute", - "Dense", - "true", - "1", - null, - "5.0", - "30.0", - "15.0", - "60.0", - "30.0", - "BaroneAdesiWhaleyApproximationEngine", - "Daily", - "1", - new List() - ); - - var securityManager = new SecurityManager(new TimeKeeper(settings.Start, new[] { TimeZones.Utc })); + var startDate = new DateTime(2024, 1, 1); + var securityManager = new SecurityManager(new TimeKeeper(startDate, new[] { TimeZones.Utc })); var securityService = new SecurityService( new CashBook(), @@ -210,10 +189,7 @@ public void SecurityServiceShouldNotThrowWhenAlgorithmIsNullInDataGenerationCont Assert.DoesNotThrow(() => { var symbol = Symbol.Create("TEST", SecurityType.Equity, Market.USA); - var security = securityManager.CreateSecurity( - symbol, - new List(), - underlying: null); + var security = securityManager.CreateSecurity(symbol, new List(), underlying: null); }); } From 6b3f95af0dcf22cefe34631606cca1eda222c779 Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Fri, 9 Jan 2026 02:50:39 -0500 Subject: [PATCH 3/5] Add unit test for RandomDataGenerator --- .../RandomDataGeneratorTests.cs | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs index f5f5ba041bab..58d1409f2bd5 100644 --- a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs +++ b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs @@ -29,6 +29,7 @@ using static QuantConnect.ToolBox.RandomDataGenerator.RandomDataGenerator; using QuantConnect.Algorithm; using System.Linq; +using System.IO; namespace QuantConnect.Tests.ToolBox.RandomDataGenerator { @@ -193,6 +194,102 @@ public void SecurityServiceShouldNotThrowWhenAlgorithmIsNull() }); } + [Test] + public void RandomDataGeneratorGeneratesDataSuccessfully() + { + var tempFolder = Path.Combine(Path.GetTempPath(), $"LeanTest_{Guid.NewGuid()}"); + var originalDataFolder = Config.Get("data-folder"); + try + { + Directory.CreateDirectory(tempFolder); + Config.Set("data-folder", tempFolder); + Globals.Reset(); + + var hourPath = Path.Combine(tempFolder, "equity", "usa", "hour"); + var dailyPath = Path.Combine(tempFolder, "equity", "usa", "daily"); + var factorFilesPath = Path.Combine(tempFolder, "equity", "usa", "factor_files"); + var mapFilesPath = Path.Combine(tempFolder, "equity", "usa", "map_files"); + + // Create the required folders + Directory.CreateDirectory(hourPath); + Directory.CreateDirectory(dailyPath); + Directory.CreateDirectory(factorFilesPath); + Directory.CreateDirectory(mapFilesPath); + + var settings = new RandomDataGeneratorSettings + { + Start = new DateTime(2024, 1, 1, 9, 30, 0), + End = new DateTime(2024, 1, 2, 16, 0, 0), + SymbolCount = 1, + Market = "usa", + SecurityType = SecurityType.Equity, + Resolution = Resolution.Hour, + DataDensity = DataDensity.Dense, + IncludeCoarse = false, + QuoteTradeRatio = 1.0, + RandomSeed = 123456, + HasDividendsPercentage = 0, + HasSplitsPercentage = 0, + HasIpoPercentage = 0, + HasRenamePercentage = 0, + Tickers = new List() { "AAPL" } + }; + + var generator = GetGenerator(settings); + + Assert.DoesNotThrow(() => generator.Run()); + + var allFiles = Directory.GetFiles(tempFolder, "*", SearchOption.AllDirectories); + Assert.Greater(allFiles.Length, 0); + + var hourFiles = Directory.GetFiles(hourPath, "*.zip"); + Assert.Greater(hourFiles.Length, 0); + } + finally + { + Config.Set("data-folder", originalDataFolder); + Globals.Reset(); + Directory.Delete(tempFolder, true); + } + } + + private static QuantConnect.ToolBox.RandomDataGenerator.RandomDataGenerator GetGenerator(RandomDataGeneratorSettings settings) + { + var securityManager = new SecurityManager(new TimeKeeper(settings.Start, new[] { TimeZones.Utc })); + + var securityService = new SecurityService( + new CashBook(), + MarketHoursDatabase.FromDataFolder(), + SymbolPropertiesDatabase.FromDataFolder(), + new SecurityInitializerProvider(new FuncSecurityInitializer(security => + { + // init price + security.SetMarketPrice(new Tick(settings.Start, security.Symbol, 100, 100)); + security.SetMarketPrice(new OpenInterest(settings.Start, security.Symbol, 10000)); + + // from settings + security.VolatilityModel = new StandardDeviationOfReturnsVolatilityModel(settings.VolatilityModelResolution); + + // from settings + if (security is Option option) + { + option.PriceModel = OptionPriceModels.Create(settings.OptionPriceEngineName, + _interestRateProvider.GetRiskFreeRate(settings.Start, settings.End)); + } + })), + RegisteredSecurityDataTypesProvider.Null, + new SecurityCacheProvider( + new SecurityPortfolioManager(securityManager, new SecurityTransactionManager(null, securityManager), new AlgorithmSettings())), + new MapFilePrimaryExchangeProvider(Composer.Instance.GetExportedValueByTypeName(Config.Get("map-file-provider", "LocalDiskMapFileProvider"))) + ); + + securityManager.SetSecurityService(securityService); + + var generator = new QuantConnect.ToolBox.RandomDataGenerator.RandomDataGenerator(); + generator.Init(settings, securityManager); + return generator; + } + private static readonly IRiskFreeInterestRateModel _interestRateProvider = new InterestRateProvider(); private static SecurityService GetSecurityService(RandomDataGeneratorSettings settings, SecurityManager securityManager) From 8239d6a5984d7600f2c9633472048292f9e2ce8e Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Fri, 9 Jan 2026 03:32:30 -0500 Subject: [PATCH 4/5] Improve unit test name --- Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs index 58d1409f2bd5..7ce130401f85 100644 --- a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs +++ b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs @@ -195,7 +195,7 @@ public void SecurityServiceShouldNotThrowWhenAlgorithmIsNull() } [Test] - public void RandomDataGeneratorGeneratesDataSuccessfully() + public void RandomDataGeneratorCompletesSuccessfully() { var tempFolder = Path.Combine(Path.GetTempPath(), $"LeanTest_{Guid.NewGuid()}"); var originalDataFolder = Config.Get("data-folder"); From 3af49f1c3b92e8b75787796ae000f1f36dd89fd3 Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Fri, 9 Jan 2026 14:54:19 -0500 Subject: [PATCH 5/5] Solve review comments --- .../Common/Securities/SecurityServiceTests.cs | 36 ++++++++++++++++++- .../RandomDataGeneratorTests.cs | 31 ---------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/Tests/Common/Securities/SecurityServiceTests.cs b/Tests/Common/Securities/SecurityServiceTests.cs index 5e8eb8019b7c..94625b7ac20b 100644 --- a/Tests/Common/Securities/SecurityServiceTests.cs +++ b/Tests/Common/Securities/SecurityServiceTests.cs @@ -19,6 +19,7 @@ using Moq; using NUnit.Framework; using QuantConnect.Algorithm.CSharp; +using QuantConnect.Configuration; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Market; @@ -26,6 +27,8 @@ using QuantConnect.Interfaces; using QuantConnect.Securities; using QuantConnect.Tests.Engine.DataFeeds; +using QuantConnect.ToolBox.RandomDataGenerator; +using QuantConnect.Util; namespace QuantConnect.Tests.Common.Securities { @@ -216,7 +219,7 @@ public void CreatesEquityOptionWithContractMultiplierEqualsToContractUnitOfTrade var equityOptionSecurity = (QuantConnect.Securities.Option.Option)_securityService.CreateSecurity(equityOption, configs, 1.0m); Assert.AreEqual(100, equityOptionSecurity.ContractMultiplier); - Assert.AreEqual(100,equityOptionSecurity.ContractUnitOfTrade); + Assert.AreEqual(100, equityOptionSecurity.ContractUnitOfTrade); } [Test] @@ -277,5 +280,36 @@ public void AddPrimaryExchangeToSecurityObject() Assert.AreEqual(equity.Subscriptions.First().TickType, TickType.Trade); Assert.AreEqual(((QuantConnect.Securities.Equity.Equity)equity).PrimaryExchange, Exchange.NASDAQ); } + + [Test] + public void CreateSecurityDoesNotThrowWithNullAlgorithm() + { + var startDate = new DateTime(2024, 1, 1); + var securityManager = new SecurityManager(new TimeKeeper(startDate, new[] { TimeZones.Utc })); + + var securityService = new SecurityService( + new CashBook(), + MarketHoursDatabase.FromDataFolder(), + SymbolPropertiesDatabase.FromDataFolder(), + new SecurityInitializerProvider(new FuncSecurityInitializer(security => { })), + RegisteredSecurityDataTypesProvider.Null, + new SecurityCacheProvider( + new SecurityPortfolioManager(securityManager, + new SecurityTransactionManager(null, securityManager), + new AlgorithmSettings())), + new MapFilePrimaryExchangeProvider( + Composer.Instance.GetExportedValueByTypeName( + Config.Get("map-file-provider", "LocalDiskMapFileProvider"))), + algorithm: null + ); + + securityManager.SetSecurityService(securityService); + + Assert.DoesNotThrow(() => + { + var symbol = Symbol.Create("TEST", SecurityType.Equity, Market.USA); + var security = securityManager.CreateSecurity(symbol, new List(), underlying: null); + }); + } } } diff --git a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs index 7ce130401f85..3cb3559c0a1e 100644 --- a/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs +++ b/Tests/ToolBox/RandomDataGenerator/RandomDataGeneratorTests.cs @@ -163,37 +163,6 @@ public void GetProgressAsPercentageShouldLogWhenProgressExceedsThreshold(Resolut Assert.IsTrue(logs.All(p => p >= 0 && p <= 100)); } - [Test] - public void SecurityServiceShouldNotThrowWhenAlgorithmIsNull() - { - var startDate = new DateTime(2024, 1, 1); - var securityManager = new SecurityManager(new TimeKeeper(startDate, new[] { TimeZones.Utc })); - - var securityService = new SecurityService( - new CashBook(), - MarketHoursDatabase.FromDataFolder(), - SymbolPropertiesDatabase.FromDataFolder(), - new SecurityInitializerProvider(new FuncSecurityInitializer(security => { })), - RegisteredSecurityDataTypesProvider.Null, - new SecurityCacheProvider( - new SecurityPortfolioManager(securityManager, - new SecurityTransactionManager(null, securityManager), - new AlgorithmSettings())), - new MapFilePrimaryExchangeProvider( - Composer.Instance.GetExportedValueByTypeName( - Config.Get("map-file-provider", "LocalDiskMapFileProvider"))), - algorithm: null - ); - - securityManager.SetSecurityService(securityService); - - Assert.DoesNotThrow(() => - { - var symbol = Symbol.Create("TEST", SecurityType.Equity, Market.USA); - var security = securityManager.CreateSecurity(symbol, new List(), underlying: null); - }); - } - [Test] public void RandomDataGeneratorCompletesSuccessfully() {