diff --git a/Algorithm.CSharp/SetCustomSettlementModelRegressionAlgorithm.cs b/Algorithm.CSharp/SetCustomSettlementModelRegressionAlgorithm.cs
new file mode 100644
index 000000000000..31302cb63859
--- /dev/null
+++ b/Algorithm.CSharp/SetCustomSettlementModelRegressionAlgorithm.cs
@@ -0,0 +1,137 @@
+/*
+ * 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;
+using QuantConnect.Securities;
+using QuantConnect.Brokerages;
+using System;
+using QuantConnect.Interfaces;
+using System.Collections.Generic;
+
+namespace QuantConnect.Algorithm.CSharp
+{
+ ///
+ /// Regression algorithm to test we can specify a custom settlement model using Security.SetSettlementModel() method
+ ///
+ public class SetCustomSettlementModelRegressionAlgorithm: QCAlgorithm, IRegressionAlgorithmDefinition
+ {
+ private Security _spy;
+ public override void Initialize()
+ {
+ SetStartDate(2013, 10, 7);
+ SetEndDate(2013, 10, 11);
+ SetCash(10000);
+ _spy = AddEquity("SPY", Resolution.Daily);
+ _spy.SetSettlementModel(new CustomSettlementModel());
+ }
+
+ public override void OnData(Slice slice)
+ {
+ if (Portfolio.CashBook[Currencies.USD].Amount == 10000)
+ {
+ var parameters = new ApplyFundsSettlementModelParameters(Portfolio, _spy, Time, new CashAmount(101, Currencies.USD), null);
+ _spy.SettlementModel.ApplyFunds(parameters);
+ }
+ }
+
+ public override void OnEndOfAlgorithm()
+ {
+ if (Portfolio.CashBook[Currencies.USD].Amount != 10101)
+ {
+ throw new Exception($"It was expected to have 10101 USD in Portfolio, but was {Portfolio.CashBook[Currencies.USD].Amount}");
+ }
+
+ var parameters = new ScanSettlementModelParameters(Portfolio, _spy, new DateTime(2013, 10, 6));
+ _spy.SettlementModel.Scan(parameters);
+
+ if (Portfolio.CashBook[Currencies.USD].Amount != 10000)
+ {
+ throw new Exception($"It was expected to have 10000 USD in Portfolio, but was {Portfolio.CashBook[Currencies.USD].Amount}");
+ }
+ }
+
+ ///
+ /// 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, Language.Python };
+
+ ///
+ /// Data Points count of all timeslices of algorithm
+ ///
+ public long DataPoints => 48;
+
+ ///
+ /// 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", "0"},
+ {"Average Win", "0%"},
+ {"Average Loss", "0%"},
+ {"Compounding Annual Return", "108.257%"},
+ {"Drawdown", "0%"},
+ {"Expectancy", "0"},
+ {"Net Profit", "1.010%"},
+ {"Sharpe Ratio", "10.983"},
+ {"Sortino Ratio", "0"},
+ {"Probabilistic Sharpe Ratio", "95.977%"},
+ {"Loss Rate", "0%"},
+ {"Win Rate", "0%"},
+ {"Profit-Loss Ratio", "0"},
+ {"Alpha", "1.42"},
+ {"Beta", "-0.273"},
+ {"Annual Standard Deviation", "0.08"},
+ {"Annual Variance", "0.006"},
+ {"Information Ratio", "-3.801"},
+ {"Tracking Error", "0.288"},
+ {"Treynor Ratio", "-3.226"},
+ {"Total Fees", "$0.00"},
+ {"Estimated Strategy Capacity", "$0"},
+ {"Lowest Capacity Asset", ""},
+ {"Portfolio Turnover", "0%"},
+ {"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
+ };
+ }
+
+ public class CustomSettlementModel : ISettlementModel
+ {
+ private string _currency;
+ private decimal _amount;
+ public void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsParameters)
+ {
+ _currency = applyFundsParameters.CashAmount.Currency;
+ _amount = applyFundsParameters.CashAmount.Amount;
+ applyFundsParameters.Portfolio.CashBook[_currency].AddAmount(_amount);
+ }
+
+ public void Scan(ScanSettlementModelParameters settlementParameters)
+ {
+ if (settlementParameters.UtcTime == new DateTime(2013, 10, 6))
+ {
+ settlementParameters.Portfolio.CashBook[_currency].AddAmount(-_amount);
+ }
+ }
+ }
+}
diff --git a/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py b/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py
index c6996da1cc93..b8d070ca4d81 100644
--- a/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py
+++ b/Algorithm.Python/CustomSettlementModelRegressionAlgorithm.py
@@ -23,6 +23,9 @@ def Initialize(self):
self.SetEndDate(2013,10,11)
self.SetCash(10000)
self.spy = self.AddEquity("SPY", Resolution.Daily)
+ self.SetSettlementModel(self.spy)
+
+ def SetSettlementModel(self, security):
self.SetBrokerageModel(CustomBrokerageModelWithCustomSettlementModel())
self.updateRequestSubmitted = False
@@ -34,15 +37,20 @@ def OnData(self, slice):
def OnEndOfAlgorithm(self):
if self.Portfolio.CashBook[Currencies.USD].Amount != 10101:
raise Exception(f"It was expected to have 10101 USD in Portfolio, but was {self.Portfolio.CashBook[Currencies.USD].Amount}")
+ parameters = ScanSettlementModelParameters(self.Portfolio, self.spy, datetime(2013, 10, 6))
+ self.spy.SettlementModel.Scan(parameters)
+ if self.Portfolio.CashBook[Currencies.USD].Amount != 10000:
+ raise Exception(f"It was expected to have 10000 USD in Portfolio, but was {self.Portfolio.CashBook[Currencies.USD].Amount}")
class CustomSettlementModel:
def ApplyFunds(self, parameters):
- currency = parameters.CashAmount.Currency;
- amount = parameters.CashAmount.Amount
- parameters.Portfolio.CashBook[currency].AddAmount(amount)
+ self.currency = parameters.CashAmount.Currency;
+ self.amount = parameters.CashAmount.Amount
+ parameters.Portfolio.CashBook[self.currency].AddAmount(self.amount)
def Scan(self, parameters):
- pass
+ if parameters.UtcTime == datetime(2013, 10, 6):
+ parameters.Portfolio.CashBook[self.currency].AddAmount(-self.amount)
class CustomBrokerageModelWithCustomSettlementModel(CustomBrokerageModel):
def GetSettlementModel(self, security):
diff --git a/Algorithm.Python/SetCustomSettlementModelRegressionAlgorithm.py b/Algorithm.Python/SetCustomSettlementModelRegressionAlgorithm.py
new file mode 100644
index 000000000000..02e688fd955e
--- /dev/null
+++ b/Algorithm.Python/SetCustomSettlementModelRegressionAlgorithm.py
@@ -0,0 +1,10 @@
+from AlgorithmImports import *
+from CustomSettlementModelRegressionAlgorithm import CustomSettlementModel, CustomSettlementModelRegressionAlgorithm
+
+###
+### Regression algorithm to test we can specify a custom settlement model using Security.SetSettlementModel() method
+### (without a custom brokerage model)
+###
+class SetCustomSettlementModelRegressionAlgorithm(CustomSettlementModelRegressionAlgorithm):
+ def SetSettlementModel(self, security):
+ security.SetSettlementModel(CustomSettlementModel())
diff --git a/Common/Securities/Security.cs b/Common/Securities/Security.cs
index ab50b4ef644d..539cf2fe3919 100644
--- a/Common/Securities/Security.cs
+++ b/Common/Securities/Security.cs
@@ -720,6 +720,24 @@ public void SetFillModel(PyObject fillModel)
FillModel = new FillModelPythonWrapper(fillModel);
}
+ ///
+ /// Sets the settlement model
+ ///
+ /// Model that represents a settlement model
+ public void SetSettlementModel(ISettlementModel settlementModel)
+ {
+ SettlementModel = settlementModel;
+ }
+
+ ///
+ /// Sets the settlement model
+ ///
+ /// Model that represents a settlement model
+ public void SetSettlementModel(PyObject settlementModel)
+ {
+ SettlementModel = new SettlementModelPythonWrapper(settlementModel);
+ }
+
///
/// Sets the slippage model
///