2525using System . Collections . Generic ;
2626using System . Linq ;
2727using System . Threading ;
28+ using TradeEvent = Alpaca . Markets . TradeEvent ;
29+ using QuantConnect . Brokerages . Alpaca . Tests . Models ;
2830using static QuantConnect . Brokerages . Alpaca . Tests . AlpacaBrokerageAdditionalTests ;
2931
3032namespace QuantConnect . Brokerages . Alpaca . Tests
3133{
3234 [ TestFixture ]
3335 public partial class AlpacaBrokerageTests : BrokerageTests
3436 {
37+ private TestAlpacaBrokerage AlpacaBrokerage => Brokerage as TestAlpacaBrokerage ;
38+
3539 protected override Symbol Symbol { get ; } = Symbols . AAPL ;
3640 protected override SecurityType SecurityType { get ; }
3741
@@ -46,7 +50,7 @@ protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISec
4650 protected override bool IsAsync ( ) => false ;
4751 protected override decimal GetAskPrice ( Symbol symbol )
4852 {
49- return ( Brokerage as TestAlpacaBrokerage ) . GetLatestQuotePublic ( symbol ) . AskPrice ;
53+ return AlpacaBrokerage . GetLatestQuotePublic ( symbol ) . AskPrice ;
5054 }
5155
5256 /// <summary>
@@ -418,5 +422,215 @@ public void PlaceOutsideRegularHoursLimitOrders()
418422 Assert . IsTrue ( Brokerage . PlaceOrder ( limitOrder ) ) ;
419423 Assert . IsTrue ( submittedOrderEvent . WaitOne ( TimeSpan . FromSeconds ( 5 ) ) ) ;
420424 }
425+
426+ [ Test ]
427+ public void HandleTradeUpdateNormalCase ( )
428+ {
429+ // pending_new -> new -> partial_fill -> fill
430+ // the same to all trade updates
431+ var orderId = Guid . NewGuid ( ) ;
432+
433+ var order = new LimitOrder ( Symbols . AAPL , 3m , 1m , default ) ;
434+ order . BrokerId . Add ( orderId . ToString ( ) ) ;
435+ OrderProvider . Add ( order ) ;
436+
437+ var tradeUpdates = new List < TestTradeUpdate > ( 4 )
438+ {
439+ new ( TradeEvent . PendingNew , null , new TestOrder ( orderId ) ) ,
440+ new ( TradeEvent . New , Guid . NewGuid ( ) , new TestOrder ( orderId ) ) ,
441+ new ( TradeEvent . PartialFill , Guid . NewGuid ( ) , new TestOrder ( orderId , 1 ) ) ,
442+ new ( TradeEvent . Fill , Guid . NewGuid ( ) , new TestOrder ( orderId , 3 ) )
443+ } ;
444+
445+ foreach ( var tradeUpdate in tradeUpdates )
446+ {
447+ AlpacaBrokerage . HandleTradeUpdate ( tradeUpdate ) ;
448+
449+ switch ( tradeUpdate . Event )
450+ {
451+ case TradeEvent . PendingNew :
452+ case TradeEvent . New :
453+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
454+ break ;
455+ case TradeEvent . PartialFill :
456+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
457+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId [ orderId ] . Count ) ;
458+ break ;
459+ case TradeEvent . Fill :
460+ Assert . AreEqual ( 0 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
461+ break ;
462+ }
463+ }
464+ }
465+
466+ [ Test ]
467+ public void HandleTradeUpdateShouldSkipDuplication ( )
468+ {
469+ // pending_new -> new -> partial_fill -> fill
470+ // the same to all trade updates
471+ var orderId = Guid . NewGuid ( ) ;
472+
473+ var order = new LimitOrder ( Symbols . AAPL , 3m , 1m , default ) ;
474+ order . BrokerId . Add ( orderId . ToString ( ) ) ;
475+
476+ OrderProvider . Add ( order ) ;
477+
478+ var tradeUpdates = new List < TestTradeUpdate > ( )
479+ {
480+ new ( TradeEvent . PendingNew , null , new TestOrder ( orderId ) ) ,
481+ new ( TradeEvent . New , Guid . NewGuid ( ) , new TestOrder ( orderId ) )
482+ } ;
483+
484+ var partialFillExecutionId_1 = Guid . NewGuid ( ) ;
485+ var partialFill_1 = new TestTradeUpdate ( TradeEvent . PartialFill , partialFillExecutionId_1 , new TestOrder ( orderId , 1 ) ) ;
486+
487+ tradeUpdates . Add ( partialFill_1 ) ;
488+ tradeUpdates . Add ( partialFill_1 ) ;
489+
490+ var partialFillExecutionId_2 = Guid . NewGuid ( ) ;
491+ var partialFill_2 = new TestTradeUpdate ( TradeEvent . PartialFill , partialFillExecutionId_2 , new TestOrder ( orderId , 2 ) ) ;
492+
493+ tradeUpdates . Add ( partialFill_2 ) ;
494+ tradeUpdates . Add ( partialFill_2 ) ;
495+
496+ var fillExecutionId = Guid . NewGuid ( ) ;
497+ var fill = new TestTradeUpdate ( TradeEvent . Fill , fillExecutionId , new TestOrder ( orderId , 3 ) ) ;
498+
499+ tradeUpdates . Add ( fill ) ;
500+ tradeUpdates . Add ( fill ) ;
501+
502+ foreach ( var tradeUpdate in tradeUpdates )
503+ {
504+ AlpacaBrokerage . HandleTradeUpdate ( tradeUpdate ) ;
505+
506+ switch ( tradeUpdate . Event )
507+ {
508+ case TradeEvent . PendingNew :
509+ case TradeEvent . New :
510+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
511+ break ;
512+ case TradeEvent . PartialFill when tradeUpdate . ExecutionId . Equals ( partialFillExecutionId_1 ) :
513+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
514+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId [ orderId ] . Count ) ;
515+ break ;
516+ case TradeEvent . PartialFill when tradeUpdate . ExecutionId . Equals ( partialFillExecutionId_2 ) :
517+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
518+ Assert . AreEqual ( 2 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId [ orderId ] . Count ) ;
519+ break ;
520+ case TradeEvent . Fill when tradeUpdate . ExecutionId . Equals ( fillExecutionId ) :
521+ Assert . AreEqual ( 0 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
522+ break ;
523+ }
524+ }
525+ }
526+
527+ [ Test ]
528+ public void HandleTradeUpdateShouldSkipCanceledDuplication ( )
529+ {
530+ // pending_new -> new -> partial_fill -> fill
531+ // the same to all trade updates
532+ var orderId = Guid . NewGuid ( ) ;
533+
534+ var order = new LimitOrder ( Symbols . AAPL , 3m , 1m , default ) ;
535+ order . BrokerId . Add ( orderId . ToString ( ) ) ;
536+ OrderProvider . Add ( order ) ;
537+
538+ var tradeUpdates = new List < TestTradeUpdate > ( )
539+ {
540+ new ( TradeEvent . PendingNew , null , new TestOrder ( orderId ) ) ,
541+ new ( TradeEvent . New , Guid . NewGuid ( ) , new TestOrder ( orderId ) )
542+ } ;
543+
544+ var pendingCancelExecutionId = Guid . NewGuid ( ) ;
545+ var pendingCancel = new TestTradeUpdate ( TradeEvent . PendingCancel , pendingCancelExecutionId , new TestOrder ( orderId , 1 ) ) ;
546+
547+ tradeUpdates . Add ( pendingCancel ) ;
548+ tradeUpdates . Add ( pendingCancel ) ;
549+
550+ var cancelExecutionId = Guid . NewGuid ( ) ;
551+ var cancel = new TestTradeUpdate ( TradeEvent . Canceled , cancelExecutionId , new TestOrder ( orderId , 1 ) ) ;
552+
553+ tradeUpdates . Add ( cancel ) ;
554+ tradeUpdates . Add ( cancel ) ;
555+
556+ foreach ( var tradeUpdate in tradeUpdates )
557+ {
558+ AlpacaBrokerage . HandleTradeUpdate ( tradeUpdate ) ;
559+
560+ switch ( tradeUpdate . Event )
561+ {
562+ case TradeEvent . PendingNew :
563+ case TradeEvent . New :
564+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
565+ Assert . AreEqual ( 0 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId [ orderId ] . Count ) ;
566+ break ;
567+ case TradeEvent . PendingCancel :
568+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
569+ Assert . AreEqual ( 0 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId [ orderId ] . Count ) ;
570+ break ;
571+ case TradeEvent . Canceled :
572+ Assert . AreEqual ( 0 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
573+ break ;
574+ }
575+ }
576+ }
577+
578+ [ Test ]
579+ public void HandleTradeUpdateShouldSkipUpdateDuplication ( )
580+ {
581+ // pending_new -> new -> replaced -> canceled
582+ // the same to all trade updates
583+ var orderId = Guid . NewGuid ( ) ;
584+
585+ var order = new LimitOrder ( Symbols . AAPL , 3m , 1m , default ) ;
586+ order . BrokerId . Add ( orderId . ToString ( ) ) ;
587+ OrderProvider . Add ( order ) ;
588+
589+ // Call: GetOpenOrders() has added already order
590+ AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId [ orderId ] = [ ] ;
591+
592+ var tradeUpdates = new List < TestTradeUpdate > ( )
593+ {
594+ } ;
595+
596+ // Call: UpdateOrder
597+ var oldOrderId = orderId ;
598+ orderId = Guid . NewGuid ( ) ;
599+ order . BrokerId . Add ( orderId . ToString ( ) ) ;
600+
601+ var replacedExecutionId = Guid . NewGuid ( ) ;
602+ var replaced = new TestTradeUpdate ( TradeEvent . Replaced , replacedExecutionId , new TestOrder ( oldOrderId , 0 ) { ReplacedByOrderId = orderId } ) ;
603+
604+ tradeUpdates . Add ( replaced ) ;
605+ tradeUpdates . Add ( replaced ) ;
606+
607+ var pendingCancelExecutionId = Guid . NewGuid ( ) ;
608+ var pendingCancel = new TestTradeUpdate ( TradeEvent . PendingCancel , pendingCancelExecutionId , new TestOrder ( orderId , 1 ) ) ;
609+
610+ tradeUpdates . Add ( pendingCancel ) ;
611+ tradeUpdates . Add ( pendingCancel ) ;
612+
613+ var cancelExecutionId = Guid . NewGuid ( ) ;
614+ var cancel = new TestTradeUpdate ( TradeEvent . Canceled , cancelExecutionId , new TestOrder ( orderId , 1 ) ) ;
615+
616+ tradeUpdates . Add ( cancel ) ;
617+ tradeUpdates . Add ( cancel ) ;
618+
619+ foreach ( var tradeUpdate in tradeUpdates )
620+ {
621+ AlpacaBrokerage . HandleTradeUpdate ( tradeUpdate ) ;
622+
623+ switch ( tradeUpdate . Event )
624+ {
625+ case TradeEvent . Replaced :
626+ Assert . AreEqual ( 1 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
627+ Assert . AreEqual ( 0 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId [ orderId ] . Count ) ;
628+ break ;
629+ case TradeEvent . Canceled :
630+ Assert . AreEqual ( 0 , AlpacaBrokerage . _duplicationExecutionOrderIdByBrokerageOrderId . Count ) ;
631+ break ;
632+ }
633+ }
634+ }
421635 }
422636}
0 commit comments