Lean  $LEAN_TAG$
SecurityPortfolioManager.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;
18 using System.Collections.Generic;
19 using System.Linq;
20 using Python.Runtime;
23 using QuantConnect.Logging;
24 using QuantConnect.Orders;
25 using QuantConnect.Python;
28 
30 {
31  /// <summary>
32  /// Portfolio manager class groups popular properties and makes them accessible through one interface.
33  /// It also provide indexing by the vehicle symbol to get the Security.Holding objects.
34  /// </summary>
35  public class SecurityPortfolioManager : ExtendedDictionary<SecurityHolding>, IDictionary<Symbol, SecurityHolding>, ISecurityProvider
36  {
37  private Cash _baseCurrencyCash;
38  private bool _setCashWasCalled;
39  private decimal _totalPortfolioValue;
40  private bool _isTotalPortfolioValueValid;
41  private object _totalPortfolioValueLock = new();
42  private bool _setAccountCurrencyWasCalled;
43  private decimal _freePortfolioValue;
44  private SecurityPositionGroupModel _positions;
45  private IAlgorithmSettings _algorithmSettings;
46 
47  /// <summary>
48  /// Local access to the securities collection for the portfolio summation.
49  /// </summary>
50  public SecurityManager Securities { get; init; }
51 
52  /// <summary>
53  /// Local access to the transactions collection for the portfolio summation and updates.
54  /// </summary>
56 
57  /// <summary>
58  /// Local access to the position manager
59  /// </summary>
61  {
62  get
63  {
64  return _positions;
65  }
66  set
67  {
68  value?.Initialize(Securities);
69  _positions = value;
70  }
71  }
72 
73  /// <summary>
74  /// Gets the cash book that keeps track of all currency holdings (only settled cash)
75  /// </summary>
76  public CashBook CashBook { get; }
77 
78  /// <summary>
79  /// Gets the cash book that keeps track of all currency holdings (only unsettled cash)
80  /// </summary>
81  public CashBook UnsettledCashBook { get; }
82 
83  /// <summary>
84  /// Initialise security portfolio manager.
85  /// </summary>
86  public SecurityPortfolioManager(SecurityManager securityManager, SecurityTransactionManager transactions, IAlgorithmSettings algorithmSettings, IOrderProperties defaultOrderProperties = null)
87  {
88  Securities = securityManager;
89  Transactions = transactions;
90  _algorithmSettings = algorithmSettings;
92  MarginCallModel = new DefaultMarginCallModel(this, defaultOrderProperties);
93 
94  CashBook = new CashBook();
96 
97  _baseCurrencyCash = CashBook[CashBook.AccountCurrency];
98 
99  // default to $100,000.00
100  _baseCurrencyCash.SetAmount(100000);
101 
102  CashBook.Updated += (sender, args) =>
103  {
104  if (args.UpdateType == CashBookUpdateType.Added)
105  {
106  // add the same currency entry to the unsettled cashbook as well
107  var cash = args.Cash;
108  var unsettledCash = new Cash(cash.Symbol, 0m, cash.ConversionRate);
109  unsettledCash.CurrencyConversion = cash.CurrencyConversion;
110 
111  cash.CurrencyConversionUpdated += (sender, args) =>
112  {
113  // Share the currency conversion instance between the settled and unsettled cash instances to synchronize the conversion rates
114  UnsettledCashBook[((Cash)sender).Symbol].CurrencyConversion = cash.CurrencyConversion;
115  };
116 
117  UnsettledCashBook.Add(cash.Symbol, unsettledCash);
118  }
119 
121  };
123  }
124 
125  #region IDictionary Implementation
126 
127  /// <summary>
128  /// Add a new securities string-security to the portfolio.
129  /// </summary>
130  /// <param name="symbol">Symbol of dictionary</param>
131  /// <param name="holding">SecurityHoldings object</param>
132  /// <exception cref="NotImplementedException">Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.</exception>
133  /// <remarks>This method is not implemented and using it will throw an exception</remarks>
134  public void Add(Symbol symbol, SecurityHolding holding) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryAddNotImplemented); }
135 
136  /// <summary>
137  /// Add a new securities key value pair to the portfolio.
138  /// </summary>
139  /// <param name="pair">Key value pair of dictionary</param>
140  /// <exception cref="NotImplementedException">Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.</exception>
141  /// <remarks>This method is not implemented and using it will throw an exception</remarks>
142  public void Add(KeyValuePair<Symbol, SecurityHolding> pair) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryAddNotImplemented); }
143 
144  /// <summary>
145  /// Clear the portfolio of securities objects.
146  /// </summary>
147  /// <exception cref="NotImplementedException">Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.</exception>
148  /// <remarks>This method is not implemented and using it will throw an exception</remarks>
149  public override void Clear() { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryClearNotImplemented); }
150 
151  /// <summary>
152  /// Remove this keyvalue pair from the portfolio.
153  /// </summary>
154  /// <exception cref="NotImplementedException">Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.</exception>
155  /// <param name="pair">Key value pair of dictionary</param>
156  /// <remarks>This method is not implemented and using it will throw an exception</remarks>
157  public bool Remove(KeyValuePair<Symbol, SecurityHolding> pair) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryRemoveNotImplemented); }
158 
159  /// <summary>
160  /// Remove this symbol from the portfolio.
161  /// </summary>
162  /// <exception cref="NotImplementedException">Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.</exception>
163  /// <param name="symbol">Symbol of dictionary</param>
164  /// <remarks>This method is not implemented and using it will throw an exception</remarks>
165  public override bool Remove(Symbol symbol) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryRemoveNotImplemented); }
166 
167  /// <summary>
168  /// Check if the portfolio contains this symbol string.
169  /// </summary>
170  /// <param name="symbol">String search symbol for the security</param>
171  /// <returns>Boolean true if portfolio contains this symbol</returns>
172  public bool ContainsKey(Symbol symbol)
173  {
174  return Securities.ContainsKey(symbol);
175  }
176 
177  /// <summary>
178  /// Check if the key-value pair is in the portfolio.
179  /// </summary>
180  /// <remarks>IDictionary implementation calling the underlying Securities collection</remarks>
181  /// <param name="pair">Pair we're searching for</param>
182  /// <returns>True if we have this object</returns>
183  public bool Contains(KeyValuePair<Symbol, SecurityHolding> pair)
184  {
185  return Securities.ContainsKey(pair.Key);
186  }
187 
188  /// <summary>
189  /// Count the securities objects in the portfolio.
190  /// </summary>
191  /// <remarks>IDictionary implementation calling the underlying Securities collection</remarks>
192  public int Count
193  {
194  get
195  {
196  return Securities.Count;
197  }
198  }
199 
200  /// <summary>
201  /// Check if the underlying securities array is read only.
202  /// </summary>
203  /// <remarks>IDictionary implementation calling the underlying Securities collection</remarks>
204  public override bool IsReadOnly
205  {
206  get
207  {
208  return Securities.IsReadOnly;
209  }
210  }
211 
212  /// <summary>
213  /// Copy contents of the portfolio collection to a new destination.
214  /// </summary>
215  /// <remarks>IDictionary implementation calling the underlying Securities collection</remarks>
216  /// <param name="array">Destination array</param>
217  /// <param name="index">Position in array to start copying</param>
218  public void CopyTo(KeyValuePair<Symbol, SecurityHolding>[] array, int index)
219  {
220  array = new KeyValuePair<Symbol, SecurityHolding>[Securities.Count];
221  var i = 0;
222  foreach (var asset in Securities.Values)
223  {
224  if (i >= index)
225  {
226  array[i] = new KeyValuePair<Symbol, SecurityHolding>(asset.Symbol, asset.Holdings);
227  }
228  i++;
229  }
230  }
231 
232  /// <summary>
233  /// Gets an <see cref="System.Collections.Generic.ICollection{T}"/> containing the Symbol objects of the <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/>.
234  /// </summary>
235  /// <returns>
236  /// An <see cref="System.Collections.Generic.ICollection{T}"/> containing the Symbol objects of the object that implements <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/>.
237  /// </returns>
238  protected override IEnumerable<Symbol> GetKeys => Keys;
239 
240  /// <summary>
241  /// Gets an <see cref="System.Collections.Generic.ICollection{T}"/> containing the values in the <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/>.
242  /// </summary>
243  /// <returns>
244  /// An <see cref="System.Collections.Generic.ICollection{T}"/> containing the values in the object that implements <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/>.
245  /// </returns>
246  protected override IEnumerable<SecurityHolding> GetValues => Securities.Select(pair => pair.Value.Holdings);
247 
248  /// <summary>
249  /// Symbol keys collection of the underlying assets in the portfolio.
250  /// </summary>
251  /// <remarks>IDictionary implementation calling the underlying securities key symbols</remarks>
252  public ICollection<Symbol> Keys
253  {
254  get
255  {
256  return Securities.Keys;
257  }
258  }
259 
260  /// <summary>
261  /// Collection of securities objects in the portfolio.
262  /// </summary>
263  /// <remarks>IDictionary implementation calling the underlying securities values collection</remarks>
264  public ICollection<SecurityHolding> Values
265  {
266  get
267  {
268  return GetValues.ToList();
269  }
270  }
271 
272  /// <summary>
273  /// Attempt to get the value of the securities holding class if this symbol exists.
274  /// </summary>
275  /// <param name="symbol">String search symbol</param>
276  /// <param name="holding">Holdings object of this security</param>
277  /// <remarks>IDictionary implementation</remarks>
278  /// <returns>Boolean true if successful locating and setting the holdings object</returns>
279  public override bool TryGetValue(Symbol symbol, out SecurityHolding holding)
280  {
281  Security security;
282  var success = Securities.TryGetValue(symbol, out security);
283  holding = success ? security.Holdings : null;
284  return success;
285  }
286 
287  /// <summary>
288  /// Get the enumerator for the underlying securities collection.
289  /// </summary>
290  /// <remarks>IDictionary implementation</remarks>
291  /// <returns>Enumerable key value pair</returns>
292  IEnumerator<KeyValuePair<Symbol, SecurityHolding>> IEnumerable<KeyValuePair<Symbol, SecurityHolding>>.GetEnumerator()
293  {
294  return Securities.Select(x => new KeyValuePair<Symbol, SecurityHolding>(x.Key, x.Value.Holdings)).GetEnumerator();
295  }
296 
297  /// <summary>
298  /// Get the enumerator for the underlying securities collection.
299  /// </summary>
300  /// <remarks>IDictionary implementation</remarks>
301  /// <returns>Enumerator</returns>
302  IEnumerator IEnumerable.GetEnumerator()
303  {
304  return Securities.Select(x => new KeyValuePair<Symbol, SecurityHolding>(x.Key, x.Value.Holdings)).GetEnumerator();
305  }
306 
307  #endregion
308 
309  /// <summary>
310  /// Sum of all currencies in account in US dollars (only settled cash)
311  /// </summary>
312  /// <remarks>
313  /// This should not be mistaken for margin available because Forex uses margin
314  /// even though the total cash value is not impact
315  /// </remarks>
316  public decimal Cash
317  {
318  get { return CashBook.TotalValueInAccountCurrency; }
319  }
320 
321  /// <summary>
322  /// Sum of all currencies in account in US dollars (only unsettled cash)
323  /// </summary>
324  /// <remarks>
325  /// This should not be mistaken for margin available because Forex uses margin
326  /// even though the total cash value is not impact
327  /// </remarks>
328  public decimal UnsettledCash
329  {
331  }
332 
333  /// <summary>
334  /// Absolute value of cash discounted from our total cash by the holdings we own.
335  /// </summary>
336  /// <remarks>When account has leverage the actual cash removed is a fraction of the purchase price according to the leverage</remarks>
338  {
339  get
340  {
341  return Securities.Values.Sum(security => security.Holdings.UnleveredAbsoluteHoldingsCost);
342  }
343  }
344 
345  /// <summary>
346  /// Gets the total absolute holdings cost of the portfolio. This sums up the individual
347  /// absolute cost of each holding
348  /// </summary>
349  public decimal TotalAbsoluteHoldingsCost
350  {
351  get
352  {
353  return Securities.Values.Sum(security => security.Holdings.AbsoluteHoldingsCost);
354  }
355  }
356 
357  /// <summary>
358  /// Absolute sum the individual items in portfolio.
359  /// </summary>
360  public decimal TotalHoldingsValue
361  {
362  get
363  {
364  //Sum sum of holdings
365  return Securities.Values.Sum(security => security.Holdings.AbsoluteHoldingsValue);
366  }
367  }
368 
369  /// <summary>
370  /// Boolean flag indicating we have any holdings in the portfolio.
371  /// </summary>
372  /// <remarks>Assumes no asset can have $0 price and uses the sum of total holdings value</remarks>
373  /// <seealso cref="Invested"/>
374  public bool HoldStock
375  {
376  get
377  {
378  foreach (var security in Securities.Values)
379  {
380  if (security.HoldStock)
381  {
382  return true;
383  }
384  }
385  return false;
386  }
387  }
388 
389  /// <summary>
390  /// Alias for HoldStock. Check if we have any holdings.
391  /// </summary>
392  /// <seealso cref="HoldStock"/>
393  public bool Invested => HoldStock;
394 
395  /// <summary>
396  /// Get the total unrealised profit in our portfolio from the individual security unrealized profits.
397  /// </summary>
398  public decimal TotalUnrealisedProfit
399  {
400  get
401  {
402  return Securities.Values.Sum(security => security.Holdings.UnrealizedProfit);
403  }
404  }
405 
406  /// <summary>
407  /// Get the total unrealised profit in our portfolio from the individual security unrealized profits.
408  /// </summary>
409  /// <remarks>Added alias for American spelling</remarks>
410  public decimal TotalUnrealizedProfit
411  {
412  get { return TotalUnrealisedProfit; }
413  }
414 
415  /// <summary>
416  /// Total portfolio value if we sold all holdings at current market rates.
417  /// </summary>
418  /// <remarks>Cash + TotalUnrealisedProfit + TotalUnleveredAbsoluteHoldingsCost</remarks>
419  /// <seealso cref="Cash"/>
420  /// <seealso cref="TotalUnrealizedProfit"/>
421  /// <seealso cref="TotalUnleveredAbsoluteHoldingsCost"/>
422  public decimal TotalPortfolioValue
423  {
424  get
425  {
426  lock (_totalPortfolioValueLock)
427  {
428  if (!_isTotalPortfolioValueValid)
429  {
430  decimal totalHoldingsValueWithoutForexCryptoFutureCfd = 0;
431  decimal totalFuturesAndCfdHoldingsValue = 0;
432  foreach (var security in Securities.Values.Where((x) => x.Holdings.Invested))
433  {
434  var position = security;
435  var securityType = position.Type;
436  // We can't include forex in this calculation since we would be double accounting with respect to the cash book
437  // We also exclude futures and CFD as they are calculated separately because they do not impact the account's cash.
438  // We include futures options as part of this calculation because IB chooses to change our account's cash balance
439  // when we buy or sell a futures options contract.
440  if (securityType != SecurityType.Forex && securityType != SecurityType.Crypto
441  && securityType != SecurityType.Future && securityType != SecurityType.Cfd
442  && securityType != SecurityType.CryptoFuture)
443  {
444  totalHoldingsValueWithoutForexCryptoFutureCfd += position.Holdings.HoldingsValue;
445  }
446 
447  // CFDs don't impact account cash, so they must be calculated
448  // by applying the unrealized P&L to the cash balance.
449  if (securityType == SecurityType.Cfd || securityType == SecurityType.CryptoFuture)
450  {
451  totalFuturesAndCfdHoldingsValue += position.Holdings.UnrealizedProfit;
452  }
453  // Futures P&L is settled daily into cash, here we take into account the current days unsettled profit
454  if (securityType == SecurityType.Future)
455  {
456  var futureHoldings = (FutureHolding)position.Holdings;
457  totalFuturesAndCfdHoldingsValue += futureHoldings.UnsettledProfit;
458  }
459  }
460 
461  _totalPortfolioValue = CashBook.TotalValueInAccountCurrency +
463  totalHoldingsValueWithoutForexCryptoFutureCfd +
464  totalFuturesAndCfdHoldingsValue;
465 
466  _isTotalPortfolioValueValid = true;
467  }
468  }
469 
470  return _totalPortfolioValue;
471  }
472  }
473 
474  /// <summary>
475  /// Returns the adjusted total portfolio value removing the free amount
476  /// If the <see cref="IAlgorithmSettings.FreePortfolioValue"/> has not been set, the free amount will have a trailing behavior and be updated when requested
477  /// </summary>
478  public decimal TotalPortfolioValueLessFreeBuffer
479  {
480  get
481  {
482  if (_algorithmSettings.FreePortfolioValue.HasValue)
483  {
484  // the user set it, we will respect the value set
485  _freePortfolioValue = _algorithmSettings.FreePortfolioValue.Value;
486  }
487  else
488  {
489  // keep the free portfolio value up to date every time we use it
490  _freePortfolioValue = TotalPortfolioValue * _algorithmSettings.FreePortfolioValuePercentage;
491  }
492 
493  return TotalPortfolioValue - _freePortfolioValue;
494 
495  }
496  }
497 
498  /// <summary>
499  /// Will flag the current <see cref="TotalPortfolioValue"/> as invalid
500  /// so it is recalculated when gotten
501  /// </summary>
503  {
504  _isTotalPortfolioValueValid = false;
505  }
506 
507  /// <summary>
508  /// Total fees paid during the algorithm operation across all securities in portfolio.
509  /// </summary>
510  public decimal TotalFees
511  {
512  get
513  {
514  return Securities.Total.Sum(security => security.Holdings.TotalFees);
515  }
516  }
517 
518  /// <summary>
519  /// Sum of all gross profit across all securities in portfolio and dividend payments.
520  /// </summary>
521  public decimal TotalProfit
522  {
523  get
524  {
525  return Securities.Total.Sum(security => security.Holdings.Profit);
526  }
527  }
528 
529  /// <summary>
530  /// Sum of all net profit across all securities in portfolio and dividend payments.
531  /// </summary>
532  public decimal TotalNetProfit
533  {
534  get
535  {
536  return Securities.Total.Sum(security => security.Holdings.NetProfit);
537  }
538  }
539 
540  /// <summary>
541  /// Total sale volume since the start of algorithm operations.
542  /// </summary>
543  public decimal TotalSaleVolume
544  {
545  get
546  {
547  return Securities.Total.Sum(security => security.Holdings.TotalSaleVolume);
548  }
549  }
550 
551  /// <summary>
552  /// Gets the total margin used across all securities in the account's currency
553  /// </summary>
554  public decimal TotalMarginUsed
555  {
556  get
557  {
558  decimal sum = 0;
559  foreach (var group in Positions.Groups)
560  {
561  sum += group.BuyingPowerModel.GetReservedBuyingPowerForPositionGroup(this, group);
562  }
563 
564  return sum;
565  }
566  }
567 
568  /// <summary>
569  /// Gets the remaining margin on the account in the account's currency
570  /// </summary>
571  /// <see cref="GetMarginRemaining(decimal)"/>
573 
574  /// <summary>
575  /// Gets the remaining margin on the account in the account's currency
576  /// for the given total portfolio value
577  /// </summary>
578  /// <remarks>This method is for performance, for when the user already knows
579  /// the total portfolio value, we can avoid re calculating it. Else use
580  /// <see cref="MarginRemaining"/></remarks>
581  /// <param name="totalPortfolioValue">The total portfolio value <see cref="TotalPortfolioValue"/></param>
582  public decimal GetMarginRemaining(decimal totalPortfolioValue)
583  {
584  return totalPortfolioValue - UnsettledCashBook.TotalValueInAccountCurrency - TotalMarginUsed;
585  }
586 
587  /// <summary>
588  /// Gets or sets the <see cref="MarginCallModel"/> for the portfolio. This
589  /// is used to executed margin call orders.
590  /// </summary>
591  public IMarginCallModel MarginCallModel { get; set; }
592 
593  /// <summary>
594  /// Indexer for the PortfolioManager class to access the underlying security holdings objects.
595  /// </summary>
596  /// <param name="symbol">Symbol object indexer</param>
597  /// <returns>SecurityHolding class from the algorithm securities</returns>
598  public override SecurityHolding this[Symbol symbol]
599  {
600  get { return Securities[symbol].Holdings; }
601  set { Securities[symbol].Holdings = value; }
602  }
603 
604  /// <summary>
605  /// Sets the account currency cash symbol this algorithm is to manage, as well
606  /// as the starting cash in this currency if given
607  /// </summary>
608  /// <remarks>Has to be called before calling <see cref="SetCash(decimal)"/>
609  /// or adding any <see cref="Security"/></remarks>
610  /// <param name="accountCurrency">The account currency cash symbol to set</param>
611  /// <param name="startingCash">The account currency starting cash to set</param>
612  public void SetAccountCurrency(string accountCurrency, decimal? startingCash = null)
613  {
614  accountCurrency = accountCurrency.LazyToUpper();
615 
616  // only allow setting account currency once
617  // we could try to set it twice when backtesting and the job packet specifies the initial CashAmount to use
618  if (_setAccountCurrencyWasCalled)
619  {
620  if (accountCurrency != CashBook.AccountCurrency)
621  {
622  Log.Trace("SecurityPortfolioManager.SetAccountCurrency(): " +
624  }
625  return;
626  }
627  _setAccountCurrencyWasCalled = true;
628 
629  if (Securities.Count > 0)
630  {
631  throw new InvalidOperationException("SecurityPortfolioManager.SetAccountCurrency(): " +
633  }
634 
635  if (_setCashWasCalled)
636  {
637  throw new InvalidOperationException("SecurityPortfolioManager.SetAccountCurrency(): " +
639  }
640 
641  Log.Trace("SecurityPortfolioManager.SetAccountCurrency(): " +
643 
644  UnsettledCashBook.AccountCurrency = accountCurrency;
645  CashBook.AccountCurrency = accountCurrency;
646 
647  _baseCurrencyCash = CashBook[accountCurrency];
648 
649  if (startingCash != null)
650  {
651  SetCash((decimal)startingCash);
652  }
653  }
654 
655  /// <summary>
656  /// Set the account currency cash this algorithm is to manage.
657  /// </summary>
658  /// <param name="cash">Decimal cash value of portfolio</param>
659  public void SetCash(decimal cash)
660  {
661  _setCashWasCalled = true;
662  _baseCurrencyCash.SetAmount(cash);
663  }
664 
665  /// <summary>
666  /// Set the cash for the specified symbol
667  /// </summary>
668  /// <param name="symbol">The cash symbol to set</param>
669  /// <param name="cash">Decimal cash value of portfolio</param>
670  /// <param name="conversionRate">The current conversion rate for the</param>
671  public void SetCash(string symbol, decimal cash, decimal conversionRate)
672  {
673  _setCashWasCalled = true;
674  Cash item;
675  symbol = symbol.LazyToUpper();
676  if (CashBook.TryGetValue(symbol, out item))
677  {
678  item.SetAmount(cash);
679  item.ConversionRate = conversionRate;
680  }
681  else
682  {
683  CashBook.Add(symbol, cash, conversionRate);
684  }
685  }
686 
687  // TODO: Review and fix these comments: it doesn't return what it says it does.
688  /// <summary>
689  /// Gets the margin available for trading a specific symbol in a specific direction.
690  /// </summary>
691  /// <param name="symbol">The symbol to compute margin remaining for</param>
692  /// <param name="direction">The order/trading direction</param>
693  /// <returns>The maximum order size that is currently executable in the specified direction</returns>
694  public decimal GetMarginRemaining(Symbol symbol, OrderDirection direction = OrderDirection.Buy)
695  {
696  var security = Securities[symbol];
697 
698  var positionGroup = Positions.GetOrCreateDefaultGroup(security);
699  // Order direction in GetPositionGroupBuyingPower is regarding buying or selling the position group sent as parameter.
700  // Since we are passing the same position group as the one in the holdings, we need to invert the direction.
701  // Buying the means increasing the position group (in the same direction it is currently held) and selling means decreasing it.
702  var positionGroupOrderDirection = direction;
703  if (security.Holdings.IsShort)
704  {
705  positionGroupOrderDirection = direction == OrderDirection.Buy ? OrderDirection.Sell : OrderDirection.Buy;
706  }
707 
708  var parameters = new PositionGroupBuyingPowerParameters(this, positionGroup, positionGroupOrderDirection);
709  return positionGroup.BuyingPowerModel.GetPositionGroupBuyingPower(parameters);
710  }
711 
712  /// <summary>
713  /// Gets the margin available for trading a specific symbol in a specific direction.
714  /// Alias for <see cref="GetMarginRemaining(Symbol, OrderDirection)"/>
715  /// </summary>
716  /// <param name="symbol">The symbol to compute margin remaining for</param>
717  /// <param name="direction">The order/trading direction</param>
718  /// <returns>The maximum order size that is currently executable in the specified direction</returns>
719  public decimal GetBuyingPower(Symbol symbol, OrderDirection direction = OrderDirection.Buy)
720  {
721  return GetMarginRemaining(symbol, direction);
722  }
723 
724  /// <summary>
725  /// Calculate the new average price after processing a list of partial/complete order fill events.
726  /// </summary>
727  /// <remarks>
728  /// For purchasing stocks from zero holdings, the new average price is the sale price.
729  /// When simply partially reducing holdings the average price remains the same.
730  /// When crossing zero holdings the average price becomes the trade price in the new side of zero.
731  /// </remarks>
732  public virtual void ProcessFills(List<OrderEvent> fills)
733  {
734  lock (_totalPortfolioValueLock)
735  {
736  for (var i = 0; i < fills.Count; i++)
737  {
738  var fill = fills[i];
739  var security = Securities[fill.Symbol];
740  security.PortfolioModel.ProcessFill(this, security, fill);
741  }
742 
744  }
745 
746  }
747 
748  /// <summary>
749  /// Applies a dividend to the portfolio
750  /// </summary>
751  /// <param name="dividend">The dividend to be applied</param>
752  /// <param name="liveMode">True if live mode, false for backtest</param>
753  /// <param name="mode">The <see cref="DataNormalizationMode"/> for this security</param>
754  public void ApplyDividend(Dividend dividend, bool liveMode, DataNormalizationMode mode)
755  {
756  // we currently don't properly model dividend payable dates, so in
757  // live mode it's more accurate to rely on the brokerage cash sync
758  if (liveMode)
759  {
760  return;
761  }
762 
763  // only apply dividends when we're in raw mode or split adjusted mode
764  if (mode == DataNormalizationMode.Raw || mode == DataNormalizationMode.SplitAdjusted)
765  {
766  var security = Securities[dividend.Symbol];
767 
768  // longs get benefits, shorts get clubbed on dividends
769  var total = security.Holdings.Quantity * dividend.Distribution * security.QuoteCurrency.ConversionRate;
770 
771  // assuming USD, we still need to add Currency to the security object
772  _baseCurrencyCash.AddAmount(total);
773  security.Holdings.AddNewDividend(total);
774  }
775  }
776 
777  /// <summary>
778  /// Applies a split to the portfolio
779  /// </summary>
780  /// <param name="split">The split to be applied</param>
781  /// <param name="security">The security the split will be applied to</param>
782  /// <param name="liveMode">True if live mode, false for backtest</param>
783  /// <param name="mode">The <see cref="DataNormalizationMode"/> for this security</param>
784  public void ApplySplit(Split split, Security security, bool liveMode, DataNormalizationMode mode)
785  {
786  // only apply splits to equities
787  if (security.Type != SecurityType.Equity)
788  {
789  return;
790  }
791 
792  // only apply splits in live or raw data mode
793  if (!liveMode && mode != DataNormalizationMode.Raw)
794  {
795  return;
796  }
797 
798  // we need to modify our holdings in lght of the split factor
799  var quantity = security.Holdings.Quantity / split.SplitFactor;
800  var avgPrice = security.Holdings.AveragePrice * split.SplitFactor;
801 
802  // we'll model this as a cash adjustment
803  var leftOver = quantity - (int)quantity;
804 
805  security.Holdings.SetHoldings(avgPrice, (int)quantity);
806 
807  // build a 'next' value to update the market prices in light of the split factor
808  var next = security.GetLastData();
809  if (next == null)
810  {
811  // sometimes we can get splits before we receive data which
812  // will cause this to return null, in this case we can't possibly
813  // have any holdings or price to set since we haven't received
814  // data yet, so just do nothing
815  _baseCurrencyCash.AddAmount(leftOver * split.ReferencePrice * split.SplitFactor);
816  return;
817  }
818 
819  security.ApplySplit(split);
820  // The data price should have been adjusted already
821  _baseCurrencyCash.AddAmount(leftOver * next.Price);
822 
823  // security price updated
825  }
826 
827  /// <summary>
828  /// Record the transaction value and time in a list to later be processed for statistics creation.
829  /// </summary>
830  /// <param name="time">Time of order processed </param>
831  /// <param name="transactionProfitLoss">Profit Loss.</param>
832  /// <param name="isWin">
833  /// Whether the transaction is a win.
834  /// For options exercise, this might not depend only on the profit/loss value
835  /// </param>
836  public void AddTransactionRecord(DateTime time, decimal transactionProfitLoss, bool isWin)
837  {
838  Transactions.AddTransactionRecord(time, transactionProfitLoss, isWin);
839  }
840 
841  /// <summary>
842  /// Retrieves a summary of the holdings for the specified symbol
843  /// </summary>
844  /// <param name="symbol">The symbol to get holdings for</param>
845  /// <returns>The holdings for the symbol or null if the symbol is invalid and/or not in the portfolio</returns>
847  {
848  Security security;
849 
850  if (Securities.TryGetValue(symbol, out security))
851  {
852  return security;
853  }
854 
855  return null;
856  }
857 
858  /// <summary>
859  /// Logs margin information for debugging
860  /// </summary>
861  public void LogMarginInformation(OrderRequest orderRequest = null)
862  {
864 
865  var orderSubmitRequest = orderRequest as SubmitOrderRequest;
866  if (orderSubmitRequest != null)
867  {
868  var direction = orderSubmitRequest.Quantity > 0 ? OrderDirection.Buy : OrderDirection.Sell;
869  var security = Securities[orderSubmitRequest.Symbol];
870 
871  var positionGroup = Positions.GetOrCreateDefaultGroup(security);
872  var marginUsed = positionGroup.BuyingPowerModel.GetReservedBuyingPowerForPositionGroup(
873  this, positionGroup
874  );
875 
876  var marginRemaining = positionGroup.BuyingPowerModel.GetPositionGroupBuyingPower(
877  this, positionGroup, direction
878  );
879 
880  Log.Trace(Messages.SecurityPortfolioManager.OrderRequestMarginInformation(marginUsed, marginRemaining.Value));
881  }
882  }
883 
884  /// <summary>
885  /// Sets the margin call model
886  /// </summary>
887  /// <param name="marginCallModel">Model that represents a portfolio's model to executed margin call orders.</param>
888  public void SetMarginCallModel(IMarginCallModel marginCallModel)
889  {
890  MarginCallModel = marginCallModel;
891  }
892 
893  /// <summary>
894  /// Sets the margin call model
895  /// </summary>
896  /// <param name="pyObject">Model that represents a portfolio's model to executed margin call orders.</param>
897  public void SetMarginCallModel(PyObject pyObject)
898  {
900  }
901 
902  /// <summary>
903  /// Will determine if the algorithms portfolio has enough buying power to fill the given orders
904  /// </summary>
905  /// <param name="orders">The orders to check</param>
906  /// <returns>True if the algorithm has enough buying power available</returns>
908  {
909  if (Positions.TryCreatePositionGroup(orders, out var group))
910  {
911  return group.BuyingPowerModel.HasSufficientBuyingPowerForOrder(new HasSufficientPositionGroupBuyingPowerForOrderParameters(this, group, orders));
912  }
913 
914  for (var i = 0; i < orders.Count; i++)
915  {
916  var order = orders[i];
917  var security = Securities[order.Symbol];
918  var result = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(this, security, order);
919  if (!result.IsSufficient)
920  {
921  // if any fails, we fail all
922  return result;
923  }
924  }
926  }
927 
928  /// <summary>
929  /// Will set the security position group model to use
930  /// </summary>
931  /// <param name="positionGroupModel">The position group model instance</param>
932  public void SetPositions(SecurityPositionGroupModel positionGroupModel)
933  {
934  Positions = positionGroupModel;
935  }
936  }
937 }