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/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 099fe2b1cf92..3cb3559c0a1e 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 { @@ -162,6 +163,102 @@ public void GetProgressAsPercentageShouldLogWhenProgressExceedsThreshold(Resolut Assert.IsTrue(logs.All(p => p >= 0 && p <= 100)); } + [Test] + public void RandomDataGeneratorCompletesSuccessfully() + { + 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)