Skip to content

Commit 102b0e8

Browse files
authored
Feat: additional log (#31)
* feat: add log for unexpected order event test:feat: market long/short 100 trades * feat: skip some event to prevent flooding in log * feat: wrap HandleTradeUpdate in try/catch * refactor: log error in HandleTradeUpdate
1 parent 00f5226 commit 102b0e8

File tree

2 files changed

+108
-57
lines changed

2 files changed

+108
-57
lines changed

QuantConnect.AlpacaBrokerage.Tests/AlpacaBrokerageTests.cs

+38-1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,43 @@ public void LongFromZeroOption(OrderTestParameters parameters)
139139
base.LongFromZero(parameters);
140140
}
141141

142+
[TestCase(100)]
143+
public void PlaceLongThenShortMarketOrderMultipleTime(int amountOfTrade)
144+
{
145+
var GLD = Symbol.Create("GLD", SecurityType.Equity, Market.USA);
146+
var parameters = new MarketOrderTestParameters(GLD);
147+
var resetEvent = new ManualResetEvent(false);
148+
149+
Brokerage.OrdersStatusChanged += (object _, List<OrderEvent> orderEvents) =>
150+
{
151+
var orderEvent = orderEvents[0];
152+
153+
if (orderEvent.Status == OrderStatus.Filled)
154+
{
155+
resetEvent.Set();
156+
}
157+
};
158+
159+
var switcher = default(bool);
160+
do
161+
{
162+
if (!switcher)
163+
{
164+
switcher = true;
165+
PlaceOrderWaitForStatus(parameters.CreateLongOrder(100), parameters.ExpectedStatus);
166+
}
167+
else
168+
{
169+
switcher = false;
170+
PlaceOrderWaitForStatus(parameters.CreateShortMarketOrder(100), parameters.ExpectedStatus);
171+
}
172+
173+
resetEvent.WaitOne(TimeSpan.FromSeconds(60));
174+
resetEvent.Reset();
175+
176+
} while (amountOfTrade-- > 0);
177+
}
178+
142179
[Test, TestCaseSource(nameof(EquityOrderParameters))]
143180
public override void CloseFromLong(OrderTestParameters parameters)
144181
{
@@ -272,7 +309,7 @@ public void PlaceMarketOpenCloseOrder(Order order, bool marketIsOpen)
272309
}
273310
};
274311

275-
312+
276313

277314
if (marketIsOpen)
278315
{

QuantConnect.AlpacaBrokerage/AlpacaBrokerage.cs

+70-56
Original file line numberDiff line numberDiff line change
@@ -394,72 +394,86 @@ public override bool PlaceOrder(Order order)
394394

395395
private void HandleTradeUpdate(ITradeUpdate obj)
396396
{
397-
if (Log.DebuggingEnabled)
398-
{
399-
Log.Debug($"{nameof(AlpacaBrokerage)}.{nameof(HandleTradeUpdate)}: {obj}");
400-
}
401-
402-
var brokerageOrderId = obj.Order.OrderId.ToString();
403-
var newLeanOrderStatus = GetOrderStatus(obj.Event);
404-
if (!TryGetOrRemoveCrossZeroOrder(brokerageOrderId, newLeanOrderStatus, out var leanOrder))
405-
{
406-
leanOrder = _orderProvider.GetOrdersByBrokerageId(brokerageOrderId)?.SingleOrDefault();
407-
}
408-
if (leanOrder == null)
397+
try
409398
{
410-
Log.Error($"{nameof(AlpacaBrokerage)}.{nameof(HandleTradeUpdate)}: order id not found: {obj.Order.OrderId}");
411-
return;
412-
}
399+
if (Log.DebuggingEnabled)
400+
{
401+
Log.Debug($"{nameof(AlpacaBrokerage)}.{nameof(HandleTradeUpdate)}: {obj}");
402+
}
413403

414-
switch (obj.Event)
415-
{
416-
case TradeEvent.New:
417-
case TradeEvent.PendingNew:
418-
// we don't send anything for this event
419-
return;
420-
case TradeEvent.Rejected:
421-
case TradeEvent.Canceled:
422-
case TradeEvent.Replaced:
423-
OnOrderEvent(new OrderEvent(leanOrder, DateTime.UtcNow, OrderFee.Zero, $"{nameof(AlpacaBrokerage)} Order Event") { Status = newLeanOrderStatus });
424-
return;
425-
case TradeEvent.Fill:
426-
case TradeEvent.PartialFill:
427-
break;
428-
default:
404+
var brokerageOrderId = obj.Order.OrderId.ToString();
405+
var newLeanOrderStatus = GetOrderStatus(obj.Event);
406+
if (!TryGetOrRemoveCrossZeroOrder(brokerageOrderId, newLeanOrderStatus, out var leanOrder))
407+
{
408+
leanOrder = _orderProvider.GetOrdersByBrokerageId(brokerageOrderId)?.SingleOrDefault();
409+
}
410+
if (leanOrder == null)
411+
{
412+
Log.Error($"{nameof(AlpacaBrokerage)}.{nameof(HandleTradeUpdate)}: order id not found: {obj.Order.OrderId}");
429413
return;
430-
}
414+
}
431415

432-
var leanSymbol = _symbolMapper.GetLeanSymbol(obj.Order.AssetClass, obj.Order.Symbol);
416+
switch (obj.Event)
417+
{
418+
case TradeEvent.New:
419+
case TradeEvent.PendingNew:
420+
// we don't send anything for this event
421+
return;
422+
case TradeEvent.Rejected:
423+
case TradeEvent.Canceled:
424+
case TradeEvent.Replaced:
425+
OnOrderEvent(new OrderEvent(leanOrder, DateTime.UtcNow, OrderFee.Zero, $"{nameof(AlpacaBrokerage)} Order Event") { Status = newLeanOrderStatus });
426+
return;
427+
case TradeEvent.Fill:
428+
case TradeEvent.PartialFill:
429+
break;
430+
case TradeEvent.Accepted:
431+
case TradeEvent.PendingReplace:
432+
case TradeEvent.PendingCancel:
433+
// Skip this event to avoid flooding logs
434+
return;
435+
default:
436+
Log.Trace($"{nameof(AlpacaBrokerage)}.{nameof(HandleTradeUpdate)}.Event: {obj.Event}. TradeUpdate: {obj}");
437+
return;
438+
}
433439

434-
// alpaca sends the accumulative filled quantity but we need the partial amount for our event
435-
_orderIdToFillQuantity.TryGetValue(leanOrder.Id, out var previouslyFilledAmount);
436-
var accumulativeFilledQuantity = _orderIdToFillQuantity[leanOrder.Id] =
437-
obj.Order.OrderSide == OrderSide.Buy ? obj.Order.FilledQuantity : decimal.Negate(obj.Order.FilledQuantity);
440+
var leanSymbol = _symbolMapper.GetLeanSymbol(obj.Order.AssetClass, obj.Order.Symbol);
438441

439-
if (newLeanOrderStatus.IsClosed())
440-
{
441-
// cleanup
442-
_orderIdToFillQuantity.TryRemove(leanOrder.Id, out _);
443-
}
442+
// alpaca sends the accumulative filled quantity but we need the partial amount for our event
443+
_orderIdToFillQuantity.TryGetValue(leanOrder.Id, out var previouslyFilledAmount);
444+
var accumulativeFilledQuantity = _orderIdToFillQuantity[leanOrder.Id] =
445+
obj.Order.OrderSide == OrderSide.Buy ? obj.Order.FilledQuantity : decimal.Negate(obj.Order.FilledQuantity);
444446

445-
var fee = new OrderFee(new CashAmount(0, Currencies.USD));
446-
if (newLeanOrderStatus == Orders.OrderStatus.Filled)
447-
{
448-
var security = _securityProvider.GetSecurity(leanOrder.Symbol);
449-
fee = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, leanOrder));
450-
}
447+
if (newLeanOrderStatus.IsClosed())
448+
{
449+
// cleanup
450+
_orderIdToFillQuantity.TryRemove(leanOrder.Id, out _);
451+
}
451452

452-
var orderEvent = new OrderEvent(leanOrder, obj.TimestampUtc.HasValue ? obj.TimestampUtc.Value : DateTime.UtcNow, fee)
453-
{
454-
Status = newLeanOrderStatus,
455-
FillPrice = obj.Price ?? 0m,
456-
FillQuantity = accumulativeFilledQuantity - previouslyFilledAmount,
457-
};
453+
var fee = new OrderFee(new CashAmount(0, Currencies.USD));
454+
if (newLeanOrderStatus == Orders.OrderStatus.Filled)
455+
{
456+
var security = _securityProvider.GetSecurity(leanOrder.Symbol);
457+
fee = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, leanOrder));
458+
}
459+
460+
var orderEvent = new OrderEvent(leanOrder, obj.TimestampUtc.HasValue ? obj.TimestampUtc.Value : DateTime.UtcNow, fee)
461+
{
462+
Status = newLeanOrderStatus,
463+
FillPrice = obj.Price ?? 0m,
464+
FillQuantity = accumulativeFilledQuantity - previouslyFilledAmount,
465+
};
458466

459-
// if we filled the order and have another contingent order waiting, submit it
460-
if (!TryHandleRemainingCrossZeroOrder(leanOrder, orderEvent))
467+
// if we filled the order and have another contingent order waiting, submit it
468+
if (!TryHandleRemainingCrossZeroOrder(leanOrder, orderEvent))
469+
{
470+
OnOrderEvent(orderEvent);
471+
}
472+
}
473+
catch (Exception ex)
461474
{
462-
OnOrderEvent(orderEvent);
475+
Log.Error(ex, $"TradeUpdate: {obj}");
476+
throw;
463477
}
464478
}
465479

0 commit comments

Comments
 (0)