83f9499b4a
* Refactor HasSufficientBuyingPowerForOrder implementations Adds Sufficient and Insufficient helper methods to HashSufficientbuyingPowerForOrderParameters enabling syntax like: return paraeeters.Sufficient() returnparameters.Insufficient(reason) The next change will add the initial margin required which will simply require updating both of these helper methods to accept the value. * IBuyingPowerModel: Add margin functions Maintenance/Initial/ForOrder These were originally hidden in an effort to only expose what's necessary for the engine to perform its work. Additionally, we encapsulated all of the method arguments into parameters classes to prevent having to break anyone in the future. Not including these foundational methods turns out to be an oversight. These methods are not required by the engine, but rather by other models. Another possible solution here is to add an additional abstraction and include these methods on this new abstraction. BuyingPowerModel would then explicitly implement these methods and models that depend on them would require two code paths, one for when the buying power model implements this interface and another for when it doesn't. Tests were additionally updated to remove test model implementations created for the sole purpose of exposing these private methods. * Add ConstantBuyingPowerModel Provides an implementation of IBuyingPowerModel that returns the same constant value * Update BuyingPowerModelPythonWrapper to use reflection for method names Having a bunch of hard-coded strings is a sure fire way for someone to overlook when changing methods. This change ensures that noone needs to remember that this code exists :) Cleans up the syntax around verifying a python object implements a particular C# interface via the ValidateImplementationOf<T> method by having it return a value since the only use cases are in constructors when setting the models. I was initially going to update ALL python wrappers to validate the passed in models, but such a change could break many things that are 'working' right now. Such an effort should be saved for its own dedicated PR. * Add Parameters/Result types for new buying power model methods * Support computing maintenance margin for arbitrary quantities The existing GetMaintenanceMargin function assumes that we're only interested in the maintenance margin for the entirety of the provided security's holdings. This makes it impossible to perform what-if analysis or to even ask how much maintenance margin is devoted to a particular subset of the security's holdings. This change adds the quantity to the MaintenanceMarginParameters class. Futures and Options models also depend on holdings cost and holdings value, so they have also been added to the parameters type. Finally, static factory methods were added to improve discernment of intent: ForCurrentHoldings provides the existing behavior and then ForQuantityAtCurrentPrice to support what-if scenarios where we're looking for the change in maintenance margin if we were to execute an order for the securiy at the current time step. Obviously a constructor is provided to set all of the values explicitly, using any price metric the caller desires. * Address review - Fix BPM xml documentation - Fix python unit tests and PythonWrapper validate method * Add SecurityHolding.QuantityChanged event Adding event handlers will allow us to orchestrate complex events from distant parts of the codebase through wiring them up. If we continue down this path, it will move us away from the current, very 'mechanical' data flows expressed in LEAN and towards a more modern, event processing based system. This is but a baby step in that direction and the initial use case is using this QuantityChanged event to trigger resolution of the algoritm's positions groups. This is part of an effort to improve the fidelity of options margin modeling where we'll model an OptionStrategy as an IPositionGroup. This will allow us to compute the margin requirements of an OptionStrategy as a unit instead of computing margin of each security individually in isolation. See #4065 * PortfolioManager: Group fields and remove unused field This codebase generally places fields as the first members, but this class had some fields at the top, then some properties, and then some more fields. This change brings all the fields together at the top of the file and also removes pointless comments placed directly above some of the fields. Additionally, an unused field was removed. * Remove unused _currencyConverter from Security Looks like at some point the only code using this member variable was removed and the necessary clean up was overlooked. * Add Parse.Enum functions * Support disabling regression algorithms by language via config.json Adds 'regression-test-languages' to config.json and filters regerssion algorithms to run based on this value. When cycling on a particular feature, it's nice to be able to run the entire regression set while ignoring the python algorithms. Once the C# algorithms are all passing, one can then go back and run C# and Python in a final run, since 99% of feature work doesn't impact python specifically. * Implement IComparable in SecurityIdentitfier This can be used to deterministically sort securities and symbols * Add .editorconfig to enforce common formatting for json/sh files * Fix typo in IBuyingPowerModel.GetBuyingPower xml docs * Add ListEquals/GetListHashCode and OrderDirection.Closes(PositionSide) ListEquals and GetListHashCode are designed to be used together as they complement each other according to C#'s requirements for Equals and GetHashCode functions. PositionSide.ToOrderDirection() extension simply converts a PositionSide to its logical equivalent OrderDirection. Long->Buy, Short->Sell, None->Hold OrderDirection.Closes(PositionSide) determines if a particular OrderDirection would have the effect of reducing a position's absolute size. This function greatly improves the readability of buying power functions that must provide adjustments when an order/contemplated trade reduces/closes an existing position. OrderDirection.Buy.Closes(PositionSide.Short) OrderDirection.Sell.Closes(PositionSide.Long) All other combinations return false Adds ToArray/ToImmutableArray convenience functions that combine a call to Select followed by To(Immutable)Array all in one function call. * Add decimal.DiscretelyRoundBy extension method Supports rounding a decimal value by an arbitrarily chosen maximum precision, or 'quanta' * Update FutureMarginBuyingPowerModelTests to respect the security's lot size * Add core position group classes and abstractions * Add initial/maintenance margin support, buying power model consistency tests * Add SufficientBuyingPower and GetReservedBuyingPower to position group model Includes update to BrokerageTransactionHandler to use position group BPM for sufficient buying power checks. * Resolve position groups on each fill We need to update the state of our position groups on each fill so that we can properly handle multiple orders within the same time step. We also limit the number of positions sent into the resolver by removing securities without any holdings. * fixup! Add SufficientBuyingPower and GetReservedBuyingPower to position group model * Add GetMaximumLotsFor{Target|Delta}BuyingPower Instead of computing order quantity, these functions compute the maximum number of position group lots, which is the position group quantity, and is guaranteed to be a whole number, for the provided target/delta buying power parameters. The SecurityPositionGroupBuyingPowerModel delegates to the security's IBuyingPowerModel by applying a scaling factor equal to the security's lot size. This change also updates references to IBuyingPowerModel.GetMaximum... to use the new position group model methods. * Convert remaining IBuyingPowerModel call sites to position groups * Rename PositionManasger.CreateDefaultGroup -> GetOrCreateDefaultGroup Better describes its behavior * Add Position Groups readme.md * Add Option Strategy BuyingPowerModel - Adding CompositePrositionGroupResolver and OptionStrategyPositionGroupResolver - Adding OptionStrategyPositionGroupBuyingPowerModel handling option strategies based on IBs margin table. Adding regression algorithms - Few changes so that option strategies executed by multiple orders are detected - Adjust OptionStrategyDefinitionMatch to include equity legs in the matching result - Minor tweaks fixing previous rebase - Minor fixes for existing option strategies definitions, adding new missing strategies. - Fixing minor bugs in option strategy matcher. Adding more unit tests * Address self reviews - Fixing bug in 'PositionGroupCollection' - Few minor simplificaitons - Adding BasicTemplateOptionEquityStrategyAlgorithm * Address reviews - Improve regression algorithms margin remaining and used assert logic to be exact. Taking into account spread and fees Co-authored-by: Michael Handschuh <mhandschuh@gmail.com>
847 lines
30 KiB
C#
847 lines
30 KiB
C#
/*
|
|
* 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.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using QuantConnect.Data;
|
|
using QuantConnect.Orders.Fees;
|
|
using QuantConnect.Orders.Fills;
|
|
using QuantConnect.Orders.Slippage;
|
|
using QuantConnect.Securities.Equity;
|
|
using QuantConnect.Securities.Forex;
|
|
using QuantConnect.Securities.Interfaces;
|
|
using QuantConnect.Data.Market;
|
|
using QuantConnect.Python;
|
|
using Python.Runtime;
|
|
using QuantConnect.Data.Fundamental;
|
|
using QuantConnect.Data.UniverseSelection;
|
|
using QuantConnect.Interfaces;
|
|
|
|
namespace QuantConnect.Securities
|
|
{
|
|
/// <summary>
|
|
/// A base vehicle properties class for providing a common interface to all assets in QuantConnect.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Security object is intended to hold properties of the specific security asset. These properties can include trade start-stop dates,
|
|
/// price, market hours, resolution of the security, the holdings information for this security and the specific fill model.
|
|
/// </remarks>
|
|
public class Security : ISecurityPrice
|
|
{
|
|
private LocalTimeKeeper _localTimeKeeper;
|
|
|
|
/// <summary>
|
|
/// Collection of SubscriptionDataConfigs for this security.
|
|
/// Uses concurrent bag to avoid list enumeration threading issues
|
|
/// </summary>
|
|
protected readonly ConcurrentBag<SubscriptionDataConfig> SubscriptionsBag;
|
|
|
|
/// <summary>
|
|
/// This securities <see cref="IShortableProvider"/>
|
|
/// </summary>
|
|
protected IShortableProvider ShortableProvider { get; private set; }
|
|
|
|
/// <summary>
|
|
/// A null security leverage value
|
|
/// </summary>
|
|
/// <remarks>This value is used to determine when the
|
|
/// <see cref="SecurityInitializer"/> leverage is used</remarks>
|
|
public const decimal NullLeverage = 0;
|
|
|
|
/// <summary>
|
|
/// Gets all the subscriptions for this security
|
|
/// </summary>
|
|
public IEnumerable<SubscriptionDataConfig> Subscriptions => SubscriptionsBag;
|
|
|
|
/// <summary>
|
|
/// <see cref="Symbol"/> for the asset.
|
|
/// </summary>
|
|
public Symbol Symbol { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the Cash object used for converting the quote currency to the account currency
|
|
/// </summary>
|
|
public Cash QuoteCurrency
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the symbol properties for this security
|
|
/// </summary>
|
|
public SymbolProperties SymbolProperties
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type of the security.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// QuantConnect currently only supports Equities and Forex
|
|
/// </remarks>
|
|
public SecurityType Type => Symbol.ID.SecurityType;
|
|
|
|
/// <summary>
|
|
/// Resolution of data requested for this security.
|
|
/// </summary>
|
|
/// <remarks>Tick, second or minute resolution for QuantConnect assets.</remarks>
|
|
[Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
|
|
public Resolution Resolution { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the data will use previous bars when there was no trading in this time period. This was a configurable datastream setting set in initialization.
|
|
/// </summary>
|
|
[Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
|
|
public bool IsFillDataForward { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the security will continue feeding data after the primary market hours have closed. This was a configurable setting set in initialization.
|
|
/// </summary>
|
|
[Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
|
|
public bool IsExtendedMarketHours { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets the data normalization mode used for this security
|
|
/// </summary>
|
|
[Obsolete("This property is obsolete. Use the 'SubscriptionDataConfig' exposed by 'SubscriptionManager'")]
|
|
public DataNormalizationMode DataNormalizationMode { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets the subscription configuration for this security
|
|
/// </summary>
|
|
[Obsolete("This property returns only the first subscription. Use the 'Subscriptions' property for all of this security's subscriptions.")]
|
|
public SubscriptionDataConfig SubscriptionDataConfig => SubscriptionsBag.FirstOrDefault();
|
|
|
|
/// <summary>
|
|
/// There has been at least one datapoint since our algorithm started running for us to determine price.
|
|
/// </summary>
|
|
public bool HasData => GetLastData() != null;
|
|
|
|
/// <summary>
|
|
/// Gets or sets whether or not this security should be considered tradable
|
|
/// </summary>
|
|
public virtual bool IsTradable
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if the security has been delisted from exchanges and is no longer tradable
|
|
/// </summary>
|
|
public bool IsDelisted { get; set; }
|
|
|
|
/// <summary>
|
|
/// Data cache for the security to store previous price information.
|
|
/// </summary>
|
|
/// <seealso cref="EquityCache"/>
|
|
/// <seealso cref="ForexCache"/>
|
|
public SecurityCache Cache
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Holdings class contains the portfolio, cash and processes order fills.
|
|
/// </summary>
|
|
/// <seealso cref="EquityHolding"/>
|
|
/// <seealso cref="ForexHolding"/>
|
|
public SecurityHolding Holdings
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exchange class contains the market opening hours, along with pre-post market hours.
|
|
/// </summary>
|
|
/// <seealso cref="EquityExchange"/>
|
|
/// <seealso cref="ForexExchange"/>
|
|
public SecurityExchange Exchange
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fee model used to compute order fees for this security
|
|
/// </summary>
|
|
public IFeeModel FeeModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill model used to produce fill events for this security
|
|
/// </summary>
|
|
public IFillModel FillModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Slippage model use to compute slippage of market orders
|
|
/// </summary>
|
|
public ISlippageModel SlippageModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the portfolio model used by this security
|
|
/// </summary>
|
|
public ISecurityPortfolioModel PortfolioModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the buying power model used for this security
|
|
/// </summary>
|
|
public IBuyingPowerModel BuyingPowerModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the buying power model used for this security, an alias for <see cref="BuyingPowerModel"/>
|
|
/// </summary>
|
|
public IBuyingPowerModel MarginModel
|
|
{
|
|
get { return BuyingPowerModel; }
|
|
set { BuyingPowerModel = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the settlement model used for this security
|
|
/// </summary>
|
|
public ISettlementModel SettlementModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the volatility model used for this security
|
|
/// </summary>
|
|
public IVolatilityModel VolatilityModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Customizable data filter to filter outlier ticks before they are passed into user event handlers.
|
|
/// By default all ticks are passed into the user algorithms.
|
|
/// </summary>
|
|
/// <remarks>TradeBars (seconds and minute bars) are prefiltered to ensure the ticks which build the bars are realistically tradeable</remarks>
|
|
/// <seealso cref="EquityDataFilter"/>
|
|
/// <seealso cref="ForexDataFilter"/>
|
|
public ISecurityDataFilter DataFilter
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Customizable price variation model used to define the minimum price variation of this security.
|
|
/// By default minimum price variation is a constant find in the symbol-properties-database.
|
|
/// </summary>
|
|
/// <seealso cref="AdjustedPriceVariationModel"/>
|
|
/// <seealso cref="SecurityPriceVariationModel"/>
|
|
/// <seealso cref="EquityPriceVariationModel"/>
|
|
public IPriceVariationModel PriceVariationModel
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides dynamic access to data in the cache
|
|
/// </summary>
|
|
public dynamic Data
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct a new security vehicle based on the user options.
|
|
/// </summary>
|
|
public Security(SecurityExchangeHours exchangeHours,
|
|
SubscriptionDataConfig config,
|
|
Cash quoteCurrency,
|
|
SymbolProperties symbolProperties,
|
|
ICurrencyConverter currencyConverter,
|
|
IRegisteredSecurityDataTypesProvider registeredTypesProvider,
|
|
SecurityCache cache
|
|
)
|
|
: this(config,
|
|
quoteCurrency,
|
|
symbolProperties,
|
|
new SecurityExchange(exchangeHours),
|
|
cache,
|
|
new SecurityPortfolioModel(),
|
|
new ImmediateFillModel(),
|
|
new InteractiveBrokersFeeModel(),
|
|
new ConstantSlippageModel(0),
|
|
new ImmediateSettlementModel(),
|
|
Securities.VolatilityModel.Null,
|
|
new SecurityMarginModel(),
|
|
new SecurityDataFilter(),
|
|
new SecurityPriceVariationModel(),
|
|
currencyConverter,
|
|
registeredTypesProvider
|
|
)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct a new security vehicle based on the user options.
|
|
/// </summary>
|
|
public Security(Symbol symbol,
|
|
SecurityExchangeHours exchangeHours,
|
|
Cash quoteCurrency,
|
|
SymbolProperties symbolProperties,
|
|
ICurrencyConverter currencyConverter,
|
|
IRegisteredSecurityDataTypesProvider registeredTypesProvider,
|
|
SecurityCache cache
|
|
)
|
|
: this(symbol,
|
|
quoteCurrency,
|
|
symbolProperties,
|
|
new SecurityExchange(exchangeHours),
|
|
cache,
|
|
new SecurityPortfolioModel(),
|
|
new ImmediateFillModel(),
|
|
new InteractiveBrokersFeeModel(),
|
|
new ConstantSlippageModel(0),
|
|
new ImmediateSettlementModel(),
|
|
Securities.VolatilityModel.Null,
|
|
new SecurityMarginModel(),
|
|
new SecurityDataFilter(),
|
|
new SecurityPriceVariationModel(),
|
|
currencyConverter,
|
|
registeredTypesProvider
|
|
)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct a new security vehicle based on the user options.
|
|
/// </summary>
|
|
protected Security(Symbol symbol,
|
|
Cash quoteCurrency,
|
|
SymbolProperties symbolProperties,
|
|
SecurityExchange exchange,
|
|
SecurityCache cache,
|
|
ISecurityPortfolioModel portfolioModel,
|
|
IFillModel fillModel,
|
|
IFeeModel feeModel,
|
|
ISlippageModel slippageModel,
|
|
ISettlementModel settlementModel,
|
|
IVolatilityModel volatilityModel,
|
|
IBuyingPowerModel buyingPowerModel,
|
|
ISecurityDataFilter dataFilter,
|
|
IPriceVariationModel priceVariationModel,
|
|
ICurrencyConverter currencyConverter,
|
|
IRegisteredSecurityDataTypesProvider registeredTypesProvider
|
|
)
|
|
{
|
|
if (symbolProperties == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(symbolProperties), "Security requires a valid SymbolProperties instance.");
|
|
}
|
|
|
|
if (symbolProperties.QuoteCurrency != quoteCurrency.Symbol)
|
|
{
|
|
throw new ArgumentException("symbolProperties.QuoteCurrency must match the quoteCurrency.Symbol");
|
|
}
|
|
|
|
Symbol = symbol;
|
|
SubscriptionsBag = new ConcurrentBag<SubscriptionDataConfig>();
|
|
QuoteCurrency = quoteCurrency;
|
|
SymbolProperties = symbolProperties;
|
|
IsTradable = true;
|
|
Cache = cache;
|
|
Exchange = exchange;
|
|
DataFilter = dataFilter;
|
|
PriceVariationModel = priceVariationModel;
|
|
PortfolioModel = portfolioModel;
|
|
BuyingPowerModel = buyingPowerModel;
|
|
FillModel = fillModel;
|
|
FeeModel = feeModel;
|
|
SlippageModel = slippageModel;
|
|
SettlementModel = settlementModel;
|
|
VolatilityModel = volatilityModel;
|
|
Holdings = new SecurityHolding(this, currencyConverter);
|
|
Data = new DynamicSecurityData(registeredTypesProvider, Cache);
|
|
|
|
UpdateSubscriptionProperties();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Temporary convenience constructor
|
|
/// </summary>
|
|
protected Security(SubscriptionDataConfig config,
|
|
Cash quoteCurrency,
|
|
SymbolProperties symbolProperties,
|
|
SecurityExchange exchange,
|
|
SecurityCache cache,
|
|
ISecurityPortfolioModel portfolioModel,
|
|
IFillModel fillModel,
|
|
IFeeModel feeModel,
|
|
ISlippageModel slippageModel,
|
|
ISettlementModel settlementModel,
|
|
IVolatilityModel volatilityModel,
|
|
IBuyingPowerModel buyingPowerModel,
|
|
ISecurityDataFilter dataFilter,
|
|
IPriceVariationModel priceVariationModel,
|
|
ICurrencyConverter currencyConverter,
|
|
IRegisteredSecurityDataTypesProvider registeredTypesProvider
|
|
)
|
|
: this(config.Symbol,
|
|
quoteCurrency,
|
|
symbolProperties,
|
|
exchange,
|
|
cache,
|
|
portfolioModel,
|
|
fillModel,
|
|
feeModel,
|
|
slippageModel,
|
|
settlementModel,
|
|
volatilityModel,
|
|
buyingPowerModel,
|
|
dataFilter,
|
|
priceVariationModel,
|
|
currencyConverter,
|
|
registeredTypesProvider
|
|
)
|
|
{
|
|
SubscriptionsBag.Add(config);
|
|
UpdateSubscriptionProperties();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read only property that checks if we currently own stock in the company.
|
|
/// </summary>
|
|
public virtual bool HoldStock => Holdings.AbsoluteQuantity > 0;
|
|
|
|
/// <summary>
|
|
/// Alias for HoldStock - Do we have any of this security
|
|
/// </summary>
|
|
public virtual bool Invested => HoldStock;
|
|
|
|
/// <summary>
|
|
/// Local time for this market
|
|
/// </summary>
|
|
public virtual DateTime LocalTime
|
|
{
|
|
get
|
|
{
|
|
if (_localTimeKeeper == null)
|
|
{
|
|
throw new InvalidOperationException("Security.SetLocalTimeKeeper(LocalTimeKeeper) must be called in order to use the LocalTime property.");
|
|
}
|
|
|
|
return _localTimeKeeper.LocalTime;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the current value of the security.
|
|
/// </summary>
|
|
public virtual decimal Price => Cache.Price;
|
|
|
|
/// <summary>
|
|
/// Leverage for this Security.
|
|
/// </summary>
|
|
public virtual decimal Leverage => Holdings.Leverage;
|
|
|
|
/// <summary>
|
|
/// If this uses tradebar data, return the most recent high.
|
|
/// </summary>
|
|
public virtual decimal High => Cache.High == 0 ? Price : Cache.High;
|
|
|
|
/// <summary>
|
|
/// If this uses tradebar data, return the most recent low.
|
|
/// </summary>
|
|
public virtual decimal Low => Cache.Low == 0 ? Price : Cache.Low;
|
|
|
|
/// <summary>
|
|
/// If this uses tradebar data, return the most recent close.
|
|
/// </summary>
|
|
public virtual decimal Close => Cache.Close == 0 ? Price : Cache.Close;
|
|
|
|
/// <summary>
|
|
/// If this uses tradebar data, return the most recent open.
|
|
/// </summary>
|
|
public virtual decimal Open => Cache.Open == 0 ? Price : Cache.Open;
|
|
|
|
/// <summary>
|
|
/// Access to the volume of the equity today
|
|
/// </summary>
|
|
public virtual decimal Volume => Cache.Volume;
|
|
|
|
/// <summary>
|
|
/// Gets the most recent bid price if available
|
|
/// </summary>
|
|
public virtual decimal BidPrice => Cache.BidPrice == 0 ? Price : Cache.BidPrice;
|
|
|
|
/// <summary>
|
|
/// Gets the most recent bid size if available
|
|
/// </summary>
|
|
public virtual decimal BidSize => Cache.BidSize;
|
|
|
|
/// <summary>
|
|
/// Gets the most recent ask price if available
|
|
/// </summary>
|
|
public virtual decimal AskPrice => Cache.AskPrice == 0 ? Price : Cache.AskPrice;
|
|
|
|
/// <summary>
|
|
/// Gets the most recent ask size if available
|
|
/// </summary>
|
|
public virtual decimal AskSize => Cache.AskSize;
|
|
|
|
/// <summary>
|
|
/// Access to the open interest of the security today
|
|
/// </summary>
|
|
public virtual long OpenInterest => Cache.OpenInterest;
|
|
|
|
/// <summary>
|
|
/// Gets the fundamental data associated with the security if there is any, otherwise null.
|
|
/// </summary>
|
|
public Fundamentals Fundamentals
|
|
{
|
|
get
|
|
{
|
|
if (Cache.GetData<Fundamentals>() != null)
|
|
{
|
|
return Cache.GetData<Fundamentals>();
|
|
}
|
|
|
|
var coarse = Cache.GetData<CoarseFundamental>();
|
|
if (coarse == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new Fundamentals
|
|
{
|
|
Symbol = Symbol,
|
|
Value = coarse.Value,
|
|
EndTime = coarse.EndTime,
|
|
DollarVolume = coarse.DollarVolume,
|
|
DataType = coarse.DataType,
|
|
Market = coarse.Market,
|
|
Volume = coarse.Volume
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the last price update set to the security.
|
|
/// </summary>
|
|
/// <returns>BaseData object for this security</returns>
|
|
public BaseData GetLastData()
|
|
{
|
|
return Cache.GetData();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the <see cref="LocalTimeKeeper"/> to be used for this <see cref="Security"/>.
|
|
/// This is the source of this instance's time.
|
|
/// </summary>
|
|
/// <param name="localTimeKeeper">The source of this <see cref="Security"/>'s time.</param>
|
|
public void SetLocalTimeKeeper(LocalTimeKeeper localTimeKeeper)
|
|
{
|
|
_localTimeKeeper = localTimeKeeper;
|
|
Exchange.SetLocalDateTimeFrontier(localTimeKeeper.LocalTime);
|
|
|
|
_localTimeKeeper.TimeUpdated += (sender, args) =>
|
|
{
|
|
//Update the Exchange/Timer:
|
|
Exchange.SetLocalDateTimeFrontier(args.Time);
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update any security properties based on the latest market data and time
|
|
/// </summary>
|
|
/// <param name="data">New data packet from LEAN</param>
|
|
public void SetMarketPrice(BaseData data)
|
|
{
|
|
//Add new point to cache:
|
|
if (data == null) return;
|
|
Cache.AddData(data);
|
|
|
|
UpdateConsumersMarketPrice(data);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates all of the security properties, such as price/OHLCV/bid/ask based
|
|
/// on the data provided. Data is also stored into the security's data cache
|
|
/// </summary>
|
|
/// <param name="data">The security update data</param>
|
|
/// <param name="dataType">The data type</param>
|
|
/// <param name="containsFillForwardData">Flag indicating whether
|
|
/// <paramref name="data"/> contains any fill forward bar or not</param>
|
|
public void Update(IReadOnlyList<BaseData> data, Type dataType, bool? containsFillForwardData = null)
|
|
{
|
|
Cache.AddDataList(data, dataType, containsFillForwardData);
|
|
|
|
UpdateConsumersMarketPrice(data[data.Count - 1]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the security contains at least one subscription that represents custom data
|
|
/// </summary>
|
|
[Obsolete("This method is obsolete. Use the 'SubscriptionDataConfig' exposed by" +
|
|
" 'SubscriptionManager' and the 'IsCustomData()' extension method")]
|
|
public bool IsCustomData()
|
|
{
|
|
if (Subscriptions == null || !Subscriptions.Any())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Subscriptions.Any(x => x.IsCustomData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the leverage parameter for this security
|
|
/// </summary>
|
|
/// <param name="leverage">Leverage for this asset</param>
|
|
public void SetLeverage(decimal leverage)
|
|
{
|
|
if (Symbol.ID.SecurityType == SecurityType.Future || Symbol.ID.SecurityType.IsOption())
|
|
{
|
|
return;
|
|
}
|
|
|
|
BuyingPowerModel.SetLeverage(this, leverage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the data normalization mode to be used by this security
|
|
/// </summary>
|
|
[Obsolete("This method is obsolete. Use the 'SubscriptionDataConfig' exposed by" +
|
|
" 'SubscriptionManager' and the 'SetDataNormalizationMode()' extension method")]
|
|
public virtual void SetDataNormalizationMode(DataNormalizationMode mode)
|
|
{
|
|
foreach (var subscription in SubscriptionsBag)
|
|
{
|
|
subscription.DataNormalizationMode = mode;
|
|
}
|
|
UpdateSubscriptionProperties();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method will refresh the value of the <see cref="DataNormalizationMode"/> property.
|
|
/// This is required for backward-compatibility.
|
|
/// TODO: to be deleted with the DataNormalizationMode property
|
|
/// </summary>
|
|
public void RefreshDataNormalizationModeProperty()
|
|
{
|
|
DataNormalizationMode = SubscriptionsBag
|
|
.Select(x => x.DataNormalizationMode)
|
|
.DefaultIfEmpty(DataNormalizationMode.Adjusted)
|
|
.FirstOrDefault();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the fee model
|
|
/// </summary>
|
|
/// <param name="feelModel">Model that represents a fee model</param>
|
|
public void SetFeeModel(IFeeModel feelModel)
|
|
{
|
|
FeeModel = feelModel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the fee model
|
|
/// </summary>
|
|
/// <param name="feelModel">Model that represents a fee model</param>
|
|
public void SetFeeModel(PyObject feelModel)
|
|
{
|
|
FeeModel = new FeeModelPythonWrapper(feelModel);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the fill model
|
|
/// </summary>
|
|
/// <param name="fillModel">Model that represents a fill model</param>
|
|
public void SetFillModel(IFillModel fillModel)
|
|
{
|
|
FillModel = fillModel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the fill model
|
|
/// </summary>
|
|
/// <param name="fillModel">Model that represents a fill model</param>
|
|
public void SetFillModel(PyObject fillModel)
|
|
{
|
|
FillModel = new FillModelPythonWrapper(fillModel);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the slippage model
|
|
/// </summary>
|
|
/// <param name="slippageModel">Model that represents a slippage model</param>
|
|
public void SetSlippageModel(ISlippageModel slippageModel)
|
|
{
|
|
SlippageModel = slippageModel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the slippage model
|
|
/// </summary>
|
|
/// <param name="slippageModel">Model that represents a slippage model</param>
|
|
public void SetSlippageModel(PyObject slippageModel)
|
|
{
|
|
SlippageModel = new SlippageModelPythonWrapper(slippageModel);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the volatility model
|
|
/// </summary>
|
|
/// <param name="volatilityModel">Model that represents a volatility model</param>
|
|
public void SetVolatilityModel(IVolatilityModel volatilityModel)
|
|
{
|
|
VolatilityModel = volatilityModel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the volatility model
|
|
/// </summary>
|
|
/// <param name="volatilityModel">Model that represents a volatility model</param>
|
|
public void SetVolatilityModel(PyObject volatilityModel)
|
|
{
|
|
VolatilityModel = new VolatilityModelPythonWrapper(volatilityModel);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the buying power model
|
|
/// </summary>
|
|
/// <param name="buyingPowerModel">Model that represents a security's model of buying power</param>
|
|
public void SetBuyingPowerModel(IBuyingPowerModel buyingPowerModel)
|
|
{
|
|
BuyingPowerModel = buyingPowerModel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the buying power model
|
|
/// </summary>
|
|
/// <param name="pyObject">Model that represents a security's model of buying power</param>
|
|
public void SetBuyingPowerModel(PyObject pyObject)
|
|
{
|
|
SetBuyingPowerModel(new BuyingPowerModelPythonWrapper(pyObject));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the margin model
|
|
/// </summary>
|
|
/// <param name="marginModel">Model that represents a security's model of buying power</param>
|
|
public void SetMarginModel(IBuyingPowerModel marginModel)
|
|
{
|
|
MarginModel = marginModel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the margin model
|
|
/// </summary>
|
|
/// <param name="pyObject">Model that represents a security's model of buying power</param>
|
|
public void SetMarginModel(PyObject pyObject)
|
|
{
|
|
SetMarginModel(new BuyingPowerModelPythonWrapper(pyObject));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set Shortable Provider for this <see cref="Security"/>
|
|
/// </summary>
|
|
/// <param name="shortableProvider">Provider to use</param>
|
|
public void SetShortableProvider(IShortableProvider shortableProvider)
|
|
{
|
|
ShortableProvider = shortableProvider;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a string that represents the current object.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A string that represents the current object.
|
|
/// </returns>
|
|
/// <filterpriority>2</filterpriority>
|
|
public override string ToString()
|
|
{
|
|
return Symbol.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the specified data subscription to this security.
|
|
/// </summary>
|
|
/// <param name="subscription">The subscription configuration to add. The Symbol and ExchangeTimeZone properties must match the existing Security object</param>
|
|
internal void AddData(SubscriptionDataConfig subscription)
|
|
{
|
|
if (subscription.Symbol != Symbol) throw new ArgumentException("Symbols must match.", "subscription.Symbol");
|
|
if (!subscription.ExchangeTimeZone.Equals(Exchange.TimeZone)) throw new ArgumentException("ExchangeTimeZones must match.", "subscription.ExchangeTimeZone");
|
|
SubscriptionsBag.Add(subscription);
|
|
UpdateSubscriptionProperties();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the specified data subscriptions to this security.
|
|
/// </summary>
|
|
/// <param name="subscriptions">The subscription configuration to add. The Symbol and ExchangeTimeZone properties must match the existing Security object</param>
|
|
internal void AddData(SubscriptionDataConfigList subscriptions)
|
|
{
|
|
foreach (var subscription in subscriptions)
|
|
{
|
|
if (subscription.Symbol != Symbol) throw new ArgumentException("Symbols must match.", "subscription.Symbol");
|
|
if (!subscription.ExchangeTimeZone.Equals(Exchange.TimeZone)) throw new ArgumentException("ExchangeTimeZones must match.", "subscription.ExchangeTimeZone");
|
|
SubscriptionsBag.Add(subscription);
|
|
}
|
|
UpdateSubscriptionProperties();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update market price of this Security
|
|
/// </summary>
|
|
/// <param name="data">Data to pull price from</param>
|
|
protected virtual void UpdateConsumersMarketPrice(BaseData data)
|
|
{
|
|
if (data is OpenInterest || data.Price == 0m) return;
|
|
Holdings.UpdateMarketPrice(Price);
|
|
VolatilityModel.Update(this, data);
|
|
}
|
|
|
|
private void UpdateSubscriptionProperties()
|
|
{
|
|
Resolution = SubscriptionsBag.Select(x => x.Resolution).DefaultIfEmpty(Resolution.Daily).Min();
|
|
IsFillDataForward = SubscriptionsBag.Any(x => x.FillDataForward);
|
|
IsExtendedMarketHours = SubscriptionsBag.Any(x => x.ExtendedMarketHours);
|
|
RefreshDataNormalizationModeProperty();
|
|
}
|
|
}
|
|
}
|