/*
* 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
));
}
}
}