Lean  $LEAN_TAG$
DelayedSettlementModel.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections.Generic;
18 using System.Linq;
19 
21 {
22  /// <summary>
23  /// Represents the model responsible for applying cash settlement rules
24  /// </summary>
25  /// <remarks>This model applies cash settlement after T+N days</remarks>
27  {
28  private readonly int _numberOfDays;
29  private readonly TimeSpan _timeOfDay;
30  private CashBook _cashBook;
31 
32  /// <summary>
33  /// The list of pending funds waiting for settlement time
34  /// </summary>
35  private readonly Queue<UnsettledCashAmount> _unsettledCashAmounts;
36 
37  /// <summary>
38  /// Creates an instance of the <see cref="DelayedSettlementModel"/> class
39  /// </summary>
40  /// <param name="numberOfDays">The number of days required for settlement</param>
41  /// <param name="timeOfDay">The time of day used for settlement</param>
42  public DelayedSettlementModel(int numberOfDays, TimeSpan timeOfDay)
43  {
44  _timeOfDay = timeOfDay;
45  _numberOfDays = numberOfDays;
46  _unsettledCashAmounts = new();
47  }
48 
49  /// <summary>
50  /// Applies cash settlement rules
51  /// </summary>
52  /// <param name="applyFundsParameters">The funds application parameters</param>
53  public void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsParameters)
54  {
55  var currency = applyFundsParameters.CashAmount.Currency;
56  var amount = applyFundsParameters.CashAmount.Amount;
57  var security = applyFundsParameters.Security;
58  var portfolio = applyFundsParameters.Portfolio;
59  if (amount > 0)
60  {
61  // positive amount: sell order filled
62 
63  portfolio.UnsettledCashBook[currency].AddAmount(amount);
64 
65  // find the correct settlement date (usually T+3 or T+1)
66  var settlementDate = applyFundsParameters.UtcTime.ConvertFromUtc(security.Exchange.TimeZone).Date;
67  for (var i = 0; i < _numberOfDays; i++)
68  {
69  settlementDate = settlementDate.AddDays(1);
70 
71  // only count days when market is open
72  if (!security.Exchange.Hours.IsDateOpen(settlementDate))
73  i--;
74  }
75 
76  // use correct settlement time
77  var settlementTimeUtc = settlementDate.Add(_timeOfDay).ConvertToUtc(security.Exchange.Hours.TimeZone);
78 
79  lock (_unsettledCashAmounts)
80  {
81  _unsettledCashAmounts.Enqueue(new UnsettledCashAmount(settlementTimeUtc, currency, amount));
82  }
83  }
84  else
85  {
86  // negative amount: buy order filled
87 
88  portfolio.CashBook[currency].AddAmount(amount);
89  }
90 
91  // We just keep it to use currency conversion in GetUnsettledCash method
92  if (_cashBook == null)
93  {
94  _cashBook = portfolio.UnsettledCashBook;
95  }
96  }
97 
98  /// <summary>
99  /// Scan for pending settlements
100  /// </summary>
101  /// <param name="settlementParameters">The settlement parameters</param>
102  public void Scan(ScanSettlementModelParameters settlementParameters)
103  {
104  lock (_unsettledCashAmounts)
105  {
106  while (_unsettledCashAmounts.TryPeek(out var item)
107  // check if settlement time has passed
108  && settlementParameters.UtcTime >= item.SettlementTimeUtc)
109  {
110  // remove item from unsettled funds list
111  _unsettledCashAmounts.Dequeue();
112 
113  // update unsettled cashbook
114  settlementParameters.Portfolio.UnsettledCashBook[item.Currency].AddAmount(-item.Amount);
115 
116  // update settled cashbook
117  settlementParameters.Portfolio.CashBook[item.Currency].AddAmount(item.Amount);
118  }
119  }
120  }
121 
122  /// <summary>
123  /// Gets the unsettled cash amount for the security
124  /// </summary>
126  {
127  var accountCurrency = _cashBook != null ? _cashBook.AccountCurrency : Currencies.USD;
128 
129  lock (_unsettledCashAmounts)
130  {
131  if (_unsettledCashAmounts.Count == 0)
132  {
133  return default;
134  }
135 
136  return new CashAmount(_unsettledCashAmounts.Sum(x => _cashBook.ConvertToAccountCurrency(x.Amount, x.Currency)), accountCurrency);
137  }
138 
139  }
140  }
141 }