/*
* 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;
using System.Collections.Generic;
using System.Linq;
using Python.Runtime;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Python;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Positions;
namespace QuantConnect.Securities
{
///
/// Portfolio manager class groups popular properties and makes them accessible through one interface.
/// It also provide indexing by the vehicle symbol to get the Security.Holding objects.
///
public class SecurityPortfolioManager : ExtendedDictionary, IDictionary, ISecurityProvider
{
private Cash _baseCurrencyCash;
private bool _setCashWasCalled;
private decimal _totalPortfolioValue;
private bool _isTotalPortfolioValueValid;
private object _totalPortfolioValueLock = new();
private bool _setAccountCurrencyWasCalled;
private decimal _freePortfolioValue;
private SecurityPositionGroupModel _positions;
private IAlgorithmSettings _algorithmSettings;
///
/// Local access to the securities collection for the portfolio summation.
///
public SecurityManager Securities { get; init; }
///
/// Local access to the transactions collection for the portfolio summation and updates.
///
public SecurityTransactionManager Transactions { get; init; }
///
/// Local access to the position manager
///
public SecurityPositionGroupModel Positions
{
get
{
return _positions;
}
set
{
value?.Initialize(Securities);
_positions = value;
}
}
///
/// Gets the cash book that keeps track of all currency holdings (only settled cash)
///
public CashBook CashBook { get; }
///
/// Gets the cash book that keeps track of all currency holdings (only unsettled cash)
///
public CashBook UnsettledCashBook { get; }
///
/// Initialise security portfolio manager.
///
public SecurityPortfolioManager(SecurityManager securityManager, SecurityTransactionManager transactions, IAlgorithmSettings algorithmSettings, IOrderProperties defaultOrderProperties = null)
{
Securities = securityManager;
Transactions = transactions;
_algorithmSettings = algorithmSettings;
Positions = new SecurityPositionGroupModel();
MarginCallModel = new DefaultMarginCallModel(this, defaultOrderProperties);
CashBook = new CashBook();
UnsettledCashBook = new CashBook();
_baseCurrencyCash = CashBook[CashBook.AccountCurrency];
// default to $100,000.00
_baseCurrencyCash.SetAmount(100000);
CashBook.Updated += (sender, args) =>
{
if (args.UpdateType == CashBookUpdateType.Added)
{
// add the same currency entry to the unsettled cashbook as well
var cash = args.Cash;
var unsettledCash = new Cash(cash.Symbol, 0m, cash.ConversionRate);
unsettledCash.CurrencyConversion = cash.CurrencyConversion;
cash.CurrencyConversionUpdated += (sender, args) =>
{
// Share the currency conversion instance between the settled and unsettled cash instances to synchronize the conversion rates
UnsettledCashBook[((Cash)sender).Symbol].CurrencyConversion = cash.CurrencyConversion;
};
UnsettledCashBook.Add(cash.Symbol, unsettledCash);
}
InvalidateTotalPortfolioValue();
};
UnsettledCashBook.Updated += (sender, args) => InvalidateTotalPortfolioValue();
}
#region IDictionary Implementation
///
/// Add a new securities string-security to the portfolio.
///
/// Symbol of dictionary
/// SecurityHoldings object
/// Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.
/// This method is not implemented and using it will throw an exception
public void Add(Symbol symbol, SecurityHolding holding) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryAddNotImplemented); }
///
/// Add a new securities key value pair to the portfolio.
///
/// Key value pair of dictionary
/// Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.
/// This method is not implemented and using it will throw an exception
public void Add(KeyValuePair pair) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryAddNotImplemented); }
///
/// Clear the portfolio of securities objects.
///
/// Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.
/// This method is not implemented and using it will throw an exception
public override void Clear() { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryClearNotImplemented); }
///
/// Remove this keyvalue pair from the portfolio.
///
/// Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.
/// Key value pair of dictionary
/// This method is not implemented and using it will throw an exception
public bool Remove(KeyValuePair pair) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryRemoveNotImplemented); }
///
/// Remove this symbol from the portfolio.
///
/// Portfolio object is an adaptor for Security Manager. This method is not applicable for PortfolioManager class.
/// Symbol of dictionary
/// This method is not implemented and using it will throw an exception
public override bool Remove(Symbol symbol) { throw new NotImplementedException(Messages.SecurityPortfolioManager.DictionaryRemoveNotImplemented); }
///
/// Check if the portfolio contains this symbol string.
///
/// String search symbol for the security
/// Boolean true if portfolio contains this symbol
public override bool ContainsKey(Symbol symbol)
{
return Securities.ContainsKey(symbol);
}
///
/// Check if the key-value pair is in the portfolio.
///
/// IDictionary implementation calling the underlying Securities collection
/// Pair we're searching for
/// True if we have this object
public bool Contains(KeyValuePair pair)
{
return Securities.ContainsKey(pair.Key);
}
///
/// Count the securities objects in the portfolio.
///
/// IDictionary implementation calling the underlying Securities collection
public override int Count
{
get
{
return Securities.Count;
}
}
///
/// Check if the underlying securities array is read only.
///
/// IDictionary implementation calling the underlying Securities collection
public override bool IsReadOnly
{
get
{
return Securities.IsReadOnly;
}
}
///
/// Copy contents of the portfolio collection to a new destination.
///
/// IDictionary implementation calling the underlying Securities collection
/// Destination array
/// Position in array to start copying
public void CopyTo(KeyValuePair[] array, int index)
{
array = new KeyValuePair[Securities.Count];
var i = 0;
foreach (var asset in Securities.Values)
{
if (i >= index)
{
array[i] = new KeyValuePair(asset.Symbol, asset.Holdings);
}
i++;
}
}
///
/// Gets all the items in the dictionary
///
/// All the items in the dictionary
public override IEnumerable> GetItems() =>
Securities.GetItems().Select(kvp => KeyValuePair.Create(kvp.Key, kvp.Value.Holdings));
///
/// Gets an containing the Symbol objects of the .
///
///
/// An containing the Symbol objects of the object that implements .
///
protected override IEnumerable GetKeys => Keys;
///
/// Gets an containing the values in the .
///
///
/// An containing the values in the object that implements .
///
protected override IEnumerable GetValues => Securities.Select(pair => pair.Value.Holdings);
///
/// Symbol keys collection of the underlying assets in the portfolio.
///
/// IDictionary implementation calling the underlying securities key symbols
public ICollection Keys
{
get
{
return Securities.Keys;
}
}
///
/// Collection of securities objects in the portfolio.
///
/// IDictionary implementation calling the underlying securities values collection
public ICollection Values
{
get
{
return GetValues.ToList();
}
}
///
/// Attempt to get the value of the securities holding class if this symbol exists.
///
/// String search symbol
/// Holdings object of this security
/// IDictionary implementation
/// Boolean true if successful locating and setting the holdings object
public override bool TryGetValue(Symbol symbol, out SecurityHolding holding)
{
Security security;
var success = Securities.TryGetValue(symbol, out security);
holding = success ? security.Holdings : null;
return success;
}
///
/// Get the enumerator for the underlying securities collection.
///
/// IDictionary implementation
/// Enumerable key value pair
IEnumerator> IEnumerable>.GetEnumerator()
{
return Securities.Select(x => new KeyValuePair(x.Key, x.Value.Holdings)).GetEnumerator();
}
///
/// Get the enumerator for the underlying securities collection.
///
/// IDictionary implementation
/// Enumerator
IEnumerator IEnumerable.GetEnumerator()
{
return Securities.Select(x => new KeyValuePair(x.Key, x.Value.Holdings)).GetEnumerator();
}
#endregion
///
/// Sum of all currencies in account in US dollars (only settled cash)
///
///
/// This should not be mistaken for margin available because Forex uses margin
/// even though the total cash value is not impact
///
public decimal Cash
{
get { return CashBook.TotalValueInAccountCurrency; }
}
///
/// Sum of all currencies in account in US dollars (only unsettled cash)
///
///
/// This should not be mistaken for margin available because Forex uses margin
/// even though the total cash value is not impact
///
public decimal UnsettledCash
{
get { return UnsettledCashBook.TotalValueInAccountCurrency; }
}
///
/// Absolute value of cash discounted from our total cash by the holdings we own.
///
/// When account has leverage the actual cash removed is a fraction of the purchase price according to the leverage
public decimal TotalUnleveredAbsoluteHoldingsCost
{
get
{
return Securities.Values.Sum(security => security.Holdings.UnleveredAbsoluteHoldingsCost);
}
}
///
/// Gets the total absolute holdings cost of the portfolio. This sums up the individual
/// absolute cost of each holding
///
public decimal TotalAbsoluteHoldingsCost
{
get
{
return Securities.Values.Sum(security => security.Holdings.AbsoluteHoldingsCost);
}
}
///
/// Absolute sum the individual items in portfolio.
///
public decimal TotalHoldingsValue
{
get
{
//Sum sum of holdings
return Securities.Values.Sum(security => security.Holdings.AbsoluteHoldingsValue);
}
}
///
/// Boolean flag indicating we have any holdings in the portfolio.
///
/// Assumes no asset can have $0 price and uses the sum of total holdings value
///
public bool HoldStock
{
get
{
foreach (var security in Securities.Values)
{
if (security.HoldStock)
{
return true;
}
}
return false;
}
}
///
/// Alias for HoldStock. Check if we have any holdings.
///
///
public bool Invested => HoldStock;
///
/// Get the total unrealised profit in our portfolio from the individual security unrealized profits.
///
public decimal TotalUnrealisedProfit
{
get
{
return Securities.Values.Sum(security => security.Holdings.UnrealizedProfit);
}
}
///
/// Get the total unrealised profit in our portfolio from the individual security unrealized profits.
///
/// Added alias for American spelling
public decimal TotalUnrealizedProfit
{
get { return TotalUnrealisedProfit; }
}
///
/// Total portfolio value if we sold all holdings at current market rates.
///
/// Cash + TotalUnrealisedProfit + TotalUnleveredAbsoluteHoldingsCost
///
///
///
public decimal TotalPortfolioValue
{
get
{
lock (_totalPortfolioValueLock)
{
if (!_isTotalPortfolioValueValid)
{
decimal totalHoldingsValueWithoutForexCryptoFutureCfd = 0;
decimal totalFuturesAndCfdHoldingsValue = 0;
foreach (var security in Securities.Values.Where((x) => x.Holdings.Invested))
{
var position = security;
var securityType = position.Type;
// We can't include forex in this calculation since we would be double accounting with respect to the cash book
// We also exclude futures and CFD as they are calculated separately because they do not impact the account's cash.
// We include futures options as part of this calculation because IB chooses to change our account's cash balance
// when we buy or sell a futures options contract.
if (securityType != SecurityType.Forex && securityType != SecurityType.Crypto
&& securityType != SecurityType.Future && securityType != SecurityType.Cfd
&& securityType != SecurityType.CryptoFuture)
{
totalHoldingsValueWithoutForexCryptoFutureCfd += position.Holdings.HoldingsValue;
}
// CFDs don't impact account cash, so they must be calculated
// by applying the unrealized P&L to the cash balance.
if (securityType == SecurityType.Cfd || securityType == SecurityType.CryptoFuture)
{
totalFuturesAndCfdHoldingsValue += position.Holdings.UnrealizedProfit;
}
// Futures P&L is settled daily into cash, here we take into account the current days unsettled profit
if (securityType == SecurityType.Future)
{
var futureHoldings = (FutureHolding)position.Holdings;
totalFuturesAndCfdHoldingsValue += futureHoldings.UnsettledProfit;
}
}
_totalPortfolioValue = CashBook.TotalValueInAccountCurrency +
UnsettledCashBook.TotalValueInAccountCurrency +
totalHoldingsValueWithoutForexCryptoFutureCfd +
totalFuturesAndCfdHoldingsValue;
_isTotalPortfolioValueValid = true;
}
}
return _totalPortfolioValue;
}
}
///
/// Returns the adjusted total portfolio value removing the free amount
/// If the has not been set, the free amount will have a trailing behavior and be updated when requested
///
public decimal TotalPortfolioValueLessFreeBuffer
{
get
{
if (_algorithmSettings.FreePortfolioValue.HasValue)
{
// the user set it, we will respect the value set
_freePortfolioValue = _algorithmSettings.FreePortfolioValue.Value;
}
else
{
// keep the free portfolio value up to date every time we use it
_freePortfolioValue = TotalPortfolioValue * _algorithmSettings.FreePortfolioValuePercentage;
}
return TotalPortfolioValue - _freePortfolioValue;
}
}
///
/// Will flag the current as invalid
/// so it is recalculated when gotten
///
public void InvalidateTotalPortfolioValue()
{
_isTotalPortfolioValueValid = false;
}
///
/// Total fees paid during the algorithm operation across all securities in portfolio.
///
public decimal TotalFees
{
get
{
return Securities.Total.Sum(security => security.Holdings.TotalFees);
}
}
///
/// Sum of all gross profit across all securities in portfolio and dividend payments.
///
public decimal TotalProfit
{
get
{
return Securities.Total.Sum(security => security.Holdings.Profit);
}
}
///
/// Sum of all net profit across all securities in portfolio and dividend payments.
///
public decimal TotalNetProfit
{
get
{
return Securities.Total.Sum(security => security.Holdings.NetProfit);
}
}
///
/// Total sale volume since the start of algorithm operations.
///
public decimal TotalSaleVolume
{
get
{
return Securities.Total.Sum(security => security.Holdings.TotalSaleVolume);
}
}
///
/// Gets the total margin used across all securities in the account's currency
///
public decimal TotalMarginUsed
{
get
{
decimal sum = 0;
foreach (var group in Positions.Groups)
{
sum += group.BuyingPowerModel.GetReservedBuyingPowerForPositionGroup(this, group);
}
return sum;
}
}
///
/// Gets the remaining margin on the account in the account's currency
///
///
public decimal MarginRemaining => GetMarginRemaining(TotalPortfolioValue);
///
/// Gets the remaining margin on the account in the account's currency
/// for the given total portfolio value
///
/// This method is for performance, for when the user already knows
/// the total portfolio value, we can avoid re calculating it. Else use
///
/// The total portfolio value
public decimal GetMarginRemaining(decimal totalPortfolioValue)
{
return totalPortfolioValue - UnsettledCashBook.TotalValueInAccountCurrency - TotalMarginUsed;
}
///
/// Gets or sets the for the portfolio. This
/// is used to executed margin call orders.
///
public IMarginCallModel MarginCallModel { get; set; }
///
/// Indexer for the PortfolioManager class to access the underlying security holdings objects.
///
/// Symbol object indexer
/// SecurityHolding class from the algorithm securities
public override SecurityHolding this[Symbol symbol]
{
get
{
return Securities[symbol].Holdings;
}
set
{
Securities[symbol].Holdings = value;
}
}
///
/// Sets the account currency cash symbol this algorithm is to manage, as well
/// as the starting cash in this currency if given
///
/// Has to be called before calling
/// or adding any
/// The account currency cash symbol to set
/// The account currency starting cash to set
public void SetAccountCurrency(string accountCurrency, decimal? startingCash = null)
{
accountCurrency = accountCurrency.LazyToUpper();
// only allow setting account currency once
// we could try to set it twice when backtesting and the job packet specifies the initial CashAmount to use
if (_setAccountCurrencyWasCalled)
{
if (accountCurrency != CashBook.AccountCurrency)
{
Log.Trace("SecurityPortfolioManager.SetAccountCurrency(): " +
Messages.SecurityPortfolioManager.AccountCurrencyAlreadySet(CashBook, accountCurrency));
}
return;
}
_setAccountCurrencyWasCalled = true;
if (Securities.Count > 0)
{
throw new InvalidOperationException("SecurityPortfolioManager.SetAccountCurrency(): " +
Messages.SecurityPortfolioManager.CannotChangeAccountCurrencyAfterAddingSecurity);
}
if (_setCashWasCalled)
{
throw new InvalidOperationException("SecurityPortfolioManager.SetAccountCurrency(): " +
Messages.SecurityPortfolioManager.CannotChangeAccountCurrencyAfterSettingCash);
}
Log.Trace("SecurityPortfolioManager.SetAccountCurrency(): " +
Messages.SecurityPortfolioManager.SettingAccountCurrency(accountCurrency));
UnsettledCashBook.AccountCurrency = accountCurrency;
CashBook.AccountCurrency = accountCurrency;
_baseCurrencyCash = CashBook[accountCurrency];
if (startingCash != null)
{
SetCash((decimal)startingCash);
}
}
///
/// Set the account currency cash this algorithm is to manage.
///
/// Decimal cash value of portfolio
public void SetCash(decimal cash)
{
_setCashWasCalled = true;
_baseCurrencyCash.SetAmount(cash);
}
///
/// Set the cash for the specified symbol
///
/// The cash symbol to set
/// Decimal cash value of portfolio
/// The current conversion rate for the
public void SetCash(string symbol, decimal cash, decimal conversionRate)
{
_setCashWasCalled = true;
Cash item;
symbol = symbol.LazyToUpper();
if (CashBook.TryGetValue(symbol, out item))
{
item.SetAmount(cash);
item.ConversionRate = conversionRate;
}
else
{
CashBook.Add(symbol, cash, conversionRate);
}
}
// TODO: Review and fix these comments: it doesn't return what it says it does.
///
/// Gets the margin available for trading a specific symbol in a specific direction.
///
/// The symbol to compute margin remaining for
/// The order/trading direction
/// The maximum order size that is currently executable in the specified direction
public decimal GetMarginRemaining(Symbol symbol, OrderDirection direction = OrderDirection.Buy)
{
var security = Securities[symbol];
var positionGroup = Positions.GetOrCreateDefaultGroup(security);
// Order direction in GetPositionGroupBuyingPower is regarding buying or selling the position group sent as parameter.
// Since we are passing the same position group as the one in the holdings, we need to invert the direction.
// Buying the means increasing the position group (in the same direction it is currently held) and selling means decreasing it.
var positionGroupOrderDirection = direction;
if (security.Holdings.IsShort)
{
positionGroupOrderDirection = direction == OrderDirection.Buy ? OrderDirection.Sell : OrderDirection.Buy;
}
var parameters = new PositionGroupBuyingPowerParameters(this, positionGroup, positionGroupOrderDirection);
return positionGroup.BuyingPowerModel.GetPositionGroupBuyingPower(parameters);
}
///
/// Gets the margin available for trading a specific symbol in a specific direction.
/// Alias for
///
/// The symbol to compute margin remaining for
/// The order/trading direction
/// The maximum order size that is currently executable in the specified direction
public decimal GetBuyingPower(Symbol symbol, OrderDirection direction = OrderDirection.Buy)
{
return GetMarginRemaining(symbol, direction);
}
///
/// Calculate the new average price after processing a list of partial/complete order fill events.
///
///
/// For purchasing stocks from zero holdings, the new average price is the sale price.
/// When simply partially reducing holdings the average price remains the same.
/// When crossing zero holdings the average price becomes the trade price in the new side of zero.
///
public virtual void ProcessFills(List fills)
{
lock (_totalPortfolioValueLock)
{
for (var i = 0; i < fills.Count; i++)
{
var fill = fills[i];
var security = Securities[fill.Symbol];
security.PortfolioModel.ProcessFill(this, security, fill);
}
InvalidateTotalPortfolioValue();
}
}
///
/// Applies a dividend to the portfolio
///
/// The dividend to be applied
/// True if live mode, false for backtest
/// The for this security
public void ApplyDividend(Dividend dividend, bool liveMode, DataNormalizationMode mode)
{
// we currently don't properly model dividend payable dates, so in
// live mode it's more accurate to rely on the brokerage cash sync
if (liveMode)
{
return;
}
// only apply dividends when we're in raw mode or split adjusted mode
if (mode == DataNormalizationMode.Raw || mode == DataNormalizationMode.SplitAdjusted)
{
var security = Securities[dividend.Symbol];
// longs get benefits, shorts get clubbed on dividends
var total = security.Holdings.Quantity * dividend.Distribution * security.QuoteCurrency.ConversionRate;
// assuming USD, we still need to add Currency to the security object
_baseCurrencyCash.AddAmount(total);
security.Holdings.AddNewDividend(total);
}
}
///
/// Applies a split to the portfolio
///
/// The split to be applied
/// The security the split will be applied to
/// True if live mode, false for backtest
/// The for this security
public void ApplySplit(Split split, Security security, bool liveMode, DataNormalizationMode mode)
{
// only apply splits to equities
if (security.Type != SecurityType.Equity)
{
return;
}
// only apply splits in live or raw data mode
if (!liveMode && mode != DataNormalizationMode.Raw)
{
return;
}
// we need to modify our holdings in lght of the split factor
var quantity = security.Holdings.Quantity / split.SplitFactor;
var avgPrice = security.Holdings.AveragePrice * split.SplitFactor;
// we'll model this as a cash adjustment
var leftOver = quantity - (int)quantity;
security.Holdings.SetHoldings(avgPrice, (int)quantity);
// build a 'next' value to update the market prices in light of the split factor
var next = security.GetLastData();
if (next == null)
{
// sometimes we can get splits before we receive data which
// will cause this to return null, in this case we can't possibly
// have any holdings or price to set since we haven't received
// data yet, so just do nothing
_baseCurrencyCash.AddAmount(leftOver * split.ReferencePrice * split.SplitFactor);
return;
}
security.ApplySplit(split);
// The data price should have been adjusted already
_baseCurrencyCash.AddAmount(leftOver * next.Price);
// security price updated
InvalidateTotalPortfolioValue();
}
///
/// Record the transaction value and time in a list to later be processed for statistics creation.
///
/// Time of order processed
/// Profit Loss.
///
/// Whether the transaction is a win.
/// For options exercise, this might not depend only on the profit/loss value
///
public void AddTransactionRecord(DateTime time, decimal transactionProfitLoss, bool isWin)
{
Transactions.AddTransactionRecord(time, transactionProfitLoss, isWin);
}
///
/// Retrieves a summary of the holdings for the specified symbol
///
/// The symbol to get holdings for
/// The holdings for the symbol or null if the symbol is invalid and/or not in the portfolio
Security ISecurityProvider.GetSecurity(Symbol symbol)
{
Security security;
if (Securities.TryGetValue(symbol, out security))
{
return security;
}
return null;
}
///
/// Logs margin information for debugging
///
public void LogMarginInformation(OrderRequest orderRequest = null)
{
Log.Trace(Messages.SecurityPortfolioManager.TotalMarginInformation(TotalMarginUsed, MarginRemaining));
var orderSubmitRequest = orderRequest as SubmitOrderRequest;
if (orderSubmitRequest != null)
{
var direction = orderSubmitRequest.Quantity > 0 ? OrderDirection.Buy : OrderDirection.Sell;
var security = Securities[orderSubmitRequest.Symbol];
var positionGroup = Positions.GetOrCreateDefaultGroup(security);
var marginUsed = positionGroup.BuyingPowerModel.GetReservedBuyingPowerForPositionGroup(
this, positionGroup
);
var marginRemaining = positionGroup.BuyingPowerModel.GetPositionGroupBuyingPower(
this, positionGroup, direction
);
Log.Trace(Messages.SecurityPortfolioManager.OrderRequestMarginInformation(marginUsed, marginRemaining.Value));
}
}
///
/// Sets the margin call model
///
/// Model that represents a portfolio's model to executed margin call orders.
public void SetMarginCallModel(IMarginCallModel marginCallModel)
{
MarginCallModel = marginCallModel;
}
///
/// Sets the margin call model
///
/// Model that represents a portfolio's model to executed margin call orders.
public void SetMarginCallModel(PyObject pyObject)
{
SetMarginCallModel(new MarginCallModelPythonWrapper(pyObject));
}
///
/// Will determine if the algorithms portfolio has enough buying power to fill the given orders
///
/// The orders to check
/// True if the algorithm has enough buying power available
public HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(List orders)
{
if (Positions.TryCreatePositionGroup(orders, out var group))
{
return group.BuyingPowerModel.HasSufficientBuyingPowerForOrder(new HasSufficientPositionGroupBuyingPowerForOrderParameters(this, group, orders));
}
for (var i = 0; i < orders.Count; i++)
{
var order = orders[i];
var security = Securities[order.Symbol];
var result = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(this, security, order);
if (!result.IsSufficient)
{
// if any fails, we fail all
return result;
}
}
return new HasSufficientBuyingPowerForOrderResult(true);
}
///
/// Will set the security position group model to use
///
/// The position group model instance
public void SetPositions(SecurityPositionGroupModel positionGroupModel)
{
Positions = positionGroupModel;
}
}
}