/*
* 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.Data;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Orders.OptionExercise;
using Python.Runtime;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Util;
namespace QuantConnect.Securities.Option
{
///
/// Option Security Object Implementation for Option Assets
///
///
public class Option : Security, IDerivativeSecurity, IOptionPrice
{
///
/// The default number of days required to settle an equity sale
///
public const int DefaultSettlementDays = 1;
///
/// The default time of day for settlement
///
public static readonly TimeSpan DefaultSettlementTime = new TimeSpan(8, 0, 0);
///
/// Constructor for the option security
///
/// Defines the hours this exchange is open
/// The cash object that represent the quote currency
/// The subscription configuration for this security
/// The symbol properties for this security
/// Currency converter used to convert
/// instances into units of the account currency
public Option(SecurityExchangeHours exchangeHours, SubscriptionDataConfig config, Cash quoteCurrency, OptionSymbolProperties symbolProperties, ICurrencyConverter currencyConverter)
: base(config,
quoteCurrency,
symbolProperties,
new OptionExchange(exchangeHours),
new OptionCache(),
new OptionPortfolioModel(),
new ImmediateFillModel(),
new InteractiveBrokersFeeModel(),
new ConstantSlippageModel(0),
new ImmediateSettlementModel(),
Securities.VolatilityModel.Null,
new OptionMarginModel(),
new OptionDataFilter(),
new SecurityPriceVariationModel(),
currencyConverter
)
{
ExerciseSettlement = SettlementType.PhysicalDelivery;
SetDataNormalizationMode(DataNormalizationMode.Raw);
OptionExerciseModel = new DefaultExerciseModel();
PriceModel = new CurrentPriceOptionPriceModel();
Holdings = new OptionHolding(this, currencyConverter);
_symbolProperties = symbolProperties;
SetFilter(-1, 1, TimeSpan.Zero, TimeSpan.FromDays(35));
}
///
/// Constructor for the option security
///
/// The symbol of the security
/// Defines the hours this exchange is open
/// The cash object that represent the quote currency
/// The symbol properties for this security
/// Currency converter used to convert
/// instances into units of the account currency
public Option(Symbol symbol, SecurityExchangeHours exchangeHours, Cash quoteCurrency, OptionSymbolProperties symbolProperties, ICurrencyConverter currencyConverter)
: base(symbol,
quoteCurrency,
symbolProperties,
new OptionExchange(exchangeHours),
new OptionCache(),
new OptionPortfolioModel(),
new ImmediateFillModel(),
new InteractiveBrokersFeeModel(),
new ConstantSlippageModel(0),
new ImmediateSettlementModel(),
Securities.VolatilityModel.Null,
new OptionMarginModel(),
new OptionDataFilter(),
new SecurityPriceVariationModel(),
currencyConverter
)
{
ExerciseSettlement = SettlementType.PhysicalDelivery;
SetDataNormalizationMode(DataNormalizationMode.Raw);
OptionExerciseModel = new DefaultExerciseModel();
PriceModel = new CurrentPriceOptionPriceModel();
Holdings = new OptionHolding(this, currencyConverter);
_symbolProperties = symbolProperties;
SetFilter(-1, 1, TimeSpan.Zero, TimeSpan.FromDays(35));
}
// save off a strongly typed version of symbol properties
private readonly OptionSymbolProperties _symbolProperties;
///
/// Returns true if this is the option chain security, false if it is a specific option contract
///
public bool IsOptionChain => Symbol.IsCanonical();
///
/// Returns true if this is a specific option contract security, false if it is the option chain security
///
public bool IsOptionContract => !Symbol.IsCanonical();
///
/// Gets the strike price
///
public decimal StrikePrice
{
get { return Symbol.ID.StrikePrice; }
}
///
/// Gets the expiration date
///
public DateTime Expiry
{
get { return Symbol.ID.Date; }
}
///
/// Gets the right being purchased (call [right to buy] or put [right to sell])
///
public OptionRight Right
{
get { return Symbol.ID.OptionRight; }
}
///
/// Gets the option style
///
public OptionStyle Style
{
get { return Symbol.ID.OptionStyle; }
}
///
/// When the holder of an equity option exercises one contract, or when the writer of an equity option is assigned
/// an exercise notice on one contract, this unit of trade, usually 100 shares of the underlying security, changes hands.
///
public int ContractUnitOfTrade
{
get
{
return _symbolProperties.ContractUnitOfTrade;
}
set
{
_symbolProperties.SetContractUnitOfTrade(value);
}
}
///
/// The contract multiplier for the option security
///
public int ContractMultiplier
{
get
{
return (int)_symbolProperties.ContractMultiplier;
}
set
{
_symbolProperties.SetContractMultiplier(value);
}
}
///
/// Aggregate exercise amount or aggregate contract value. It is the total amount of cash one will pay (or receive) for the shares of the
/// underlying stock if he/she decides to exercise (or is assigned an exercise notice). This amount is not the premium paid or received for an equity option.
///
public decimal GetAggregateExerciseAmount()
{
return StrikePrice * ContractMultiplier;
}
///
/// Returns the actual number of the underlying shares that are going to change hands on exercise. For instance, after reverse split
/// we may have 1 option contract with multiplier of 100 with right to buy/sell only 50 shares of underlying stock.
///
///
public decimal GetExerciseQuantity(decimal quantity)
{
return quantity * ContractUnitOfTrade;
}
///
/// Checks if option is eligible for automatic exercise on expiration
///
public bool IsAutoExercised(decimal underlyingPrice)
{
return GetIntrinsicValue(underlyingPrice) >= 0.01m;
}
///
/// Intrinsic value function of the option
///
public decimal GetIntrinsicValue(decimal underlyingPrice)
{
return Math.Max(0.0m, GetPayOff(underlyingPrice));
}
///
/// Option payoff function at expiration time
///
/// The price of the underlying
///
public decimal GetPayOff(decimal underlyingPrice)
{
return Right == OptionRight.Call ? underlyingPrice - StrikePrice : StrikePrice - underlyingPrice;
}
///
/// Specifies if option contract has physical or cash settlement on exercise
///
public SettlementType ExerciseSettlement
{
get; set;
}
///
/// Gets or sets the underlying security object.
///
public Security Underlying
{
get; set;
}
///
/// Gets a reduced interface of the underlying security object.
///
ISecurityPrice IOptionPrice.Underlying => Underlying;
///
/// For this option security object, evaluates the specified option
/// contract to compute a theoretical price, IV and greeks
///
/// The current data slice. This can be used to access other information
/// available to the algorithm
/// The option contract to evaluate
/// An instance of containing the theoretical
/// price of the specified option contract
public OptionPriceModelResult EvaluatePriceModel(Slice slice, OptionContract contract)
{
return PriceModel.Evaluate(this, slice, contract);
}
///
/// Gets or sets the price model for this option security
///
public IOptionPriceModel PriceModel
{
get; set;
}
///
/// Fill model used to produce fill events for this security
///
public IOptionExerciseModel OptionExerciseModel
{
get; set;
}
///
/// When enabled, approximates Greeks if corresponding pricing model didn't calculate exact numbers
///
[Obsolete("This property has been deprecated. Please use QLOptionPriceModel.EnableGreekApproximation instead.")]
public bool EnableGreekApproximation
{
get
{
var model = PriceModel as QLOptionPriceModel;
if (model != null)
{
return model.EnableGreekApproximation;
}
return false;
}
set
{
var model = PriceModel as QLOptionPriceModel;
if (model != null)
{
model.EnableGreekApproximation = value;
}
}
}
///
/// Gets or sets the contract filter
///
public IDerivativeSecurityFilter ContractFilter
{
get; set;
}
///
/// Sets the to a new instance of the filter
/// using the specified min and max strike values. Contracts with expirations further than 35
/// days out will also be filtered.
///
/// The min strike rank relative to market price, for example, -1 would put
/// a lower bound of one strike under market price, where a +1 would put a lower bound of one strike
/// over market price
/// The max strike rank relative to market place, for example, -1 would put
/// an upper bound of on strike under market price, where a +1 would be an upper bound of one strike
/// over market price
public void SetFilter(int minStrike, int maxStrike)
{
SetFilter(universe => universe.Strikes(minStrike, maxStrike));
}
///
/// Sets the to a new instance of the filter
/// using the specified min and max strike and expiration range values
///
/// The minimum time until expiry to include, for example, TimeSpan.FromDays(10)
/// would exclude contracts expiring in less than 10 days
/// The maxmium time until expiry to include, for example, TimeSpan.FromDays(10)
/// would exclude contracts expiring in more than 10 days
public void SetFilter(TimeSpan minExpiry, TimeSpan maxExpiry)
{
SetFilter(universe => universe.Expiration(minExpiry, maxExpiry));
}
///
/// Sets the to a new instance of the filter
/// using the specified min and max strike and expiration range values
///
/// The min strike rank relative to market price, for example, -1 would put
/// a lower bound of one strike under market price, where a +1 would put a lower bound of one strike
/// over market price
/// The max strike rank relative to market place, for example, -1 would put
/// an upper bound of on strike under market price, where a +1 would be an upper bound of one strike
/// over market price
/// The minimum time until expiry to include, for example, TimeSpan.FromDays(10)
/// would exclude contracts expiring in less than 10 days
/// The maxmium time until expiry to include, for example, TimeSpan.FromDays(10)
/// would exclude contracts expiring in more than 10 days
public void SetFilter(int minStrike, int maxStrike, TimeSpan minExpiry, TimeSpan maxExpiry)
{
SetFilter(universe => universe
.Strikes(minStrike, maxStrike)
.Expiration(minExpiry, maxExpiry));
}
///
/// Sets the to a new universe selection function
///
/// new universe selection function
public void SetFilter(Func universeFunc)
{
ContractFilter = new FuncSecurityDerivativeFilter(universe =>
{
var optionUniverse = universe as OptionFilterUniverse;
var result = universeFunc(optionUniverse);
return result.ApplyOptionTypesFilter();
});
}
///
/// Sets the to a new universe selection function
///
/// new universe selection function
public void SetFilter(PyObject universeFunc)
{
var pyUniverseFunc = PythonUtil.ToFunc(universeFunc);
SetFilter(pyUniverseFunc);
}
///
/// Sets the data normalization mode to be used by this security
///
public override void SetDataNormalizationMode(DataNormalizationMode mode)
{
if (mode != DataNormalizationMode.Raw)
{
throw new ArgumentException("DataNormalizationMode.Raw must be used with options");
}
base.SetDataNormalizationMode(mode);
}
}
}