eb1181f5f7
* Adds preliminary universe selection for Future Options
* Fixes scaling issues with Future Options
* Fixes scaling multiplying by 10000x instead of using _scaleFactor
* Fixes scaling for Tick
* Revert changes to Tick since it divides the scaling factor
* Changes stale method name to new method name after rebase
* Fixes selection bugs, adds new methods, and adds unit tests
* Fixes bug where Equity Symbol was created for an underlying
non-equity Symbol, resulting in equity data trying to be loaded
* Adds unit tests covering changes to Tick, QuoteBar, TradeBar and
LeanData
* Adds regression test for AddUniverseOption filter contract selection
for Future Options
* Addresses review - modifies the AddFutureOption signature
* Adds new AddUniverseOptions method overload
* Removes and adds a new unit test
* Misc. modifications to account for new changes
* Fixes bug where futures were loaded using default SID Date
* Refactors and removes unnecessary work
* Fixes regression algorithm, which previously made no trades
* Adds future option data
* Adds the corresponding underlying data, in this case, futures data
to enable usage of future options data
* Replaces data with new data (ES18Z20)
* Improves Future chain filtering and updates regression stats
* Add AddFutureOptionContract API
* Expands regression and unit tests to test in finer detail
* Adds Python regression algorithms for AddFutureOption[Contract] methods
* Adds new unit test for BacktestingOptionChainProvider
* Fixes bug with BacktesingOptionChainProvider where we
attempted to load the Trades option chain first, resulting
in breakage of backwards compatibility and limitation of the
option chain.
* Adds new regression algorithms (Py) to Algorithm.Python project
* Adds FutureOptionMarginBuyingPowerModel
* Modifies code paths used to select margin model
* Adds related unit tests for margin model
* Fixes issue with unit test and MHDB/SPDB lookup for Future Options
* Preliminary regression algorithm testing ITM call/put option buying
* Fixes bug where fee model used did not find non-US market
options fee model. We now use the futures fee model for future
options because IB charges the same commissions per contract
between futures and futures options
* Adds proper regression algorithm for ITM future options expiration
* Pushing broken algorithm for review
* Currently, algorithm does not fill forward, causing
a single future option to not get exercised when it is delisted.
* Adds FutureOptionPutITMExpiryRegressionAlgorithm
* Improves existing regression algorithm for call side
* Fixes bug in existing regression algorithm
* Adds AAPL daily data to advance enumerator for ^^^ fix
* Adds additional future option regression algorithms
* Adds Buy OTM expiration regression algorithms
* Adds Sell ITM/OTM expiration regression algorithms
* Adds missing Python regression algorithms
* Adds remaining Python regression algorithms and fixes issues
* Fixes naming issues and statistics
* Adds short option OTM regression algorithms (Py)
* Add license header and class comments to python algorithms
* Cleans up comments and docstrings
* Create Buy/Sell call intraday regression algo
* Redirects future options symbol properties to futures symbol properties
* Asserts exercise/assignment price and updates stats in regression algos
* Adds new unit test covering changes to SecurityService
* Adds comments and fixes failing test
* Partially fixes future option mis-calculated profit/loss
* Adjusts portfolio model to calculate FOP as a no upfront pay asset class
* Updates regression algorithm statistics
* Begin IB FOP support
* Initial support for FOP IB data streaming, live í¾
* Adds additional functionality to LiveOptionChainProvider
- Allows querying CME API to retrieve option chains for CME products
- Ultimately, it's also the groundwork for the CME
LiveFutureChainProvider
* Edits IDataQueueUniverseProvider interface to provide greater
control to implementors of it
* Misc. bug fixes required to get FOP data streaming through IB
* Adds comments, adds missing rategate call, and cleans up code
* Force exchange for FOP and Futures when no exchange is provided
* Fixes bug with Portfolio modeling across all asset classes
* Adds LiveOptionChainProvider tests for Future Options
* IB brokerage option symbol bug fixes and improvements
* Fixes contract multiplier lookup bug
* Fixes issue where we attempted to subscribe to IB data feed with canonical security
* Adds ES MHDB entry
* Reverts portfolio modeling changes for Futures Options
* Since IB eats into our account's cash balance when
a new FOP contract is purchased, we must model by applying funds
to our cash whenever a new purchase/sell occurs.
If we choose to model FOPs exactly as we do with futures, we
will end up with an invalid TotalPortfolioValue on algorithm
restart. By all means and purposes, FOPs are modeled exactly
the same as equity options with respect to the portfolio.
* Adds comments clarifying portfolio modeling and clarifies
existing portfolio modeling comments with additional context.
* Fixes IB symbol lookup for future options
* Fixes LiveOptionChainProvider looping 5 times per option chain
request, even on success
* Sets OptionChainedUniverseSelectionModel to produce a canonical
future/future option/option Symbol to avoid creating two Symbols
* Adds GLOBEX future option symbol mapping from future -> fop
* Fixes LiveOptionChainProvider loading wrong contract option chains
* Fixes loading of futures options ZIP files when backtesting
* Adds a string -> decimal JSON converter
* Additional fixes/refactoring to the LiveOptionChainProvider
* Adds tests for changes to Symbol and LeanData
* Reverts changes to IB-symbol-map
* Fixes Value for mapped future options tickers
* Fixes Symbol test
* Changes path of future options to future's expiry date
* Extra changes made to remove scaling from writing CSV
* Added method to map from FOP Globex -> FUT Globex
* Fixes MOO and MOC orders for future options
* Note: this order type might not be supported by IB or CME.
* Bug fixes and updates unit tests
* Update regression tests and data format
* Rebase changes
* 1. Multiple bug fixes for LiveOptionChainProvider, reverts IQFeed changes
2. Address review (partial): Code reuse and cleanup
1.
* Modifies check in
`AddFutureOptionShort(Call|Put)ITMExpiryRegressionAlgorithm`
to ensure no buys have negative quantity
* Code reuse changes in IB brokerage
* Bug fix in IB brokerage where we assigned the FOP expiry
as the futures expiry (requires verification)
* Doc changes and adds missing summaries/license banners
* Disposes of HTTP client resources in LiveOptionChainProvider
* Renames classes and adds FutureOption folder in Common/Securities
2.
* We revert back to the quotes API for the option chain,
since the settlement API sometimes had missing strikes.
* Fixes future option expiry being set as future's expiry
in LiveOptionChainProvider
* Fixes bug where wrong option chain was selected because of bad
expiry lookup in the futures expiries returned from CME
* Fixes multiple looping bug in LiveOptionChainProvider
* Adds strike price scaling for LiveOptionChainProvider
* Reverts IQFeed changes and simplifies interface upgrade changes
Some additional challenges we'll have to solve as part of FOPs:
- The `OptionSymbol.IsStandard` method makes the assumption that
weeklies contracts follow the pattern equities follows, which
does not apply to Futures Options
- The Subscription created in:
`OptionChainUniverseSubscriptionEnumeratorFactory`
...adds a Trade config. For illiquid contracts, this
will delay universe selection for the option symbol
until we get a trade. However, if we add a quote config,
the data would instead be loaded based on the first quote
we received from the brokerage.
But since we're currently using a trade config, illiquid
contracts won't start streaming data until it receives a trade.
NOTE: this commit is a WIP to addressing the reviews received in the PR,
but has been committed early for efficiency in the review process
* Fixes regression algorithms and misc. bugs
* Fixes map file lookup for non-equity options
* Adds extra assertion at end of algorithm to ensure no holdings are
left when the algorithm ends.
* Adds FutureOptionSymbol, allowing all contracts through as standard
* Changes SPDB to allow defaulting to underlying future symbol
properties if no entry is found for the given FOP
* Fixes calls to SPDB in SecurityService, IBBrokerage
* Reverts AAPL daily ZIP file to fix majority of regression algorithms
* Adds FOPs symbol properties
* Fixes existing symbol properties for a few futures
* Adds tests for changes to Symbol Properties Database
* Removes string SPDB lookup method
* Updates tests and misc callees of previous method
* Updates all regression tests to use data of already expired contracts
* Adds Futures Options Expiry Functions tests
* Adds required futures data for 2020-01-05
* Address review (partial): Expands test coverage and fixes tests
* Set option chain tests parallelism to fixture only
* Fixes broken test for contract month delta for FuturesOptionsExpiryFunctions
* Changes delisting date logic for Futures Options
* Address review: removes duplicate code, misc code fixes
* Bug fix in MarketHoursDatabase.GetDatabaseSymbolKey() where
we would use the underlying's Symbol for lookup in the MHDB
* Adds missing license banner
* Removes Futures Options entries from MHDB
* Adds new tests
* Adds SecurityType.FutureOption
* Converts any underlying comparisons and uses SecurityType directly
instead for FOP specific behavior
* Extra code modifications to acommodate new SecurityType
* Addresses review: fixes order fee bug on exercise
* Additional bug fixes and adding of SecurityType.FutureOption
* Updates regression algorithms OrderListHash
* Fixes various bugs in IB live implementation
* Fixes bug setting the right contract expiration date for FOP
generated by LiveOptionChainProvider
* Adds new function to FuturesOptionsExpiryFunctions
* Clarifies parameter names better in some functions/methods
* Fixes bugs in IB brokerage for FOPs
* Address review - code cleanup and refactor
* Remove MappingEventProvider, SplitEventProvider, and
DividendEventProvider for Futures Options in
CorporateEventEnumeratorFactory
* Address review: Use MHDB key resolver in SPDB
* Makes regression tests pass and adds comment for expiry issue
* Fixes MHDB lookup on string symbol method
* Adds Futures Options greeks regression algorithm (C# only)
* Adds explanitory comment on MHDB FOP lookup
* Remove python from FutureOptionCallITMGreeksExpiryRegressionAlgorithm
829 lines
30 KiB
C#
829 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 readonly ICurrencyConverter _currencyConverter;
|
|
|
|
private LocalTimeKeeper _localTimeKeeper;
|
|
// using concurrent bag to avoid list enumeration threading issues
|
|
protected readonly ConcurrentBag<SubscriptionDataConfig> SubscriptionsBag;
|
|
|
|
/// <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 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");
|
|
}
|
|
|
|
this._currencyConverter = currencyConverter;
|
|
|
|
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 == SecurityType.Option ||
|
|
Symbol.ID.SecurityType == SecurityType.FutureOption)
|
|
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>
|
|
/// 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();
|
|
}
|
|
|
|
private 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();
|
|
}
|
|
}
|
|
}
|