From 50e7db204c8736a0a51fe3eb1684bfc963208198 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 26 Feb 2026 11:59:57 -0400 Subject: [PATCH 1/6] Use universe settings when adding derivatives Use resolution, fill forward and extended market hours settings from universe settings when adding derivative securities (options, index options, futures and future options) --- ...ionsUniverseSettingsRegressionAlgorithm.cs | 153 ++++++++++++++++++ ...ionsUniverseSettingsRegressionAlgorithm.cs | 44 +++++ ...ionsUniverseSettingsRegressionAlgorithm.cs | 78 +++++++++ Algorithm/QCAlgorithm.cs | 34 ++-- .../Python/Wrappers/AlgorithmPythonWrapper.cs | 4 +- Common/Interfaces/IAlgorithm.cs | 4 +- 6 files changed, 298 insertions(+), 19 deletions(-) create mode 100644 Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs create mode 100644 Algorithm.CSharp/FuturesAndFutureOptionsUniverseSettingsRegressionAlgorithm.cs create mode 100644 Algorithm.CSharp/IndexOptionsUniverseSettingsRegressionAlgorithm.cs diff --git a/Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs b/Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs new file mode 100644 index 000000000000..ec172d79ec78 --- /dev/null +++ b/Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs @@ -0,0 +1,153 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +using System.Collections.Generic; +using System.Linq; +using QuantConnect.Data; +using QuantConnect.Interfaces; +using QuantConnect.Data.UniverseSelection; +using QuantConnect.Securities; +using System; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Regression algorithm asserting that options from universe are added with the same resolution, fill forward and extended market hours settings as the universe settings. + /// + public class EquityOptionsUniverseSettingsRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition + { + private SecurityType[] _securityTypes; + private HashSet _checkedSecurityTypes = new(); + + protected virtual DateTime TestStartDate => new DateTime(2015, 12, 24); + + public override void Initialize() + { + SetStartDate(TestStartDate); + SetEndDate(TestStartDate.AddDays(1)); + SetCash(100000); + + UniverseSettings.Resolution = Resolution.Daily; + UniverseSettings.FillForward = false; + UniverseSettings.ExtendedMarketHours = true; + + _securityTypes = AddSecurity(); + } + + protected virtual SecurityType[] AddSecurity() + { + var equity = AddEquity("GOOG"); + var option = AddOption(equity.Symbol); + option.SetFilter(u => u.StandardsOnly().Strikes(-2, +2).Expiration(0, 180)); + + return [option.Symbol.SecurityType]; + } + + public override void OnSecuritiesChanged(SecurityChanges changes) + { + var securities = changes.AddedSecurities.Where(x => _securityTypes.Contains(x.Type) && !x.Symbol.IsCanonical()).Select(x => x.Symbol).ToList(); + var configs = SubscriptionManager.Subscriptions.Where(x => securities.Contains(x.Symbol)); + + foreach (var config in configs) + { + if (config.Resolution != UniverseSettings.Resolution) + { + throw new RegressionTestException($"Config '{config}' resolution {config.Resolution} does not match universe settings resolution {UniverseSettings.Resolution}"); + } + + if (config.FillDataForward != UniverseSettings.FillForward) + { + throw new RegressionTestException($"Config '{config}' fill forward {config.FillDataForward} does not match universe settings fill forward {UniverseSettings.FillForward}"); + } + + if (config.ExtendedMarketHours != UniverseSettings.ExtendedMarketHours) + { + throw new RegressionTestException($"Config '{config}' extended market hours {config.ExtendedMarketHours} does not match universe settings extended market hours {UniverseSettings.ExtendedMarketHours}"); + } + + _checkedSecurityTypes.Add(config.SecurityType); + } + } + + public override void OnEndOfAlgorithm() + { + if (_checkedSecurityTypes.Count != _securityTypes.Length || !_securityTypes.All(_checkedSecurityTypes.Contains)) + { + throw new RegressionTestException($"Not all security types were checked. Expected: {string.Join(", ", _securityTypes)}. Checked: {string.Join(", ", _checkedSecurityTypes)}"); + } + } + + /// + /// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm. + /// + public bool CanRunLocally { get; } = true; + + /// + /// This is used by the regression test system to indicate which languages this algorithm is written in. + /// + public List Languages { get; } = new() { Language.CSharp }; + + /// + /// Data Points count of all timeslices of algorithm + /// + public virtual long DataPoints => 4694; + + /// + /// Data Points count of the algorithm history + /// + public int AlgorithmHistoryDataPoints => 0; + + /// + /// Final status of the algorithm + /// + public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed; + + /// + /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm + /// + public virtual Dictionary ExpectedStatistics => new Dictionary + { + {"Total Orders", "0"}, + {"Average Win", "0%"}, + {"Average Loss", "0%"}, + {"Compounding Annual Return", "0%"}, + {"Drawdown", "0%"}, + {"Expectancy", "0"}, + {"Start Equity", "100000"}, + {"End Equity", "100000"}, + {"Net Profit", "0%"}, + {"Sharpe Ratio", "0"}, + {"Sortino Ratio", "0"}, + {"Probabilistic Sharpe Ratio", "0%"}, + {"Loss Rate", "0%"}, + {"Win Rate", "0%"}, + {"Profit-Loss Ratio", "0"}, + {"Alpha", "0"}, + {"Beta", "0"}, + {"Annual Standard Deviation", "0"}, + {"Annual Variance", "0"}, + {"Information Ratio", "0"}, + {"Tracking Error", "0"}, + {"Treynor Ratio", "0"}, + {"Total Fees", "$0.00"}, + {"Estimated Strategy Capacity", "$0"}, + {"Lowest Capacity Asset", ""}, + {"Portfolio Turnover", "0%"}, + {"Drawdown Recovery", "0"}, + {"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"} + }; + } +} diff --git a/Algorithm.CSharp/FuturesAndFutureOptionsUniverseSettingsRegressionAlgorithm.cs b/Algorithm.CSharp/FuturesAndFutureOptionsUniverseSettingsRegressionAlgorithm.cs new file mode 100644 index 000000000000..eee258196fc6 --- /dev/null +++ b/Algorithm.CSharp/FuturesAndFutureOptionsUniverseSettingsRegressionAlgorithm.cs @@ -0,0 +1,44 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +using QuantConnect.Securities; +using System; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Regression algorithm asserting that futures and future options from universe are added with the same resolution, fill forward and extended market hours settings as the universe settings. + /// + public class FuturesAndFutureOptionsUniverseSettingsRegressionAlgorithm : EquityOptionsUniverseSettingsRegressionAlgorithm + { + protected override DateTime TestStartDate => new DateTime(2020, 01, 03); + + protected override SecurityType[] AddSecurity() + { + var futures = AddFuture(Futures.Indices.SP500EMini); + futures.SetFilter(0, 180); + + AddFutureOption(futures.Symbol, universe => universe.Strikes(-5, +5)); + + return [SecurityType.Future, SecurityType.FutureOption]; + } + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 456; + } +} diff --git a/Algorithm.CSharp/IndexOptionsUniverseSettingsRegressionAlgorithm.cs b/Algorithm.CSharp/IndexOptionsUniverseSettingsRegressionAlgorithm.cs new file mode 100644 index 000000000000..5bc4a843f1c3 --- /dev/null +++ b/Algorithm.CSharp/IndexOptionsUniverseSettingsRegressionAlgorithm.cs @@ -0,0 +1,78 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +using System; +using System.Collections.Generic; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Regression algorithm asserting that index options from universe are added with the same resolution, fill forward and extended market hours settings as the universe settings. + /// + public class IndexOptionsUniverseSettingsRegressionAlgorithm : EquityOptionsUniverseSettingsRegressionAlgorithm + { + protected override DateTime TestStartDate => new DateTime(2021, 01, 05); + + protected override SecurityType[] AddSecurity() + { + var index = AddIndex("SPX"); + var indexOption = AddOption(index.Symbol); + indexOption.SetFilter(u => u.Strikes(-2, +2).Expiration(0, 180)); + + return [indexOption.Symbol.SecurityType]; + } + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 46; + + /// + /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm + /// + public override Dictionary ExpectedStatistics => new Dictionary + { + {"Total Orders", "0"}, + {"Average Win", "0%"}, + {"Average Loss", "0%"}, + {"Compounding Annual Return", "0%"}, + {"Drawdown", "0%"}, + {"Expectancy", "0"}, + {"Start Equity", "100000"}, + {"End Equity", "100000"}, + {"Net Profit", "0%"}, + {"Sharpe Ratio", "0"}, + {"Sortino Ratio", "0"}, + {"Probabilistic Sharpe Ratio", "0%"}, + {"Loss Rate", "0%"}, + {"Win Rate", "0%"}, + {"Profit-Loss Ratio", "0"}, + {"Alpha", "0"}, + {"Beta", "0"}, + {"Annual Standard Deviation", "0"}, + {"Annual Variance", "0"}, + {"Information Ratio", "-16.713"}, + {"Tracking Error", "0.067"}, + {"Treynor Ratio", "0"}, + {"Total Fees", "$0.00"}, + {"Estimated Strategy Capacity", "$0"}, + {"Lowest Capacity Asset", ""}, + {"Portfolio Turnover", "0%"}, + {"Drawdown Recovery", "0"}, + {"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"} + }; + } +} diff --git a/Algorithm/QCAlgorithm.cs b/Algorithm/QCAlgorithm.cs index aa88d9d4a498..a9d86204bda5 100644 --- a/Algorithm/QCAlgorithm.cs +++ b/Algorithm/QCAlgorithm.cs @@ -1903,7 +1903,7 @@ public void SetTradeBuilder(ITradeBuilder tradeBuilder) /// The contract mapping mode to use for the security /// The price scaling mode to use for the security [DocumentationAttribute(AddingData)] - public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution = null, bool fillForward = true, bool extendedMarketHours = false, + public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) { return AddSecurity(securityType, ticker, resolution, fillForward, Security.NullLeverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); @@ -1922,7 +1922,7 @@ public Security AddSecurity(SecurityType securityType, string ticker, Resolution /// The price scaling mode to use for the security /// AddSecurity(SecurityType securityType, Symbol symbol, Resolution resolution, bool fillForward, decimal leverage, bool extendedMarketHours) [DocumentationAttribute(AddingData)] - public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution, bool fillForward, decimal leverage, bool extendedMarketHours, + public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution, bool? fillForward, decimal leverage, bool? extendedMarketHours, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) { return AddSecurity(securityType, ticker, resolution, null, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); @@ -1941,7 +1941,7 @@ public Security AddSecurity(SecurityType securityType, string ticker, Resolution /// The contract mapping mode to use for the security /// The price scaling mode to use for the security [DocumentationAttribute(AddingData)] - public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution, string market, bool fillForward, decimal leverage, bool extendedMarketHours, + public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution, string market, bool? fillForward, decimal leverage, bool? extendedMarketHours, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) { // if AddSecurity method is called to add an option or a future, we delegate a call to respective methods @@ -1990,7 +1990,7 @@ public Security AddSecurity(SecurityType securityType, string ticker, Resolution /// For example, 0 (default) will use the front month, 1 will use the back month contract /// The new Security that was added to the algorithm [DocumentationAttribute(AddingData)] - public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, + public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? fillForward = null, decimal leverage = Security.NullLeverage, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0) { // allow users to specify negative numbers, we get the abs of it @@ -2002,15 +2002,16 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool f } var isCanonical = symbol.IsCanonical(); + var securityFillForward = fillForward ?? true; + var securityExtendedMarketHours = extendedMarketHours ?? false; // Short-circuit to AddOptionContract because it will add the underlying if required if (!isCanonical && symbol.SecurityType.IsOption()) { - return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours); + return AddOptionContract(symbol, resolution, securityFillForward, leverage, securityExtendedMarketHours); } var securityResolution = resolution; - var securityFillForward = fillForward; if (isCanonical) { // canonical options and futures are daily only @@ -2027,7 +2028,7 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool f configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, securityResolution, securityFillForward, - extendedMarketHours, + securityExtendedMarketHours, isFilteredSubscription, dataNormalizationMode: dataNormalizationMode.Value, contractDepthOffset: (uint)contractDepthOffset); @@ -2037,7 +2038,7 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool f configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, securityResolution, securityFillForward, - extendedMarketHours, + securityExtendedMarketHours, isFilteredSubscription, contractDepthOffset: (uint)contractDepthOffset); } @@ -2055,7 +2056,9 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool f { var canonicalConfig = configs.First(); var universeSettingsResolution = resolution ?? UniverseSettings.Resolution; - var settings = new UniverseSettings(universeSettingsResolution, leverage, fillForward, extendedMarketHours, UniverseSettings.MinimumTimeInUniverse) + var universeSettingsFillForward = fillForward ?? UniverseSettings.FillForward; + var universeSettingsExtendedMarketHours = extendedMarketHours ?? UniverseSettings.ExtendedMarketHours; + var settings = new UniverseSettings(universeSettingsResolution, leverage, universeSettingsFillForward, universeSettingsExtendedMarketHours, UniverseSettings.MinimumTimeInUniverse) { Asynchronous = UniverseSettings.Asynchronous }; @@ -2071,7 +2074,8 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool f GetResolution(symbol, resolution, null), isCanonical: false); var continuousUniverseSettings = new UniverseSettings(settings) { - ExtendedMarketHours = extendedMarketHours, + ExtendedMarketHours = universeSettingsExtendedMarketHours, + FillForward = universeSettingsFillForward, DataMappingMode = dataMappingMode ?? UniverseSettings.GetUniverseMappingModeOrDefault(symbol.SecurityType, symbol.ID.Market), DataNormalizationMode = dataNormalizationMode ?? UniverseSettings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType), ContractDepthOffset = (int)contractOffset, @@ -2131,7 +2135,7 @@ public Equity AddEquity(string ticker, Resolution? resolution = null, string mar /// The requested leverage for this equity. Default is set by /// The new security [DocumentationAttribute(AddingData)] - public Option AddOption(string underlying, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) + public Option AddOption(string underlying, Resolution? resolution = null, string market = null, bool? fillForward = null, decimal leverage = Security.NullLeverage) { market = GetMarket(market, underlying, SecurityType.Option); @@ -2152,7 +2156,7 @@ public Option AddOption(string underlying, Resolution? resolution = null, string /// The new option security instance /// [DocumentationAttribute(AddingData)] - public Option AddOption(Symbol underlying, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) + public Option AddOption(Symbol underlying, Resolution? resolution = null, string market = null, bool? fillForward = null, decimal leverage = Security.NullLeverage) { return AddOption(underlying, null, resolution, market, fillForward, leverage); } @@ -2172,7 +2176,7 @@ public Option AddOption(Symbol underlying, Resolution? resolution = null, string /// [DocumentationAttribute(AddingData)] public Option AddOption(Symbol underlying, string targetOption, Resolution? resolution = null, - string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) + string market = null, bool? fillForward = null, decimal leverage = Security.NullLeverage) { var optionType = QuantConnect.Symbol.GetOptionTypeFromUnderlying(underlying); @@ -2215,7 +2219,7 @@ public Option AddOption(Symbol underlying, string targetOption, Resolution? reso /// The new security [DocumentationAttribute(AddingData)] public Future AddFuture(string ticker, Resolution? resolution = null, string market = null, - bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, + bool? fillForward = null, decimal leverage = Security.NullLeverage, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0) { market = GetMarket(market, ticker, SecurityType.Future); @@ -2283,7 +2287,7 @@ public void AddFutureOption(Symbol symbol, FuncThe leverage to apply to the option contract /// Use extended market hours data /// Option security - /// Symbol is canonical (i.e. a generic Symbol returned from or ) + /// Symbol is canonical (i.e. a generic Symbol returned from or ) [DocumentationAttribute(AddingData)] public Option AddFutureOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false) diff --git a/AlgorithmFactory/Python/Wrappers/AlgorithmPythonWrapper.cs b/AlgorithmFactory/Python/Wrappers/AlgorithmPythonWrapper.cs index 1d31c7398079..6af94c1b877b 100644 --- a/AlgorithmFactory/Python/Wrappers/AlgorithmPythonWrapper.cs +++ b/AlgorithmFactory/Python/Wrappers/AlgorithmPythonWrapper.cs @@ -593,7 +593,7 @@ public int ProjectId /// Use extended market hours data /// The contract mapping mode to use for the security /// The price scaling mode to use for the security - public Security AddSecurity(SecurityType securityType, string symbol, Resolution? resolution, string market, bool fillForward, decimal leverage, bool extendedMarketHours, + public Security AddSecurity(SecurityType securityType, string symbol, Resolution? resolution, string market, bool? fillForward, decimal leverage, bool? extendedMarketHours, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) => _baseAlgorithm.AddSecurity(securityType, symbol, resolution, market, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); @@ -611,7 +611,7 @@ public Security AddSecurity(SecurityType securityType, string symbol, Resolution /// The continuous contract desired offset from the current front month. /// For example, 0 (default) will use the front month, 1 will use the back month contract /// The new Security that was added to the algorithm - public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, + public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? fillForward = null, decimal leverage = Security.NullLeverage, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0) => _baseAlgorithm.AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode, contractDepthOffset); diff --git a/Common/Interfaces/IAlgorithm.cs b/Common/Interfaces/IAlgorithm.cs index 87489d7a1472..a4221bd440e5 100644 --- a/Common/Interfaces/IAlgorithm.cs +++ b/Common/Interfaces/IAlgorithm.cs @@ -710,7 +710,7 @@ InsightManager Insights /// ExtendedMarketHours send in data from 4am - 8pm, not used for FOREX /// The contract mapping mode to use for the security /// The price scaling mode to use for the security - Security AddSecurity(SecurityType securityType, string symbol, Resolution? resolution, string market, bool fillForward, decimal leverage, bool extendedMarketHours, + Security AddSecurity(SecurityType securityType, string symbol, Resolution? resolution, string market, bool? fillForward, decimal leverage, bool? extendedMarketHours, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null); /// @@ -726,7 +726,7 @@ Security AddSecurity(SecurityType securityType, string symbol, Resolution? resol /// The continuous contract desired offset from the current front month. /// For example, 0 (default) will use the front month, 1 will use the back month contract /// The new Security that was added to the algorithm - Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, + Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? fillForward = null, decimal leverage = Security.NullLeverage, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0); /// From e616dd8f6af10f073a98c2c906aa0bd590ea5109 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 26 Feb 2026 17:58:54 -0400 Subject: [PATCH 2/6] Minor python syntax check fix --- Algorithm.Python/IndexOptionCallButterflyAlgorithm.py | 3 +-- Algorithm.Python/IndexOptionPutButterflyAlgorithm.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Algorithm.Python/IndexOptionCallButterflyAlgorithm.py b/Algorithm.Python/IndexOptionCallButterflyAlgorithm.py index db29bd15c86a..715b13c0f7e7 100644 --- a/Algorithm.Python/IndexOptionCallButterflyAlgorithm.py +++ b/Algorithm.Python/IndexOptionCallButterflyAlgorithm.py @@ -65,6 +65,5 @@ def on_data(self, slice: Slice) -> None: call_butterfly = OptionStrategies.call_butterfly(self.spxw, otm_strike, atm_strike, itm_strike, expiry) price = sum([abs(self.securities[x.symbol].price * x.quantity) * self.multiplier for x in call_butterfly.underlying_legs]) if price > 0: - quantity = self.portfolio.total_portfolio_value // price + quantity = int(self.portfolio.total_portfolio_value // price) self.tickets = self.buy(call_butterfly, quantity, asynchronous=True) - \ No newline at end of file diff --git a/Algorithm.Python/IndexOptionPutButterflyAlgorithm.py b/Algorithm.Python/IndexOptionPutButterflyAlgorithm.py index f6e0d82a816a..5ee1a3055d54 100644 --- a/Algorithm.Python/IndexOptionPutButterflyAlgorithm.py +++ b/Algorithm.Python/IndexOptionPutButterflyAlgorithm.py @@ -65,6 +65,5 @@ def on_data(self, slice: Slice) -> None: put_butterfly = OptionStrategies.put_butterfly(self.spxw, itm_strike, atm_strike, otm_strike, expiry) price = sum([abs(self.securities[x.symbol].price * x.quantity) * self.multiplier for x in put_butterfly.underlying_legs]) if price > 0: - quantity = self.portfolio.total_portfolio_value // price + quantity = int(self.portfolio.total_portfolio_value // price) self.tickets = self.buy(put_butterfly, quantity, asynchronous=True) - \ No newline at end of file From 872de87db3a5c3be8b49b2500fbf0a7b1e74ad49 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 27 Feb 2026 11:19:02 -0400 Subject: [PATCH 3/6] Minor change --- Algorithm/QCAlgorithm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Algorithm/QCAlgorithm.cs b/Algorithm/QCAlgorithm.cs index a9d86204bda5..60d4b0e28dc7 100644 --- a/Algorithm/QCAlgorithm.cs +++ b/Algorithm/QCAlgorithm.cs @@ -2002,8 +2002,8 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? } var isCanonical = symbol.IsCanonical(); - var securityFillForward = fillForward ?? true; - var securityExtendedMarketHours = extendedMarketHours ?? false; + var securityFillForward = fillForward ?? UniverseSettings.FillForward; + var securityExtendedMarketHours = extendedMarketHours ?? UniverseSettings.ExtendedMarketHours; // Short-circuit to AddOptionContract because it will add the underlying if required if (!isCanonical && symbol.SecurityType.IsOption()) From 8187b65252d82bb553cba2492bff66a0d389d2b6 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 27 Feb 2026 13:00:20 -0400 Subject: [PATCH 4/6] Default resolution from universe settings for common lean types securities --- ...uxiliaryDataHandlersRegressionAlgorithm.cs | 2 +- ...ionsUniverseSettingsRegressionAlgorithm.cs | 2 +- .../FundamentalRegressionAlgorithm.cs | 32 +++++++++---------- Engine/DataFeeds/DataManager.cs | 26 ++++++++++++++- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/Algorithm.CSharp/AuxiliaryDataHandlersRegressionAlgorithm.cs b/Algorithm.CSharp/AuxiliaryDataHandlersRegressionAlgorithm.cs index 26b22be5da87..ac9b6cc3ba3d 100644 --- a/Algorithm.CSharp/AuxiliaryDataHandlersRegressionAlgorithm.cs +++ b/Algorithm.CSharp/AuxiliaryDataHandlersRegressionAlgorithm.cs @@ -119,7 +119,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 126222; + public long DataPoints => 17270; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs b/Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs index ec172d79ec78..98be0c1c70e1 100644 --- a/Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs +++ b/Algorithm.CSharp/EquityOptionsUniverseSettingsRegressionAlgorithm.cs @@ -103,7 +103,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public virtual long DataPoints => 4694; + public virtual long DataPoints => 4276; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/FundamentalRegressionAlgorithm.cs b/Algorithm.CSharp/FundamentalRegressionAlgorithm.cs index 9de985b97bf0..8ef65262f824 100644 --- a/Algorithm.CSharp/FundamentalRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FundamentalRegressionAlgorithm.cs @@ -222,7 +222,7 @@ public override void OnSecuritiesChanged(SecurityChanges changes) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 77169; + public long DataPoints => 70972; /// /// Data Points count of the algorithm history @@ -239,34 +239,34 @@ public override void OnSecuritiesChanged(SecurityChanges changes) /// public Dictionary ExpectedStatistics => new Dictionary { - {"Total Orders", "2"}, + {"Total Orders", "3"}, {"Average Win", "0%"}, {"Average Loss", "0%"}, - {"Compounding Annual Return", "-1.016%"}, + {"Compounding Annual Return", "-1.169%"}, {"Drawdown", "0.100%"}, {"Expectancy", "0"}, {"Start Equity", "100000"}, - {"End Equity", "99963.64"}, - {"Net Profit", "-0.036%"}, - {"Sharpe Ratio", "-4.731"}, - {"Sortino Ratio", "-6.776"}, - {"Probabilistic Sharpe Ratio", "24.373%"}, + {"End Equity", "99958.14"}, + {"Net Profit", "-0.042%"}, + {"Sharpe Ratio", "-3.451"}, + {"Sortino Ratio", "-4.933"}, + {"Probabilistic Sharpe Ratio", "27.530%"}, {"Loss Rate", "0%"}, {"Win Rate", "0%"}, {"Profit-Loss Ratio", "0"}, {"Alpha", "-0.013"}, - {"Beta", "0.023"}, - {"Annual Standard Deviation", "0.003"}, + {"Beta", "0.043"}, + {"Annual Standard Deviation", "0.005"}, {"Annual Variance", "0"}, {"Information Ratio", "0.607"}, - {"Tracking Error", "0.095"}, - {"Treynor Ratio", "-0.654"}, - {"Total Fees", "$2.00"}, + {"Tracking Error", "0.093"}, + {"Treynor Ratio", "-0.381"}, + {"Total Fees", "$3.00"}, {"Estimated Strategy Capacity", "$1900000000.00"}, {"Lowest Capacity Asset", "IBM R735QTJ8XC9X"}, - {"Portfolio Turnover", "0.30%"}, - {"Drawdown Recovery", "5"}, - {"OrderListHash", "9b3bf202c3d5707779f25e9c7f7fdc92"} + {"Portfolio Turnover", "0.45%"}, + {"Drawdown Recovery", "4"}, + {"OrderListHash", "63a37fcfe86bca2e037d9dbb9c531e43"} }; } } diff --git a/Engine/DataFeeds/DataManager.cs b/Engine/DataFeeds/DataManager.cs index 4f21d859231e..8ed7d121bc1c 100644 --- a/Engine/DataFeeds/DataManager.cs +++ b/Engine/DataFeeds/DataManager.cs @@ -41,6 +41,9 @@ public class DataManager : IAlgorithmSubscriptionManager, IDataFeedSubscriptionM private readonly IRegisteredSecurityDataTypesProvider _registeredTypesProvider; private readonly IDataPermissionManager _dataPermissionManager; private List _subscriptionDataConfigsEnumerator; + private readonly IAlgorithm _algorithm; + + private bool _unsupportedUniverseSettingsResolutionWarningSent; /// There is no ConcurrentHashSet collection in .NET, /// so we use ConcurrentDictionary with byte value to minimize memory usage @@ -78,6 +81,7 @@ public DataManager( _liveMode = liveMode; _registeredTypesProvider = registeredTypesProvider; _dataPermissionManager = dataPermissionManager; + _algorithm = algorithm; // wire ourselves up to receive notifications when universes are added/removed algorithm.UniverseManager.CollectionChanged += (sender, args) => @@ -596,7 +600,27 @@ public List Add( baseInstance.Symbol = symbol; if (!resolutionWasProvided) { - var defaultResolution = baseInstance.DefaultResolution(); + Resolution? defaultResolution = null; + if (LeanData.IsCommonLeanDataType(typeTuple.Item1)) + { + var res = _algorithm.UniverseSettings.Resolution; + + if (!_liveMode && !baseInstance.SupportedResolutions().Contains(res)) + { + if (!_unsupportedUniverseSettingsResolutionWarningSent) + { + _algorithm.Log($"Warning: Resolution {_algorithm.UniverseSettings.Resolution} for {symbol} and type {typeTuple.Item1} is not supported. " + + $"The data type default resolution will be tried instead"); + _unsupportedUniverseSettingsResolutionWarningSent = true; + } + } + else + { + defaultResolution = res; + } + } + + defaultResolution ??= baseInstance.DefaultResolution(); if (resolution.HasValue && resolution != defaultResolution) { // we are here because there are multiple 'dataTypes'. From bc109172cdce32f073aba3e6f0282f200a99ef14 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 27 Feb 2026 17:36:10 -0400 Subject: [PATCH 5/6] Minor test fix --- .../FundamentalUniverseSelectionModelTests.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Tests/Common/Data/Fundamental/FundamentalUniverseSelectionModelTests.cs b/Tests/Common/Data/Fundamental/FundamentalUniverseSelectionModelTests.cs index c69adc811762..ac81b133b086 100644 --- a/Tests/Common/Data/Fundamental/FundamentalUniverseSelectionModelTests.cs +++ b/Tests/Common/Data/Fundamental/FundamentalUniverseSelectionModelTests.cs @@ -27,30 +27,30 @@ public void PythonAlgorithmUsingCSharpSelection() { var parameter = new RegressionTests.AlgorithmStatisticsTestParameters("FundamentalUniverseSelectionAlgorithm", new Dictionary { - {PerformanceMetrics.TotalOrders, "2"}, + {PerformanceMetrics.TotalOrders, "3"}, {"Average Win", "0%"}, {"Average Loss", "0%"}, - {"Compounding Annual Return", "-2.391%"}, + {"Compounding Annual Return", "-3.123%"}, {"Drawdown", "0.100%"}, {"Expectancy", "0"}, - {"Net Profit", "-0.093%"}, - {"Sharpe Ratio", "-6.659"}, - {"Probabilistic Sharpe Ratio", "9.792%"}, + {"Net Profit", "-0.122%"}, + {"Sharpe Ratio", "-5.568"}, + {"Probabilistic Sharpe Ratio", "11.594%"}, {"Loss Rate", "0%"}, {"Win Rate", "0%"}, {"Profit-Loss Ratio", "0"}, - {"Alpha", "-0.02"}, - {"Beta", "0.028"}, - {"Annual Standard Deviation", "0.004"}, + {"Alpha", "-0.022"}, + {"Beta", "0.048"}, + {"Annual Standard Deviation", "0.006"}, {"Annual Variance", "0"}, - {"Information Ratio", "1.733"}, - {"Tracking Error", "0.095"}, - {"Treynor Ratio", "-0.914"}, - {"Total Fees", "$2.00"}, + {"Information Ratio", "1.712"}, + {"Tracking Error", "0.093"}, + {"Treynor Ratio", "-0.636"}, + {"Total Fees", "$3.00"}, {"Estimated Strategy Capacity", "$2300000000.00"}, {"Lowest Capacity Asset", "IBM R735QTJ8XC9X"}, - {"Portfolio Turnover", "0.28%"}, - {"OrderListHash", "b1cac6a779b73bde09a895c7cc970082"} + {"Portfolio Turnover", "0.42%"}, + {"OrderListHash", "9bd2017fdcf8f503e86dfa3bf6e33520"} }, Language.Python, AlgorithmStatus.Completed); From 6ec49e1f80b6f387c372e20bb2c17d1f965b260a Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 2 Mar 2026 11:32:59 -0400 Subject: [PATCH 6/6] Cleanup --- Algorithm/QCAlgorithm.cs | 18 ++++++++---------- Engine/DataFeeds/DataManager.cs | 5 ++--- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Algorithm/QCAlgorithm.cs b/Algorithm/QCAlgorithm.cs index 60d4b0e28dc7..28ae1f6e0802 100644 --- a/Algorithm/QCAlgorithm.cs +++ b/Algorithm/QCAlgorithm.cs @@ -2002,13 +2002,13 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? } var isCanonical = symbol.IsCanonical(); - var securityFillForward = fillForward ?? UniverseSettings.FillForward; - var securityExtendedMarketHours = extendedMarketHours ?? UniverseSettings.ExtendedMarketHours; + var securityFillForward = fillForward ??= UniverseSettings.FillForward; + extendedMarketHours ??= UniverseSettings.ExtendedMarketHours; // Short-circuit to AddOptionContract because it will add the underlying if required if (!isCanonical && symbol.SecurityType.IsOption()) { - return AddOptionContract(symbol, resolution, securityFillForward, leverage, securityExtendedMarketHours); + return AddOptionContract(symbol, resolution, securityFillForward, leverage, extendedMarketHours.Value); } var securityResolution = resolution; @@ -2028,7 +2028,7 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, securityResolution, securityFillForward, - securityExtendedMarketHours, + extendedMarketHours.Value, isFilteredSubscription, dataNormalizationMode: dataNormalizationMode.Value, contractDepthOffset: (uint)contractDepthOffset); @@ -2038,7 +2038,7 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, securityResolution, securityFillForward, - securityExtendedMarketHours, + extendedMarketHours.Value, isFilteredSubscription, contractDepthOffset: (uint)contractDepthOffset); } @@ -2056,9 +2056,7 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? { var canonicalConfig = configs.First(); var universeSettingsResolution = resolution ?? UniverseSettings.Resolution; - var universeSettingsFillForward = fillForward ?? UniverseSettings.FillForward; - var universeSettingsExtendedMarketHours = extendedMarketHours ?? UniverseSettings.ExtendedMarketHours; - var settings = new UniverseSettings(universeSettingsResolution, leverage, universeSettingsFillForward, universeSettingsExtendedMarketHours, UniverseSettings.MinimumTimeInUniverse) + var settings = new UniverseSettings(universeSettingsResolution, leverage, fillForward.Value, extendedMarketHours.Value, UniverseSettings.MinimumTimeInUniverse) { Asynchronous = UniverseSettings.Asynchronous }; @@ -2074,8 +2072,8 @@ public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool? GetResolution(symbol, resolution, null), isCanonical: false); var continuousUniverseSettings = new UniverseSettings(settings) { - ExtendedMarketHours = universeSettingsExtendedMarketHours, - FillForward = universeSettingsFillForward, + ExtendedMarketHours = extendedMarketHours.Value, + FillForward = fillForward.Value, DataMappingMode = dataMappingMode ?? UniverseSettings.GetUniverseMappingModeOrDefault(symbol.SecurityType, symbol.ID.Market), DataNormalizationMode = dataNormalizationMode ?? UniverseSettings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType), ContractDepthOffset = (int)contractOffset, diff --git a/Engine/DataFeeds/DataManager.cs b/Engine/DataFeeds/DataManager.cs index 8ed7d121bc1c..b37761a077e6 100644 --- a/Engine/DataFeeds/DataManager.cs +++ b/Engine/DataFeeds/DataManager.cs @@ -600,7 +600,7 @@ public List Add( baseInstance.Symbol = symbol; if (!resolutionWasProvided) { - Resolution? defaultResolution = null; + var defaultResolution = baseInstance.DefaultResolution(); if (LeanData.IsCommonLeanDataType(typeTuple.Item1)) { var res = _algorithm.UniverseSettings.Resolution; @@ -610,7 +610,7 @@ public List Add( if (!_unsupportedUniverseSettingsResolutionWarningSent) { _algorithm.Log($"Warning: Resolution {_algorithm.UniverseSettings.Resolution} for {symbol} and type {typeTuple.Item1} is not supported. " + - $"The data type default resolution will be tried instead"); + $"The data type default resolution '{defaultResolution}' will be used instead"); _unsupportedUniverseSettingsResolutionWarningSent = true; } } @@ -620,7 +620,6 @@ public List Add( } } - defaultResolution ??= baseInstance.DefaultResolution(); if (resolution.HasValue && resolution != defaultResolution) { // we are here because there are multiple 'dataTypes'.