/// <summary> /// Initializes a new instance of the <see cref="PositionGroupMaintenanceMarginParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> public PositionGroupMaintenanceMarginParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup ) { Portfolio = portfolio; PositionGroup = positionGroup; }
/// <summary> /// Initializes a new instance of the <see cref="PositionGroupInitialMarginParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> public PositionGroupInitialMarginParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup ) { Portfolio = portfolio; PositionGroup = positionGroup; }
/// <summary> /// Initializes a new instance of the <see cref="ReservedBuyingPowerForPositionGroupParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> public ReservedBuyingPowerForPositionGroupParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup ) { Portfolio = portfolio; PositionGroup = positionGroup; }
/// <summary> /// Gets a user friendly name for the provided <paramref name="group"/> /// </summary> public static string GetUserFriendlyName(this IPositionGroup group) { if (group.Count == 1) { return(group.Single().Symbol.ToString()); } return(string.Join("|", group.Select(p => p.Symbol.ToString()))); }
/// <summary> /// Computes the amount of buying power reserved by the provided position group /// </summary> public static decimal GetReservedBuyingPowerForPositionGroup( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup ) { return(model.GetReservedBuyingPowerForPositionGroup( new ReservedBuyingPowerForPositionGroupParameters(portfolio, positionGroup) ).AbsoluteUsedBuyingPower); }
/// <summary> /// The margin that must be held in order to change positions by the changes defined by the provided position group /// </summary> public static decimal GetInitialMarginRequirement( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup ) { return(model.GetInitialMarginRequirement( new PositionGroupInitialMarginParameters(portfolio, positionGroup) ).Value); }
/// <summary> /// Gets the margin currently allocated to the specified position group /// </summary> public static decimal GetMaintenanceMargin( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup ) { return(model.GetMaintenanceMargin( new PositionGroupMaintenanceMarginParameters(portfolio, positionGroup) )); }
/// <summary> /// Initializes a new instance of the <see cref="HasSufficientPositionGroupBuyingPowerForOrderParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> /// <param name="order">The order</param> public HasSufficientPositionGroupBuyingPowerForOrderParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup, Order order ) { Order = order; Portfolio = portfolio; PositionGroup = positionGroup; }
/// <summary> /// Initializes a new instance of the <see cref="PositionGroupInitialMarginForOrderParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> /// <param name="order">The order</param> public PositionGroupInitialMarginForOrderParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup, Order order ) { Portfolio = portfolio; PositionGroup = positionGroup; Order = order; }
/// <summary> /// Initializes a new instance of the <see cref="ReservedBuyingPowerImpactParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="contemplatedChanges">The position changes being contemplated</param> /// <param name="order">The order associated with this request</param> public ReservedBuyingPowerImpactParameters( SecurityPortfolioManager portfolio, IPositionGroup contemplatedChanges, Order order ) { Order = order; Portfolio = portfolio; ContemplatedChanges = contemplatedChanges; }
/// <summary> /// Initializes a new instance of the <see cref="PositionGroupBuyingPowerParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> /// <param name="direction">The direction to compute buying power in</param> public PositionGroupBuyingPowerParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup, OrderDirection direction ) { Portfolio = portfolio; Direction = direction; PositionGroup = positionGroup; }
/// <summary> /// Gets the total margin required to execute the specified order in units of the account currency including fees /// </summary> public static decimal GetInitialMarginRequiredForOrder( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup, Order order ) { return(model.GetInitialMarginRequiredForOrder( new PositionGroupInitialMarginForOrderParameters(portfolio, positionGroup, order) ).Value); }
/// <summary> /// Gets the buying power available for a position group trade /// </summary> public static PositionGroupBuyingPower GetPositionGroupBuyingPower( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup, OrderDirection direction ) { return(model.GetPositionGroupBuyingPower(new PositionGroupBuyingPowerParameters( portfolio, positionGroup, direction ))); }
/// <summary> /// Check if there is sufficient buying power for the position group to execute this order. /// </summary> public static HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup, Order order ) { return(model.HasSufficientBuyingPowerForOrder(new HasSufficientPositionGroupBuyingPowerForOrderParameters( portfolio, positionGroup, order ))); }
/// <summary> /// Get the maximum market position group order quantity to obtain a delta in the buying power used by a position group. /// The deltas sign defines the position side to apply it to, positive long, negative short. /// </summary> public static GetMaximumLotsResult GetMaximumLotsForDeltaBuyingPower( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup, decimal deltaBuyingPower ) { return(model.GetMaximumLotsForDeltaBuyingPower(new GetMaximumLotsForDeltaBuyingPowerParameters( portfolio, positionGroup, deltaBuyingPower ))); }
/// <summary> /// Computes the change in reserved buying power we expect the portfolio to experience if the specified position /// group is added to the algorithm's holdings /// </summary> public static decimal GetChangeInReservedBuyingPower( this IPositionGroupBuyingPowerModel model, SecurityPortfolioManager portfolio, IPositionGroup positionGroup, Order order ) { return(model.GetReservedBuyingPowerImpact( new ReservedBuyingPowerImpactParameters(portfolio, positionGroup, order) ).Delta); }
/// <summary> /// Creates a new <see cref="IPositionGroup"/> with the specified <paramref name="groupQuantity"/>. /// If the quantity provided equals the template's quantity then the template is returned. /// </summary> /// <param name="template">The group template</param> /// <param name="groupQuantity">The quantity of the new group</param> /// <returns>A position group with the same position ratios as the template but with the specified group quantity</returns> public static IPositionGroup WithQuantity(this IPositionGroup template, decimal groupQuantity) { if (template.Quantity == groupQuantity) { return(template); } var positions = template.ToArray(p => p.WithLots(groupQuantity)); return(new PositionGroup(template.Key, positions)); }
/// <summary> /// Gets the position in the <paramref name="group"/> matching the provided <param name="symbol"></param> /// </summary> public static IPosition GetPosition(this IPositionGroup group, Symbol symbol) { IPosition position; if (!group.TryGetPosition(symbol, out position)) { throw new KeyNotFoundException($"No position with symbol '{symbol}' exists in the group: {group}"); } return(position); }
/// <summary> /// Initializes a new instance of the <see cref="GetMaximumLotsForTargetBuyingPowerParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> /// <param name="targetBuyingPower">The target buying power</param> /// <param name="silenceNonErrorReasons">True will not return <see cref="GetMaximumLotsResult.Reason"/> /// set for non error situation, this is for performance</param> public GetMaximumLotsForTargetBuyingPowerParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup, decimal targetBuyingPower, bool silenceNonErrorReasons = false ) { Portfolio = portfolio; PositionGroup = positionGroup; TargetBuyingPower = targetBuyingPower; SilenceNonErrorReasons = silenceNonErrorReasons; }
/// <summary> /// Returns the Maximum (Middle Strike - Lowest Strike, 0) /// </summary> private static decimal GetMiddleAndLowStrikeDifference(IPositionGroup positionGroup, SecurityPortfolioManager portfolio) { var options = positionGroup.Positions.OrderBy(position => position.Symbol.ID.StrikePrice).ToList(); var lowestCallStrike = options[0].Symbol.ID.StrikePrice; var middleCallStrike = options[1].Symbol.ID.StrikePrice; var optionSecurity = (Option)portfolio.Securities[options[0].Symbol]; var strikeDifference = Math.Max((middleCallStrike - lowestCallStrike) * optionSecurity.ContractUnitOfTrade * Math.Abs(positionGroup.Quantity), 0); // convert into account currency return(portfolio.CashBook.ConvertToAccountCurrency(strikeDifference, optionSecurity.QuoteCurrency.Symbol)); }
/// <summary> /// Returns the Maximum (Strike Long Call - Strike Short Call, 0) /// </summary> private static decimal GetLongCallShortCallStrikeDifferenceMargin(IPositionGroup positionGroup, SecurityPortfolioManager portfolio) { var longOption = positionGroup.Positions.Single(position => position.Symbol.ID.OptionRight == OptionRight.Call && position.Quantity > 0); var shortOption = positionGroup.Positions.Single(position => position.Symbol.ID.OptionRight == OptionRight.Call && position.Quantity < 0); var optionSecurity = (Option)portfolio.Securities[longOption.Symbol]; var strikeDifference = longOption.Symbol.ID.StrikePrice - shortOption.Symbol.ID.StrikePrice; var result = Math.Max(strikeDifference * optionSecurity.ContractUnitOfTrade * Math.Abs(positionGroup.Quantity), 0); // convert into account currency return(portfolio.CashBook.ConvertToAccountCurrency(result, optionSecurity.QuoteCurrency.Symbol)); }
/// <summary> /// Initializes a new instance of the <see cref="GetMaximumLotsForDeltaBuyingPowerParameters"/> class /// </summary> /// <param name="portfolio">The algorithm's portfolio manager</param> /// <param name="positionGroup">The position group</param> /// <param name="deltaBuyingPower">The delta buying power to apply. Sign defines the position side to apply the delta</param> /// <param name="minimumOrderMarginPortfolioPercentage">Configurable minimum order margin portfolio percentage to ignore orders with unrealistic small sizes</param> /// <param name="silenceNonErrorReasons">True will not return <see cref="GetMaximumLotsResult.Reason"/> /// set for non error situation, this is for performance</param> public GetMaximumLotsForDeltaBuyingPowerParameters( SecurityPortfolioManager portfolio, IPositionGroup positionGroup, decimal deltaBuyingPower, decimal minimumOrderMarginPortfolioPercentage, bool silenceNonErrorReasons = false ) { Portfolio = portfolio; PositionGroup = positionGroup; DeltaBuyingPower = deltaBuyingPower; SilenceNonErrorReasons = silenceNonErrorReasons; MinimumOrderMarginPortfolioPercentage = minimumOrderMarginPortfolioPercentage; }
/// <summary> /// Gets the position side (long/short/none) of the specified <paramref name="group"/> /// </summary> public static PositionSide GetPositionSide(this IPositionGroup group) { if (group.Quantity > 0) { return(PositionSide.Long); } if (group.Quantity < 0) { return(PositionSide.Short); } return(PositionSide.None); }
/// <summary> /// Helper function to compute the order fees associated with executing market orders for the specified <paramref name="positionGroup"/> /// </summary> protected virtual decimal GetOrderFeeInAccountCurrency(SecurityPortfolioManager portfolio, IPositionGroup positionGroup) { // TODO : Add Order parameter to support Combo order type, pulling the orders per position var orderFee = 0m; var utcTime = portfolio.Securities.UtcTime; foreach (var position in positionGroup) { var security = portfolio.Securities[position.Symbol]; var order = new MarketOrder(position.Symbol, position.Quantity, utcTime); var positionOrderFee = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, order)).Value; orderFee += ToAccountCurrency(portfolio, positionOrderFee); } return(orderFee); }
/// <summary> /// Attempts to group the specified positions into a new <see cref="IPositionGroup"/> using an /// appropriate <see cref="IPositionGroupBuyingPowerModel"/> for position groups created via this /// resolver. /// </summary> /// <param name="newPositions">The positions to be grouped</param> /// <param name="currentPositions">The currently grouped positions</param> /// <param name="group">The grouped positions when this resolver is able to, otherwise null</param> /// <returns>True if this resolver can group the specified positions, otherwise false</returns> public bool TryGroup(IReadOnlyCollection <IPosition> newPositions, PositionGroupCollection currentPositions, out IPositionGroup group) { foreach (var resolver in _resolvers) { if (resolver.TryGroup(newPositions, currentPositions, out group)) { return(true); } } group = null; return(false); }
/// <summary> /// Attempts to group the specified positions into a new <see cref="IPositionGroup"/> using an /// appropriate <see cref="IPositionGroupBuyingPowerModel"/> for position groups created via this /// resolver. /// </summary> /// <param name="newPositions">The positions to be grouped</param> /// <param name="currentPositions">The currently grouped positions</param> /// <param name="group">The grouped positions when this resolver is able to, otherwise null</param> /// <returns>True if this resolver can group the specified positions, otherwise false</returns> public bool TryGroup(IReadOnlyCollection <IPosition> newPositions, PositionGroupCollection currentPositions, out IPositionGroup @group) { var impactedGroups = GetImpactedGroups(currentPositions, newPositions); var positionsToConsiderInNewGroup = impactedGroups.SelectMany(positionGroup => positionGroup.Positions); @group = GetPositionGroups(newPositions.Concat(positionsToConsiderInNewGroup)).Where(positionGroup => { // from the resolved position groups we will take those which use our buying power model and which are related to the new positions to be executed if (positionGroup.BuyingPowerModel.GetType() == typeof(OptionStrategyPositionGroupBuyingPowerModel)) { return(newPositions.Any(position => positionGroup.TryGetPosition(position.Symbol, out position))); } return(false); }).FirstOrDefault(); return(@group != null); }
/// <summary> /// Checks if <paramref name="lastOrderQuantity"/> equals <paramref name="positionGroupQuantity"/> indicating we got the same result on this iteration /// meaning we're unable to converge through iteration. This function was split out to support derived types using the same error message as well /// as removing the added noise of the check and message creation. /// </summary> protected static bool UnableToConverge(decimal lastOrderQuantity, decimal positionGroupQuantity, IPositionGroup groupUnit, SecurityPortfolioManager portfolio, decimal target, decimal orderMargin, decimal absUnitMargin, decimal orderFees, out ArgumentException error) { // determine if we're unable to converge by seeing if quantity estimate hasn't changed if (lastOrderQuantity == positionGroupQuantity) { string message; if (groupUnit.Count == 1) { // single security group var security = portfolio.Securities[groupUnit.Single().Symbol]; message = "GetMaximumPositionGroupOrderQuantityForTargetBuyingPower failed to converge to target order margin " + Invariant($"{target}. Current order margin is {orderMargin}. Order quantity {positionGroupQuantity}. ") + Invariant($"Lot size is {security.SymbolProperties.LotSize}. Order fees {orderFees}. Security symbol ") + $"{security.Symbol}. Margin unit {absUnitMargin}."; } else { message = "GetMaximumPositionGroupOrderQuantityForTargetBuyingPower failed to converge to target order margin " + Invariant($"{target}. Current order margin is {orderMargin}. Order quantity {positionGroupQuantity}. ") + Invariant($"Position Group Unit is {groupUnit.Key}. Order fees {orderFees}. Position Group Name ") + $"{groupUnit.GetUserFriendlyName()}. Margin unit {absUnitMargin}."; } error = new ArgumentException(message); return(true); } error = null; return(false); }
/// <summary> /// Attempts to group the specified positions into a new <see cref="IPositionGroup"/> using an /// appropriate <see cref="IPositionGroupBuyingPowerModel"/> for position groups created via this /// resolver. /// </summary> /// <param name="newPositions">The positions to be grouped</param> /// <param name="currentPositions">The currently grouped positions</param> /// <param name="group">The grouped positions when this resolver is able to, otherwise null</param> /// <returns>True if this resolver can group the specified positions, otherwise false</returns> public bool TryGroup(IReadOnlyCollection <IPosition> newPositions, PositionGroupCollection currentPositions, out IPositionGroup group) { // we can only create default groupings containing a single security if (newPositions.Count != 1) { group = null; return(false); } var key = new PositionGroupKey(_buyingPowerModel, newPositions); group = new PositionGroup(key, newPositions.ToDictionary(p => p.Symbol)); return(true); }