/* * 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 System.Linq; using QuantConnect.Logging; using QuantConnect.Securities; using System.Collections.Generic; namespace QuantConnect.Orders { /// /// Group (combo) orders extension methods for easiest combo order manipulation /// public static class GroupOrderExtensions { /// /// Gets the grouped orders (legs) of a group order /// /// Target order, which can be any of the legs of the combo /// Order provider to use to access the existing orders /// List of orders in the combo /// False if any of the orders in the combo is not yet found in the order provider. True otherwise /// If the target order is not a combo order, the resulting list will contain that single order alone public static bool TryGetGroupOrders(this Order order, Func orderProvider, out List orders) { orders = new List { order }; if (order.GroupOrderManager != null) { lock (order.GroupOrderManager.OrderIds) { foreach (var otherOrdersId in order.GroupOrderManager.OrderIds.Where(id => id != order.Id)) { var otherOrder = orderProvider(otherOrdersId); if (otherOrder != null) { orders.Add(otherOrder); } else { // this will happen while all the orders haven't arrived yet, we will retry return false; } } } if (order.GroupOrderManager.Count != orders.Count) { if (Log.DebuggingEnabled) { Log.Debug($"GroupOrderExtensions.TryGetGroupOrders(): missing orders of group {order.GroupOrderManager.Id}." + $" We have {orders.Count}/{order.GroupOrderManager.Count} orders will skip"); } return false; } } orders.Sort((x, y) => x.Id.CompareTo(y.Id)); return true; } /// /// Gets the securities corresponding to each order in the group /// /// List of orders to map /// The security provider to use /// The resulting map of order to security /// True if the mapping is successful, false otherwise. public static bool TryGetGroupOrdersSecurities(this List orders, ISecurityProvider securityProvider, out Dictionary securities) { securities = new(orders.Count); for (var i = 0; i < orders.Count; i++) { var order = orders[i]; var security = securityProvider.GetSecurity(order.Symbol); if (security == null) { return false; } securities[order] = security; } return true; } /// /// Returns an error string message saying there is insufficient buying power for the given orders associated with their respective /// securities /// public static string GetErrorMessage(this Dictionary securities, HasSufficientBuyingPowerForOrderResult hasSufficientBuyingPowerResult) { return Messages.GroupOrderExtensions.InsufficientBuyingPowerForOrders(securities, hasSufficientBuyingPowerResult); } /// /// Gets the combo order leg group quantity, that is, the total number of shares to be bought/sold from this leg, /// from its ratio and the group order quantity /// /// The leg ratio /// The group order manager /// The total number of shares to be bought/sold from this leg public static decimal GetOrderLegGroupQuantity(this decimal legRatio, GroupOrderManager groupOrderManager) { return groupOrderManager != null ? legRatio * groupOrderManager.Quantity : legRatio; } /// /// Gets the combo order leg ratio from its group quantity and the group order quantity /// /// /// The total number of shares to be bought/sold from this leg, that is, the result of the let ratio times the group quantity /// /// The group order manager /// The ratio of this combo order leg public static decimal GetOrderLegRatio(this decimal legGroupQuantity, GroupOrderManager groupOrderManager) { return groupOrderManager != null ? legGroupQuantity / groupOrderManager.Quantity : legGroupQuantity; } } }