Skip to content

Commit

Permalink
Add support for brokerage side new orders (QuantConnect#7228)
Browse files Browse the repository at this point in the history
* Add support for brokerage side new orders

- Add support for brokerage side new order events for liquidation cases

* Minor cash delta fix

* Improve account cash logging

* Fix null reference exception for open orders
  • Loading branch information
Martin-Molinero authored May 2, 2023
1 parent 416d382 commit 884bfe4
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 25 deletions.
23 changes: 23 additions & 0 deletions Brokerages/Brokerage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ public abstract class Brokerage : IBrokerage
/// </summary>
public event EventHandler<OptionNotificationEventArgs> OptionNotification;

/// <summary>
/// Event that fires each time there's a brokerage side generated order
/// </summary>
public event EventHandler<NewBrokerageOrderNotificationEventArgs> NewBrokerageOrderNotification;

/// <summary>
/// Event that fires each time a delisting occurs
/// </summary>
Expand Down Expand Up @@ -208,6 +213,24 @@ protected virtual void OnOptionNotification(OptionNotificationEventArgs e)
}
}

/// <summary>
/// Event invocator for the NewBrokerageOrderNotification event
/// </summary>
/// <param name="e">The NewBrokerageOrderNotification event arguments</param>
protected virtual void OnNewBrokerageOrderNotification(NewBrokerageOrderNotificationEventArgs e)
{
try
{
Log.Debug("Brokerage.OnNewBrokerageOrderNotification(): " + e);

NewBrokerageOrderNotification?.Invoke(this, e);
}
catch (Exception err)
{
Log.Error(err);
}
}

/// <summary>
/// Event invocator for the DelistingNotification event
/// </summary>
Expand Down
39 changes: 39 additions & 0 deletions Common/Brokerages/NewBrokerageOrderNotificationEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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 QuantConnect.Orders;
using QuantConnect.Interfaces;

namespace QuantConnect.Brokerages
{
/// <summary>
/// Event arguments class for the <see cref="IBrokerage.NewBrokerageOrderNotification"/> event
/// </summary>
public class NewBrokerageOrderNotificationEventArgs
{
/// <summary>
/// The new brokerage side generated order
/// </summary>
public Order Order { get; set; }

/// <summary>
/// Creates a new instance
/// </summary>
public NewBrokerageOrderNotificationEventArgs(Order order)
{
Order = order;
}
}
}
5 changes: 5 additions & 0 deletions Common/Interfaces/IBrokerage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public interface IBrokerage : IBrokerageCashSynchronizer, IDisposable
/// </summary>
event EventHandler<OptionNotificationEventArgs> OptionNotification;

/// <summary>
/// Event that fires each time there's a brokerage side generated order
/// </summary>
event EventHandler<NewBrokerageOrderNotificationEventArgs> NewBrokerageOrderNotification;

/// <summary>
/// Event that fires each time a delisting occurs
/// </summary>
Expand Down
5 changes: 1 addition & 4 deletions Engine/Setup/BrokerageSetupHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -526,14 +526,11 @@ protected void GetOpenOrders(IAlgorithm algorithm, IResultHandler resultHandler,
// add options first to ensure raw data normalization mode is set on the equity underlyings
foreach (var order in openOrders.OrderByDescending(x => x.SecurityType))
{
// be sure to assign order IDs such that we increment from the SecurityTransactionManager to avoid ID collisions
order.Id = algorithm.Transactions.GetIncrementOrderId();
transactionHandler.AddOpenOrder(order, algorithm);

Log.Trace($"BrokerageSetupHandler.Setup(): Has open order: {order}");
resultHandler.DebugMessage($"BrokerageSetupHandler.Setup(): Open order detected. Creating order tickets for open order {order.Symbol.Value} with quantity {order.Quantity}. Beware that this order ticket may not accurately reflect the quantity of the order if the open order is partially filled.");

transactionHandler.AddOpenOrder(order, order.ToOrderTicket(algorithm.Transactions));

// verify existing holding security type
if (!supportedSecurityTypes.Contains(order.SecurityType))
{
Expand Down
36 changes: 17 additions & 19 deletions Engine/TransactionHandlers/BrokerageTransactionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ public virtual void Initialize(IAlgorithm algorithm, IBrokerage brokerage, IResu
HandleOptionNotification(e);
};

_brokerage.NewBrokerageOrderNotification += (sender, e) =>
{
AddOpenOrder(e.Order, _algorithm);
};

_brokerage.DelistingNotification += (sender, e) =>
{
HandleDelistingNotification(e);
Expand Down Expand Up @@ -663,12 +668,18 @@ public virtual void ProcessSynchronousEvents()
/// <summary>
/// Register an already open Order
/// </summary>
public void AddOpenOrder(Order order, OrderTicket orderTicket)
public void AddOpenOrder(Order order, IAlgorithm algorithm)
{
order.Id = algorithm.Transactions.GetIncrementOrderId();

var orderTicket = order.ToOrderTicket(algorithm.Transactions);

_openOrders.AddOrUpdate(order.Id, order, (i, o) => order);
_completeOrders.AddOrUpdate(order.Id, order, (i, o) => order);
_openOrderTickets.AddOrUpdate(order.Id, orderTicket);
_completeOrderTickets.AddOrUpdate(order.Id, orderTicket);

Interlocked.Increment(ref _totalOrderCount);
}


Expand Down Expand Up @@ -1237,10 +1248,10 @@ private static void LogOrderEvent(OrderEvent e)
private void HandleAccountChanged(AccountEvent account)
{
// how close are we?
var delta = _algorithm.Portfolio.CashBook[account.CurrencySymbol].Amount - account.CashBalance;
if (delta != 0)
var existingCashBalance = _algorithm.Portfolio.CashBook[account.CurrencySymbol].Amount;
if (existingCashBalance != account.CashBalance)
{
Log.Trace($"BrokerageTransactionHandler.HandleAccountChanged(): {account.CurrencySymbol} Cash Delta: {delta}");
Log.Trace($"BrokerageTransactionHandler.HandleAccountChanged(): {account.CurrencySymbol} Cash Lean: {existingCashBalance} Brokerage: {account.CashBalance}. Will update: {_brokerage.AccountInstantlyUpdated}");
}

// maybe we don't actually want to do this, this data can be delayed. Must be explicitly supported by brokerage
Expand Down Expand Up @@ -1299,7 +1310,7 @@ private void HandleDelistingNotification(DelistingNotificationEventArgs e)

// Create our order and add it
var order = new MarketOrder(security.Symbol, quantity, _algorithm.UtcTime, tag);
AddBrokerageOrder(order);
AddOpenOrder(order, _algorithm);

// Create our fill with the latest price
var fill = new OrderEvent(order, _algorithm.UtcTime, OrderFee.Zero)
Expand Down Expand Up @@ -1421,23 +1432,10 @@ private OptionExerciseOrder GenerateOptionExerciseOrder(Security security, decim
{
// generate new exercise order and ticket for the option
var order = new OptionExerciseOrder(security.Symbol, quantity, CurrentTimeUtc);
AddBrokerageOrder(order);
AddOpenOrder(order, _algorithm);
return order;
}

/// <summary>
/// Helper to process internally created orders for delistings/exercise orders
/// </summary>
/// <param name="order">order to </param>
private void AddBrokerageOrder(Order order)
{
order.Id = _algorithm.Transactions.GetIncrementOrderId();

var ticket = order.ToOrderTicket(_algorithm.Transactions);
AddOpenOrder(order, ticket);
Interlocked.Increment(ref _totalOrderCount);
}

private void EmitOptionNotificationEvents(Security security, OptionExerciseOrder order)
{
// generate the order events reusing the option exercise model
Expand Down
4 changes: 2 additions & 2 deletions Engine/TransactionHandlers/ITransactionHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
Expand Down Expand Up @@ -79,6 +79,6 @@ ConcurrentDictionary<int, OrderTicket> OrderTickets
/// <summary>
/// Register an already open Order
/// </summary>
void AddOpenOrder(Order order, OrderTicket orderTicket);
void AddOpenOrder(Order order, IAlgorithm algorithm);
}
}
1 change: 1 addition & 0 deletions Tests/Algorithm/AlgorithmLiveTradingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public virtual void Dispose() {}
public event EventHandler<BrokerageMessageEvent> Message;
public event EventHandler<DelistingNotificationEventArgs> DelistingNotification;
public event EventHandler<BrokerageOrderIdChangedEvent> OrderIdChanged;
public event EventHandler<NewBrokerageOrderNotificationEventArgs> NewBrokerageOrderNotification;
#pragma warning restore 0067

public string Name => "NullBrokerage";
Expand Down
1 change: 1 addition & 0 deletions Tests/Engine/Setup/BrokerageSetupHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public void Setup()
_brokerage = new TestBrokerage();

_brokerageSetupHandler = new TestableBrokerageSetupHandler();
_transactionHandler.Initialize(_algorithm, _brokerage, _resultHandler);
}

[OneTimeTearDown]
Expand Down

0 comments on commit 884bfe4

Please sign in to comment.