diff --git a/Algorithm.CSharp/DelayedSettlementAfterManualSecurityRemovalAlgorithm.cs b/Algorithm.CSharp/DelayedSettlementAfterManualSecurityRemovalAlgorithm.cs new file mode 100644 index 000000000000..1c59e0914a48 --- /dev/null +++ b/Algorithm.CSharp/DelayedSettlementAfterManualSecurityRemovalAlgorithm.cs @@ -0,0 +1,146 @@ +/* + * 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 QuantConnect.Interfaces; +using System.Linq; +using System; +using QuantConnect.Securities; +using QuantConnect.Securities.Option; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Algorithm asserting that delayed cash settlement is applied even when the option contract is manually removed + /// + public class DelayedSettlementAfterManualSecurityRemovalAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition + { + private Symbol _optionSymbol; + + public override void Initialize() + { + SetStartDate(2015, 12, 24); + SetEndDate(2015, 12, 31); + SetCash(100000); + + var equity = AddEquity("GOOG"); + + _optionSymbol = OptionChainProvider.GetOptionContractList(equity.Symbol, Time) + .OrderByDescending(symbol => symbol.ID.Date) + .First(optionContract => optionContract.ID.OptionRight == OptionRight.Call); + var option = AddOptionContract(_optionSymbol); + + option.SetSettlementModel(new DelayedSettlementModel(Option.DefaultSettlementDays, Option.DefaultSettlementTime)); + + Schedule.On(DateRules.On(StartDate), TimeRules.BeforeMarketClose(_optionSymbol, 30), () => + { + MarketOrder(_optionSymbol, 1); + }); + + Schedule.On(DateRules.On(StartDate), TimeRules.BeforeMarketClose(_optionSymbol, 1), () => + { + RemoveOptionContract(_optionSymbol); + }); + + var expectedSettlementDate = new DateTime(2015, 12, 28); + + Schedule.On(DateRules.On(expectedSettlementDate), TimeRules.AfterMarketOpen(_optionSymbol), () => + { + if (Portfolio.UnsettledCash == 0) + { + throw new Exception($"Expected unsettled cash to be non-zero at {Time}"); + } + }); + + Schedule.On(DateRules.On(expectedSettlementDate), TimeRules.BeforeMarketClose(_optionSymbol), () => + { + if (Portfolio.UnsettledCash != 0) + { + throw new Exception($"Expected unsettled cash to be zero at {Time}"); + } + }); + } + + public override void OnEndOfAlgorithm() + { + if (Transactions.OrdersCount != 2) + { + throw new Exception($"Expected 2 orders, found {Transactions.OrdersCount}"); + } + + if (Portfolio.Invested) + { + throw new Exception("Expected no holdings at end of algorithm"); + } + + if (Portfolio.UnsettledCash != 0) + { + throw new Exception($"Expected no unsettled cash at end of algorithm, found {Portfolio.UnsettledCash}"); + } + } + + /// + /// 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 Language[] Languages { get; } = { Language.CSharp }; + + /// + /// Data Points count of all timeslices of algorithm + /// + public long DataPoints => 7122; + + /// + /// Data Points count of the algorithm history + /// + public int AlgorithmHistoryDataPoints => 0; + + /// + /// 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 Trades", "2"}, + {"Average Win", "0%"}, + {"Average Loss", "-0.36%"}, + {"Compounding Annual Return", "-15.857%"}, + {"Drawdown", "0.400%"}, + {"Expectancy", "-1"}, + {"Net Profit", "-0.362%"}, + {"Sharpe Ratio", "0"}, + {"Sortino Ratio", "0"}, + {"Probabilistic Sharpe Ratio", "0%"}, + {"Loss Rate", "100%"}, + {"Win Rate", "0%"}, + {"Profit-Loss Ratio", "0"}, + {"Alpha", "0"}, + {"Beta", "0"}, + {"Annual Standard Deviation", "0"}, + {"Annual Variance", "0"}, + {"Information Ratio", "2.537"}, + {"Tracking Error", "0.104"}, + {"Treynor Ratio", "0"}, + {"Total Fees", "$2.00"}, + {"Estimated Strategy Capacity", "$150000.00"}, + {"Lowest Capacity Asset", "GOOCV WRCOZDXBITL2|GOOCV VP83T1ZUHROL"}, + {"Portfolio Turnover", "1.06%"}, + {"OrderListHash", "490dd9430dec6cb3daadcca495ff5f12"} + }; + } +} diff --git a/Algorithm.CSharp/SetCustomSettlementModelRegressionAlgorithm.cs b/Algorithm.CSharp/SetCustomSettlementModelRegressionAlgorithm.cs index 31302cb63859..c572b778c9f1 100644 --- a/Algorithm.CSharp/SetCustomSettlementModelRegressionAlgorithm.cs +++ b/Algorithm.CSharp/SetCustomSettlementModelRegressionAlgorithm.cs @@ -15,7 +15,6 @@ using QuantConnect.Data; using QuantConnect.Securities; -using QuantConnect.Brokerages; using System; using QuantConnect.Interfaces; using System.Collections.Generic; @@ -133,5 +132,13 @@ public void Scan(ScanSettlementModelParameters settlementParameters) settlementParameters.Portfolio.CashBook[_currency].AddAmount(-_amount); } } + + /// + /// Gets the unsettled cash amount for the security + /// + public CashAmount GetUnsettledCash() + { + return default; + } } } diff --git a/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py b/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py index e9a417af3e0b..23d31b4e0d75 100644 --- a/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py +++ b/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py @@ -51,6 +51,9 @@ def Scan(self, parameters): if parameters.UtcTime == datetime(2013, 10, 6): parameters.Portfolio.CashBook[self.currency].AddAmount(-self.amount) + def GetUnsettledCash(self): + return None + class CustomBrokerageModelWithCustomSettlementModel(CustomBrokerageModel): def GetSettlementModel(self, security): return CustomSettlementModel() diff --git a/Common/Python/SettlementModelPythonWrapper.cs b/Common/Python/SettlementModelPythonWrapper.cs index 2bb368e02a37..aac75b3a9c1c 100644 --- a/Common/Python/SettlementModelPythonWrapper.cs +++ b/Common/Python/SettlementModelPythonWrapper.cs @@ -31,7 +31,7 @@ public class SettlementModelPythonWrapper : ISettlementModel /// Settlement Python Model public SettlementModelPythonWrapper(PyObject model) { - _model = model; + _model = model.ValidateImplementationOf(); } /// @@ -57,5 +57,22 @@ public void Scan(ScanSettlementModelParameters settlementParameters) _model.Scan(settlementParameters); } } + + /// + /// Gets the unsettled cash amount for the security + /// + public CashAmount GetUnsettledCash() + { + using (Py.GIL()) + { + var result = _model.GetUnsettledCash(); + if (result == null) + { + return default; + } + + return result; + } + } } } diff --git a/Common/Securities/AccountCurrencyImmediateSettlementModel.cs b/Common/Securities/AccountCurrencyImmediateSettlementModel.cs index abe1f00942a0..3681d432ccc9 100644 --- a/Common/Securities/AccountCurrencyImmediateSettlementModel.cs +++ b/Common/Securities/AccountCurrencyImmediateSettlementModel.cs @@ -19,13 +19,13 @@ namespace QuantConnect.Securities /// Represents the model responsible for applying cash settlement rules /// /// This model converts the amount to the account currency and applies cash settlement immediately - public class AccountCurrencyImmediateSettlementModel : ISettlementModel + public class AccountCurrencyImmediateSettlementModel : ImmediateSettlementModel { /// /// Applies cash settlement rules /// /// The funds application parameters - public void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsParameters) + public override void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsParameters) { var currency = applyFundsParameters.CashAmount.Currency; var amount = applyFundsParameters.CashAmount.Amount; @@ -34,13 +34,5 @@ public void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsParameters) portfolio.CashBook[portfolio.CashBook.AccountCurrency].AddAmount(amountInAccountCurrency); } - - /// - /// Scan for pending settlements - /// - /// The settlement parameters - public void Scan(ScanSettlementModelParameters settlementParameters) - { - } } } diff --git a/Common/Securities/CashAmount.cs b/Common/Securities/CashAmount.cs index 8920a55176cb..7a2673dcc5ea 100644 --- a/Common/Securities/CashAmount.cs +++ b/Common/Securities/CashAmount.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * diff --git a/Common/Securities/DelayedSettlementModel.cs b/Common/Securities/DelayedSettlementModel.cs index 332071ff5809..49ccb78bb72f 100644 --- a/Common/Securities/DelayedSettlementModel.cs +++ b/Common/Securities/DelayedSettlementModel.cs @@ -1,11 +1,11 @@ /* * 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"); + * + * 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. @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace QuantConnect.Securities { @@ -26,6 +27,7 @@ public class DelayedSettlementModel : ISettlementModel { private readonly int _numberOfDays; private readonly TimeSpan _timeOfDay; + private CashBook _cashBook; /// /// The list of pending funds waiting for settlement time @@ -85,6 +87,12 @@ public void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsParameters) portfolio.CashBook[currency].AddAmount(amount); } + + // We just keep it to use currency conversion in GetUnsettledCash method + if (_cashBook == null) + { + _cashBook = portfolio.UnsettledCashBook; + } } /// @@ -110,5 +118,24 @@ public void Scan(ScanSettlementModelParameters settlementParameters) } } } + + /// + /// Gets the unsettled cash amount for the security + /// + public CashAmount GetUnsettledCash() + { + var accountCurrency = _cashBook != null ? _cashBook.AccountCurrency : Currencies.USD; + + lock (_unsettledCashAmounts) + { + if (_unsettledCashAmounts.Count == 0) + { + return default; + } + + return new CashAmount(_unsettledCashAmounts.Sum(x => _cashBook.ConvertToAccountCurrency(x.Amount, x.Currency)), accountCurrency); + } + + } } } diff --git a/Common/Securities/ISettlementModel.cs b/Common/Securities/ISettlementModel.cs index 22b0b67a1652..04b6bc8e75c5 100644 --- a/Common/Securities/ISettlementModel.cs +++ b/Common/Securities/ISettlementModel.cs @@ -1,11 +1,11 @@ /* * 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"); + * + * 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. @@ -31,5 +31,10 @@ public interface ISettlementModel /// /// The settlement parameters void Scan(ScanSettlementModelParameters settlementParameters); + + /// + /// Gets the unsettled cash amount for the security + /// + CashAmount GetUnsettledCash(); } } diff --git a/Common/Securities/ImmediateSettlementModel.cs b/Common/Securities/ImmediateSettlementModel.cs index 6b727043e6d7..5becda904e9d 100644 --- a/Common/Securities/ImmediateSettlementModel.cs +++ b/Common/Securities/ImmediateSettlementModel.cs @@ -1,11 +1,11 @@ /* * 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"); + * + * 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. @@ -39,5 +39,13 @@ public virtual void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsPar public virtual void Scan(ScanSettlementModelParameters settlementParameters) { } + + /// + /// Gets the unsettled cash amount for the security + /// + public virtual CashAmount GetUnsettledCash() + { + return default; + } } } diff --git a/Engine/DataFeeds/PendingRemovalsManager.cs b/Engine/DataFeeds/PendingRemovalsManager.cs index 13c9a7d1b9dd..dfdb37406cd8 100644 --- a/Engine/DataFeeds/PendingRemovalsManager.cs +++ b/Engine/DataFeeds/PendingRemovalsManager.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -61,6 +61,14 @@ private bool IsSafeToRemove(Security member, Universe universe) // covers the options use case return false; } + + // don't remove if there are unsettled positions + var unsettledCash = member.SettlementModel.GetUnsettledCash(); + if (unsettledCash != default && unsettledCash.Amount > 0) + { + return false; + } + return true; } diff --git a/Tests/Common/Brokerages/BrokerageModelTests.cs b/Tests/Common/Brokerages/BrokerageModelTests.cs index a61bebd0aaf7..7231cb15a7a4 100644 --- a/Tests/Common/Brokerages/BrokerageModelTests.cs +++ b/Tests/Common/Brokerages/BrokerageModelTests.cs @@ -410,6 +410,9 @@ raise ValueError(""Pepe"") def Scan(self, parameters): raise ValueError(""Pepe2"") + def GetUnsettledCash(self): + raise ValueError(""Pepe3"") + class CustomBrokerageModel(DefaultBrokerageModel): def GetSettlementModel(self, securities): return CustomSettlementModel() @@ -429,6 +432,9 @@ def GetSettlementModel(self, securities): ex = Assert.Throws(() => ((dynamic)settlementModel).Scan(scanParameters)); Assert.AreEqual("ValueError", ex.Type.Name); Assert.AreEqual("Pepe2", ex.Message); + ex = Assert.Throws(() => ((dynamic)settlementModel).GetUnsettledCash()); + Assert.AreEqual("ValueError", ex.Type.Name); + Assert.AreEqual("Pepe3", ex.Message); } } diff --git a/Tests/Engine/DataFeeds/PendingRemovalsManagerTests.cs b/Tests/Engine/DataFeeds/PendingRemovalsManagerTests.cs index b216ca4b5ff1..51becf08fc83 100644 --- a/Tests/Engine/DataFeeds/PendingRemovalsManagerTests.cs +++ b/Tests/Engine/DataFeeds/PendingRemovalsManagerTests.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -30,13 +30,16 @@ namespace QuantConnect.Tests.Engine.DataFeeds [TestFixture] public class PendingRemovalsManagerTests { + private static readonly DateTime Noon = new DateTime(2015, 11, 2, 12, 0, 0); + private static readonly TimeKeeper TimeKeeper = new TimeKeeper(Noon.ConvertToUtc(TimeZones.NewYork), new[] { TimeZones.NewYork }); + [Test] public void ReturnedRemoved_Add() { var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); var result = pendingRemovals.TryRemoveMember(security, universe); @@ -54,7 +57,7 @@ public void ReturnedRemoved_Check() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); orderProvider.AddOrder(new LimitOrder(security.Symbol, 1, 1, DateTime.UtcNow)); pendingRemovals.TryRemoveMember(security, universe); orderProvider.Clear(); @@ -79,7 +82,7 @@ public void WontRemoveBecauseOfUnderlying() // we add an order of the equity option orderProvider.AddOrder(new LimitOrder(equityOption.Symbol, 1, 1, DateTime.UtcNow)); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); universe.AddMember(DateTime.UtcNow, equity, false); universe.AddMember(DateTime.UtcNow, equityOption, false); @@ -97,7 +100,7 @@ public void WontRemoveBecauseOpenOrder_Add() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); orderProvider.AddOrder(new LimitOrder(security.Symbol, 1, 1, DateTime.UtcNow)); Assert.IsNull(pendingRemovals.TryRemoveMember(security, universe)); @@ -113,7 +116,7 @@ public void WontRemoveBecauseOpenOrder_Check() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); orderProvider.AddOrder(new LimitOrder(security.Symbol, 1, 1, DateTime.UtcNow)); Assert.IsNull(pendingRemovals.TryRemoveMember(security, universe)); @@ -130,7 +133,7 @@ public void WontRemoveBecauseHoldings_Add() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); security.Holdings.SetHoldings(10, 10); Assert.IsNull(pendingRemovals.TryRemoveMember(security, universe)); @@ -146,7 +149,7 @@ public void WontRemoveBecauseHoldings_Check() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); security.Holdings.SetHoldings(10, 10); Assert.IsNull(pendingRemovals.TryRemoveMember(security, universe)); @@ -163,7 +166,7 @@ public void WontRemoveBecauseTarget_Add() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); security.Holdings.Target = new PortfolioTarget(security.Symbol, 10); Assert.IsNull(pendingRemovals.TryRemoveMember(security, universe)); @@ -179,7 +182,7 @@ public void WontRemoveBecauseTarget_Check() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); security.Holdings.Target = new PortfolioTarget(security.Symbol, 10); Assert.IsNull(pendingRemovals.TryRemoveMember(security, universe)); @@ -196,7 +199,7 @@ public void WontBeReturnedBecauseReSelected() var orderProvider = new FakeOrderProcessor(); var pendingRemovals = new PendingRemovalsManager(orderProvider); var security = SecurityTests.GetSecurity(); - var universe = new TestUniverse(); + using var universe = new TestUniverse(); orderProvider.AddOrder(new LimitOrder(security.Symbol, 1, 1, DateTime.UtcNow)); pendingRemovals.TryRemoveMember(security, universe); @@ -208,6 +211,29 @@ public void WontBeReturnedBecauseReSelected() Assert.AreEqual(0, pendingRemovals.PendingRemovals.Values.Count()); } + [Test] + public void WontRemoveBecauseUnsettledFunds() + { + var orderProvider = new FakeOrderProcessor(); + var pendingRemovals = new PendingRemovalsManager(orderProvider); + using var universe = new TestUniverse(); + var security = SecurityTests.GetSecurity(); + + security.SetSettlementModel(new DelayedSettlementModel(1, TimeSpan.FromHours(8))); + var securities = new SecurityManager(TimeKeeper); + var transactions = new SecurityTransactionManager(null, securities); + var portfolio = new SecurityPortfolioManager(securities, transactions, new AlgorithmSettings()); + security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(portfolio, security, TimeKeeper.UtcTime.Date.AddDays(1), + new CashAmount(1000, Currencies.USD), null)); + + Assert.IsNull(pendingRemovals.TryRemoveMember(security, universe)); + Assert.IsFalse(pendingRemovals.CheckPendingRemovals(new HashSet(), universe).Any()); + Assert.AreEqual(1, pendingRemovals.PendingRemovals.Keys.Count()); + Assert.AreEqual(1, pendingRemovals.PendingRemovals.Values.Count()); + Assert.AreEqual(universe, pendingRemovals.PendingRemovals.Keys.First()); + Assert.AreEqual(security, pendingRemovals.PendingRemovals.Values.First().First()); + } + private class TestUniverse : Universe { public TestUniverse() diff --git a/Tests/Python/SettlementModelPythonWrapperTests.cs b/Tests/Python/SettlementModelPythonWrapperTests.cs new file mode 100644 index 000000000000..8198a77735e2 --- /dev/null +++ b/Tests/Python/SettlementModelPythonWrapperTests.cs @@ -0,0 +1,59 @@ +/* + * 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 NUnit.Framework; +using Python.Runtime; +using QuantConnect.AlgorithmFactory.Python.Wrappers; +using System; +using System.Collections.Generic; +using System.IO; +using QuantConnect.Orders; +using Moq; +using static QuantConnect.Tests.Engine.PerformanceBenchmarkAlgorithms; +using QuantConnect.Python; +using QuantConnect.Securities; + +namespace QuantConnect.Tests.Python +{ + [TestFixture] + public class SettlementModelPythonWrapperTests + { + [Test] + public void GetsDefaultUnsettledCashFromNone() + { + using (Py.GIL()) + { + var testModule = PyModule.FromString("testModule", + @" +class CustomSettlementModel: + def ApplyFunds(self, parameters): + pass + + def Scan(self, parameters): + pass + + def GetUnsettledCash(self): + return None + "); + + var settlementModel = new SettlementModelPythonWrapper(testModule.GetAttr("CustomSettlementModel").Invoke()); + var result = settlementModel.GetUnsettledCash(); + Assert.AreEqual(default(CashAmount), result); + + } + } + } +}