Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Exit Order accepted with an incorrect Quantity

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Exit Order accepted with an incorrect Quantity

    Having a peculiar problem with an exit order being accepted with an incorrect quantity.
    In this strategy, a successful trade's position is partially exited at a few different prices.
    One stop-loss exit order and one profit target exit order are in place at any one time.
    The stop-loss exit is for the whole position, but the profit target exits are each for part of the position: when one fills, another profit target exit order is placed at the next price (from within 'OnExecution').
    Upon filling the second-to-last profit target exit, it places the last profit target exit order for the remainder of the position.
    When running the strategy on Market Replay, the last profit target exit order is accepted with a quantity smaller than the position.
    When doing the same in a backtest, this order's quantity is correct.
    I'm printing the quantity requested to the output window both immediately before and immediately after the exit order function call in the code, and the reported value is correct.
    For some reason, the platform accepts the order with a different quantity, and only running on Market Replay.

    #2
    Shogun SunTzu, which NT version are you working with here (Help > About)?

    Can you please let your strategy run in Market Replay with TraceOrders enabeld and then post the relevant output here for the period in question so we can take a look?

    Thanks,

    Comment


      #3
      Supporting Data

      Here's some trace data of the event in question when run on Market Replay (CalculateOnBarClose=false):

      OnOrderUpdate: ProfitTargetOrder PARTFILLED
      OnPosition: StopLossOrder.Quantity does not match Position --> update from 176 to 156
      TraceOrder: Entered internal PlaceOrder() method at 9/1/2010 8:10:03 AM: Action=Sell OrderType=StopLimit Quantity=156 LimitPrice=43.56 StopPrice=43.59 SignalName='StopLoss' FromEntrySignal='Long'
      TraceOrder: Amended matching order: Action=Sell OrderType=StopLimit Quantity=156 LimitPrice=43.56 StopPrice=43.59 SignalName='StopLoss' FromEntrySignal='Long'
      OnOrderUpdate: StopLossOrder PENDINGCHANGE
      OnOrderUpdate: StopLossOrder ACCEPTED -- Quantity = 156
      OnOrderUpdate: ProfitTargetOrder FILLED
      TraceOrder: Entered internal PlaceOrder() method at 9/1/2010 8:10:03 AM: Action=Sell OrderType=Limit Quantity=137 LimitPrice=44.37 StopPrice=0 SignalName='ProfitTarget' FromEntrySignal='Long'
      OnOrderUpdate: ProfitTargetOrder PENDINGSUBMIT
      OnOrderUpdate: ProfitTargetOrder ACCEPTED -- Quantity = 78
      OnOrderUpdate: ProfitTargetOrder WORKING -- Quantity = 78
      OnPosition: StopLossOrder.Quantity does not match Position --> update from 156 to 137
      TraceOrder: Entered internal PlaceOrder() method at 9/1/2010 8:10:03 AM: Action=Sell OrderType=StopLimit Quantity=137 LimitPrice=43.56 StopPrice=43.59 SignalName='StopLoss' FromEntrySignal='Long'
      TraceOrder: Amended matching order: Action=Sell OrderType=StopLimit Quantity=137 LimitPrice=43.56 StopPrice=43.59 SignalName='StopLoss' FromEntrySignal='Long'
      OnOrderUpdate: StopLossOrder PENDINGCHANGE
      OnOrderUpdate: StopLossOrder ACCEPTED -- Quantity = 137
      ...and the same event BackTested:

      OnOrderUpdate: ProfitTargetOrder FILLED
      TraceOrder: Entered internal PlaceOrder() method at 9/1/2010 8:12:00 AM: Action=Sell OrderType=Limit Quantity=137 LimitPrice=44.37 StopPrice=0 SignalName='ProfitTarget' FromEntrySignal='Long'
      OnOrderUpdate: ProfitTargetOrder PENDINGSUBMIT
      OnOrderUpdate: ProfitTargetOrder ACCEPTED -- Quantity = 137
      OnOrderUpdate: ProfitTargetOrder WORKING -- Quantity = 137
      OnPosition: StopLossOrder.Quantity does not match Position --> update from 176 to 137
      TraceOrder: Entered internal PlaceOrder() method at 9/1/2010 8:12:00 AM: Action=Sell OrderType=StopLimit Quantity=137 LimitPrice=43.60 StopPrice=43.60 SignalName='StopLoss' FromEntrySignal='Long'
      TraceOrder: Amended open order: Action=Sell OrderType=StopLimit Quantity=137 LimitPrice=43.60 StopPrice=43.60 SignalName='StopLoss' FromEntrySignal='Long'
      OnOrderUpdate: StopLossOrder PENDINGCHANGE
      OnOrderUpdate: StopLossOrder ACCEPTED -- Quantity = 137
      OnOrderUpdate: StopLossOrder WORKING -- Quantity = 137
      You can see the basic story is when a Profit Target Order fills (entirely), another Profit Target Order is placed for the next exit, in this case the remainder of the Position.
      Any change in the Position prompts an amendment to the existing Stop-Loss Order so that its Quantity matches the Position.
      The only difference here is that under Market Replay, the fill happens in two pieces so the Stop-Loss Order is amended in response to each, which shouldn't effect the Profit Target Order.
      The TraceOrder messages in each situation reflect the correct requested Quantity (137), but under Market Replay, it is accepted with the incorrect Quantity of 78.

      An additional clue: the size of the error, 59 shares, happens to be the sum of two other Profit Target fills which happened earlier in the trade, for 20 and 39 shares.

      ( Running NT 7.0.0.2 / same thing in 7.0.1000.2 )
      Last edited by Shogun SunTzu; 01-14-2011, 02:29 PM. Reason: added NT version information

      Comment


        #4
        Shogun SunTzu, can you please post the code you're using so we can take a deeper look?
        AustinNinjaTrader Customer Service

        Comment


          #5
          Example Code that reproduces the problem

          Here is a simple strategy that reproduces the problem.
          The third and last target exit order is placed for the remainder of the position (70 shares), and the TraceOrder print reflects this quantity correctly.
          However, the order is "accepted" for a quantity only as large as the current position less any previous fills on this trade (here, fills of 10 and 20 for a total of 30 shares), which maxes out the quantity honored for the last exit at 40 shares.
          Requested values of 40 or smaller are reflected correctly in the resulting order, but any over 40 are limited to 40.
          I checked the "filled" value for "TargetExitIO", but it does not accumulate, rather reflecting only the current execution.

          Code:
          #region Using declarations
          using System;
          using System.ComponentModel;
          using System.Diagnostics;
          using System.Drawing;
          using System.Drawing.Drawing2D;
          using System.Xml.Serialization;
          using NinjaTrader.Cbi;
          using NinjaTrader.Data;
          using NinjaTrader.Indicator;
          using NinjaTrader.Gui.Chart;
          using NinjaTrader.Strategy;
          #endregion
          
          namespace NinjaTrader.Strategy {
              public class TargetProblemIllustration : Strategy {
                  
                  //  Variables
                  private IOrder EntryIO = null;
                  private IOrder StopLossIO = null;
                  private IOrder TargetExitIO = null;
                  private double TargetPrice1 = 0;
                  private double TargetPrice2 = 0;
                  private double TargetPrice3 = 0;
                  private int TargetQty1 = 0;
                  private int TargetQty2 = 0;
                  private int TargetQty3 = 0;
                  private bool WaitingToEnter = true;
                  private int TargetsFilled = 0;
                  
                  protected override void Initialize() {
                      CalculateOnBarClose = false;
                      TraceOrders = true;
                  }
                  
                  protected override void OnBarUpdate() {
                      if (Historical) { return; }
                      if (WaitingToEnter) {
                          EntryIO = EnterLongLimit(0, false, 100, (Close[0] + 0.03), "LongEntry");
                      }
                  }
                  
                  protected override void OnExecution(IExecution execution)  {
                      if ((EntryIO != null)
                      &&    (EntryIO == execution.Order)
                      &&    (execution.Order.OrderState == OrderState.Filled)
                      )  {
                          WaitingToEnter = false;
                          TargetsFilled = 0;
                          TargetPrice1 = execution.Order.AvgFillPrice + 0.20;
                          TargetPrice2 = execution.Order.AvgFillPrice + 0.40;
                          TargetPrice3 = execution.Order.AvgFillPrice + 0.60;
                          TargetQty1 = 10;
                          TargetQty2 = 20;
                          TargetQty3 = 70;   // <--------  the last TargetExit order only honors a ShareSize up to the current ShareSize minus any previous fills, in this case 70 - 20 - 10 = 40 shares
                          
                          //  Place the first target exit order
                          TargetExitIO = ExitLongLimit(0, true, TargetQty1, TargetPrice1, "TargetExit1", "LongEntry");
                          Print(Time[0] + "    TargetExitIO 1 placed @ " + Math.Round(TargetPrice1, 2) + " for " + TargetQty1 + " shares");
                          //  Place the stop-loss order
                          StopLossIO = ExitLongStopLimit(0, true, Position.Quantity, (execution.Order.AvgFillPrice - 0.33), (execution.Order.AvgFillPrice - 0.30), "StopLoss", "LongEntry");
                          Print(Time[0] + "    StopLossIO placed @ " + Math.Round((execution.Order.AvgFillPrice - 0.30), 2) + " for " + Position.Quantity + " shares");
                          EntryIO = null;
                      }
                      
                      if ((StopLossIO != null)
                      &&    (StopLossIO == execution.Order)
                      &&    (execution.Order.OrderState == OrderState.Filled)
                      )  {
                          //  if StopLoss order fills, cancel a TargetExit order
                          if (TargetExitIO != null) {
                              CancelOrder(TargetExitIO);
                          }
                          Print(Time[0] + "    StopLoss order filled @ " + Math.Round(TargetPrice1, 2) + " for " + TargetQty1 + " shares");
                          WaitingToEnter = true;
                          StopLossIO = null;
                      }
                      
                      if ((TargetExitIO != null)
                      &&    (TargetExitIO == execution.Order)
                      &&    (execution.Order.OrderState == OrderState.Filled)
                      )  {
                          if (Position.MarketPosition == MarketPosition.Flat) {
                              WaitingToEnter = true;
                              if (StopLossIO != null) {   //  cancel the StopLoss order
                                  CancelOrder(StopLossIO);
                              }
                          } else if (Position.Quantity != StopLossIO.Quantity) {
                              StopLossIO = ExitLongStopLimit(0, true, Position.Quantity, StopLossIO.LimitPrice, StopLossIO.StopPrice, "StopLoss", "LongEntry");
                              Print(Time[0] + "    changing StopLossIO quantity to " + Position.Quantity);
                          }
                          TargetsFilled += 1;
                          
                          //  Place the next target exit order
                          switch (TargetsFilled) {
                              case 1:
                                  TargetExitIO = ExitLongLimit(0, true, TargetQty2, TargetPrice2, "TargetExit2", "LongEntry");
                                  Print(Time[0] + "    TargetExitIO 2 placed @ " + Math.Round(TargetPrice2, 2) + " for " + TargetQty2 + " shares");
                                  break;
                              case 2:
                                  TargetExitIO = ExitLongLimit(0, true, TargetQty3, TargetPrice3, "TargetExit3", "LongEntry");
                                  Print(Time[0] + "    TargetExitIO 3 placed @ " + Math.Round(TargetPrice3, 2) + " for " + TargetQty3 + " shares");
                                  break;
                              case 3:
                                  TargetExitIO = null;
                                  break;
                          }
                      }
                  }
                  
                  protected override void OnOrderUpdate(IOrder order)  {
                  
                      if ((TargetExitIO != null) && (TargetExitIO == order)) {
                          
                          if (order.OrderState == OrderState.Accepted) {
                              Print(Time[0] + "    OnOrderUpdate:  TargetExitIO ACCEPTED @ " + Math.Round(order.LimitPrice, 2) + " for " + order.Quantity + " shares");
                          }
                          
                      }
                  
                  }
              }
          }

          Comment


            #6
            Thanks Shogun SunTzu - we'll give this a run and check into it.

            Comment


              #7
              We were unfortunately unable to reproduce here running one week in Market Replay ES 1 min charts and forcing partials to true in the simulator - can you please send your log / trace files and the replay data used via Help > Mail to Support to us? Please then include the symbol and timeframe as well.

              Thanks,

              Comment


                #8
                Explicit Example

                Here's a simplified example which should reproduce the problem:


                Instrument: RIMM, 1-minute bars
                Enforce partial fills: off
                Enforce immediate fills: on

                Notes:
                Run this strategy on September 1, 2010 and it will enter a position at 8:00am.
                The third and last target exit order is placed for the remainder of the position (70 shares), and the TraceOrder print reflects this quantity correctly.
                However, when under Market Replay, the order is "accepted" for a quantity only as large as the current position less any previous fills on this trade (here, fills of 10 and 20 for a total of 30 shares), which maxes out the quantity honored for the last exit at 40 shares.
                Requested values of 40 or smaller are reflected correctly in the resulting order, but any over 40 are limited to 40.
                The "Filled" value for "TargetExitIO" does not accumulate as it is being used for further target exits, reflecting only the current execution.

                Strategy code:
                Code:
                #region Using declarations
                using System;
                using System.ComponentModel;
                using System.Diagnostics;
                using System.Drawing;
                using System.Drawing.Drawing2D;
                using System.Xml.Serialization;
                using NinjaTrader.Cbi;
                using NinjaTrader.Data;
                using NinjaTrader.Indicator;
                using NinjaTrader.Gui.Chart;
                using NinjaTrader.Strategy;
                #endregion
                
                namespace NinjaTrader.Strategy {
                    public class TargetProblemIllustration : Strategy {
                        
                        //  Variables
                        private IOrder EntryIO = null;
                        private IOrder StopLossIO = null;
                        private IOrder TargetExitIO = null;
                        private double TargetPrice1 = 0;
                        private double TargetPrice2 = 0;
                        private double TargetPrice3 = 0;
                        private int TargetQty1 = 0;
                        private int TargetQty2 = 0;
                        private int TargetQty3 = 0;
                        private bool WaitingToEnter = true;
                        private int TargetsFilled = 0;
                        
                        protected override void Initialize() {
                            //CalculateOnBarClose = false;
                            TraceOrders = true;
                        }
                        
                        protected override void OnBarUpdate() {
                            
                            if ((! CalculateOnBarClose) && Historical) { return; }
                            
                            // enter long at 8:00am
                            if (WaitingToEnter && (ToTime(Time[0]) == ToTime(8, 0, 0))) {
                                WaitingToEnter = false;
                                EntryIO = EnterLongLimit(0, false, 100, (Close[0] + 0.03), "LongEntry");
                            }
                        }
                        
                        protected override void OnExecution(IExecution execution)  {
                            if ((EntryIO != null)
                            &&    (EntryIO == execution.Order)
                            &&    (execution.Order.OrderState == OrderState.Filled)
                            )  {
                                WaitingToEnter = false;
                                TargetsFilled = 0;
                                TargetPrice1 = execution.Order.AvgFillPrice + 0.20;
                                TargetPrice2 = execution.Order.AvgFillPrice + 0.40;
                                TargetPrice3 = execution.Order.AvgFillPrice + 0.60;
                                TargetQty1 = 10;
                                TargetQty2 = 20;
                                TargetQty3 = 70;   // <--------  the last TargetExit order only honors a ShareSize up to the current ShareSize minus any previous fills, in this case 70 - 20 - 10 = 40 shares
                                
                                //  Place the first target exit order
                                TargetExitIO = ExitLongLimit(0, true, TargetQty1, TargetPrice1, "TargetExit1", "LongEntry");
                                Print(Time[0] + "    TargetExitIO 1 placed @ " + Math.Round(TargetPrice1, 2) + " for " + TargetQty1 + " shares");
                                //  Place the stop-loss order
                                StopLossIO = ExitLongStopLimit(0, true, Position.Quantity, (execution.Order.AvgFillPrice - 0.33), (execution.Order.AvgFillPrice - 0.30), "StopLoss", "LongEntry");
                                Print(Time[0] + "    StopLossIO placed @ " + Math.Round((execution.Order.AvgFillPrice - 0.30), 2) + " for " + Position.Quantity + " shares");
                                EntryIO = null;
                            }
                            
                            if ((StopLossIO != null)
                            &&    (StopLossIO == execution.Order)
                            &&    (execution.Order.OrderState == OrderState.Filled)
                            )  {
                                //  if StopLoss order fills, cancel a TargetExit order
                                if (TargetExitIO != null) {
                                    CancelOrder(TargetExitIO);
                                }
                                Print(Time[0] + "    StopLoss order filled @ " + Math.Round(TargetPrice1, 2) + " for " + TargetQty1 + " shares");
                                WaitingToEnter = true;
                                StopLossIO = null;
                            }
                            
                            if ((TargetExitIO != null)
                            &&    (TargetExitIO == execution.Order)
                            &&    (execution.Order.OrderState == OrderState.Filled)
                            )  {
                                
                                if (Position.MarketPosition == MarketPosition.Flat) {
                                    if (StopLossIO != null) {   //  cancel the StopLoss order
                                        CancelOrder(StopLossIO);
                                    }
                                } else if (Position.Quantity != StopLossIO.Quantity) {
                                    StopLossIO = ExitLongStopLimit(0, true, Position.Quantity, StopLossIO.LimitPrice, StopLossIO.StopPrice, "StopLoss", "LongEntry");
                                    Print(Time[0] + "    changing StopLossIO quantity to " + Position.Quantity);
                                }
                                TargetsFilled += 1;
                                Print(Time[0] + "    TargetExitIO " + TargetsFilled + " filled for " + execution.Order.Filled + " shares");
                                
                                //  Place the next target exit order
                                switch (TargetsFilled) {
                                    case 1:
                                        TargetExitIO = ExitLongLimit(0, true, TargetQty2, TargetPrice2, "TargetExit2", "LongEntry");
                                        Print(Time[0] + "    TargetExitIO 2 placed @ " + Math.Round(TargetPrice2, 2) + " for " + TargetQty2 + " shares");
                                        break;
                                    case 2:
                                        TargetExitIO = ExitLongLimit(0, true, TargetQty3, TargetPrice3, "TargetExit3", "LongEntry");
                                        Print(Time[0] + "    TargetExitIO 3 placed @ " + Math.Round(TargetPrice3, 2) + " for " + TargetQty3 + " shares");
                                        break;
                                    case 3:
                                        TargetExitIO = null;
                                        break;
                                }
                            }
                        }
                        
                        protected override void OnOrderUpdate(IOrder order)  {
                        
                            if ((TargetExitIO != null) && (TargetExitIO == order)) {
                                if (order.OrderState == OrderState.Accepted) {
                                    Print(Time[0] + "    OnOrderUpdate:  TargetExitIO ACCEPTED @ " + Math.Round(order.LimitPrice, 2) + " for " + order.Quantity + " shares");
                                }
                            }
                            
                        }
                        
                    }
                }

                Comment


                  #9
                  Shogun SunTzu, I could reproduce your issue here - we're looking into and will provide an update then.

                  Thanks for reporting in

                  Comment


                    #10
                    Wondering what anyone may have found on this.

                    Since this doesn't happen in a backtest but does to market replay, could it have something to do with the simulator ?

                    Comment


                      #11
                      Shogun SunTzu, sorry for my late update on your report:

                      We've analyzed the issue brought up and you're unfortunately running into race conditions on Market Replay here - to workaround we suggest either to

                      a) Scale into the position to be able to scale out later as you intend (managed mode)

                      b) recode the strategy with the unmanaged order submission methods (SubmitOrder, ChangeOrder, CancelOrder).

                      Thanks again for the report and patience,

                      Comment


                        #12
                        I'm not familiar with "race conditions" -- what is that exactly ?
                        Is this a limitation of Market Replay, or could this happen with any type of data feed ?
                        Also, is this not limited to simulation accounts ?

                        Comment


                          #13
                          Shogun SunTzu, the term race condition originates from two events 'racing' to influence the output seen first, it's unfortunately a timing / sequencing issue you ran into and it could be seen on other accounts / connections as well.

                          Comment

                          Latest Posts

                          Collapse

                          Topics Statistics Last Post
                          Started by Geovanny Suaza, 02-11-2026, 06:32 PM
                          0 responses
                          637 views
                          0 likes
                          Last Post Geovanny Suaza  
                          Started by Geovanny Suaza, 02-11-2026, 05:51 PM
                          0 responses
                          366 views
                          1 like
                          Last Post Geovanny Suaza  
                          Started by Mindset, 02-09-2026, 11:44 AM
                          0 responses
                          107 views
                          0 likes
                          Last Post Mindset
                          by Mindset
                           
                          Started by Geovanny Suaza, 02-02-2026, 12:30 PM
                          0 responses
                          569 views
                          1 like
                          Last Post Geovanny Suaza  
                          Started by RFrosty, 01-28-2026, 06:49 PM
                          0 responses
                          571 views
                          1 like
                          Last Post RFrosty
                          by RFrosty
                           
                          Working...
                          X