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