Files
2026-07-13 13:02:50 +08:00

109 lines
5.0 KiB
C#

/*
* 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.Collections.Concurrent;
using QuantConnect.Orders;
namespace QuantConnect.Lean.Engine.TransactionHandlers
{
/// <summary>
/// Class used to keep track of CancelPending orders and their original or updated status
/// </summary>
public class CancelPendingOrders
{
private readonly ConcurrentDictionary<int, CancelPendingOrder> _cancelPendingOrders = new ConcurrentDictionary<int, CancelPendingOrder>();
/// <summary>
/// Amount of CancelPending Orders
/// </summary>
public int GetCancelPendingOrdersSize => _cancelPendingOrders.Count;
/// <summary>
/// Adds an order which will be canceled and we want to keep track of it Status in case of fallback
/// </summary>
/// <param name="orderId">The order id</param>
/// <param name="status">The order Status, before the cancel request</param>
public void Set(int orderId, OrderStatus status)
{
_cancelPendingOrders[orderId] = new CancelPendingOrder { Status = status };
}
/// <summary>
/// Updates an order that is pending to be canceled.
/// </summary>
/// <param name="newStatus">The new status of the order. If its OrderStatus.Canceled or OrderStatus.Filled it will be removed</param>
/// <param name="orderId">The id of the order</param>
public void UpdateOrRemove(int orderId, OrderStatus newStatus)
{
CancelPendingOrder cancelPendingOrder;
if (_cancelPendingOrders.TryGetValue(orderId, out cancelPendingOrder))
{
// The purpose of this pattern 'trygetvalue/lock/if containskey' is to guarantee that threads working on the same order will be correctly synchronized
// Thread 1 at HandleCancelOrderRequest() processing a failed cancel request will call RemoveAndFallback() and revert order status
// Thread 2 at HandleOrderEvent() with a filled order event will call UpdateOrRemove() and remove the order, ignoring its 'saved' status.
lock (cancelPendingOrder)
{
if (newStatus.IsClosed())
{
RemoveOrderFromCollection(orderId);
}
else if (newStatus != OrderStatus.CancelPending)
{
// check again because it might of been removed by the failed to cancel event
if (_cancelPendingOrders.ContainsKey(orderId))
{
_cancelPendingOrders[orderId].Status = newStatus;
}
}
}
}
}
/// <summary>
/// Removes an order which we failed to cancel and falls back the order Status to previous value
/// </summary>
/// <param name="order">The order that failed to be canceled</param>
public void RemoveAndFallback(Order order)
{
CancelPendingOrder cancelPendingOrder;
if (_cancelPendingOrders.TryGetValue(order.Id, out cancelPendingOrder))
{
// The purpose of this pattern 'trygetvalue/lock/if containskey' is to guarantee that threads working on the same order will be correctly synchronized
// Thread 1 at HandleCancelOrderRequest() processing a failed cancel request will call RemoveAndFallback() and revert order status
// Thread 2 at HandleOrderEvent() with a filled order event will call UpdateOrRemove() and remove the order, ignoring its 'saved' status.
lock (cancelPendingOrder)
{
if (_cancelPendingOrders.ContainsKey(order.Id))
{
// update Status before removing from _cancelPendingOrders
order.Status = _cancelPendingOrders[order.Id].Status;
RemoveOrderFromCollection(order.Id);
}
}
}
}
private void RemoveOrderFromCollection(int orderId)
{
CancelPendingOrder cancelPendingOrderTrash;
_cancelPendingOrders.TryRemove(orderId, out cancelPendingOrderTrash);
}
private class CancelPendingOrder
{
public OrderStatus Status { get; set; }
}
}
}