/* * 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 QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Algorithm.Framework.Portfolio; namespace QuantConnect.Securities { /// /// SecurityHolding is a base class for purchasing and holding a market item which manages the asset portfolio /// public class SecurityHolding { /// /// Event raised each time the holdings quantity is changed. /// public event EventHandler QuantityChanged; //Working Variables private bool _invested; private decimal _averagePrice; private decimal _quantity; private decimal _price; private decimal _totalSaleVolume; private decimal _profit; private decimal _lastTradeProfit; private decimal _totalFees; private decimal _totalDividends; private readonly Security _security; private readonly ICurrencyConverter _currencyConverter; /// /// Create a new holding class instance setting the initial properties to $0. /// /// The security being held /// A currency converter instance public SecurityHolding(Security security, ICurrencyConverter currencyConverter) { _security = security; //Total Sales Volume for the day _totalSaleVolume = 0; _lastTradeProfit = 0; _currencyConverter = currencyConverter; } /// /// Create a new holding class instance copying the initial properties /// /// The security being held protected SecurityHolding(SecurityHolding holding) { _security = holding._security; _averagePrice = holding._averagePrice; Quantity = holding._quantity; _price = holding._price; _totalSaleVolume = holding._totalSaleVolume; _profit = holding._profit; _lastTradeProfit = holding._lastTradeProfit; _totalFees = holding._totalFees; _currencyConverter = holding._currencyConverter; } /// /// The security being held /// protected Security Security { get { return _security; } } /// /// Gets the current target holdings for this security /// public IPortfolioTarget Target { get; set; } /// /// Average price of the security holdings. /// public decimal AveragePrice { get { return _averagePrice; } protected set { _averagePrice = value; } } /// /// Quantity of the security held. /// /// Positive indicates long holdings, negative quantity indicates a short holding /// public decimal Quantity { get { return _quantity; } protected set { // avoid any small values, due to differences in lot size, to return invested true but lean not allowing us to trade sice it will be rounded down to 0 // specially useful to crypto assets which take fees from the base or quote currency _invested = Math.Abs(value) >= _security.SymbolProperties.LotSize; _quantity = value; } } /// /// Symbol identifier of the underlying security. /// public Symbol Symbol { get { return _security.Symbol; } } /// /// The security type of the symbol /// public SecurityType Type { get { return _security.Type; } } /// /// Leverage of the underlying security. /// public virtual decimal Leverage { get { return _security.BuyingPowerModel.GetLeverage(_security); } } /// /// Acquisition cost of the security total holdings in units of the account's currency. /// public virtual decimal HoldingsCost { get { if (Quantity == 0) { return 0; } return GetQuantityValue(Quantity, AveragePrice).InAccountCurrency; } } /// /// Unlevered Acquisition cost of the security total holdings in units of the account's currency. /// public virtual decimal UnleveredHoldingsCost { get { return HoldingsCost/Leverage; } } /// /// Current market price of the security. /// public virtual decimal Price { get { return _price; } protected set { _price = value; } } /// /// Absolute holdings cost for current holdings in units of the account's currency. /// /// public virtual decimal AbsoluteHoldingsCost { get { return Math.Abs(HoldingsCost); } } /// /// Unlevered absolute acquisition cost of the security total holdings in units of the account's currency. /// public virtual decimal UnleveredAbsoluteHoldingsCost { get { return Math.Abs(UnleveredHoldingsCost); } } /// /// Market value of our holdings in units of the account's currency. /// public virtual decimal HoldingsValue { get { if (Quantity == 0) { return 0; } return GetQuantityValue(Quantity).InAccountCurrency; } } /// /// Absolute of the market value of our holdings in units of the account's currency. /// /// public virtual decimal AbsoluteHoldingsValue { get { return Math.Abs(HoldingsValue); } } /// /// Boolean flag indicating if we hold any of the security /// public virtual bool HoldStock => _invested; /// /// Boolean flag indicating if we hold any of the security /// /// Alias of HoldStock /// public virtual bool Invested => _invested; /// /// The total transaction volume for this security since the algorithm started in units of the account's currency. /// public virtual decimal TotalSaleVolume { get { return _totalSaleVolume; } } /// /// Total fees for this company since the algorithm started in units of the account's currency. /// public virtual decimal TotalFees { get { return _totalFees; } } /// /// Total dividends for this company since the algorithm started in units of the account's currency. /// public virtual decimal TotalDividends { get { return _totalDividends; } } /// /// Boolean flag indicating we have a net positive holding of the security. /// /// public virtual bool IsLong { get { return Quantity > 0; } } /// /// BBoolean flag indicating we have a net negative holding of the security. /// /// public virtual bool IsShort { get { return Quantity < 0; } } /// /// Absolute quantity of holdings of this security /// /// public virtual decimal AbsoluteQuantity { get { return Math.Abs(Quantity); } } /// /// Record of the closing profit from the last trade conducted in units of the account's currency. /// public virtual decimal LastTradeProfit { get { return _lastTradeProfit; } } /// /// Calculate the total profit for this security in units of the account's currency. /// /// public virtual decimal Profit { get { return _profit + _totalDividends; } } /// /// Return the net for this company measured by the profit less fees in units of the account's currency. /// /// /// public virtual decimal NetProfit { get { return Profit - TotalFees; } } /// /// Gets the unrealized profit as a percentage of holdings cost /// public virtual decimal UnrealizedProfitPercent { get { if (AbsoluteHoldingsCost == 0) return 0m; return UnrealizedProfit/AbsoluteHoldingsCost; } } /// /// Unrealized profit of this security when absolute quantity held is more than zero in units of the account's currency. /// public virtual decimal UnrealizedProfit { get { return TotalCloseProfit(); } } /// /// Adds a fee to the running total of total fees in units of the account's currency. /// /// public void AddNewFee(decimal newFee) { _totalFees += newFee; } /// /// Adds a profit record to the running total of profit in units of the account's currency. /// /// The cash change in portfolio from closing a position public void AddNewProfit(decimal profitLoss) { _profit += profitLoss; } /// /// Adds a new sale value to the running total trading volume in units of the account's currency. /// /// public void AddNewSale(decimal saleValue) { _totalSaleVolume += saleValue; } /// /// Adds a new dividend payment to the running total dividend in units of the account's currency. /// /// public void AddNewDividend(decimal dividend) { _totalDividends += dividend; } /// /// Set the last trade profit for this security from a Portfolio.ProcessFill call in units of the account's currency. /// /// Value of the last trade profit public void SetLastTradeProfit(decimal lastTradeProfit) { _lastTradeProfit = lastTradeProfit; } /// /// Set the quantity of holdings and their average price after processing a portfolio fill. /// public virtual void SetHoldings(decimal averagePrice, int quantity) { SetHoldings(averagePrice, (decimal) quantity); } /// /// Set the quantity of holdings and their average price after processing a portfolio fill. /// public virtual void SetHoldings(decimal averagePrice, decimal quantity) { var previousQuantity = _quantity; var previousAveragePrice = _averagePrice; Quantity = quantity; _averagePrice = averagePrice; OnQuantityChanged(previousAveragePrice, previousQuantity); } /// /// Update local copy of closing price value. /// /// Price of the underlying asset to be used for calculating market price / portfolio value public virtual void UpdateMarketPrice(decimal closingPrice) { _price = closingPrice; } /// /// Gets the total value of the specified of shares of this security /// in the account currency /// /// The quantity of shares /// The value of the quantity of shares in the account currency public virtual ConvertibleCashAmount GetQuantityValue(decimal quantity) { return GetQuantityValue(quantity, _price); } /// /// Gets the total value of the specified of shares of this security /// in the account currency /// /// The quantity of shares /// The current price /// The value of the quantity of shares in the account currency public virtual ConvertibleCashAmount GetQuantityValue(decimal quantity, decimal price) { var amount = price * quantity * _security.SymbolProperties.ContractMultiplier; return new ConvertibleCashAmount(amount, _security.QuoteCurrency); } /// /// Profit if we closed the holdings right now including the approximate fees in units of the account's currency. /// /// Does not use the transaction model for market fills but should. public virtual decimal TotalCloseProfit(bool includeFees = true, decimal? exitPrice = null, decimal? entryPrice = null, decimal? quantity = null) { var quantityToUse = quantity ?? Quantity; if (quantityToUse == 0) { return 0; } // this is in the account currency var orderFee = Extensions.GetMarketOrderFees(_security, -quantityToUse, _security.LocalTime.ConvertToUtc(_security.Exchange.TimeZone), out var marketOrder); var feesInAccountCurrency = 0m; if (includeFees) { feesInAccountCurrency = _currencyConverter.ConvertToAccountCurrency(orderFee).Amount; } var price = marketOrder.Direction == OrderDirection.Sell ? _security.BidPrice : _security.AskPrice; if (price == 0) { // Bid/Ask prices can both be equal to 0. This usually happens when we request our holdings from // the brokerage, but only the last trade price was provided. price = _security.Price; } var entryValue = GetQuantityValue(quantityToUse, entryPrice ?? AveragePrice).InAccountCurrency; var potentialExitValue = GetQuantityValue(quantityToUse, exitPrice ?? price).InAccountCurrency; return potentialExitValue - entryValue - feesInAccountCurrency; } /// /// Writes out the properties of this instance to string /// public override string ToString() { return Messages.SecurityHolding.ToString(this); } /// /// Event invocator for the event /// protected virtual void OnQuantityChanged(decimal previousAveragePrice, decimal previousQuantity) { QuantityChanged?.Invoke(this, new SecurityHoldingQuantityChangedEventArgs( _security, previousAveragePrice, previousQuantity )); } } }