21 using System.Collections.Generic;
42 _optionStrategy = optionStrategy;
52 if (_optionStrategy ==
null)
60 var optionPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
61 var underlyingPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
65 var absOptionQuantity = Math.Abs(optionPosition.Quantity);
66 var outOfTheMoneyAmount = optionSecurity.OutOfTheMoneyAmount(underlyingSecurity.Price) * optionSecurity.ContractUnitOfTrade * absOptionQuantity;
69 underlyingSecurity, underlyingPosition.Quantity)));
71 var result = Math.Min(0.1m * optionSecurity.StrikePrice * optionSecurity.ContractUnitOfTrade * absOptionQuantity + outOfTheMoneyAmount, underlyingMarginRequired);
79 var optionPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
80 var underlyingPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
84 var intrinsicValue = optionSecurity.GetIntrinsicValue(underlyingSecurity.Price);
85 var inTheMoneyAmount = intrinsicValue * optionSecurity.ContractUnitOfTrade * Math.Abs(optionPosition.Quantity);
87 var underlyingValue = underlyingSecurity.Holdings.GetQuantityValue(underlyingPosition.Quantity).InAccountCurrency;
88 var optionValue = optionSecurity.Holdings.GetQuantityValue(optionPosition.Quantity).InAccountCurrency;
91 var underlyingPriceToEvaluate = Math.Min(underlyingSecurity.Price, optionSecurity.ScaledStrikePrice);
92 var underlyingHypotheticalValue = underlyingSecurity.Holdings.GetQuantityValue(underlyingPosition.Quantity, underlyingPriceToEvaluate).InAccountCurrency;
94 var hypotheticalMarginRequired = underlyingSecurity.BuyingPowerModel.GetMaintenanceMargin(
96 var marginRequired = underlyingSecurity.BuyingPowerModel.GetMaintenanceMargin(
99 var secondOperand = Math.Min(underlyingValue, Math.Max(optionValue, marginRequired));
100 var result = Math.Max(inTheMoneyAmount + hypotheticalMarginRequired, secondOperand);
108 var optionPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
109 var underlyingPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
113 var intrinsicValue = optionSecurity.GetIntrinsicValue(underlyingSecurity.Price);
114 var inTheMoneyAmount = intrinsicValue * optionSecurity.ContractUnitOfTrade * Math.Abs(optionPosition.Quantity);
116 var initialMarginRequirement = underlyingSecurity.BuyingPowerModel.GetInitialMarginRequirement(underlyingSecurity, underlyingPosition.Quantity);
118 var result = Math.Abs(initialMarginRequirement) + inTheMoneyAmount;
127 position.Symbol.SecurityType.IsOption() && position.Symbol.ID.OptionRight ==
OptionRight.Put);
129 position.Symbol.SecurityType.IsOption() && position.Symbol.ID.OptionRight ==
OptionRight.Call);
130 var underlyingPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
135 var putMarginRequirement = 0.1m * putSecurity.StrikePrice + putSecurity.OutOfTheMoneyAmount(underlyingSecurity.Price);
136 var callMarginRequirement = 0.25m * callSecurity.StrikePrice;
139 var contractUnits = Math.Abs(putPosition.Quantity) * putSecurity.ContractUnitOfTrade;
140 var result = Math.Min(putMarginRequirement, callMarginRequirement) * contractUnits;
180 shortCallSecurity, shortCall.Quantity));
198 (option, quantity) => Math.Abs(option.BuyingPowerModel.GetMaintenanceMargin(
225 position => position.Quantity > 0 && position.Symbol.ID.OptionRight ==
OptionRight.Call);
227 position => position.Quantity < 0 && position.Symbol.ID.OptionRight ==
OptionRight.Call);
229 position => position.Quantity > 0 && position.Symbol.ID.OptionRight ==
OptionRight.Put);
231 position => position.Quantity < 0 && position.Symbol.ID.OptionRight ==
OptionRight.Put);
238 var commissionFees = Math.Max(Math.Abs(longCallPosition.Quantity) * 0.65m, 1m) * 4m;
239 var orderCosts = shortCallSecurity.AskPrice - longCallSecurity.BidPrice + shortPutSecurity.AskPrice - longPutSecurity.BidPrice;
240 var multiplier = Math.Abs(longCallPosition.Quantity) * longCallSecurity.ContractUnitOfTrade;
241 var closeCost = commissionFees + orderCosts * multiplier;
243 var strikeDifference = longCallPosition.Symbol.ID.StrikePrice - shortCallPosition.Symbol.ID.StrikePrice;
245 var result = Math.Max(1.02m * closeCost, strikeDifference * multiplier);
257 position.Quantity < 0 && position.Symbol.ID.Date == furtherExpiry);
259 var result = Math.Abs(shortCalendarSpreadShortLegSecurity.BuyingPowerModel.GetMaintenanceMargin(
266 return GetCallLadderMargin(parameters,
true);
270 return GetPutLadderMargin(parameters,
false);
274 return GetCallLadderMargin(parameters,
false);
278 return GetPutLadderMargin(parameters,
true);
281 throw new NotImplementedException($
"Option strategy {_optionStrategy.Name} margin modeling has yet to be implemented");
292 if (_optionStrategy ==
null)
299 var underlyingPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
302 result = Math.Abs(underlyingSecurity.BuyingPowerModel.GetInitialMarginRequirement(underlyingSecurity, underlyingPosition.Quantity));
308 var optionPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => position.Symbol.SecurityType.IsOption());
309 var underlyingPosition = parameters.
PositionGroup.
Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
313 var optionValue = Math.Abs(optionSecurity.Holdings.GetQuantityValue(optionPosition.Quantity).InAccountCurrency);
315 var marginRequired = underlyingSecurity.BuyingPowerModel.GetInitialMarginRequirement(underlyingSecurity, underlyingPosition.Quantity);
318 result = optionValue * 0.8m + marginRequired;
339 var margin = security.BuyingPowerModel.GetInitialMarginRequirement(
new InitialMarginParameters(security, option.Quantity));
342 if (optionMargin !=
null)
344 return new OptionInitialMargin(Math.Abs(optionMargin.ValueWithoutPremium), optionMargin.Premium);
364 result = Math.Abs(shortOption.BuyingPowerModel.GetInitialMarginRequirement(shortOption, shortOptionPosition.Quantity));
379 (option, quantity) => Math.Abs(option.BuyingPowerModel.GetInitialMarginRequirement(option, quantity)));
414 throw new NotImplementedException($
"Option strategy {_optionStrategy.Name} margin modeling has yet to be implemented");
419 foreach (var position
in parameters.PositionGroup.Positions.Where(position => position.Symbol.SecurityType.IsOption()))
421 var option = (
Option)parameters.Portfolio.Securities[position.Symbol];
422 premium += option.Holdings.GetQuantityValue(position.Quantity).InAccountCurrency;
441 var feesWithSign = Math.Sign(initialMarginRequired) * feesInAccountCurrency.Amount;
443 return new InitialMargin(feesWithSign + initialMarginRequired);
451 List<IPosition> ordersPositions)
453 var contemplatedMargin = 0m;
454 foreach (var contemplatedGroup
in contemplatedGroups)
458 var initialMargin = contemplatedGroup.BuyingPowerModel.GetInitialMarginRequirement(
466 var ordersGroups = portfolio.Positions.ResolvePositionGroups(
new PositionCollection(ordersPositions));
467 foreach (var orderGroup
in ordersGroups)
469 var initialMargin = orderGroup.BuyingPowerModel.GetInitialMarginRequirement(
473 if (optionInitialMargin !=
null)
477 contemplatedMargin += optionInitialMargin.
Value - optionInitialMargin.ValueWithoutPremium;
481 return contemplatedMargin;
490 return _optionStrategy.Name;
496 private static decimal GetShortPutLongPutStrikeDifferenceMargin(IEnumerable<IPosition> positions,
SecurityPortfolioManager portfolio, decimal quantity)
498 var longOption = positions.Single(position => position.Symbol.ID.OptionRight ==
OptionRight.Put && position.Quantity > 0);
499 var shortOption = positions.Single(position => position.Symbol.ID.OptionRight ==
OptionRight.Put && position.Quantity < 0);
503 var strikeDifference = shortOption.Symbol.ID.StrikePrice - longOption.Symbol.ID.StrikePrice;
505 var result = Math.Max(strikeDifference * optionSecurity.ContractUnitOfTrade * Math.Abs(quantity), 0);
514 private static decimal GetLongCallShortCallStrikeDifferenceMargin(IEnumerable<IPosition> positions,
SecurityPortfolioManager portfolio, decimal quantity)
516 var longOption = positions.Single(position => position.Symbol.ID.OptionRight ==
OptionRight.Call && position.Quantity > 0);
517 var shortOption = positions.Single(position => position.Symbol.ID.OptionRight ==
OptionRight.Call && position.Quantity < 0);
520 var strikeDifference = longOption.Symbol.ID.StrikePrice - shortOption.Symbol.ID.StrikePrice;
522 var result = Math.Max(strikeDifference * optionSecurity.ContractUnitOfTrade * Math.Abs(quantity), 0);
531 private static decimal GetMiddleAndLowStrikeDifference(
IPositionGroup positionGroup, SecurityPortfolioManager portfolio)
533 var options = positionGroup.
Positions.OrderBy(position => position.Symbol.ID.StrikePrice).ToList();
534 var lowestCallStrike = options[0].Symbol.ID.StrikePrice;
535 var middleCallStrike = options[1].Symbol.ID.StrikePrice;
536 var optionSecurity = (
Option)portfolio.Securities[options[0].Symbol];
538 var strikeDifference = Math.Max((middleCallStrike - lowestCallStrike) * optionSecurity.ContractUnitOfTrade * Math.Abs(positionGroup.
Quantity), 0);
541 return portfolio.CashBook.ConvertToAccountCurrency(strikeDifference, optionSecurity.QuoteCurrency.Symbol);
548 private static decimal GetShortStraddleStrangleMargin(
IPositionGroup positionGroup, SecurityPortfolioManager portfolio,
549 Func<Option, decimal, decimal> getOptionMargin)
551 var callOption = positionGroup.
Positions.Single(position => position.Symbol.ID.OptionRight ==
OptionRight.Call);
552 var callSecurity = (Option)portfolio.Securities[callOption.Symbol];
553 var callMargin = getOptionMargin(callSecurity, callOption.Quantity);
555 var putOption = positionGroup.
Positions.Single(position => position.Symbol.ID.OptionRight ==
OptionRight.Put);
556 var putSecurity = (Option)portfolio.Securities[putOption.Symbol];
557 var putMargin = getOptionMargin(putSecurity, putOption.Quantity);
561 if (putMargin > callMargin)
563 result = putMargin + callSecurity.Price * callSecurity.ContractUnitOfTrade * Math.Abs(callOption.Quantity);
567 result = callMargin + putSecurity.Price * putSecurity.ContractUnitOfTrade * Math.Abs(putOption.Quantity);
576 private static decimal GetConversionMaintenanceMargin(
IPositionGroup positionGroup, SecurityPortfolioManager portfolio,
OptionRight optionRight)
579 var optionPosition = positionGroup.
Positions.Single(position =>
580 position.Symbol.SecurityType.IsOption() && position.Symbol.ID.OptionRight == optionRight);
581 var underlyingPosition = positionGroup.
Positions.FirstOrDefault(position => !position.Symbol.SecurityType.IsOption());
582 var optionSecurity = (
Option)portfolio.Securities[optionPosition.Symbol];
583 var underlyingSecurity = portfolio.Securities[underlyingPosition.Symbol];
585 var marginRequirement = 0.1m * optionSecurity.StrikePrice + optionSecurity.GetIntrinsicValue(underlyingSecurity.Price);
586 var result = marginRequirement * Math.Abs(optionPosition.Quantity) * optionSecurity.ContractUnitOfTrade;
587 var inAccountCurrency = portfolio.CashBook.ConvertToAccountCurrency(result, underlyingSecurity.QuoteCurrency.Symbol);
589 return new MaintenanceMargin(inAccountCurrency);
595 private static decimal GetCollarConversionInitialMargin(
IPositionGroup positionGroup, SecurityPortfolioManager portfolio,
OptionRight optionRight)
598 var optionPosition = positionGroup.
Positions.Single(position =>
599 position.Symbol.SecurityType.IsOption() && position.Symbol.ID.OptionRight == optionRight);
600 var underlyingPosition = positionGroup.
Positions.Single(position => !position.Symbol.SecurityType.IsOption());
601 var optionSecurity = (
Option)portfolio.Securities[optionPosition.Symbol];
602 var underlyingSecurity = portfolio.Securities[underlyingPosition.Symbol];
604 var intrinsicValue = optionSecurity.GetIntrinsicValue(underlyingSecurity.Price);
605 var inTheMoneyAmount = intrinsicValue * optionSecurity.ContractUnitOfTrade * Math.Abs(optionPosition.Quantity);
607 var initialMarginRequirement = underlyingSecurity.BuyingPowerModel.GetInitialMarginRequirement(underlyingSecurity, underlyingPosition.Quantity);
609 var result = Math.Abs(initialMarginRequirement) + inTheMoneyAmount;
610 return portfolio.CashBook.ConvertToAccountCurrency(result, optionSecurity.QuoteCurrency.Symbol);
620 if ((quantity >= 0 && bearCallLadder) || (quantity < 0 && !bearCallLadder))
623 var callSpread = parameters.
PositionGroup.
Positions.OrderBy(position => position.Symbol.ID.StrikePrice).Take(2).ToList();
624 return GetLongCallShortCallStrikeDifferenceMargin(callSpread, parameters.
Portfolio, Math.Abs(quantity));
629 var shortNakedCall = parameters.
PositionGroup.
Positions.OrderByDescending(position => position.Symbol.ID.StrikePrice).First();
631 var margin = security.BuyingPowerModel.GetInitialMarginRequirement(
new InitialMarginParameters(security, shortNakedCall.Quantity));
632 return new MaintenanceMargin(Math.Abs(margin));
643 if ((quantity >= 0 && bullPutLadder) || (quantity < 0 && !bullPutLadder))
646 var putSpread = parameters.
PositionGroup.
Positions.OrderByDescending(position => position.Symbol.ID.StrikePrice).Take(2).ToList();
647 return GetShortPutLongPutStrikeDifferenceMargin(putSpread, parameters.
Portfolio, Math.Abs(quantity));
652 var shortNakedPut = parameters.
PositionGroup.
Positions.OrderBy(position => position.Symbol.ID.StrikePrice).First();
654 var margin = security.BuyingPowerModel.GetInitialMarginRequirement(
new InitialMarginParameters(security, shortNakedPut.Quantity));
655 return new MaintenanceMargin(Math.Abs(margin));