/* * 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.Securities.Future; namespace QuantConnect.Securities.Option { /// /// Defines a margin model for future options (an option with a future as its underlying). /// We re-use the implementation and multiply its results /// by 1.5x to simulate the increased margins seen for future options. /// public class FuturesOptionsMarginModel : FutureMarginModel { private readonly Option _futureOption; /// /// Initial Overnight margin requirement for the contract effective from the date of change /// public override decimal InitialOvernightMarginRequirement => GetMarginRequirement(_futureOption, base.InitialOvernightMarginRequirement); /// /// Maintenance Overnight margin requirement for the contract effective from the date of change /// public override decimal MaintenanceOvernightMarginRequirement => GetMarginRequirement(_futureOption, base.MaintenanceOvernightMarginRequirement); /// /// Initial Intraday margin for the contract effective from the date of change /// public override decimal InitialIntradayMarginRequirement => GetMarginRequirement(_futureOption, base.InitialIntradayMarginRequirement); /// /// Maintenance Intraday margin requirement for the contract effective from the date of change /// public override decimal MaintenanceIntradayMarginRequirement => GetMarginRequirement(_futureOption, base.MaintenanceIntradayMarginRequirement); /// /// Creates an instance of FutureOptionMarginModel /// /// The percentage used to determine the required unused buying power for the account. /// Option Security containing a Future security as the underlying public FuturesOptionsMarginModel(decimal requiredFreeBuyingPowerPercent = 0, Option futureOption = null) : base(requiredFreeBuyingPowerPercent, futureOption?.Underlying) { _futureOption = futureOption; } /// /// Gets the margin currently alloted to the specified holding. /// /// An object containing the security /// The maintenance margin required for the option /// /// We fix the option to 1.5x the maintenance because of its close coupling with the underlying. /// The option's contract multiplier is 1x, but might be more sensitive to volatility shocks in the long /// run when it comes to calculating the different market scenarios attempting to simulate VaR, resulting /// in a margin greater than the underlying's margin. /// public override MaintenanceMargin GetMaintenanceMargin(MaintenanceMarginParameters parameters) { var underlyingRequirement = base.GetMaintenanceMargin(parameters.ForUnderlying(parameters.Quantity)); var positionSide = parameters.Quantity > 0 ? PositionSide.Long : PositionSide.Short; return GetMarginRequirement(_futureOption, underlyingRequirement, positionSide); } /// /// The margin that must be held in order to increase the position by the provided quantity /// /// An object containing the security and quantity of shares /// The initial margin required for the option (i.e. the equity required to enter a position for this option) /// /// We fix the option to 1.5x the initial because of its close coupling with the underlying. /// The option's contract multiplier is 1x, but might be more sensitive to volatility shocks in the long /// run when it comes to calculating the different market scenarios attempting to simulate VaR, resulting /// in a margin greater than the underlying's margin. /// public override InitialMargin GetInitialMarginRequirement(InitialMarginParameters parameters) { var underlyingRequirement = base.GetInitialMarginRequirement(parameters.ForUnderlying()).Value; var positionSide = parameters.Quantity > 0 ? PositionSide.Long : PositionSide.Short; return new InitialMargin(GetMarginRequirement(_futureOption, underlyingRequirement, positionSide)); } /// /// Get's the margin requirement for a future option based on the underlying future margin requirement and the position side to trade. /// FOPs margin requirement is an 'S' curve based on the underlying requirement around it's current price, see https://en.wikipedia.org/wiki/Logistic_function /// /// The future option contract to trade /// The underlying future associated margin requirement /// The position side to trade, long by default. This is because short positions require higher margin requirements public static int GetMarginRequirement(Option option, decimal underlyingRequirement, PositionSide positionSide = PositionSide.Long) { var maximumValue = underlyingRequirement; var curveGrowthRate = -7.8m; var underlyingPrice = option.Underlying.Price; // If the underlying price is 0, we can't calculate a margin requirement, so return the underlying requirement. // This could be removed after GH issue #6523 is resolved. if (option.Underlying == null || option.Underlying.Price == 0m) { return 0; } if (positionSide == PositionSide.Short) { if (option.Right == OptionRight.Call) { // going short the curve growth rate is slower curveGrowthRate = -4m; // curve shifted to the right -> causes a margin requirement increase underlyingPrice *= 1.5m; } else { // higher max requirements maximumValue *= 1.25m; // puts are inverter from calls curveGrowthRate = 2.4m; // curve shifted to the left -> causes a margin requirement increase underlyingPrice *= 0.30m; } } else { if (option.Right == OptionRight.Put) { // fastest change rate curveGrowthRate = 9m; } else { maximumValue *= 1.20m; } } // we normalize the curve growth rate by dividing by the underlyings price // this way, contracts with different order of magnitude price and strike (like CL & ES) share this logic var denominator = Math.Pow(Math.E, (double) (-curveGrowthRate * (option.ScaledStrikePrice - underlyingPrice) / underlyingPrice)); if (double.IsInfinity(denominator)) { return 0; } if (denominator.IsNaNOrZero()) { return (int) maximumValue; } return (int) (maximumValue / (1 + denominator).SafeDecimalCast()); } } }