/// <summary>
        /// Gets the margin currently allocated to the specified holding
        /// </summary>
        /// <param name="parameters">An object containing the security</param>
        /// <returns>The maintenance margin required for the </returns>
        public override MaintenanceMargin GetMaintenanceMargin(PositionGroupMaintenanceMarginParameters parameters)
        {
            // SecurityPositionGroupBuyingPowerModel models buying power the same as non-grouped, so we can simply sum up
            // the reserved buying power via the security's model. We should really only ever get a single position here,
            // but it's not incorrect to ask the model for what the reserved buying power would be using default modeling
            var buyingPower = 0m;

            foreach (var position in parameters.PositionGroup)
            {
                var security = parameters.Portfolio.Securities[position.Symbol];
                var result   = security.BuyingPowerModel.GetMaintenanceMargin(
                    MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, position.Quantity)
                    );

                buyingPower += result;
            }

            return(buyingPower);
        }
예제 #2
0
        /// <summary>
        /// Gets the margin currently allocated to the specified holding
        /// </summary>
        /// <param name="parameters">An object containing the security</param>
        /// <returns>The maintenance margin required for the </returns>
        public override MaintenanceMargin GetMaintenanceMargin(PositionGroupMaintenanceMarginParameters parameters)
        {
            if (_optionStrategy.Name == OptionStrategyDefinitions.CoveredCall.Name)
            {
                // MAX[In-the-money amount + Margin(long stock evaluated at min(mark price, strike(short call))), min(stock value, max(call value, long stock margin))]
                var optionPosition     = parameters.PositionGroup.Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
                var underlyingPosition = parameters.PositionGroup.Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
                var optionSecurity     = (Option)parameters.Portfolio.Securities[optionPosition.Symbol];
                var underlyingSecurity = parameters.Portfolio.Securities[underlyingPosition.Symbol];

                var intrinsicValue   = optionSecurity.GetIntrinsicValue(underlyingSecurity.Price);
                var inTheMoneyAmount = intrinsicValue * optionSecurity.ContractUnitOfTrade * Math.Abs(optionPosition.Quantity);

                var underlyingValue = underlyingSecurity.Holdings.GetQuantityValue(underlyingPosition.Quantity);
                var optionValue     = optionSecurity.Holdings.GetQuantityValue(optionPosition.Quantity);

                // mark price, strike price
                var underlyingPriceToEvaluate   = Math.Min(optionSecurity.Price, optionSecurity.StrikePrice);
                var underlyingHypotheticalValue = underlyingSecurity.Holdings.GetQuantityValue(underlyingPosition.Quantity, underlyingPriceToEvaluate);

                var hypotheticalMarginRequired = underlyingSecurity.BuyingPowerModel.GetMaintenanceMargin(
                    new MaintenanceMarginParameters(underlyingSecurity, underlyingPosition.Quantity, 0, underlyingHypotheticalValue));
                var marginRequired = underlyingSecurity.BuyingPowerModel.GetMaintenanceMargin(
                    new MaintenanceMarginParameters(underlyingSecurity, underlyingPosition.Quantity, 0, underlyingValue));

                var secondOperand     = Math.Min(underlyingValue, Math.Max(optionValue, marginRequired));
                var result            = Math.Max(inTheMoneyAmount + hypotheticalMarginRequired, secondOperand);
                var inAccountCurrency = parameters.Portfolio.CashBook.ConvertToAccountCurrency(result, optionSecurity.QuoteCurrency.Symbol);

                return(new MaintenanceMargin(inAccountCurrency));
            }
            else if (_optionStrategy.Name == OptionStrategyDefinitions.CoveredPut.Name)
            {
                // Initial Stock Margin Requirement + In the Money Amount
                var optionPosition     = parameters.PositionGroup.Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
                var underlyingPosition = parameters.PositionGroup.Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
                var optionSecurity     = (Option)parameters.Portfolio.Securities[optionPosition.Symbol];
                var underlyingSecurity = parameters.Portfolio.Securities[underlyingPosition.Symbol];

                var intrinsicValue   = optionSecurity.GetIntrinsicValue(underlyingSecurity.Price);
                var inTheMoneyAmount = intrinsicValue * optionSecurity.ContractUnitOfTrade * Math.Abs(optionPosition.Quantity);

                var initialMarginRequirement = underlyingSecurity.BuyingPowerModel.GetInitialMarginRequirement(underlyingSecurity, underlyingPosition.Quantity);

                var result            = Math.Abs(initialMarginRequirement) + inTheMoneyAmount;
                var inAccountCurrency = parameters.Portfolio.CashBook.ConvertToAccountCurrency(result, optionSecurity.QuoteCurrency.Symbol);

                return(new MaintenanceMargin(inAccountCurrency));
            }
            else if (_optionStrategy.Name == OptionStrategyDefinitions.BearCallSpread.Name ||
                     _optionStrategy.Name == OptionStrategyDefinitions.BullCallSpread.Name ||
                     _optionStrategy.Name == OptionStrategyDefinitions.CallCalendarSpread.Name)
            {
                var result = GetLongCallShortCallStrikeDifferenceMargin(parameters.PositionGroup, parameters.Portfolio);
                return(new MaintenanceMargin(result));
            }
            else if (_optionStrategy.Name == OptionStrategyDefinitions.BearPutSpread.Name ||
                     _optionStrategy.Name == OptionStrategyDefinitions.BullPutSpread.Name ||
                     _optionStrategy.Name == OptionStrategyDefinitions.PutCalendarSpread.Name)
            {
                var result = GetShortPutLongPutStrikeDifferenceMargin(parameters.PositionGroup, parameters.Portfolio);
                return(new MaintenanceMargin(result));
            }
            else if (_optionStrategy.Name == OptionStrategyDefinitions.Straddle.Name || _optionStrategy.Name == OptionStrategyDefinitions.Strangle.Name)
            {
                // Margined as two long options.
                var callOption   = parameters.PositionGroup.Positions.Single(position => position.Symbol.ID.OptionRight == OptionRight.Call);
                var callSecurity = (Option)parameters.Portfolio.Securities[callOption.Symbol];
                var callMargin   = callSecurity.BuyingPowerModel.GetMaintenanceMargin(MaintenanceMarginParameters.ForQuantityAtCurrentPrice(
                                                                                          callSecurity, callOption.Quantity));

                var putOption   = parameters.PositionGroup.Positions.Single(position => position.Symbol.ID.OptionRight == OptionRight.Put);
                var putSecurity = (Option)parameters.Portfolio.Securities[putOption.Symbol];
                var putMargin   = putSecurity.BuyingPowerModel.GetMaintenanceMargin(MaintenanceMarginParameters.ForQuantityAtCurrentPrice(
                                                                                        putSecurity, putOption.Quantity));

                var result = callMargin.Value + putMargin.Value;
                return(new MaintenanceMargin(result));
            }
            else if (_optionStrategy.Name == OptionStrategyDefinitions.ButterflyCall.Name || _optionStrategy.Name == OptionStrategyDefinitions.ButterflyPut.Name)
            {
                return(new MaintenanceMargin(0));
            }
            else if (_optionStrategy.Name == OptionStrategyDefinitions.ShortButterflyPut.Name || _optionStrategy.Name == OptionStrategyDefinitions.ShortButterflyCall.Name)
            {
                var result = GetMiddleAndLowStrikeDifference(parameters.PositionGroup, parameters.Portfolio);
                return(new MaintenanceMargin(result));
            }
            else if (_optionStrategy.Name == OptionStrategyDefinitions.IronCondor.Name)
            {
                var result = GetShortPutLongPutStrikeDifferenceMargin(parameters.PositionGroup, parameters.Portfolio);
                return(new MaintenanceMargin(result));
            }

            throw new NotImplementedException($"Option strategy {_optionStrategy.Name} margin modeling has yet to be implemented");
        }