/* * 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.Orders.Fees; namespace QuantConnect.Securities.Option { /// /// Represents a simple option margin model. /// /// /// Options are not traded on margin. Margin requirements exist though for those portfolios with short positions. /// Current implementation covers only single long/naked short option positions. /// public class OptionMarginModel : SecurityMarginModel { // initial margin private const decimal OptionMarginRequirement = 1; private const decimal NakedPositionMarginRequirement = 0.1m; private const decimal EquityOptionNakedPositionMarginRequirementOtm = 0.2m; private const decimal IndexOptionNakedPositionMarginRequirementOtm = 0.15m; /// /// Initializes a new instance of the /// /// The percentage used to determine the required unused buying power for the account. public OptionMarginModel(decimal requiredFreeBuyingPowerPercent = 0) { RequiredFreeBuyingPowerPercent = requiredFreeBuyingPowerPercent; } /// /// Gets the current leverage of the security /// /// The security to get leverage for /// The current leverage in the security public override decimal GetLeverage(Security security) { // Options are not traded on margin return 1; } /// /// Sets the leverage for the applicable securities, i.e, options. /// /// /// The new leverage public override void SetLeverage(Security security, decimal leverage) { // Options are leveraged products and different leverage cannot be set by user. throw new InvalidOperationException("Options are leveraged products and different leverage cannot be set by user"); } /// /// Gets the total margin required to execute the specified order in units of the account currency including fees /// /// An object containing the portfolio, the security and the order /// The total margin in terms of the currency quoted in the order public override InitialMargin GetInitialMarginRequiredForOrder( InitialMarginRequiredForOrderParameters parameters ) { //Get the order value from the non-abstract order classes (MarketOrder, LimitOrder, StopMarketOrder) //Market order is approximated from the current security price and set in the MarketOrder Method in QCAlgorithm. var fees = parameters.Security.FeeModel.GetOrderFee( new OrderFeeParameters(parameters.Security, parameters.Order) ); var feesInAccountCurrency = parameters.CurrencyConverter.ConvertToAccountCurrency(fees.Value); var value = parameters.Order.GetValue(parameters.Security); var orderMargin = value * GetMarginRequirement(parameters.Security, parameters.Order.Quantity, value); return orderMargin + Math.Sign(orderMargin) * feesInAccountCurrency.Amount; } /// /// Gets the margin currently alloted to the specified holding /// /// An object containing the security /// The maintenance margin required for the provided holdings quantity/cost/value public override MaintenanceMargin GetMaintenanceMargin(MaintenanceMarginParameters parameters) { // Long options have zero maintenance margin requirement return parameters.Quantity >= 0 ? 0 : parameters.AbsoluteHoldingsCost * GetMaintenanceMarginRequirement(parameters); } /// /// The margin that must be held in order to increase the position by the provided quantity /// /// The initial margin required for the provided security and quantity public override InitialMargin GetInitialMarginRequirement(InitialMarginParameters parameters) { var security = parameters.Security; var quantity = parameters.Quantity; var value = security.QuoteCurrency.ConversionRate * security.SymbolProperties.ContractMultiplier * security.Price * quantity; // Initial margin requirement for long options is only the premium that is paid upfront return new OptionInitialMargin(parameters.Quantity >= 0 ? 0 : value * GetMarginRequirement(security, quantity, value), value); } /// /// The percentage of the holding's absolute cost that must be held in free cash in order to avoid a margin call /// private decimal GetMaintenanceMarginRequirement(MaintenanceMarginParameters parameters) { return GetMarginRequirement(parameters.Security, parameters.Quantity, parameters.HoldingsCost); } /// /// Private method takes option security and its holding and returns required margin. Method considers all short positions naked. /// /// Option security /// Holding quantity /// Holding value /// private decimal GetMarginRequirement(Security security, decimal quantity, decimal value) { var option = (Option)security; if (value == 0m || option.Close == 0m || option.StrikePrice == 0m || option.Underlying == null || option.Underlying.Close == 0m) { return 0m; } if (value > 0m) { return OptionMarginRequirement; } var absValue = -value; var optionProperties = (OptionSymbolProperties)option.SymbolProperties; var underlying = option.Underlying; // inferring ratios of the option and its underlying to get underlying security value var multiplierRatio = underlying.SymbolProperties.ContractMultiplier / optionProperties.ContractMultiplier; var quantityRatio = optionProperties.ContractUnitOfTrade; // Some options are based on a fraction of their underlying security value, such as NQX for example. Thus, // for them we need to scale the underlying value so that the later comparisons made with the option's strike // value are correct var priceRatio = (underlying.Close / option.SymbolProperties.StrikeMultiplier) / (absValue / quantityRatio); var underlyingValueRatio = multiplierRatio * quantityRatio * priceRatio; // calculating underlying security value less out-of-the-money amount var amountOTM = option.OutOfTheMoneyAmount(underlying.Close); var priceRatioOTM = amountOTM / (absValue / quantityRatio); var underlyingValueRatioOTM = multiplierRatio * quantityRatio * priceRatioOTM; var strikePriceRatio = option.StrikePrice / (absValue / quantityRatio); strikePriceRatio = multiplierRatio * quantityRatio * strikePriceRatio; var nakedMarginRequirement = option.Right == OptionRight.Call ? NakedPositionMarginRequirement * underlyingValueRatio : NakedPositionMarginRequirement * strikePriceRatio; var nakedMarginRequirementOtm = security.Type == SecurityType.Option ? EquityOptionNakedPositionMarginRequirementOtm : IndexOptionNakedPositionMarginRequirementOtm; return OptionMarginRequirement + Math.Abs(quantity) * Math.Max(nakedMarginRequirement, nakedMarginRequirementOtm * underlyingValueRatio - underlyingValueRatioOTM); } } }