/* * 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 QuantConnect.Data.UniverseSelection; using QuantConnect.Interfaces; using QuantConnect.Securities; using QuantConnect.Securities.Option; using System; namespace QuantConnect.Data.Market { /// /// Defines a single option contract at a specific expiration and strike price /// public class OptionContract : BaseContract { private IOptionData _optionData = OptionPriceModelResultData.Null; private readonly SymbolProperties _symbolProperties; /// /// Gets the strike price /// public decimal Strike => Symbol.ID.StrikePrice; /// /// Gets the strike price multiplied by the strike multiplier /// public decimal ScaledStrike => Strike * _symbolProperties.StrikeMultiplier; /// /// Gets the right being purchased (call [right to buy] or put [right to sell]) /// public OptionRight Right => Symbol.ID.OptionRight; /// /// Gets the option style /// public OptionStyle Style => Symbol.ID.OptionStyle; /// /// Gets the theoretical price of this option contract as computed by the /// public decimal TheoreticalPrice => _optionData.TheoreticalPrice; /// /// Gets the implied volatility of the option contract as computed by the /// public decimal ImpliedVolatility => _optionData.ImpliedVolatility; /// /// Gets the greeks for this contract /// public Greeks Greeks => _optionData.Greeks; /// /// Gets the open interest /// public override decimal OpenInterest => _optionData.OpenInterest; /// /// Gets the last price this contract traded at /// public override decimal LastPrice => _optionData.LastPrice; /// /// Gets the last volume this contract traded at /// public override long Volume => _optionData.Volume; /// /// Gets the current bid price /// public override decimal BidPrice => _optionData.BidPrice; /// /// Get the current bid size /// public override long BidSize => _optionData.BidSize; /// /// Gets the ask price /// public override decimal AskPrice => _optionData.AskPrice; /// /// Gets the current ask size /// public override long AskSize => _optionData.AskSize; /// /// Gets the last price the underlying security traded at /// public decimal UnderlyingLastPrice => _optionData.UnderlyingLastPrice; /// /// Initializes a new instance of the class /// /// The option contract security public OptionContract(ISecurityPrice security) : base(security.Symbol) { _symbolProperties = security.SymbolProperties; } /// /// Initializes a new option contract from a given instance /// /// The option universe contract data to use as source for this contract /// The contract symbol properties public OptionContract(OptionUniverse contractData, SymbolProperties symbolProperties) : base(contractData.Symbol) { _symbolProperties = symbolProperties; _optionData = new OptionUniverseData(contractData); } /// /// Sets the option price model evaluator function to be used for this contract /// /// Function delegate used to evaluate the option price model internal void SetOptionPriceModel(Func optionPriceModelEvaluator) { _optionData = new OptionPriceModelResultData(optionPriceModelEvaluator, _optionData as OptionPriceModelResultData); } /// /// Creates a /// /// /// Provides price properties for a /// Last underlying security trade data /// Option contract public static OptionContract Create(BaseData baseData, ISecurityPrice security, BaseData underlying) => Create(baseData.EndTime, security, underlying); /// /// Creates a /// /// local date time this contract's data was last updated /// provides price properties for a /// last underlying security trade data /// Option contract public static OptionContract Create(DateTime endTime, ISecurityPrice security, BaseData underlying) { var contract = new OptionContract(security) { Time = endTime, }; contract._optionData.SetUnderlying(underlying); return contract; } /// /// Creates a new option contract from a given instance, /// using its data to form a quote bar to source pricing data /// /// The option universe contract data to use as source for this contract /// The contract symbol properties public static OptionContract Create(OptionUniverse contractData, SymbolProperties symbolProperties) { var contract = new OptionContract(contractData, symbolProperties) { Time = contractData.EndTime, }; return contract; } /// /// Implicit conversion into /// /// The option contract to be converted public static implicit operator Symbol(OptionContract contract) { return contract.Symbol; } /// /// Updates the option contract with the new data, which can be a or or /// internal override void Update(BaseData data) { if (data.Symbol.SecurityType.IsOption()) { _optionData.Update(data); } else if (data.Symbol.SecurityType == Symbol.GetUnderlyingFromOptionType(Symbol.SecurityType)) { _optionData.SetUnderlying(data); } } #region Option Contract Data Handlers private interface IOptionData { decimal LastPrice { get; } decimal UnderlyingLastPrice { get; } long Volume { get; } decimal BidPrice { get; } long BidSize { get; } decimal AskPrice { get; } long AskSize { get; } decimal OpenInterest { get; } decimal TheoreticalPrice { get; } decimal ImpliedVolatility { get; } Greeks Greeks { get; } void Update(BaseData data); void SetUnderlying(BaseData data); } /// /// Handles option data for a contract from actual price data (trade, quote, open interest) and theoretical price model results /// private class OptionPriceModelResultData : IOptionData { public static readonly OptionPriceModelResultData Null = new(() => OptionPriceModelResult.None); private readonly Lazy _optionPriceModelResult; private TradeBar _tradeBar; private QuoteBar _quoteBar; private OpenInterest _openInterest; private BaseData _underlying; public decimal LastPrice => _tradeBar?.Close ?? decimal.Zero; public decimal UnderlyingLastPrice => _underlying?.Price ?? decimal.Zero; public long Volume => (long)(_tradeBar?.Volume ?? 0L); public decimal BidPrice => _quoteBar?.Bid?.Close ?? decimal.Zero; public long BidSize => (long)(_quoteBar?.LastBidSize ?? 0L); public decimal AskPrice => _quoteBar?.Ask?.Close ?? decimal.Zero; public long AskSize => (long)(_quoteBar?.LastAskSize ?? 0L); public decimal OpenInterest => _openInterest?.Value ?? decimal.Zero; public decimal TheoreticalPrice => _optionPriceModelResult.Value.TheoreticalPrice; public decimal ImpliedVolatility => _optionPriceModelResult.Value.ImpliedVolatility; public Greeks Greeks => _optionPriceModelResult.Value.Greeks; public OptionPriceModelResultData(Func optionPriceModelEvaluator, OptionPriceModelResultData previousOptionData = null) { _optionPriceModelResult = new(optionPriceModelEvaluator, isThreadSafe: false); if (previousOptionData != null) { _tradeBar = previousOptionData._tradeBar; _quoteBar = previousOptionData._quoteBar; _openInterest = previousOptionData._openInterest; _underlying = previousOptionData._underlying; } } public void Update(BaseData data) { switch (data) { case TradeBar tradeBar: _tradeBar = tradeBar; break; case QuoteBar quoteBar: _quoteBar = quoteBar; break; case OpenInterest openInterest: _openInterest = openInterest; break; } } public void SetUnderlying(BaseData data) { _underlying = data; } } /// /// Handles option data for a contract from a instance /// private class OptionUniverseData : IOptionData { private readonly OptionUniverse _contractData; public decimal LastPrice => _contractData.Close; // TODO: Null check required for FOPs: since OptionUniverse does not support FOPs, // these instances will by "synthetic" and will not have underlying data. // Can be removed after FOPs are supported by OptionUniverse public decimal UnderlyingLastPrice => _contractData?.Underlying?.Price ?? decimal.Zero; public long Volume => (long)_contractData.Volume; public decimal BidPrice => _contractData.Close; public long BidSize => 0; public decimal AskPrice => _contractData.Close; public long AskSize => 0; public decimal OpenInterest => _contractData.OpenInterest; public decimal TheoreticalPrice => decimal.Zero; public decimal ImpliedVolatility => _contractData.ImpliedVolatility; public Greeks Greeks => _contractData.Greeks; public OptionUniverseData(OptionUniverse contractData) { _contractData = contractData; } public void Update(BaseData data) { } public void SetUnderlying(BaseData data) { } } #endregion } }