From 3c8bf6d7bc5f6427be818bd5cca6252ab44ae976 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 12 Oct 2026 17:37:55 -0400 Subject: [PATCH 1/8] Add OrderEvent.Tag, Time and OrderType, and OrderFee.Amount Python users reach for the Order property names on order events and fees, failing with attribute errors. OrderEvent gets Tag and Time as synonyms of Message and UtcTime, an OrderType property populated from the order on every path and serialized as orderType, and OrderFee gets Amount as a synonym of Value.Amount. OrderType is nullable so events serialized before this change read back as null instead of Market. --- Common/Orders/Fees/OrderFee.cs | 7 ++ .../OptionExercise/DefaultExerciseModel.cs | 5 +- Common/Orders/OrderEvent.cs | 31 +++++++- .../Serialization/SerializedOrderEvent.cs | 7 ++ .../BrokerageTransactionHandler.cs | 1 + Tests/Common/Orders/Fees/OrderFeesTests.cs | 32 ++++++++ Tests/Common/Orders/OrderEventTests.cs | 74 +++++++++++++++++++ .../BrokerageTransactionHandlerTests.cs | 25 +++++++ 8 files changed, 179 insertions(+), 3 deletions(-) diff --git a/Common/Orders/Fees/OrderFee.cs b/Common/Orders/Fees/OrderFee.cs index 0d3d63203029..bd38033e783d 100644 --- a/Common/Orders/Fees/OrderFee.cs +++ b/Common/Orders/Fees/OrderFee.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using Newtonsoft.Json; using ProtoBuf; using QuantConnect.Securities; @@ -30,6 +31,12 @@ public class OrderFee [ProtoMember(1)] public CashAmount Value { get; set; } + /// + /// Gets the order fee amount. Synonym of . + /// + [JsonIgnore] + public decimal Amount => Value.Amount; + /// /// Initializes a new instance of the class /// diff --git a/Common/Orders/OptionExercise/DefaultExerciseModel.cs b/Common/Orders/OptionExercise/DefaultExerciseModel.cs index cb15f9a2925b..5e91bccfee09 100644 --- a/Common/Orders/OptionExercise/DefaultExerciseModel.cs +++ b/Common/Orders/OptionExercise/DefaultExerciseModel.cs @@ -52,7 +52,8 @@ public virtual IEnumerable OptionExercise(Option option, OptionExerc ) { IsAssignment = isAssignment, - IsInTheMoney = inTheMoney + IsInTheMoney = inTheMoney, + OrderType = order.Type }; // TODO : Support Manual Exercise of OTM contracts [ inTheMoney = false ] @@ -70,7 +71,7 @@ public virtual IEnumerable OptionExercise(Option option, OptionExerc exerciseQuantity, OrderFee.Zero, isAssignment ? Messages.DefaultExerciseModel.OptionAssignment : Messages.DefaultExerciseModel.OptionExercise - ) { IsInTheMoney = true }; + ) { IsInTheMoney = true, OrderType = order.Type }; } } } diff --git a/Common/Orders/OrderEvent.cs b/Common/Orders/OrderEvent.cs index 32bc2e2c7fc2..2ecfbebfe536 100644 --- a/Common/Orders/OrderEvent.cs +++ b/Common/Orders/OrderEvent.cs @@ -62,6 +62,16 @@ public class OrderEvent [ProtoMember(4)] public DateTime UtcTime { get; set; } + /// + /// The date and time of this event (UTC). Synonym of + /// + [JsonIgnore] + public DateTime Time + { + get { return UtcTime; } + set { UtcTime = value; } + } + /// /// Status message of the order. /// @@ -112,6 +122,13 @@ public decimal FillQuantity [ProtoMember(10)] public OrderDirection Direction { get; set; } + /// + /// The type of the order this event comes from. Null for events deserialized from payloads that predate this property + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [ProtoMember(20)] + public OrderType? OrderType { get; set; } + /// /// Any message from the exchange. /// @@ -119,6 +136,16 @@ public decimal FillQuantity [ProtoMember(11)] public string Message { get; set; } + /// + /// Any message from the exchange. Synonym of + /// + [JsonIgnore] + public string Tag + { + get { return Message; } + set { Message = value; } + } + /// /// True if the order event is an assignment /// @@ -290,6 +317,7 @@ public OrderEvent(Order order, DateTime utcTime, OrderFee orderFee, string messa Status = order.Status; Quantity = order.Quantity; Direction = order.Direction; + OrderType = order.Type; //Initialize to zero, manually set fill quantity FillQuantity = 0; @@ -348,7 +376,7 @@ public static OrderEvent FromSerialized(SerializedOrderEvent serializedOrderEven var orderEvent = new OrderEvent(serializedOrderEvent.OrderId, symbol, - DateTime.SpecifyKind(Time.UnixTimeStampToDateTime(serializedOrderEvent.Time), DateTimeKind.Utc), + DateTime.SpecifyKind(QuantConnect.Time.UnixTimeStampToDateTime(serializedOrderEvent.Time), DateTimeKind.Utc), serializedOrderEvent.Status, serializedOrderEvent.Direction, serializedOrderEvent.FillPrice, @@ -358,6 +386,7 @@ public static OrderEvent FromSerialized(SerializedOrderEvent serializedOrderEven { IsAssignment = serializedOrderEvent.IsAssignment, IsInTheMoney = serializedOrderEvent.IsInTheMoney, + OrderType = serializedOrderEvent.OrderType, LimitPrice = serializedOrderEvent.LimitPrice, StopPrice = serializedOrderEvent.StopPrice, FillPriceCurrency = serializedOrderEvent.FillPriceCurrency, diff --git a/Common/Orders/Serialization/SerializedOrderEvent.cs b/Common/Orders/Serialization/SerializedOrderEvent.cs index f1df7d9e60b9..d4077f03a601 100644 --- a/Common/Orders/Serialization/SerializedOrderEvent.cs +++ b/Common/Orders/Serialization/SerializedOrderEvent.cs @@ -114,6 +114,12 @@ public class SerializedOrderEvent [JsonProperty("direction"), JsonConverter(typeof(StringEnumConverter), true)] public OrderDirection Direction { get; set; } + /// + /// The type of the order this event comes from. Null for payloads that predate this property + /// + [JsonProperty("orderType", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(StringEnumConverter), true)] + public OrderType? OrderType { get; set; } + /// /// Any message from the exchange. /// @@ -179,6 +185,7 @@ public SerializedOrderEvent(OrderEvent orderEvent, string algorithmId) FillPriceCurrency = orderEvent.FillPriceCurrency; FillQuantity = orderEvent.FillQuantity; Direction = orderEvent.Direction; + OrderType = orderEvent.OrderType; Message = orderEvent.Message; IsAssignment = orderEvent.IsAssignment; IsInTheMoney = orderEvent.IsInTheMoney; diff --git a/Engine/TransactionHandlers/BrokerageTransactionHandler.cs b/Engine/TransactionHandlers/BrokerageTransactionHandler.cs index 7cb7a0b378f9..ba4fbb3fa616 100644 --- a/Engine/TransactionHandlers/BrokerageTransactionHandler.cs +++ b/Engine/TransactionHandlers/BrokerageTransactionHandler.cs @@ -1296,6 +1296,7 @@ private void HandleOrderEvents(List orderEvents) // but the order event symbol is the underlying security, not the option contract. orders.Add(new OpenOrderState(order, ticket, security != null && security.Symbol == orderEvent.Symbol ? security : _algorithm.Securities[orderEvent.Symbol])); orderEvent.Ticket = ticket; + orderEvent.OrderType = order.Type; } var fillsToProcess = new List(orderEvents.Count); diff --git a/Tests/Common/Orders/Fees/OrderFeesTests.cs b/Tests/Common/Orders/Fees/OrderFeesTests.cs index a0babded5bdb..f4a991190954 100644 --- a/Tests/Common/Orders/Fees/OrderFeesTests.cs +++ b/Tests/Common/Orders/Fees/OrderFeesTests.cs @@ -15,6 +15,7 @@ */ using System; +using Newtonsoft.Json; using NUnit.Framework; using Python.Runtime; using QuantConnect.Orders; @@ -48,6 +49,37 @@ public void OrderFeeZeroCanBeConvertedToAccountCurrency() Assert.AreEqual(0, result3); } + [Test] + public void AmountIsASynonymOfValueAmount() + { + var orderFee = new OrderFee(new CashAmount(12.5m, Currencies.USD)); + + Assert.AreEqual(12.5m, orderFee.Amount); + Assert.AreEqual(orderFee.Value.Amount, orderFee.Amount); + // The synonym is not serialized, only Value is + Assert.AreEqual("{\"Value\":{\"Amount\":12.5,\"Currency\":\"USD\"}}", JsonConvert.SerializeObject(orderFee)); + } + + [Test] + public void AmountIsAccessibleFromPython() + { + var orderFee = new OrderFee(new CashAmount(12.5m, Currencies.USD)); + + using (Py.GIL()) + { + var module = PyModule.FromString("testModule", + @" +from AlgorithmImports import * + +def get_amount(order_fee: OrderFee): + return order_fee.amount +"); + + dynamic getAmount = module.GetAttr("get_amount"); + Assert.AreEqual(12.5m, (decimal)getAmount(orderFee)); + } + } + [Test] public void OrderFeeIsNumericFromPython() { diff --git a/Tests/Common/Orders/OrderEventTests.cs b/Tests/Common/Orders/OrderEventTests.cs index b0ec564f98e0..a48aa4d8f0f6 100644 --- a/Tests/Common/Orders/OrderEventTests.cs +++ b/Tests/Common/Orders/OrderEventTests.cs @@ -38,6 +38,7 @@ public void JsonIgnores() Assert.IsFalse(json.Contains("LimitPrice", StringComparison.InvariantCulture)); Assert.IsFalse(json.Contains("StopPrice", StringComparison.InvariantCulture)); Assert.IsFalse(json.Contains(value: "IsInTheMoney", StringComparison.InvariantCulture)); + Assert.IsFalse(json.Contains("Tag", StringComparison.InvariantCulture)); json = JsonConvert.SerializeObject(new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero, "This is a message") { @@ -51,6 +52,61 @@ public void JsonIgnores() Assert.IsTrue(json.Contains("LimitPrice", StringComparison.InvariantCulture)); Assert.IsTrue(json.Contains("StopPrice", StringComparison.InvariantCulture)); Assert.IsTrue(json.Contains(value: "IsInTheMoney", StringComparison.InvariantCulture)); + // The Tag and Time synonyms are not serialized, only Message and UtcTime are + Assert.IsFalse(json.Contains("\"Tag\"", StringComparison.InvariantCulture)); + Assert.IsFalse(json.Contains("\"Time\"", StringComparison.InvariantCulture)); + } + + [Test] + public void TagAndTimeAreSynonymsOfMessageAndUtcTime() + { + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); + var utcTime = new DateTime(2024, 3, 27, 18, 45, 19, DateTimeKind.Utc); + var orderEvent = new OrderEvent(order, utcTime, OrderFee.Zero, "This is a message"); + + Assert.AreEqual("This is a message", orderEvent.Tag); + Assert.AreEqual(utcTime, orderEvent.Time); + + orderEvent.Tag = "Another message"; + orderEvent.Time = utcTime.AddHours(1); + + Assert.AreEqual("Another message", orderEvent.Message); + Assert.AreEqual(utcTime.AddHours(1), orderEvent.UtcTime); + } + + [Test] + public void TagAndTimeAreAccessibleFromPython() + { + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); + var utcTime = new DateTime(2024, 3, 27, 18, 45, 19, DateTimeKind.Utc); + var orderEvent = new OrderEvent(order, utcTime, OrderFee.Zero, "This is a message"); + + using (Py.GIL()) + { + var module = PyModule.FromString("testModule", + @" +from AlgorithmImports import * + +def get_tag_and_time(order_event: OrderEvent): + return order_event.tag, order_event.time == order_event.utc_time +"); + + dynamic getTagAndTime = module.GetAttr("get_tag_and_time"); + var result = getTagAndTime(orderEvent); + + Assert.AreEqual("This is a message", (string)result[0]); + Assert.IsTrue((bool)result[1]); + } + } + + [Test] + public void OrderTypeIsTakenFromTheOrder() + { + var order = new StopLimitOrder(Symbols.BTCUSD, 0.123m, 100m, 101m, DateTime.UtcNow); + var orderEvent = new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero); + + Assert.AreEqual(OrderType.StopLimit, orderEvent.OrderType); + Assert.AreEqual(order.Type, orderEvent.OrderType); } [Test] @@ -61,6 +117,7 @@ public void RoundTripSerialization([Values] bool isInTheMoney) { Message = "Pepe", Status = OrderStatus.PartiallyFilled, + OrderType = OrderType.StopLimit, StopPrice = 1, LimitPrice = 2, FillPrice = 11, @@ -79,8 +136,10 @@ public void RoundTripSerialization([Values] bool isInTheMoney) Assert.IsFalse(serializeObject.Contains("order-fee-amount", StringComparison.InvariantCulture)); Assert.IsFalse(serializeObject.Contains("order-fee-currency", StringComparison.InvariantCulture)); Assert.AreEqual(isInTheMoney, serializeObject.Contains("isInTheMoney", StringComparison.InvariantCulture)); + Assert.IsTrue(serializeObject.Contains("\"orderType\":\"stopLimit\"", StringComparison.InvariantCulture)); var deserializeObject = JsonConvert.DeserializeObject(serializeObject, converter); + Assert.AreEqual(orderEvent.OrderType, deserializeObject.OrderType); Assert.AreEqual(orderEvent.Symbol, deserializeObject.Symbol); Assert.AreEqual(orderEvent.StopPrice, deserializeObject.StopPrice); @@ -169,6 +228,21 @@ public void BackwardsCompatibleDeserialization() Assert.AreEqual(orderEvent.OrderFee.Value.Amount, deserializeObject.OrderFee.Value.Amount); Assert.AreEqual(orderEvent.OrderFee.Value.Currency, deserializeObject.OrderFee.Value.Currency); + // payloads that predate the order type must not be read back as market orders + Assert.IsNull(deserializeObject.OrderType); + } + + [Test] + public void UnknownOrderTypeIsNotSerialized() + { + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); + var orderEvent = new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero) { OrderType = null }; + + var json = JsonConvert.SerializeObject(orderEvent, new OrderEventJsonConverter("id")); + Assert.IsFalse(json.Contains("orderType", StringComparison.InvariantCulture)); + + json = JsonConvert.SerializeObject(orderEvent); + Assert.IsFalse(json.Contains("OrderType", StringComparison.InvariantCulture)); } [Test] diff --git a/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs b/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs index c15dc19feff8..7534be0afca0 100644 --- a/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs +++ b/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs @@ -1465,6 +1465,31 @@ public void SlowOnOrderEventHandlerWarnsOnlyOnce() Assert.AreEqual(1, _algorithm.DebugMessages.Count(message => message.Contains("OnOrderEvent"))); } + [Test] + public void BrokerageOrderEventsCarryTheOrderType() + { + _transactionHandler = new TestBrokerageTransactionHandler(); + using var brokerage = new NoSubmitTestBrokerage(_algorithm); + _transactionHandler.Initialize(_algorithm, brokerage, new BacktestingResultHandler()); + + var security = _algorithm.Securities[_symbol]; + var price = 1.12m; + security.SetMarketPrice(new Tick(DateTime.Now, security.Symbol, price, price, price)); + var orderRequest = new SubmitOrderRequest(OrderType.Limit, security.Type, security.Symbol, 1000, 0, 1.11m, DateTime.Now, ""); + _algorithm.Transactions.SetOrderProcessor(_transactionHandler); + _transactionHandler.Process(orderRequest); + _transactionHandler.PumpPendingRequests(); + + // brokerages building events from the order id alone do not know the order type + var order = _algorithm.Transactions.GetOpenOrders().Single(); + brokerage.PublishOrderEvent(new OrderEvent(order.Id, order.Symbol, _algorithm.UtcTime, OrderStatus.Submitted, + order.Direction, 0, 0, OrderFee.Zero)); + + var orderEvent = _algorithm.OrderEvents.Single(orderEvent => orderEvent.Status == OrderStatus.Submitted); + Assert.AreEqual(OrderType.Limit, orderEvent.OrderType); + Assert.AreEqual(order.Type, orderEvent.OrderType); + } + [Test] public void CancelOrderRequestShouldFailForFilledOrder() { From 029f2bf52d916676fb9fbec1e40bf13ef5502ac5 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 14 Oct 2026 14:28:31 -0400 Subject: [PATCH 2/8] Add Order.GroupId, OrderEvent.GroupId, OrderFee.Currency and combo ticket helpers Combo order legs expose their group id directly on the order and on the order event, which the transaction handler stamps from the order and which serializes as groupId. GroupOrderExtensions gains GetGroupId and IsFilled on the leg ticket collections returned by the combo order methods, and OrderFee gets a Currency synonym next to Amount. --- Common/Orders/Fees/OrderFee.cs | 6 +++ Common/Orders/GroupOrderExtensions.cs | 30 +++++++++++++ Common/Orders/Order.cs | 7 +++ Common/Orders/OrderEvent.cs | 9 ++++ .../Serialization/SerializedOrderEvent.cs | 7 +++ .../BrokerageTransactionHandler.cs | 1 + Tests/Common/Orders/Fees/OrderFeesTests.cs | 4 +- .../Orders/GroupOrderExtensionsTests.cs | 45 +++++++++++++++++++ Tests/Common/Orders/OrderEventTests.cs | 18 +++++++- Tests/Common/Orders/OrderTests.cs | 10 +++++ .../BrokerageTransactionHandlerTests.cs | 41 +++++++++++++++++ 11 files changed, 176 insertions(+), 2 deletions(-) diff --git a/Common/Orders/Fees/OrderFee.cs b/Common/Orders/Fees/OrderFee.cs index bd38033e783d..a05fd0bc32a2 100644 --- a/Common/Orders/Fees/OrderFee.cs +++ b/Common/Orders/Fees/OrderFee.cs @@ -37,6 +37,12 @@ public class OrderFee [JsonIgnore] public decimal Amount => Value.Amount; + /// + /// Gets the order fee currency. Synonym of . + /// + [JsonIgnore] + public string Currency => Value.Currency; + /// /// Initializes a new instance of the class /// diff --git a/Common/Orders/GroupOrderExtensions.cs b/Common/Orders/GroupOrderExtensions.cs index 21848d8c62b1..a0e1deb13c58 100644 --- a/Common/Orders/GroupOrderExtensions.cs +++ b/Common/Orders/GroupOrderExtensions.cs @@ -156,5 +156,35 @@ public static decimal GetGroupQuantityByEachLegQuantity(IEnumerable leg _ => throw new ArgumentException($"Unsupported {nameof(OrderDirection)} value: '{orderDirection}'.", nameof(orderDirection)) }; } + + /// + /// Gets the id of the group of orders the given combo order leg tickets belong to, null if there are no tickets + /// + /// The combo order leg tickets + /// The group order manager id of the first ticket + public static int? GetGroupId(this IEnumerable tickets) + { + return tickets.FirstOrDefault()?.SubmitRequest?.GroupOrderManager?.Id; + } + + /// + /// Determines whether every one of the given combo order leg tickets is filled + /// + /// The combo order leg tickets + /// True if there is at least one ticket and all of them are filled + public static bool IsFilled(this IEnumerable tickets) + { + var any = false; + foreach (var ticket in tickets) + { + any = true; + if (ticket.Status != OrderStatus.Filled) + { + return false; + } + } + + return any; + } } } diff --git a/Common/Orders/Order.cs b/Common/Orders/Order.cs index b98c26895eb5..8b48ee7d9a0e 100644 --- a/Common/Orders/Order.cs +++ b/Common/Orders/Order.cs @@ -232,6 +232,13 @@ public bool IsMarketable [JsonProperty(PropertyName = "groupOrderManager", DefaultValueHandling = DefaultValueHandling.Ignore)] public GroupOrderManager GroupOrderManager { get; set; } + /// + /// The id of the group of orders this order belongs to if it is a combo order leg, null otherwise. + /// Synonym of . + /// + [JsonIgnore] + public int? GroupId => GroupOrderManager?.Id; + /// /// The contingency of this order, if any: the set of contingent orders it belongs to (OCO, OTO, OUO, brackets) /// and the links defining how it relates to the rest of the orders in the set diff --git a/Common/Orders/OrderEvent.cs b/Common/Orders/OrderEvent.cs index 2ecfbebfe536..d576a2c1bb8b 100644 --- a/Common/Orders/OrderEvent.cs +++ b/Common/Orders/OrderEvent.cs @@ -129,6 +129,13 @@ public decimal FillQuantity [ProtoMember(20)] public OrderType? OrderType { get; set; } + /// + /// The id of the group of orders the order belongs to if it is a combo order leg, null otherwise + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [ProtoMember(21)] + public int? GroupId { get; set; } + /// /// Any message from the exchange. /// @@ -318,6 +325,7 @@ public OrderEvent(Order order, DateTime utcTime, OrderFee orderFee, string messa Quantity = order.Quantity; Direction = order.Direction; OrderType = order.Type; + GroupId = order.GroupId; //Initialize to zero, manually set fill quantity FillQuantity = 0; @@ -387,6 +395,7 @@ public static OrderEvent FromSerialized(SerializedOrderEvent serializedOrderEven IsAssignment = serializedOrderEvent.IsAssignment, IsInTheMoney = serializedOrderEvent.IsInTheMoney, OrderType = serializedOrderEvent.OrderType, + GroupId = serializedOrderEvent.GroupId, LimitPrice = serializedOrderEvent.LimitPrice, StopPrice = serializedOrderEvent.StopPrice, FillPriceCurrency = serializedOrderEvent.FillPriceCurrency, diff --git a/Common/Orders/Serialization/SerializedOrderEvent.cs b/Common/Orders/Serialization/SerializedOrderEvent.cs index d4077f03a601..297edb0640b4 100644 --- a/Common/Orders/Serialization/SerializedOrderEvent.cs +++ b/Common/Orders/Serialization/SerializedOrderEvent.cs @@ -120,6 +120,12 @@ public class SerializedOrderEvent [JsonProperty("orderType", DefaultValueHandling = DefaultValueHandling.Ignore), JsonConverter(typeof(StringEnumConverter), true)] public OrderType? OrderType { get; set; } + /// + /// The id of the group of orders the order belongs to if it is a combo order leg, null otherwise + /// + [JsonProperty("groupId", DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? GroupId { get; set; } + /// /// Any message from the exchange. /// @@ -186,6 +192,7 @@ public SerializedOrderEvent(OrderEvent orderEvent, string algorithmId) FillQuantity = orderEvent.FillQuantity; Direction = orderEvent.Direction; OrderType = orderEvent.OrderType; + GroupId = orderEvent.GroupId; Message = orderEvent.Message; IsAssignment = orderEvent.IsAssignment; IsInTheMoney = orderEvent.IsInTheMoney; diff --git a/Engine/TransactionHandlers/BrokerageTransactionHandler.cs b/Engine/TransactionHandlers/BrokerageTransactionHandler.cs index ba4fbb3fa616..423a0bacd8e7 100644 --- a/Engine/TransactionHandlers/BrokerageTransactionHandler.cs +++ b/Engine/TransactionHandlers/BrokerageTransactionHandler.cs @@ -1297,6 +1297,7 @@ private void HandleOrderEvents(List orderEvents) orders.Add(new OpenOrderState(order, ticket, security != null && security.Symbol == orderEvent.Symbol ? security : _algorithm.Securities[orderEvent.Symbol])); orderEvent.Ticket = ticket; orderEvent.OrderType = order.Type; + orderEvent.GroupId = order.GroupId; } var fillsToProcess = new List(orderEvents.Count); diff --git a/Tests/Common/Orders/Fees/OrderFeesTests.cs b/Tests/Common/Orders/Fees/OrderFeesTests.cs index f4a991190954..82848b56c30d 100644 --- a/Tests/Common/Orders/Fees/OrderFeesTests.cs +++ b/Tests/Common/Orders/Fees/OrderFeesTests.cs @@ -50,12 +50,14 @@ public void OrderFeeZeroCanBeConvertedToAccountCurrency() } [Test] - public void AmountIsASynonymOfValueAmount() + public void AmountAndCurrencyAreSynonymsOfTheValueAmountAndCurrency() { var orderFee = new OrderFee(new CashAmount(12.5m, Currencies.USD)); Assert.AreEqual(12.5m, orderFee.Amount); Assert.AreEqual(orderFee.Value.Amount, orderFee.Amount); + Assert.AreEqual(Currencies.USD, orderFee.Currency); + Assert.AreEqual(orderFee.Value.Currency, orderFee.Currency); // The synonym is not serialized, only Value is Assert.AreEqual("{\"Value\":{\"Amount\":12.5,\"Currency\":\"USD\"}}", JsonConvert.SerializeObject(orderFee)); } diff --git a/Tests/Common/Orders/GroupOrderExtensionsTests.cs b/Tests/Common/Orders/GroupOrderExtensionsTests.cs index 94ddbf01edde..8c8f4e8b0de2 100644 --- a/Tests/Common/Orders/GroupOrderExtensionsTests.cs +++ b/Tests/Common/Orders/GroupOrderExtensionsTests.cs @@ -50,5 +50,50 @@ public void GetGroupQuantityByEachLegQuantityShouldReturnExpectedGCD(Leg[] legs, } private static Leg[] CreateLegs(params int[] quantities) => [.. quantities.Select(q => Leg.Create(null, q))]; + + [Test] + public void GetGroupIdOfTheLegTickets() + { + var groupOrderManager = new GroupOrderManager(33, 2, 10); + var tickets = new List + { + CreateLegTicket(groupOrderManager, Symbols.SPY_C_192_Feb19_2016, 10, OrderStatus.Submitted, 1), + CreateLegTicket(groupOrderManager, Symbols.SPY_P_192_Feb19_2016, -10, OrderStatus.Submitted, 2) + }; + + Assert.AreEqual(33, tickets.GetGroupId()); + Assert.IsNull(new List().GetGroupId()); + } + + [Test] + public void IsFilledOnlyWhenEveryLegTicketIsFilled() + { + var groupOrderManager = new GroupOrderManager(33, 2, 10); + var tickets = new List + { + CreateLegTicket(groupOrderManager, Symbols.SPY_C_192_Feb19_2016, 10, OrderStatus.Filled, 1), + CreateLegTicket(groupOrderManager, Symbols.SPY_P_192_Feb19_2016, -10, OrderStatus.PartiallyFilled, 2) + }; + Assert.IsFalse(tickets.IsFilled()); + + tickets[1] = CreateLegTicket(groupOrderManager, Symbols.SPY_P_192_Feb19_2016, -10, OrderStatus.Filled, 2); + Assert.IsTrue(tickets.IsFilled()); + + Assert.IsFalse(new List().IsFilled()); + } + + private static OrderTicket CreateLegTicket(GroupOrderManager groupOrderManager, Symbol symbol, decimal quantity, OrderStatus status, int orderId) + { + var request = new SubmitOrderRequest(OrderType.ComboMarket, symbol.SecurityType, symbol, quantity, 0, 0, + new DateTime(2016, 2, 16, 11, 53, 30), "", groupOrderManager: groupOrderManager); + request.SetOrderId(orderId); + + var ticket = new OrderTicket(null, request); + var order = Order.CreateOrder(request); + order.Status = status; + ticket.SetOrder(order); + + return ticket; + } } } diff --git a/Tests/Common/Orders/OrderEventTests.cs b/Tests/Common/Orders/OrderEventTests.cs index a48aa4d8f0f6..cc638be8855e 100644 --- a/Tests/Common/Orders/OrderEventTests.cs +++ b/Tests/Common/Orders/OrderEventTests.cs @@ -109,6 +109,16 @@ public void OrderTypeIsTakenFromTheOrder() Assert.AreEqual(order.Type, orderEvent.OrderType); } + [Test] + public void GroupIdIsTakenFromTheOrder() + { + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); + Assert.IsNull(new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero).GroupId); + + var comboOrder = new ComboMarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow, new GroupOrderManager(33, 2, 10)); + Assert.AreEqual(33, new OrderEvent(comboOrder, DateTime.UtcNow, OrderFee.Zero).GroupId); + } + [Test] public void RoundTripSerialization([Values] bool isInTheMoney) { @@ -118,6 +128,7 @@ public void RoundTripSerialization([Values] bool isInTheMoney) Message = "Pepe", Status = OrderStatus.PartiallyFilled, OrderType = OrderType.StopLimit, + GroupId = 7, StopPrice = 1, LimitPrice = 2, FillPrice = 11, @@ -137,9 +148,11 @@ public void RoundTripSerialization([Values] bool isInTheMoney) Assert.IsFalse(serializeObject.Contains("order-fee-currency", StringComparison.InvariantCulture)); Assert.AreEqual(isInTheMoney, serializeObject.Contains("isInTheMoney", StringComparison.InvariantCulture)); Assert.IsTrue(serializeObject.Contains("\"orderType\":\"stopLimit\"", StringComparison.InvariantCulture)); + Assert.IsTrue(serializeObject.Contains("\"groupId\":7", StringComparison.InvariantCulture)); var deserializeObject = JsonConvert.DeserializeObject(serializeObject, converter); Assert.AreEqual(orderEvent.OrderType, deserializeObject.OrderType); + Assert.AreEqual(orderEvent.GroupId, deserializeObject.GroupId); Assert.AreEqual(orderEvent.Symbol, deserializeObject.Symbol); Assert.AreEqual(orderEvent.StopPrice, deserializeObject.StopPrice); @@ -230,19 +243,22 @@ public void BackwardsCompatibleDeserialization() Assert.AreEqual(orderEvent.OrderFee.Value.Currency, deserializeObject.OrderFee.Value.Currency); // payloads that predate the order type must not be read back as market orders Assert.IsNull(deserializeObject.OrderType); + Assert.IsNull(deserializeObject.GroupId); } [Test] - public void UnknownOrderTypeIsNotSerialized() + public void NullOrderTypeAndGroupIdAreNotSerialized() { var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); var orderEvent = new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero) { OrderType = null }; var json = JsonConvert.SerializeObject(orderEvent, new OrderEventJsonConverter("id")); Assert.IsFalse(json.Contains("orderType", StringComparison.InvariantCulture)); + Assert.IsFalse(json.Contains("groupId", StringComparison.InvariantCulture)); json = JsonConvert.SerializeObject(orderEvent); Assert.IsFalse(json.Contains("OrderType", StringComparison.InvariantCulture)); + Assert.IsFalse(json.Contains("GroupId", StringComparison.InvariantCulture)); } [Test] diff --git a/Tests/Common/Orders/OrderTests.cs b/Tests/Common/Orders/OrderTests.cs index 585fdf48ee6d..617720c6e22c 100644 --- a/Tests/Common/Orders/OrderTests.cs +++ b/Tests/Common/Orders/OrderTests.cs @@ -238,6 +238,16 @@ private static TestCaseData[] GetValueTestParameters() }.Select(x => new TestCaseData(x).SetName(x.Name)).ToArray(); } + [Test] + public void GroupIdComesFromTheGroupOrderManager() + { + var order = new MarketOrder(Symbols.SPY, 10, DateTime.UtcNow); + Assert.IsNull(order.GroupId); + + var comboOrder = new ComboMarketOrder(Symbols.SPY, 10, DateTime.UtcNow, new GroupOrderManager(33, 2, 10)); + Assert.AreEqual(33, comboOrder.GroupId); + } + public class ValueTestParameters { public string Name { get; init; } diff --git a/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs b/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs index 7534be0afca0..95e7d7ddf635 100644 --- a/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs +++ b/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs @@ -1488,6 +1488,47 @@ public void BrokerageOrderEventsCarryTheOrderType() var orderEvent = _algorithm.OrderEvents.Single(orderEvent => orderEvent.Status == OrderStatus.Submitted); Assert.AreEqual(OrderType.Limit, orderEvent.OrderType); Assert.AreEqual(order.Type, orderEvent.OrderType); + Assert.IsNull(orderEvent.GroupId); + } + + [Test] + public void BrokerageOrderEventsCarryTheGroupId() + { + var algorithm = new TestAlgorithm { HistoryProvider = new EmptyHistoryProvider() }; + algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm)); + algorithm.SetBrokerageModel(BrokerageName.Default); + algorithm.SetCash(1000000); + var index = algorithm.AddIndex("SPX").Symbol; + var option = Symbol.CreateOption(index, Market.USA, OptionStyle.European, OptionRight.Put, 300m, new DateTime(2024, 05, 16)); + algorithm.AddIndexOptionContract(option); + algorithm.SetFinishedWarmingUp(); + + _transactionHandler = new TestBrokerageTransactionHandler(); + using var brokerage = new NoSubmitTestBrokerage(algorithm); + _transactionHandler.Initialize(algorithm, brokerage, new BacktestingResultHandler()); + algorithm.Transactions.SetOrderProcessor(_transactionHandler); + + var dateTime = new DateTime(2024, 05, 14, 12, 0, 0); + var groupOrderManager = new GroupOrderManager(1, 2, 10); + foreach (var (symbol, price) in new[] { (index, 1.12m), (option, 330.12m) }) + { + var security = algorithm.Securities[symbol]; + security.SetMarketPrice(new Tick(dateTime, symbol, price, price, price)); + algorithm.Transactions.AddOrder(new SubmitOrderRequest(OrderType.ComboMarket, security.Type, symbol, 10, 0, 0, dateTime, "", + groupOrderManager: groupOrderManager)); + } + _transactionHandler.PumpPendingRequests(); + + var order = algorithm.Transactions.GetOpenOrders().Single(openOrder => openOrder.Symbol == option); + Assert.AreEqual(1, order.GroupOrderManager.Id); + + // brokerages building events from the order id alone do not know the order group + brokerage.PublishOrderEvent(new OrderEvent(order.Id, order.Symbol, algorithm.UtcTime, OrderStatus.PartiallyFilled, + order.Direction, 330.12m, 1, OrderFee.Zero)); + + var orderEvent = algorithm.OrderEvents.Single(orderEvent => orderEvent.Status == OrderStatus.PartiallyFilled); + Assert.AreEqual(OrderType.ComboMarket, orderEvent.OrderType); + Assert.AreEqual(order.GroupOrderManager.Id, orderEvent.GroupId); } [Test] From 9dd6058c9ba9a717029265e83ab9a6091bd2a0ac Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 14 Oct 2026 17:10:35 -0400 Subject: [PATCH 3/8] Hide the OrderTicket int conversion from the stubs --- Common/Orders/OrderTicket.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Common/Orders/OrderTicket.cs b/Common/Orders/OrderTicket.cs index 1931335ed913..3d16f5a61985 100644 --- a/Common/Orders/OrderTicket.cs +++ b/Common/Orders/OrderTicket.cs @@ -692,6 +692,7 @@ public override string ToString() /// an error, where it will return the integer value of the from /// the most recent response /// + [StubsIgnore] public static implicit operator int(OrderTicket ticket) { var response = ticket.GetMostRecentOrderResponse(); From a0de170d2fb61bc1579605ca5cf4bc270ef4ee1d Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 28 Oct 2026 11:51:24 -0400 Subject: [PATCH 4/8] Hide engine-only extension methods from Python instances with EditorBrowsable --- Common/Data/Auxiliary/PriceScalingExtensions.cs | 3 +++ Common/Data/SubscriptionDataConfigExtensions.cs | 6 ++++++ Common/Extensions.cs | 10 ++++++++++ Common/Orders/ContingentOrderExtensions.cs | 7 +++++++ Common/Orders/GroupOrderExtensions.cs | 2 ++ 5 files changed, 28 insertions(+) diff --git a/Common/Data/Auxiliary/PriceScalingExtensions.cs b/Common/Data/Auxiliary/PriceScalingExtensions.cs index 183fe2bba15a..6559393b85af 100644 --- a/Common/Data/Auxiliary/PriceScalingExtensions.cs +++ b/Common/Data/Auxiliary/PriceScalingExtensions.cs @@ -17,6 +17,7 @@ using System; using System.Linq; using System.Collections.Generic; +using System.ComponentModel; namespace QuantConnect.Data.Auxiliary { @@ -80,6 +81,7 @@ public static decimal GetPriceScale( /// Determines the symbol to use to fetch it's factor file /// /// This is useful for futures where the symbol to use is the canonical + [EditorBrowsable(EditorBrowsableState.Never)] public static Symbol GetFactorFileSymbol(this Symbol symbol) { return symbol.SecurityType == SecurityType.Future ? symbol.Canonical : symbol; @@ -88,6 +90,7 @@ public static Symbol GetFactorFileSymbol(this Symbol symbol) /// /// Helper method to return an empty factor file /// + [EditorBrowsable(EditorBrowsableState.Never)] public static IFactorProvider GetEmptyFactorFile(this Symbol symbol) { if (symbol.SecurityType == SecurityType.Future) diff --git a/Common/Data/SubscriptionDataConfigExtensions.cs b/Common/Data/SubscriptionDataConfigExtensions.cs index 2a47c5788f50..2f5f19cbd209 100644 --- a/Common/Data/SubscriptionDataConfigExtensions.cs +++ b/Common/Data/SubscriptionDataConfigExtensions.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using QuantConnect.Util; @@ -114,6 +115,7 @@ public static void SetDataNormalizationMode( /// One of the objectives of this method is to normalize the 'use mapping' /// check and void code duplication and related issues /// True if ticker should be mapped + [EditorBrowsable(EditorBrowsableState.Never)] public static bool TickerShouldBeMapped(this SubscriptionDataConfig config) { // we create an instance of the data type, if it is a custom type @@ -129,6 +131,7 @@ public static bool TickerShouldBeMapped(this SubscriptionDataConfig config) /// check and void code duplication and related issues /// True, is this is a live mode data stream /// True if ticker prices should be scaled + [EditorBrowsable(EditorBrowsableState.Never)] public static bool PricesShouldBeScaled(this SubscriptionDataConfig config, bool liveMode = false) { if (config.IsCustomData || config.Symbol.Value.Contains("UNIVERSE")) @@ -155,6 +158,7 @@ public static bool PricesShouldBeScaled(this SubscriptionDataConfig config, bool /// Different than because prices could be scale and no split and dividends /// really exist, like in the continuous futures case /// True if this configuration requires split and divided handling + [EditorBrowsable(EditorBrowsableState.Never)] public static bool EmitSplitsAndDividends(this SubscriptionDataConfig config) { return !config.IsCustomData && !config.Symbol.Value.Contains("UNIVERSE") && config.SecurityType == SecurityType.Equity; @@ -163,6 +167,7 @@ public static bool EmitSplitsAndDividends(this SubscriptionDataConfig config) /// /// True if this configuration is associated with an asset which can have delisting events /// + [EditorBrowsable(EditorBrowsableState.Never)] public static bool CanBeDelisted(this SubscriptionDataConfig config) { return config.SecurityType.IsOption() || config.SecurityType == SecurityType.Future || config.SecurityType == SecurityType.Equity; @@ -171,6 +176,7 @@ public static bool CanBeDelisted(this SubscriptionDataConfig config) /// /// Initializes a new instance of the type defined in with the symbol properly set /// + [EditorBrowsable(EditorBrowsableState.Never)] public static BaseData GetBaseDataInstance(this SubscriptionDataConfig config) { var instance = config.Type.GetBaseDataInstance(); diff --git a/Common/Extensions.cs b/Common/Extensions.cs index e8405ee334c6..d3f0a7bde027 100644 --- a/Common/Extensions.cs +++ b/Common/Extensions.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System.ComponentModel; using System; using System.Collections; using System.Collections.Concurrent; @@ -514,6 +515,7 @@ public static void ProtobufSerialize(this IBaseData baseData, Stream stream) /// Extension method to get security price is 0 messages for users /// /// The value of this method is normalization + [EditorBrowsable(EditorBrowsableState.Never)] public static string GetZeroPriceMessage(this Symbol symbol) { return Messages.Extensions.ZeroPriceForSecurity(symbol); @@ -2810,6 +2812,7 @@ public static string ResolutionToLower(this Resolution resolution) /// The being converted /// The transaction manager, /// + [EditorBrowsable(EditorBrowsableState.Never)] public static OrderTicket ToOrderTicket(this Order order, SecurityTransactionManager transactionManager) { var limitPrice = 0m; @@ -3559,6 +3562,7 @@ public static string RemoveFromStart(this string s, string start) /// Helper method to determine symbol for a live subscription /// /// Useful for continuous futures where we subscribe to the underlying + [EditorBrowsable(EditorBrowsableState.Never)] public static bool TryGetLiveSubscriptionSymbol(this Symbol symbol, out Symbol mapped) { mapped = null; @@ -3599,6 +3603,7 @@ public static DateTime GetDelistingDate(this Symbol symbol, MapFile mapFile = nu /// /// Helper method to determine if a given symbol is of custom data /// + [EditorBrowsable(EditorBrowsableState.Never)] public static bool IsCustomDataType(this Symbol symbol) { return symbol.SecurityType == SecurityType.Base @@ -4049,6 +4054,7 @@ public static OrderDirection GetOrderDirection(decimal quantity) /// /// Helper method to process an algorithms security changes, will add and remove securities according to them /// + [EditorBrowsable(EditorBrowsableState.Never)] public static void ProcessSecurityChanges(this IAlgorithm algorithm, SecurityChanges securityChanges) { foreach (var security in securityChanges.AddedSecurities) @@ -4082,6 +4088,7 @@ public static void MakeTradable(this Security security) /// /// Helper method to set an algorithm runtime exception in a normalized fashion /// + [EditorBrowsable(EditorBrowsableState.Never)] public static void SetRuntimeError(this IAlgorithm algorithm, Exception exception, string context) { Log.Error(exception, $"Extensions.SetRuntimeError(): {Messages.Extensions.RuntimeError(algorithm, context)}"); @@ -4216,6 +4223,7 @@ private static IEnumerable CreateFutureChain(this IAlgorithm algorithm /// The found or added security instance /// Callback to invoke in case of unsupported security type /// True if the security was found or added + [EditorBrowsable(EditorBrowsableState.Never)] public static bool GetOrAddUnrequestedSecurity(this IAlgorithm algorithm, Symbol symbol, out Security security, Action> onError = null) { @@ -4340,6 +4348,7 @@ public static SubscriptionDataConfig ToSubscriptionDataConfig(this Data.HistoryR /// dependency with the FF enumerators requiring that they receive aux data to properly handle delistings. /// Otherwise we would have issues with delisted symbols continuing to fill forward after expiry/delisting. /// Reference PR #5485 and related issues for more. + [EditorBrowsable(EditorBrowsableState.Never)] public static bool ShouldEmitData(this SubscriptionDataConfig config, BaseData data, bool isUniverse = false) { // For now we are only filtering Auxiliary data; so if its another type just return true or if it's a margin interest rate which we want to emit always @@ -4494,6 +4503,7 @@ public static int GetListHashCode(this IReadOnlyList list) /// /// Type to check /// True if it needs to be mapped + [EditorBrowsable(EditorBrowsableState.Never)] public static bool RequiresMapping(this Symbol symbol) { switch (symbol.SecurityType) diff --git a/Common/Orders/ContingentOrderExtensions.cs b/Common/Orders/ContingentOrderExtensions.cs index cb0a408220d5..5dc3852fe43d 100644 --- a/Common/Orders/ContingentOrderExtensions.cs +++ b/Common/Orders/ContingentOrderExtensions.cs @@ -17,6 +17,7 @@ using System.Linq; using QuantConnect.Logging; using System.Collections.Generic; +using System.ComponentModel; namespace QuantConnect.Orders { @@ -113,6 +114,7 @@ public static bool IsSameGroupOrder(this Order order, Order other) /// List of orders in the set, sorted by id /// False if any of the orders in the set is not yet found in the order provider. True otherwise /// If the target order is not a contingent order, the resulting list will contain that single order alone + [EditorBrowsable(EditorBrowsableState.Never)] public static bool TryGetContingentOrders(this Order order, Func orderProvider, out List orders) { var contingency = order.Contingency; @@ -166,6 +168,7 @@ public static bool TryGetContingentOrders(this Order order, Func ord /// Target order, which can be any of the orders in the set /// Order provider to use to access the existing orders /// The existing orders of the set, including the given one, sorted by id + [EditorBrowsable(EditorBrowsableState.Never)] public static List GetExistingContingentOrders(this Order order, Func orderProvider) { var contingency = order.Contingency; @@ -196,6 +199,7 @@ public static List GetExistingContingentOrders(this Order order, Func /// The parent order /// The orders in the set + [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable GetContingentChildren(this Order order, IEnumerable contingentOrders) { var parent = order.GetContingencyLink(ContingencyRole.Parent); @@ -211,6 +215,7 @@ public static IEnumerable GetContingentChildren(this Order order, IEnumer /// /// The child order /// The orders in the set + [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable GetContingentParents(this Order order, IEnumerable contingentOrders) { var child = order.GetContingencyLink(ContingencyRole.Child); @@ -227,6 +232,7 @@ public static IEnumerable GetContingentParents(this Order order, IEnumera /// /// The member order /// The orders in the set + [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable GetContingentSiblings(this Order order, IEnumerable contingentOrders) { if (order.GetSiblingLink() == null) @@ -241,6 +247,7 @@ public static IEnumerable GetContingentSiblings(this Order order, IEnumer /// /// The parent order /// The orders in the set + [EditorBrowsable(EditorBrowsableState.Never)] public static List GetContingentDescendants(this Order order, IReadOnlyCollection contingentOrders) { var result = new List(); diff --git a/Common/Orders/GroupOrderExtensions.cs b/Common/Orders/GroupOrderExtensions.cs index a0e1deb13c58..65c5216cbc6a 100644 --- a/Common/Orders/GroupOrderExtensions.cs +++ b/Common/Orders/GroupOrderExtensions.cs @@ -18,6 +18,7 @@ using QuantConnect.Logging; using QuantConnect.Securities; using System.Collections.Generic; +using System.ComponentModel; namespace QuantConnect.Orders { @@ -34,6 +35,7 @@ public static class GroupOrderExtensions /// 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 + [EditorBrowsable(EditorBrowsableState.Never)] public static bool TryGetGroupOrders(this Order order, Func orderProvider, out List orders) { orders = new List { order }; From 48f3962d4618fa70701dbd82d9862f671946d491 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 29 Oct 2026 11:58:58 -0400 Subject: [PATCH 5/8] Revert "Hide engine-only extension methods from Python instances with EditorBrowsable" This reverts commit a0de170d2fb61bc1579605ca5cf4bc270ef4ee1d. --- Common/Data/Auxiliary/PriceScalingExtensions.cs | 3 --- Common/Data/SubscriptionDataConfigExtensions.cs | 6 ------ Common/Extensions.cs | 10 ---------- Common/Orders/ContingentOrderExtensions.cs | 7 ------- Common/Orders/GroupOrderExtensions.cs | 2 -- 5 files changed, 28 deletions(-) diff --git a/Common/Data/Auxiliary/PriceScalingExtensions.cs b/Common/Data/Auxiliary/PriceScalingExtensions.cs index 6559393b85af..183fe2bba15a 100644 --- a/Common/Data/Auxiliary/PriceScalingExtensions.cs +++ b/Common/Data/Auxiliary/PriceScalingExtensions.cs @@ -17,7 +17,6 @@ using System; using System.Linq; using System.Collections.Generic; -using System.ComponentModel; namespace QuantConnect.Data.Auxiliary { @@ -81,7 +80,6 @@ public static decimal GetPriceScale( /// Determines the symbol to use to fetch it's factor file /// /// This is useful for futures where the symbol to use is the canonical - [EditorBrowsable(EditorBrowsableState.Never)] public static Symbol GetFactorFileSymbol(this Symbol symbol) { return symbol.SecurityType == SecurityType.Future ? symbol.Canonical : symbol; @@ -90,7 +88,6 @@ public static Symbol GetFactorFileSymbol(this Symbol symbol) /// /// Helper method to return an empty factor file /// - [EditorBrowsable(EditorBrowsableState.Never)] public static IFactorProvider GetEmptyFactorFile(this Symbol symbol) { if (symbol.SecurityType == SecurityType.Future) diff --git a/Common/Data/SubscriptionDataConfigExtensions.cs b/Common/Data/SubscriptionDataConfigExtensions.cs index 2f5f19cbd209..2a47c5788f50 100644 --- a/Common/Data/SubscriptionDataConfigExtensions.cs +++ b/Common/Data/SubscriptionDataConfigExtensions.cs @@ -14,7 +14,6 @@ */ using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using QuantConnect.Util; @@ -115,7 +114,6 @@ public static void SetDataNormalizationMode( /// One of the objectives of this method is to normalize the 'use mapping' /// check and void code duplication and related issues /// True if ticker should be mapped - [EditorBrowsable(EditorBrowsableState.Never)] public static bool TickerShouldBeMapped(this SubscriptionDataConfig config) { // we create an instance of the data type, if it is a custom type @@ -131,7 +129,6 @@ public static bool TickerShouldBeMapped(this SubscriptionDataConfig config) /// check and void code duplication and related issues /// True, is this is a live mode data stream /// True if ticker prices should be scaled - [EditorBrowsable(EditorBrowsableState.Never)] public static bool PricesShouldBeScaled(this SubscriptionDataConfig config, bool liveMode = false) { if (config.IsCustomData || config.Symbol.Value.Contains("UNIVERSE")) @@ -158,7 +155,6 @@ public static bool PricesShouldBeScaled(this SubscriptionDataConfig config, bool /// Different than because prices could be scale and no split and dividends /// really exist, like in the continuous futures case /// True if this configuration requires split and divided handling - [EditorBrowsable(EditorBrowsableState.Never)] public static bool EmitSplitsAndDividends(this SubscriptionDataConfig config) { return !config.IsCustomData && !config.Symbol.Value.Contains("UNIVERSE") && config.SecurityType == SecurityType.Equity; @@ -167,7 +163,6 @@ public static bool EmitSplitsAndDividends(this SubscriptionDataConfig config) /// /// True if this configuration is associated with an asset which can have delisting events /// - [EditorBrowsable(EditorBrowsableState.Never)] public static bool CanBeDelisted(this SubscriptionDataConfig config) { return config.SecurityType.IsOption() || config.SecurityType == SecurityType.Future || config.SecurityType == SecurityType.Equity; @@ -176,7 +171,6 @@ public static bool CanBeDelisted(this SubscriptionDataConfig config) /// /// Initializes a new instance of the type defined in with the symbol properly set /// - [EditorBrowsable(EditorBrowsableState.Never)] public static BaseData GetBaseDataInstance(this SubscriptionDataConfig config) { var instance = config.Type.GetBaseDataInstance(); diff --git a/Common/Extensions.cs b/Common/Extensions.cs index d3f0a7bde027..e8405ee334c6 100644 --- a/Common/Extensions.cs +++ b/Common/Extensions.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.ComponentModel; using System; using System.Collections; using System.Collections.Concurrent; @@ -515,7 +514,6 @@ public static void ProtobufSerialize(this IBaseData baseData, Stream stream) /// Extension method to get security price is 0 messages for users /// /// The value of this method is normalization - [EditorBrowsable(EditorBrowsableState.Never)] public static string GetZeroPriceMessage(this Symbol symbol) { return Messages.Extensions.ZeroPriceForSecurity(symbol); @@ -2812,7 +2810,6 @@ public static string ResolutionToLower(this Resolution resolution) /// The being converted /// The transaction manager, /// - [EditorBrowsable(EditorBrowsableState.Never)] public static OrderTicket ToOrderTicket(this Order order, SecurityTransactionManager transactionManager) { var limitPrice = 0m; @@ -3562,7 +3559,6 @@ public static string RemoveFromStart(this string s, string start) /// Helper method to determine symbol for a live subscription /// /// Useful for continuous futures where we subscribe to the underlying - [EditorBrowsable(EditorBrowsableState.Never)] public static bool TryGetLiveSubscriptionSymbol(this Symbol symbol, out Symbol mapped) { mapped = null; @@ -3603,7 +3599,6 @@ public static DateTime GetDelistingDate(this Symbol symbol, MapFile mapFile = nu /// /// Helper method to determine if a given symbol is of custom data /// - [EditorBrowsable(EditorBrowsableState.Never)] public static bool IsCustomDataType(this Symbol symbol) { return symbol.SecurityType == SecurityType.Base @@ -4054,7 +4049,6 @@ public static OrderDirection GetOrderDirection(decimal quantity) /// /// Helper method to process an algorithms security changes, will add and remove securities according to them /// - [EditorBrowsable(EditorBrowsableState.Never)] public static void ProcessSecurityChanges(this IAlgorithm algorithm, SecurityChanges securityChanges) { foreach (var security in securityChanges.AddedSecurities) @@ -4088,7 +4082,6 @@ public static void MakeTradable(this Security security) /// /// Helper method to set an algorithm runtime exception in a normalized fashion /// - [EditorBrowsable(EditorBrowsableState.Never)] public static void SetRuntimeError(this IAlgorithm algorithm, Exception exception, string context) { Log.Error(exception, $"Extensions.SetRuntimeError(): {Messages.Extensions.RuntimeError(algorithm, context)}"); @@ -4223,7 +4216,6 @@ private static IEnumerable CreateFutureChain(this IAlgorithm algorithm /// The found or added security instance /// Callback to invoke in case of unsupported security type /// True if the security was found or added - [EditorBrowsable(EditorBrowsableState.Never)] public static bool GetOrAddUnrequestedSecurity(this IAlgorithm algorithm, Symbol symbol, out Security security, Action> onError = null) { @@ -4348,7 +4340,6 @@ public static SubscriptionDataConfig ToSubscriptionDataConfig(this Data.HistoryR /// dependency with the FF enumerators requiring that they receive aux data to properly handle delistings. /// Otherwise we would have issues with delisted symbols continuing to fill forward after expiry/delisting. /// Reference PR #5485 and related issues for more. - [EditorBrowsable(EditorBrowsableState.Never)] public static bool ShouldEmitData(this SubscriptionDataConfig config, BaseData data, bool isUniverse = false) { // For now we are only filtering Auxiliary data; so if its another type just return true or if it's a margin interest rate which we want to emit always @@ -4503,7 +4494,6 @@ public static int GetListHashCode(this IReadOnlyList list) /// /// Type to check /// True if it needs to be mapped - [EditorBrowsable(EditorBrowsableState.Never)] public static bool RequiresMapping(this Symbol symbol) { switch (symbol.SecurityType) diff --git a/Common/Orders/ContingentOrderExtensions.cs b/Common/Orders/ContingentOrderExtensions.cs index 5dc3852fe43d..cb0a408220d5 100644 --- a/Common/Orders/ContingentOrderExtensions.cs +++ b/Common/Orders/ContingentOrderExtensions.cs @@ -17,7 +17,6 @@ using System.Linq; using QuantConnect.Logging; using System.Collections.Generic; -using System.ComponentModel; namespace QuantConnect.Orders { @@ -114,7 +113,6 @@ public static bool IsSameGroupOrder(this Order order, Order other) /// List of orders in the set, sorted by id /// False if any of the orders in the set is not yet found in the order provider. True otherwise /// If the target order is not a contingent order, the resulting list will contain that single order alone - [EditorBrowsable(EditorBrowsableState.Never)] public static bool TryGetContingentOrders(this Order order, Func orderProvider, out List orders) { var contingency = order.Contingency; @@ -168,7 +166,6 @@ public static bool TryGetContingentOrders(this Order order, Func ord /// Target order, which can be any of the orders in the set /// Order provider to use to access the existing orders /// The existing orders of the set, including the given one, sorted by id - [EditorBrowsable(EditorBrowsableState.Never)] public static List GetExistingContingentOrders(this Order order, Func orderProvider) { var contingency = order.Contingency; @@ -199,7 +196,6 @@ public static List GetExistingContingentOrders(this Order order, Func /// The parent order /// The orders in the set - [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable GetContingentChildren(this Order order, IEnumerable contingentOrders) { var parent = order.GetContingencyLink(ContingencyRole.Parent); @@ -215,7 +211,6 @@ public static IEnumerable GetContingentChildren(this Order order, IEnumer /// /// The child order /// The orders in the set - [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable GetContingentParents(this Order order, IEnumerable contingentOrders) { var child = order.GetContingencyLink(ContingencyRole.Child); @@ -232,7 +227,6 @@ public static IEnumerable GetContingentParents(this Order order, IEnumera /// /// The member order /// The orders in the set - [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable GetContingentSiblings(this Order order, IEnumerable contingentOrders) { if (order.GetSiblingLink() == null) @@ -247,7 +241,6 @@ public static IEnumerable GetContingentSiblings(this Order order, IEnumer /// /// The parent order /// The orders in the set - [EditorBrowsable(EditorBrowsableState.Never)] public static List GetContingentDescendants(this Order order, IReadOnlyCollection contingentOrders) { var result = new List(); diff --git a/Common/Orders/GroupOrderExtensions.cs b/Common/Orders/GroupOrderExtensions.cs index 65c5216cbc6a..a0e1deb13c58 100644 --- a/Common/Orders/GroupOrderExtensions.cs +++ b/Common/Orders/GroupOrderExtensions.cs @@ -18,7 +18,6 @@ using QuantConnect.Logging; using QuantConnect.Securities; using System.Collections.Generic; -using System.ComponentModel; namespace QuantConnect.Orders { @@ -35,7 +34,6 @@ public static class GroupOrderExtensions /// 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 - [EditorBrowsable(EditorBrowsableState.Never)] public static bool TryGetGroupOrders(this Order order, Func orderProvider, out List orders) { orders = new List { order }; From 38c45f34e005cbca0def400092c44b9f7ccc8be5 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 29 Oct 2026 11:58:58 -0400 Subject: [PATCH 6/8] Revert "Hide the OrderTicket int conversion from the stubs" This reverts commit 9dd6058c9ba9a717029265e83ab9a6091bd2a0ac. --- Common/Orders/OrderTicket.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Common/Orders/OrderTicket.cs b/Common/Orders/OrderTicket.cs index 3d16f5a61985..1931335ed913 100644 --- a/Common/Orders/OrderTicket.cs +++ b/Common/Orders/OrderTicket.cs @@ -692,7 +692,6 @@ public override string ToString() /// an error, where it will return the integer value of the from /// the most recent response /// - [StubsIgnore] public static implicit operator int(OrderTicket ticket) { var response = ticket.GetMostRecentOrderResponse(); From ec58c00c378118ae3f654bcb22f6bbf05552e6b7 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 30 Oct 2026 17:12:55 -0400 Subject: [PATCH 7/8] Make OrderEvent.Tag the order's tag instead of a Message synonym --- Common/Orders/OrderEvent.cs | 15 +++--- .../BrokerageTransactionHandler.cs | 1 + Tests/Common/Orders/OrderEventTests.cs | 50 +++++++++++++------ .../BrokerageTransactionHandlerTests.cs | 19 +++++-- 4 files changed, 57 insertions(+), 28 deletions(-) diff --git a/Common/Orders/OrderEvent.cs b/Common/Orders/OrderEvent.cs index d576a2c1bb8b..d5d2462e3a91 100644 --- a/Common/Orders/OrderEvent.cs +++ b/Common/Orders/OrderEvent.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -123,7 +123,8 @@ public decimal FillQuantity public OrderDirection Direction { get; set; } /// - /// The type of the order this event comes from. Null for events deserialized from payloads that predate this property + /// The type of the order this event comes from. Null for events built from the order id alone + /// until the transaction handler stamps it, and for payloads that predate this property /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] [ProtoMember(20)] @@ -144,14 +145,11 @@ public decimal FillQuantity public string Message { get; set; } /// - /// Any message from the exchange. Synonym of + /// The tag of the order this event comes from. Null for events built from the order id alone + /// until the transaction handler stamps it, and for deserialized events /// [JsonIgnore] - public string Tag - { - get { return Message; } - set { Message = value; } - } + public string Tag { get; internal set; } /// /// True if the order event is an assignment @@ -326,6 +324,7 @@ public OrderEvent(Order order, DateTime utcTime, OrderFee orderFee, string messa Direction = order.Direction; OrderType = order.Type; GroupId = order.GroupId; + Tag = order.Tag; //Initialize to zero, manually set fill quantity FillQuantity = 0; diff --git a/Engine/TransactionHandlers/BrokerageTransactionHandler.cs b/Engine/TransactionHandlers/BrokerageTransactionHandler.cs index 423a0bacd8e7..4e993c860492 100644 --- a/Engine/TransactionHandlers/BrokerageTransactionHandler.cs +++ b/Engine/TransactionHandlers/BrokerageTransactionHandler.cs @@ -1298,6 +1298,7 @@ private void HandleOrderEvents(List orderEvents) orderEvent.Ticket = ticket; orderEvent.OrderType = order.Type; orderEvent.GroupId = order.GroupId; + orderEvent.Tag = order.Tag; } var fillsToProcess = new List(orderEvents.Count); diff --git a/Tests/Common/Orders/OrderEventTests.cs b/Tests/Common/Orders/OrderEventTests.cs index cc638be8855e..b482a46c3df4 100644 --- a/Tests/Common/Orders/OrderEventTests.cs +++ b/Tests/Common/Orders/OrderEventTests.cs @@ -30,7 +30,7 @@ public class OrderEventTests [Test] public void JsonIgnores() { - var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow, "entry"); var json = JsonConvert.SerializeObject(new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero), new OrderEventJsonConverter("id")); @@ -52,34 +52,48 @@ public void JsonIgnores() Assert.IsTrue(json.Contains("LimitPrice", StringComparison.InvariantCulture)); Assert.IsTrue(json.Contains("StopPrice", StringComparison.InvariantCulture)); Assert.IsTrue(json.Contains(value: "IsInTheMoney", StringComparison.InvariantCulture)); - // The Tag and Time synonyms are not serialized, only Message and UtcTime are + // The order tag and the Time synonym are not serialized, only Message and UtcTime are Assert.IsFalse(json.Contains("\"Tag\"", StringComparison.InvariantCulture)); + Assert.IsFalse(json.Contains("entry", StringComparison.InvariantCulture)); Assert.IsFalse(json.Contains("\"Time\"", StringComparison.InvariantCulture)); } [Test] - public void TagAndTimeAreSynonymsOfMessageAndUtcTime() + public void TagIsTheOrderTagAndNotTheMessage() + { + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow, "entry"); + var orderEvent = new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero, "Filled at stale price"); + + Assert.AreEqual("entry", orderEvent.Tag); + Assert.AreEqual("Filled at stale price", orderEvent.Message); + + // events built from the order id alone do not know the order + var brokerageEvent = new OrderEvent(order.Id, order.Symbol, DateTime.UtcNow, OrderStatus.Filled, order.Direction, 1, 1, + OrderFee.Zero, "Filled at stale price"); + Assert.IsNull(brokerageEvent.Tag); + Assert.AreEqual("Filled at stale price", brokerageEvent.Message); + } + + [Test] + public void TimeIsASynonymOfUtcTime() { var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); var utcTime = new DateTime(2024, 3, 27, 18, 45, 19, DateTimeKind.Utc); - var orderEvent = new OrderEvent(order, utcTime, OrderFee.Zero, "This is a message"); + var orderEvent = new OrderEvent(order, utcTime, OrderFee.Zero); - Assert.AreEqual("This is a message", orderEvent.Tag); Assert.AreEqual(utcTime, orderEvent.Time); - orderEvent.Tag = "Another message"; orderEvent.Time = utcTime.AddHours(1); - Assert.AreEqual("Another message", orderEvent.Message); Assert.AreEqual(utcTime.AddHours(1), orderEvent.UtcTime); } [Test] public void TagAndTimeAreAccessibleFromPython() { - var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow, "entry"); var utcTime = new DateTime(2024, 3, 27, 18, 45, 19, DateTimeKind.Utc); - var orderEvent = new OrderEvent(order, utcTime, OrderFee.Zero, "This is a message"); + var orderEvent = new OrderEvent(order, utcTime, OrderFee.Zero, "Filled at stale price"); using (Py.GIL()) { @@ -87,15 +101,16 @@ public void TagAndTimeAreAccessibleFromPython() @" from AlgorithmImports import * -def get_tag_and_time(order_event: OrderEvent): - return order_event.tag, order_event.time == order_event.utc_time +def get_tag_message_and_time(order_event: OrderEvent): + return order_event.tag, order_event.message, order_event.time == order_event.utc_time "); - dynamic getTagAndTime = module.GetAttr("get_tag_and_time"); - var result = getTagAndTime(orderEvent); + dynamic getTagMessageAndTime = module.GetAttr("get_tag_message_and_time"); + var result = getTagMessageAndTime(orderEvent); - Assert.AreEqual("This is a message", (string)result[0]); - Assert.IsTrue((bool)result[1]); + Assert.AreEqual("entry", (string)result[0]); + Assert.AreEqual("Filled at stale price", (string)result[1]); + Assert.IsTrue((bool)result[2]); } } @@ -122,7 +137,7 @@ public void GroupIdIsTakenFromTheOrder() [Test] public void RoundTripSerialization([Values] bool isInTheMoney) { - var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow); + var order = new MarketOrder(Symbols.BTCUSD, 0.123m, DateTime.UtcNow, "entry"); var orderEvent = new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero) { Message = "Pepe", @@ -153,6 +168,9 @@ public void RoundTripSerialization([Values] bool isInTheMoney) var deserializeObject = JsonConvert.DeserializeObject(serializeObject, converter); Assert.AreEqual(orderEvent.OrderType, deserializeObject.OrderType); Assert.AreEqual(orderEvent.GroupId, deserializeObject.GroupId); + // the order tag is not part of the payload, it comes from the order + Assert.AreEqual("entry", orderEvent.Tag); + Assert.IsNull(deserializeObject.Tag); Assert.AreEqual(orderEvent.Symbol, deserializeObject.Symbol); Assert.AreEqual(orderEvent.StopPrice, deserializeObject.StopPrice); diff --git a/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs b/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs index 95e7d7ddf635..160dcf92d271 100644 --- a/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs +++ b/Tests/Engine/BrokerageTransactionHandlerTests/BrokerageTransactionHandlerTests.cs @@ -1466,7 +1466,7 @@ public void SlowOnOrderEventHandlerWarnsOnlyOnce() } [Test] - public void BrokerageOrderEventsCarryTheOrderType() + public void BrokerageOrderEventsCarryTheOrderTypeAndTag() { _transactionHandler = new TestBrokerageTransactionHandler(); using var brokerage = new NoSubmitTestBrokerage(_algorithm); @@ -1475,20 +1475,31 @@ public void BrokerageOrderEventsCarryTheOrderType() var security = _algorithm.Securities[_symbol]; var price = 1.12m; security.SetMarketPrice(new Tick(DateTime.Now, security.Symbol, price, price, price)); - var orderRequest = new SubmitOrderRequest(OrderType.Limit, security.Type, security.Symbol, 1000, 0, 1.11m, DateTime.Now, ""); + var orderRequest = new SubmitOrderRequest(OrderType.Limit, security.Type, security.Symbol, 1000, 0, 1.11m, DateTime.Now, "entry"); _algorithm.Transactions.SetOrderProcessor(_transactionHandler); _transactionHandler.Process(orderRequest); _transactionHandler.PumpPendingRequests(); - // brokerages building events from the order id alone do not know the order type + // brokerages building events from the order id alone do not know the order type or tag var order = _algorithm.Transactions.GetOpenOrders().Single(); brokerage.PublishOrderEvent(new OrderEvent(order.Id, order.Symbol, _algorithm.UtcTime, OrderStatus.Submitted, - order.Direction, 0, 0, OrderFee.Zero)); + order.Direction, 0, 0, OrderFee.Zero, "Brokerage message")); var orderEvent = _algorithm.OrderEvents.Single(orderEvent => orderEvent.Status == OrderStatus.Submitted); Assert.AreEqual(OrderType.Limit, orderEvent.OrderType); Assert.AreEqual(order.Type, orderEvent.OrderType); Assert.IsNull(orderEvent.GroupId); + Assert.AreEqual("entry", orderEvent.Tag); + Assert.AreEqual("Brokerage message", orderEvent.Message); + + // events after a tag update carry the new tag + _transactionHandler.Process(new UpdateOrderRequest(DateTime.Now, order.Id, new UpdateOrderFields { Tag = "exit" })); + _transactionHandler.PumpPendingRequests(); + brokerage.PublishOrderEvent(new OrderEvent(order.Id, order.Symbol, _algorithm.UtcTime, OrderStatus.Filled, + order.Direction, 1.11m, 1000, OrderFee.Zero)); + + orderEvent = _algorithm.OrderEvents.Single(orderEvent => orderEvent.Status == OrderStatus.Filled); + Assert.AreEqual("exit", orderEvent.Tag); } [Test] From 27132a051464f52ef6eb6ecf4fe1a87d31ac90f9 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 30 Oct 2026 17:47:54 -0400 Subject: [PATCH 8/8] Drop the BOM from OrderEvent.cs --- Common/Orders/OrderEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Orders/OrderEvent.cs b/Common/Orders/OrderEvent.cs index d5d2462e3a91..ba9794666aa4 100644 --- a/Common/Orders/OrderEvent.cs +++ b/Common/Orders/OrderEvent.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. *