diff --git a/Algorithm.CSharp/HistoricalReturnsAlphaModelFrameworkRegressionAlgorithm.cs b/Algorithm.CSharp/HistoricalReturnsAlphaModelFrameworkRegressionAlgorithm.cs index 217e431a20f2..183ec9a5a947 100644 --- a/Algorithm.CSharp/HistoricalReturnsAlphaModelFrameworkRegressionAlgorithm.cs +++ b/Algorithm.CSharp/HistoricalReturnsAlphaModelFrameworkRegressionAlgorithm.cs @@ -32,7 +32,7 @@ public override void Initialize() public override void OnEndOfAlgorithm() { - const int expected = 78; + const int expected = 80; if (Insights.TotalCount != expected) { throw new RegressionTestException($"The total number of insights should be {expected}. Actual: {Insights.TotalCount}"); @@ -54,32 +54,32 @@ public override void OnEndOfAlgorithm() public override Dictionary ExpectedStatistics => new() { {"Total Orders", "69"}, - {"Average Win", "0.18%"}, + {"Average Win", "0.17%"}, {"Average Loss", "-0.15%"}, - {"Compounding Annual Return", "42.429%"}, + {"Compounding Annual Return", "46.315%"}, {"Drawdown", "0.900%"}, - {"Expectancy", "0.367"}, + {"Expectancy", "0.292"}, {"Start Equity", "100000"}, - {"End Equity", "102949.54"}, - {"Net Profit", "2.950%"}, - {"Sharpe Ratio", "5.164"}, - {"Sortino Ratio", "8.556"}, - {"Probabilistic Sharpe Ratio", "90.449%"}, - {"Loss Rate", "38%"}, - {"Win Rate", "62%"}, - {"Profit-Loss Ratio", "1.22"}, - {"Alpha", "0.306"}, - {"Beta", "-0.129"}, - {"Annual Standard Deviation", "0.055"}, + {"End Equity", "103177.61"}, + {"Net Profit", "3.178%"}, + {"Sharpe Ratio", "5.515"}, + {"Sortino Ratio", "9.319"}, + {"Probabilistic Sharpe Ratio", "91.936%"}, + {"Loss Rate", "40%"}, + {"Win Rate", "60%"}, + {"Profit-Loss Ratio", "1.15"}, + {"Alpha", "0.333"}, + {"Beta", "-0.138"}, + {"Annual Standard Deviation", "0.056"}, {"Annual Variance", "0.003"}, - {"Information Ratio", "1.181"}, - {"Tracking Error", "0.077"}, - {"Treynor Ratio", "-2.186"}, + {"Information Ratio", "1.488"}, + {"Tracking Error", "0.078"}, + {"Treynor Ratio", "-2.221"}, {"Total Fees", "$267.37"}, - {"Estimated Strategy Capacity", "$6000000.00"}, + {"Estimated Strategy Capacity", "$4000000.00"}, {"Lowest Capacity Asset", "AIG R735QTJ8XC9X"}, - {"Portfolio Turnover", "65.87%"}, - {"OrderListHash", "15bd0a959060b7ec5d77646e7e585f04"} + {"Portfolio Turnover", "65.85%"}, + {"OrderListHash", "a30e9c3a0143e548439788896d94a670"} }; } } diff --git a/Algorithm.CSharp/OptionChainUniverseRemovalRegressionAlgorithm.cs b/Algorithm.CSharp/OptionChainUniverseRemovalRegressionAlgorithm.cs index 6ad159c66ec5..9a81573a6646 100644 --- a/Algorithm.CSharp/OptionChainUniverseRemovalRegressionAlgorithm.cs +++ b/Algorithm.CSharp/OptionChainUniverseRemovalRegressionAlgorithm.cs @@ -131,7 +131,7 @@ public override void OnSecuritiesChanged(SecurityChanges changes) } } } - // We expect the equity to get Removed + // De equity is deselected, but it should not be removed since the options universe still selects it as the underlying else if (Time.Day == 7) { if (Time.Hour != 0) @@ -141,12 +141,12 @@ public override void OnSecuritiesChanged(SecurityChanges changes) // Options can be selected/deselected on this day, but the equity should be removed - if (changes.RemovedSecurities.Count == 0 || !changes.RemovedSecurities.Any(x => x.Symbol == _aapl)) + if (changes.RemovedSecurities.Count == 0 || changes.RemovedSecurities.Any(x => x.Symbol == _aapl)) { throw new RegressionTestException($"Unexpected SecurityChanges: {changes}"); } } - // We expect the options to get Removed, happens in the next loop after removing the equity + // We expect the options to get Removed, as well as the underlying, happens in the next loop after removing the equity else if (Time.Day == 9) { if (Time.Hour != 0) diff --git a/Algorithm.CSharp/PearsonCorrelationPairsTradingAlphaModelFrameworkAlgorithm.cs b/Algorithm.CSharp/PearsonCorrelationPairsTradingAlphaModelFrameworkAlgorithm.cs index 0c20fa6608e7..5bf605cb57e2 100644 --- a/Algorithm.CSharp/PearsonCorrelationPairsTradingAlphaModelFrameworkAlgorithm.cs +++ b/Algorithm.CSharp/PearsonCorrelationPairsTradingAlphaModelFrameworkAlgorithm.cs @@ -60,9 +60,10 @@ public override void OnEndOfAlgorithm() { // We have removed all securities from the universe. The Alpha Model should remove the consolidator var consolidatorCount = SubscriptionManager.Subscriptions.Sum(s => s.Consolidators.Count); - if (consolidatorCount > 0) + // Expect 2 consolidators for SPY and AIG, which where manually added at the start + if (consolidatorCount != 2) { - throw new RegressionTestException($"The number of consolidator is should be zero. Actual: {consolidatorCount}"); + throw new RegressionTestException($"The number of consolidator is should be 2. Actual: {consolidatorCount}"); } } @@ -84,7 +85,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of the algorithm history /// - public int AlgorithmHistoryDataPoints => 1008; + public int AlgorithmHistoryDataPoints => 1512; /// /// Final status of the algorithm @@ -97,32 +98,32 @@ public override void OnEndOfAlgorithm() public Dictionary ExpectedStatistics => new Dictionary { {"Total Orders", "6"}, - {"Average Win", "0.99%"}, - {"Average Loss", "-0.84%"}, - {"Compounding Annual Return", "24.021%"}, - {"Drawdown", "0.800%"}, - {"Expectancy", "0.089"}, + {"Average Win", "0.82%"}, + {"Average Loss", "-0.39%"}, + {"Compounding Annual Return", "43.360%"}, + {"Drawdown", "0.700%"}, + {"Expectancy", "0.546"}, {"Start Equity", "100000"}, - {"End Equity", "100295.35"}, - {"Net Profit", "0.295%"}, - {"Sharpe Ratio", "4.205"}, + {"End Equity", "100494.63"}, + {"Net Profit", "0.495%"}, + {"Sharpe Ratio", "10.477"}, {"Sortino Ratio", "0"}, - {"Probabilistic Sharpe Ratio", "61.706%"}, + {"Probabilistic Sharpe Ratio", "85.907%"}, {"Loss Rate", "50%"}, {"Win Rate", "50%"}, - {"Profit-Loss Ratio", "1.18"}, - {"Alpha", "0.08"}, - {"Beta", "0.06"}, - {"Annual Standard Deviation", "0.047"}, - {"Annual Variance", "0.002"}, - {"Information Ratio", "-8.305"}, - {"Tracking Error", "0.214"}, - {"Treynor Ratio", "3.313"}, - {"Total Fees", "$31.60"}, - {"Estimated Strategy Capacity", "$3200000.00"}, + {"Profit-Loss Ratio", "2.09"}, + {"Alpha", "0.227"}, + {"Beta", "0.066"}, + {"Annual Standard Deviation", "0.034"}, + {"Annual Variance", "0.001"}, + {"Information Ratio", "-7.7"}, + {"Tracking Error", "0.21"}, + {"Treynor Ratio", "5.41"}, + {"Total Fees", "$25.78"}, + {"Estimated Strategy Capacity", "$3300000.00"}, {"Lowest Capacity Asset", "AIG R735QTJ8XC9X"}, - {"Portfolio Turnover", "80.47%"}, - {"OrderListHash", "476d54ac7295563a79add3a80310a0a8"} + {"Portfolio Turnover", "60.55%"}, + {"OrderListHash", "0eb251234d0fa772130bb341457091b4"} }; } } diff --git a/Algorithm.CSharp/RawPricesUniverseRegressionAlgorithm.cs b/Algorithm.CSharp/RawPricesUniverseRegressionAlgorithm.cs index 7d354a6566d3..afbd9d990917 100644 --- a/Algorithm.CSharp/RawPricesUniverseRegressionAlgorithm.cs +++ b/Algorithm.CSharp/RawPricesUniverseRegressionAlgorithm.cs @@ -91,12 +91,12 @@ public override void OnSecuritiesChanged(SecurityChanges changes) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 135; + public long DataPoints => 144; /// /// Data Points count of the algorithm history /// - public int AlgorithmHistoryDataPoints => 30; + public int AlgorithmHistoryDataPoints => 90; /// /// Final status of the algorithm @@ -108,33 +108,33 @@ public override void OnSecuritiesChanged(SecurityChanges changes) /// public Dictionary ExpectedStatistics => new Dictionary { - {"Total Orders", "12"}, - {"Average Win", "0.34%"}, - {"Average Loss", "-0.14%"}, - {"Compounding Annual Return", "4.586%"}, - {"Drawdown", "0.700%"}, - {"Expectancy", "0.158"}, + {"Total Orders", "33"}, + {"Average Win", "0.22%"}, + {"Average Loss", "-0.31%"}, + {"Compounding Annual Return", "-25.889%"}, + {"Drawdown", "2.200%"}, + {"Expectancy", "-0.199"}, {"Start Equity", "50000"}, - {"End Equity", "50090.17"}, - {"Net Profit", "0.180%"}, - {"Sharpe Ratio", "5.991"}, - {"Sortino Ratio", "0"}, - {"Probabilistic Sharpe Ratio", "99.393%"}, - {"Loss Rate", "67%"}, - {"Win Rate", "33%"}, - {"Profit-Loss Ratio", "2.47"}, - {"Alpha", "0.17"}, - {"Beta", "0.029"}, - {"Annual Standard Deviation", "0.028"}, - {"Annual Variance", "0.001"}, - {"Information Ratio", "2.734"}, - {"Tracking Error", "0.098"}, - {"Treynor Ratio", "5.803"}, + {"End Equity", "49401.67"}, + {"Net Profit", "-1.197%"}, + {"Sharpe Ratio", "-1.036"}, + {"Sortino Ratio", "-0.681"}, + {"Probabilistic Sharpe Ratio", "31.423%"}, + {"Loss Rate", "53%"}, + {"Win Rate", "47%"}, + {"Profit-Loss Ratio", "0.72"}, + {"Alpha", "-0.02"}, + {"Beta", "0.682"}, + {"Annual Standard Deviation", "0.086"}, + {"Annual Variance", "0.007"}, + {"Information Ratio", "0.195"}, + {"Tracking Error", "0.063"}, + {"Treynor Ratio", "-0.131"}, {"Total Fees", "$0.00"}, - {"Estimated Strategy Capacity", "$99000000.00"}, + {"Estimated Strategy Capacity", "$220000000.00"}, {"Lowest Capacity Asset", "AIG R735QTJ8XC9X"}, - {"Portfolio Turnover", "15.96%"}, - {"OrderListHash", "d915ae36ce856457b32ebbfce4581281"} + {"Portfolio Turnover", "44.69%"}, + {"OrderListHash", "93b3a1c9d6234bf616f0a69a5129a781"} }; } } diff --git a/Algorithm.CSharp/RegressionTests/Universes/ETFConstituentUniverseCompositeDelistingRegressionAlgorithm.cs b/Algorithm.CSharp/RegressionTests/Universes/ETFConstituentUniverseCompositeDelistingRegressionAlgorithm.cs index 4a812656bfbb..42df7832e752 100644 --- a/Algorithm.CSharp/RegressionTests/Universes/ETFConstituentUniverseCompositeDelistingRegressionAlgorithm.cs +++ b/Algorithm.CSharp/RegressionTests/Universes/ETFConstituentUniverseCompositeDelistingRegressionAlgorithm.cs @@ -116,8 +116,9 @@ public override void OnSecuritiesChanged(SecurityChanges changes) _universeAdded |= changes.AddedSecurities.Count == expectedChangesCount; } - // TODO: shouldn't be sending AAPL as a removed security since it was added by another universe - _universeRemoved |= changes.RemovedSecurities.Count == expectedChangesCount && + // AAPL was added by another universe, so it should be removed when this universe is removed, hence "- 1" + _universeRemoved |= changes.RemovedSecurities.Count == expectedChangesCount - 1 && + !changes.RemovedSecurities.Any(security => security.Symbol == _aapl) && UtcTime.Date >= _delistingDate && UtcTime.Date < EndDate; } diff --git a/Algorithm.CSharp/RsiAlphaModelFrameworkRegressionAlgorithm.cs b/Algorithm.CSharp/RsiAlphaModelFrameworkRegressionAlgorithm.cs index b2db834c8a4d..d69c5e181215 100644 --- a/Algorithm.CSharp/RsiAlphaModelFrameworkRegressionAlgorithm.cs +++ b/Algorithm.CSharp/RsiAlphaModelFrameworkRegressionAlgorithm.cs @@ -35,9 +35,10 @@ public override void OnEndOfAlgorithm() { // We have removed all securities from the universe. The Alpha Model should remove the consolidator var consolidatorCount = SubscriptionManager.Subscriptions.Sum(s => s.Consolidators.Count); - if (consolidatorCount > 0) - { - throw new RegressionTestException($"The number of consolidators should be zero. Actual: {consolidatorCount}"); + // Expect 2 consolidators for AAPL and AIG, which where manually added at the start + if (consolidatorCount != 2) + { + throw new RegressionTestException($"The number of consolidators should be 2. Actual: {consolidatorCount}"); } } diff --git a/Algorithm.CSharp/SecurityEquityReAdditionBehaviorRegressionAlgorithm.cs b/Algorithm.CSharp/SecurityEquityReAdditionBehaviorRegressionAlgorithm.cs new file mode 100644 index 000000000000..4c59fc6a7c38 --- /dev/null +++ b/Algorithm.CSharp/SecurityEquityReAdditionBehaviorRegressionAlgorithm.cs @@ -0,0 +1,193 @@ +/* + * 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.Data.UniverseSelection; +using QuantConnect.Interfaces; +using QuantConnect.Securities; +using QuantConnect.Securities.Equity; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// + public class SecurityEquityReAdditionBehaviorRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition + { + private Security _equity; + private Queue _tradableDates; + private bool _securityWasRemoved; + private bool _securityWasInitialized; + + public override void Initialize() + { + SetStartDate(2013, 10, 04); + SetEndDate(2013, 10, 30); + + var seeder = new FuncSecuritySeeder((security) => + { + _securityWasInitialized = true; + Debug($"[{Time}] Seeding {security.Symbol}"); + return GetLastKnownPrices(security); + }); + + SetSecurityInitializer(security => seeder.SeedSecurity(security)); + + _equity = AddEquity(); + + _tradableDates = new(QuantConnect.Time.EachTradeableDay(_equity.Exchange.Hours, StartDate, EndDate)); + } + + private Equity AddEquity() + { + _securityWasInitialized = false; + var equity = AddEquity("SPY"); + + if (!_securityWasInitialized) + { + throw new RegressionTestException("Expected the equity to be initialized"); + } + + return equity; + } + + public override void OnEndOfDay(Symbol symbol) + { + if (symbol != _equity.Symbol) + { + throw new RegressionTestException($"Expected the symbol to be the equity symbol. Got {symbol}"); + } + + var currentTradableDate = _tradableDates.Dequeue(); + if (currentTradableDate != Time.Date) + { + throw new RegressionTestException($"Expected the current tradable date to be {Time.Date}. Got {currentTradableDate}"); + } + + // Remove the security every day + Debug($"[{Time}] Removing the equity"); + _securityWasRemoved = RemoveSecurity(symbol); + + if (!_securityWasRemoved) + { + throw new RegressionTestException($"Expected the equity to be removed"); + } + } + + public override void OnSecuritiesChanged(SecurityChanges changes) + { + if (_securityWasRemoved) + { + if (changes.AddedSecurities.Count > 0) + { + throw new RegressionTestException($"Expected no securities to be added. Got {changes.AddedSecurities.Count}"); + } + + if (!changes.RemovedSecurities.Contains(_equity)) + { + throw new RegressionTestException($"Expected the equity to be removed. Got {changes.RemovedSecurities.Count}"); + } + + _securityWasRemoved = false; + + // Add the security back + Debug($"[{Time}] Re-adding the equity"); + var reAddedEquity = AddEquity(); + + if (!ReferenceEquals(reAddedEquity, _equity)) + { + throw new RegressionTestException($"Expected the re-added equity to be the same as the original equity"); + } + + if (!reAddedEquity.IsTradable) + { + throw new RegressionTestException($"Expected the re-added equity to be tradable"); + } + } + else if (!changes.AddedSecurities.Contains(_equity)) + { + throw new RegressionTestException($"Expected the equity to be added back"); + } + } + + public override void OnEndOfAlgorithm() + { + if (_tradableDates.Count > 0) + { + throw new RegressionTestException($"Expected no more tradable dates. Still have {_tradableDates.Count}"); + } + } + + /// + /// 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 long DataPoints => 5581; + + /// + /// Data Points count of the algorithm history + /// + public int AlgorithmHistoryDataPoints => 4040; + + /// + /// 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 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", "-4.884"}, + {"Tracking Error", "0.108"}, + {"Treynor Ratio", "0"}, + {"Total Fees", "$0.00"}, + {"Estimated Strategy Capacity", "$0"}, + {"Lowest Capacity Asset", ""}, + {"Portfolio Turnover", "0%"}, + {"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"} + }; + } +} diff --git a/Algorithm.CSharp/SecurityManuallyAddedOptionReAdditionBehaviorRegressionAlgorithm.cs b/Algorithm.CSharp/SecurityManuallyAddedOptionReAdditionBehaviorRegressionAlgorithm.cs new file mode 100644 index 000000000000..4674d58f3a57 --- /dev/null +++ b/Algorithm.CSharp/SecurityManuallyAddedOptionReAdditionBehaviorRegressionAlgorithm.cs @@ -0,0 +1,208 @@ +/* + * 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; +using System.Linq; +using QuantConnect.Data.UniverseSelection; +using QuantConnect.Interfaces; +using QuantConnect.Securities; +using QuantConnect.Securities.Option; + +namespace QuantConnect.Algorithm.CSharp +{ + public class SecurityManuallyAddedOptionReAdditionBehaviorRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition + { + private static readonly Symbol _optionContractSymbol = QuantConnect.Symbol.CreateOption( + QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA), + Market.USA, + OptionStyle.American, + OptionRight.Call, + 342.9m, + new DateTime(2014, 07, 19)); + + private Option _manuallyAddedContract; + + private bool _securityWasRemoved; + + private bool _securityWasInitialized; + + private Queue _tradableDates; + + public override void Initialize() + { + SetStartDate(2014, 06, 04); + SetEndDate(2014, 06, 20); + SetCash(100000); + + var seeder = new FuncSecuritySeeder((security) => + { + _securityWasInitialized = true; + Debug($"[{Time}] Seeding {security.Symbol}"); + return GetLastKnownPrices(security); + }); + + SetSecurityInitializer(security => seeder.SeedSecurity(security)); + + _manuallyAddedContract = AddOptionContract(); + + _tradableDates = new(QuantConnect.Time.EachTradeableDay(_manuallyAddedContract.Exchange.Hours, StartDate, EndDate)); + } + + public Option AddOptionContract() + { + _securityWasInitialized = false; + var option = AddOptionContract(_optionContractSymbol, Resolution.Daily); + + if (!_securityWasInitialized) + { + throw new RegressionTestException($"Expected the option contract to be initialized. Symbol: {option.Symbol}"); + } + + return option; + } + + public override void OnEndOfDay(Symbol symbol) + { + if (symbol != _manuallyAddedContract.Symbol) + { + return; + } + + var currentTradableDate = _tradableDates.Dequeue(); + if (currentTradableDate != Time.Date) + { + throw new RegressionTestException($"Expected the current tradable date to be {Time.Date}. Got {currentTradableDate}"); + } + + // Remove the security every day + Debug($"[{Time}] Removing the option contract"); + _securityWasRemoved = RemoveSecurity(symbol); + + if (!_securityWasRemoved) + { + throw new RegressionTestException($"Expected the option contract to be removed"); + } + } + + public override void OnSecuritiesChanged(SecurityChanges changes) + { + if (_securityWasRemoved) + { + if (changes.AddedSecurities.Count > 0) + { + throw new RegressionTestException($"Expected no securities to be added. Got {changes.AddedSecurities.Count}"); + } + + if (!changes.RemovedSecurities.Contains(_manuallyAddedContract)) + { + throw new RegressionTestException($"Expected the option contract to be removed. Got {changes.RemovedSecurities.Count}"); + } + + _securityWasRemoved = false; + + if (Time.Date >= EndDate.Date) + { + return; + } + + // Add the security back + Debug($"[{Time}] Re-adding the option contract"); + var reAddedContract = AddOptionContract(); + + if (!ReferenceEquals(reAddedContract, _manuallyAddedContract)) + { + throw new RegressionTestException($"Expected the re-added option contract to be the same as the original option contract"); + } + + if (!reAddedContract.IsTradable) + { + throw new RegressionTestException($"Expected the re-added option contract to be tradable"); + } + } + else if (!changes.AddedSecurities.Contains(_manuallyAddedContract)) + { + throw new RegressionTestException($"Expected the option contract to be added back"); + } + } + + public override void OnEndOfAlgorithm() + { + if (_tradableDates.Count > 0) + { + throw new RegressionTestException($"Expected no more tradable dates. Still have {_tradableDates.Count}"); + } + } + + /// + /// 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 long DataPoints => 115; + + /// + /// Data Points count of the algorithm history + /// + public int AlgorithmHistoryDataPoints => 5; + + /// + /// 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 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", "-6.27"}, + {"Tracking Error", "0.056"}, + {"Treynor Ratio", "0"}, + {"Total Fees", "$0.00"}, + {"Estimated Strategy Capacity", "$0"}, + {"Lowest Capacity Asset", ""}, + {"Portfolio Turnover", "0%"}, + {"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"} + }; + } +} diff --git a/Algorithm.CSharp/SecurityOptionReAdditionBehaviorRegressionAlgorithm.cs b/Algorithm.CSharp/SecurityOptionReAdditionBehaviorRegressionAlgorithm.cs new file mode 100644 index 000000000000..fefbe28c6021 --- /dev/null +++ b/Algorithm.CSharp/SecurityOptionReAdditionBehaviorRegressionAlgorithm.cs @@ -0,0 +1,221 @@ +/* + * 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; +using System.Linq; +using QuantConnect.Data.UniverseSelection; +using QuantConnect.Interfaces; +using QuantConnect.Securities; +using QuantConnect.Securities.Option; + +namespace QuantConnect.Algorithm.CSharp +{ + public class SecurityOptionReAdditionBehaviorRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition + { + private List _contractsToSelect; + private HashSet