Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

OnOrderUpdate filling limit order before execution

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

    OnOrderUpdate filling limit order before execution

    Hello,

    I am having an issue where my OnOrderUpdate is giving me my order fill before it is actually should be filled. In this example for NQ, the current price is 20006.75, I set limit to 19983.25, it immediate gives an OnOrderUpdate with state 'Filled'. Then more than 10 minutes later, when it hits the limit price OnExecutionUpdate triggers when it should giving me the state filled.

    This seems like the backtest is filling future entries? This causes issues as I have order logic in the OnOrderUpdate that only happens when state is not filled, i.e. setting entryOrder to be the order, then in the execution update I will need to set my stop/profit orders.

    I followed the references in https://ninjatrader.com/support/help...rderupdate.htm section "Properly assigning order object values and https://ninjatrader.com/support/help...rderupdate.htm "OnExecutionUpdate Example".

    I tried both Managed and Unmanged orders, and they give the same results. I have put in this a screenshot of the log showing the above scenario, and snippets of my code. In the code snippets, I have created a wrapper class around my order, I just then call the OnOrderUpdate/OnExecutionUpdate from the actual strategy methods.

    Thank you.

    Log:
    Code:
    2024-10-02 10:45:00 AM Confirmed Long at 10:40:00 Current=20006.75 Entry=19983.2625 TP=20171.5 SL=19832.5
    2024-10-02 10:45:00 AM Strategy 'DRStrategy/340075344': Entered internal SubmitOrderUnmanaged() method at 2024-10-02 10:45:00 AM: BarsInProgress=0 Action=Buy OrderType=Limit Quantity=1 LimitPrice=19983.25 StopPrice=0 SignalName='entry_a65c9621-c5c0-40f4-86eb-2d90167542fb'
    2024-10-02 10:45:00 AM Order Update: OrderName: entry_a65c9621-c5c0-40f4-86eb-2d90167542fb State:Filled filed 1 Strategy position: 1
    2024-10-02 10:56:00 AM Execution Update: OrderName: entry_a65c9621-c5c0-40f4-86eb-2d90167542fb State:Filled
    SubmitOrder:
    Code:
                strategy.SubmitOrderUnmanaged(0, this.action, OrderType.Limit, this.qty, this.limitPrice, 0, this.oco, this.ENTRY_ID);
    OnOrderUpdate:
    Code:
    public void OnOrderUpdate(Order order, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, OrderState orderState, DateTime time, ErrorCode error, string comment)
            {
                strategy.Print(time.ToString() + " Order Update: OrderName: " + order.Name + " State:" + order.OrderState.ToString() + " filed "  + filled.ToString() + " Strategy position: " + strategy.Position.Quantity);
                // One time only, as we transition from historical
                // Convert any old historical order object references to the live order submitted to the real-time account
                if (strategy.State == State.Realtime) {
                    if ((entryOrder != null) && (entryOrder.IsBacktestOrder))
                    {
                        entryOrder = strategy.GetRealtimeOrder(entryOrder);
                    }
                    if ((stopOrder != null) && (stopOrder.IsBacktestOrder))
                    {
                        stopOrder = strategy.GetRealtimeOrder(stopOrder);
                    }
                    if ((targetOrder != null) && (targetOrder.IsBacktestOrder))
                    {
                        targetOrder = strategy.GetRealtimeOrder(targetOrder);
                    }
                }
    
                // Assign entryOrder in OnOrderUpdate() to ensure the assignment occurs when expected.
                // This is more reliable than assigning Order objects in OnBarUpdate, as the assignment is not gauranteed to be complete if it is referenced immediately after submitting
                if (orderState != OrderState.Filled) {
                    if (order.Name == ENTRY_ID)
                    {
                        entryOrder = order;
                    }
                    else if (order.Name == SL_ID)
                    {
                        stopOrder = order;
                    }
                    else if (order.Name == TP_ID)
                    {
                        targetOrder = order;
                    }
                }
                
                // Null Entry order if filled or cancelled. We do not use the Order objects after the order is filled,
                // so we can null it here
                if ((entryOrder != null) && (entryOrder == order))
                {
                    // Check if entryOrder is cancelled.
                    if ((order.OrderState == OrderState.Cancelled) && (order.Filled == 0))
                        entryOrder = null;
                    
                    if (order.OrderState == OrderState.Filled)
                        entryOrder = null;
                }
                if ((stopOrder != null) && (stopOrder == order))
                {
                    // Check if entryOrder is cancelled.
                    if ((order.OrderState == OrderState.Cancelled) && (order.Filled == 0))
                        stopOrder = null;
                    
                    if (order.OrderState == OrderState.Filled)
                        stopOrder = null;
                }
                if ((targetOrder != null) && (targetOrder == order))
                {
                    // Check if entryOrder is cancelled.
                    if ((order.OrderState == OrderState.Cancelled) && (order.Filled == 0))
                        targetOrder = null;
                    
                    if (order.OrderState == OrderState.Filled)
                        targetOrder = null;
                }
            }
    OnExecutionUpdate:
    Code:
    public void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
            {
                strategy.Print(time.ToString() + " Execution Update: OrderName: " + execution.Order.Name + " State:" + execution.Order.OrderState.ToString());
                /* We advise monitoring OnExecution to trigger submission of stop/target orders instead of OnOrderUpdate() since OnExecution() is called after OnOrderUpdate()
                which ensures your strategy has received the execution which is used for internal signal tracking. */
                if (entryOrder != null && entryOrder == execution.Order)
                {
                    if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
                    {
                        // We sum the quantities of each execution making up the entry order
                        sumFilled += execution.Quantity;
                        
                        // Submit exit orders for partial fills
                        if ((execution.Order.OrderState == OrderState.PartFilled) || ((execution.Order.OrderState == OrderState.Filled) && (sumFilled == execution.Order.Filled)))
                        {
                            if (action == OrderAction.Buy) {
                                strategy.Print("Setting stop and tp");
                                //stopOrder = strategy.ExitLongStopMarket(0, true, execution.Order.Filled, slPrice, SL_ID, ENTRY_ID);
                                //targetOrder = strategy.ExitLongLimit(0, true, execution.Order.Filled, tpPrice, TP_ID, ENTRY_ID);
                                strategy.SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Limit, execution.Order.Filled, tpPrice, 0, BRACKET_OCO, TP_ID);
                                strategy.SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.StopMarket, execution.Order.Filled, 0, slPrice, BRACKET_OCO, SL_ID);
                            } else {
                                //stopOrder = strategy.ExitShortStopMarket(0, true, execution.Order.Filled, slPrice, SL_ID, ENTRY_ID);
                                //targetOrder = strategy.ExitShortLimit(0, true, execution.Order.Filled, tpPrice, TP_ID, ENTRY_ID);
                                strategy.SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Limit, execution.Order.Filled, tpPrice, 0, BRACKET_OCO, TP_ID);
                                strategy.SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.StopMarket, execution.Order.Filled, 0, slPrice, BRACKET_OCO, SL_ID);
                            }
                        }
                        
                        // Resets the entryOrder object and the sumFilled counter to null / 0 after the order has been filled
                        if (execution.Order.OrderState != OrderState.PartFilled && sumFilled == execution.Order.Filled)
                        {
                            entryOrder = null;
                        }
                    }
                }
                
                // Reset our stop order and target orders' Order objects after our position is closed. (1st Entry)
                if ((stopOrder != null && stopOrder == execution.Order) || (targetOrder != null && targetOrder == execution.Order))
                {
                    if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled)
                    {
                        stopOrder = null;
                        targetOrder = null;
                    }
                }
            }

    #2
    I would have expected the first set of OnOrderUpdates to get to "Accepted" and then once it fills, to give the state "Filled" at almost the same time as OnExecutionUpdate does, and not instantly when the order is placed, and the price hasn't even hit the limit price. This also causes issues as I need to be able to cancel the entry order before a certain time hits, which i need the Order object to do so.

    Comment


      #3
      Hello asaechao,

      Do you see this happening if you use the sample from the help guide directly and add the prints? If so can you attach the sample with the prints you added and also provide directions on the steps you used to see that result?


      ​​​​​​​

      Comment


        #4
        ​Thank you for looking into this.

        I have written a more simple version of the strategy with only entry long so that I can use the example code. I had to modify the ID's of the orders, and the SL + TP prices to show the issue.

        I have attached the full strategy and the log for the example trade that shows the OnOrderUpdate changing the state to be filled a few bars ahead of where it actually gets filled. See screenshot as well. In this example, it seems to give me the proper sequence of states submitted, accepted, then working, but it still gives the state filled the same bar when it is a few bars early.

        Let me know if you have any other questions for clarifications.

        Log showing order giving state "filled" same bar it is submitted (10:15am) but the actual entry and OnExecutionUpdate with the filled state happens a few bars later at 10:32am.

        Code:
        2024-10-21 10:15:00 AM Sending order to enter long. Current: 20528.25 Entry Target: 20494.525 TP: 20534.4 SL: 20422.75
        2024-10-21 10:15:00 AM Order Update: OrderName: entry_16 State: Submitted filled 0 Strategy position: 0
        orderId='NT-00023-27' account='Sim101' name='entry_16' orderState=Submitted instrument='NQ DEC24' orderAction=Buy orderType='Limit' limitPrice=20494.5 stopPrice=0 quantity=1 tif=Gtc oco='' filled=0 averageFillPrice=0 onBehalfOf='' id=-1 time='2024-10-21 10:15:00' gtd='2099-12-01' statementDate='2024-10-21'
        2024-10-21 10:15:00 AM Order Update: OrderName: entry_16 State: Accepted filled 0 Strategy position: 0
        orderId='NT-00023-27' account='Sim101' name='entry_16' orderState=Accepted instrument='NQ DEC24' orderAction=Buy orderType='Limit' limitPrice=20494.5 stopPrice=0 quantity=1 tif=Gtc oco='' filled=0 averageFillPrice=0 onBehalfOf='' id=-1 time='2024-10-21 10:15:00' gtd='2099-12-01' statementDate='2024-10-21'
        2024-10-21 10:15:00 AM Order Update: OrderName: entry_16 State: Working filled 0 Strategy position: 0
        orderId='NT-00023-27' account='Sim101' name='entry_16' orderState=Working instrument='NQ DEC24' orderAction=Buy orderType='Limit' limitPrice=20494.5 stopPrice=0 quantity=1 tif=Gtc oco='' filled=0 averageFillPrice=0 onBehalfOf='' id=-1 time='2024-10-21 10:15:00' gtd='2099-12-01' statementDate='2024-10-21'
        2024-10-21 10:15:00 AM Order Update: OrderName: entry_16 State: Filled filled 1 Strategy position: 1
        orderId='NT-00023-27' account='Sim101' name='entry_16' orderState=Filled instrument='NQ DEC24' orderAction=Buy orderType='Limit' limitPrice=20494.5 stopPrice=0 quantity=1 tif=Gtc oco='' filled=1 averageFillPrice=20494.5 onBehalfOf='' id=-1 time='2024-10-21 10:15:00' gtd='2099-12-01' statementDate='2024-10-21'
        2024-10-21 10:32:00 AM Execution Update: OrderName: entry_16 State:Filled
        2024-10-21 10:30:00 AM Order Update: OrderName: sl_16 State: Submitted filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: sl_16 State: Accepted filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: sl_16 State: Working filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: tp_16 State: Submitted filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: tp_16 State: Accepted filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: tp_16 State: Working filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: tp_16 State: CancelPending filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: tp_16 State: CancelSubmitted filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: tp_16 State: Cancelled filled 0 Strategy position: 1
        2024-10-21 10:30:00 AM Order Update: OrderName: sl_16 State: Filled filled 1 Strategy position: 0
        2024-10-21 10:41:00 AM Execution Update: OrderName: sl_16 State:Filled
        Enabling NinjaScript strategy 'MyTestStrategy/340075345' : On starting a real-time strategy - StartBehavior=WaitUntilFlat EntryHandling=All entries EntriesPerDirection=1 StopTargetHandling=Per entry execution ErrorHandling=Stop strategy, cancel orders, close positions ExitOnSessionClose=True / triggering 30 seconds before close SetOrderQuantityBy=Strategy ConnectionLossHandling=Recalculate DisconnectDelaySeconds=10 CancelEntriesOnStrategyDisable=False CancelExitsOnStrategyDisable=False Calculate=On each tick IsUnmanaged=False MaxRestarts=4 in 5 minutes
        Screenshot of 2024-10-21 attached.

        Click image for larger version

Name:	Screenshot 2024-10-21 173737.jpg
Views:	113
Size:	177.7 KB
ID:	1322157

        Comment


          #5
          Hello Jesse,

          I believe I have resolved the issue. I was wondering why after making the sample it was going through all of the proper states.

          The issue I believe is because I was submitting the order in the constructor of my helper class, which I am guessing due to the timing/multi-threading it would then try to call the methods on the object before it had completed creating. Moving out the order submission into a new method call has solved the issue, and it is now giving me the expected updates.
          thank you

          Comment

          Latest Posts

          Collapse

          Topics Statistics Last Post
          Started by NullPointStrategies, Yesterday, 05:17 AM
          0 responses
          72 views
          0 likes
          Last Post NullPointStrategies  
          Started by argusthome, 03-08-2026, 10:06 AM
          0 responses
          143 views
          0 likes
          Last Post argusthome  
          Started by NabilKhattabi, 03-06-2026, 11:18 AM
          0 responses
          76 views
          0 likes
          Last Post NabilKhattabi  
          Started by Deep42, 03-06-2026, 12:28 AM
          0 responses
          47 views
          0 likes
          Last Post Deep42
          by Deep42
           
          Started by TheRealMorford, 03-05-2026, 06:15 PM
          0 responses
          51 views
          0 likes
          Last Post TheRealMorford  
          Working...
          X