Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

How NinjaTrader fills and exits orders in Custom Strategies?

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

    How NinjaTrader fills and exits orders in Custom Strategies?

    Hi NinjaTrader Support team,

    We have developed a custom strategy using our own logic in NinjaTrader. We are working in the backtest mode in the provided examples below.
    In the scope of one OnBarUpdate event, we can submit up to 10 orders with the same stop price. As a result, the strategy returns order objects, but sometimes the fill or exit price are 1 tick away from the expected.
    The majority of the scenarios happen when we exit the orders and fill the reversal orders at the same time: the entry price of the reversal orders can be 1 tick away from the exit price. The expectation is that the entry price and exit price should be the same.
    When testing the strategy, we noticed that some of the orders are processed differently from what we expect.

    Here are some examples:
    1. At 09:01:20 AM, we are closing 5 long orders at 84.02, and fill 5 short orders at the price of 84.03.
      1. In log1.txt log1.txt , at 08.10.2023 09:01:20.417 AM the signal to go Long was received (to open a reversal)
      2. At 08.10.2023 09:01:20.417 AM the algorithm attended to open long orders at 83.99 and failed.
      3. Short positions were filled at 84.02 by 08.10.2023 09:01:20.507 AM with the stop price 83.99.
      4. Long positions were submitted with the stop price of 83.99
      5. Long positions were filled at the price of 84.03 by 08.10.2023 09:01:20.532 AM
      6. The historical data shows the following information for every tick:
        Click image for larger version  Name:	dataurl295386.png Views:	0 Size:	141.8 KB ID:	1279414
    Could you please help us and answer the following questions:
    How NinjaTrader decides when and at what price the orders are going to get closed?
    Does NinjaTrader group the orders to open them at the same price? If yes, could you please describe the grouping process or define general rules?
    Why in the case above NinjaTrader didn’t fill one position at 84.02 and the rest of orders at 84.03?
    Why in the case above the positions weren’t closed at 84.01?
    1. At 09:52:50, we are closing 5 short positions at 83.79 and open 5 long positions at 83.78
      1. In log2.txt log2.txt , at 08.10.2023 09:52:50.633 AM the signal to open reversal orders was received.
      2. At 08.10.2023 09:52:50.633 AM, the strategy failed to create 5 Long orders with stop price 83.77
      3. Short positions were closed by 08.10.2023 09:52:51.867 AM with the price of 83.79 (the stop price of all orders was 83.77)
      4. The strategy submitted 5 long orders with stop price of 83.77 at 08.10.2023 09:52:51.867 AM.
      5. The long positions were filled by 08.10.2023 09:52:51.936 AM with the fill price 83.78
    The historical data shows the following information for every tick:
    Click image for larger version  Name:	dataurl295390.png Views:	0 Size:	141.2 KB ID:	1279415
    Could you please help us and answer the following questions:

    Why in the case above NinjaTrader fill all long positions at 83.78?
    Why in the case above the positions weren’t closed at 83.78?

    Here are the parameters for Historicall fill processing:
    Click image for larger version

Name:	image.png
Views:	130
Size:	18.9 KB
ID:	1279423
    Please let us know if you have any questions or if any additional details are required.
    Last edited by savaa; 11-27-2023, 04:57 AM.

    #2
    Hello savaa,

    You can read about how the historical fill engine works in the following links, you could compare your results against the list of steps the backfill engine follows to get a better idea on what may be happening.


    Comment


      #3
      Hi NinjaTrader_Jesse,
      We have primary data series that we use for drawing a chart and it is set to 5 seconds.
      Secondary data series is hard-coded and is used for all calculations within the strategy. It is set to 1 tick.
      In the situation described below, we expected to be filled in the bar highlighted with the red arrow (because we were filled between 06:41:55.071 PM and 08.13.2023 06:42:01.086 PM according to the log file attached), but for some reason the chart shows that it was filled 2 bars after, which brings us to 10 seconds delay.

      Our expectation is that it should have been drawn filled orders on the bar where the 'S' letter and an arrow is shown:
      Click image for larger version

Name:	signal.png
Views:	138
Size:	49.0 KB
ID:	1280140

      Here is the log file:
      Code:
      2023-11-28 09:50:44:715|1|16|1258047328 | ProcessStrategy | StrategyState: CheckingForSignalReversal_Long_CreateShortOrders | barTime: 08.13.2023 06:41:55.071 PM
      2023-11-28 09:50:44:715|1|16|1258047328 | ProcessStrategy | StrategyState.CheckingForSignalReversal_Long_Creat eShortOrders | NumberOfContracts: 5 | deltaOrders: 5
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00020-53 | orderName: CloseLNStp@381-ROpenLN@376 orderState: Filled AverageFillPrice: 82.85 Filled: 1 | limitPrice: 0 | stopPrice: 82.86
      2023-11-28 09:50:44:715|1|16|1258047328 | OnExecutionUpdate | executionId: NT-00015-53 | orderId: NT-00020-53 | orderName: CloseLNStp@381-ROpenLN@376 orderState: Filled AverageFillPrice: 82.85 Filled: 1
      2023-11-28 09:50:44:715|1|16|1258047328 | DoPositionUpdateEvent | position: Long
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00021-53 | orderName: CloseLNPft@382-ROpenLN@376 orderState: CancelPending AverageFillPrice: 0 Filled: 0 | limitPrice: 83.12 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00021-53 | orderName: CloseLNPft@382-ROpenLN@376 orderState: CancelSubmitted AverageFillPrice: 0 Filled: 0 | limitPrice: 83.12 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00021-53 | orderName: CloseLNPft@382-ROpenLN@376 orderState: Cancelled AverageFillPrice: 0 Filled: 0 | limitPrice: 83.12 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00022-53 | orderName: CloseLNStp@383-ROpenLN@377 orderState: Filled AverageFillPrice: 82.85 Filled: 1 | limitPrice: 0 | stopPrice: 82.86
      2023-11-28 09:50:44:715|1|16|1258047328 | OnExecutionUpdate | executionId: NT-00016-53 | orderId: NT-00022-53 | orderName: CloseLNStp@383-ROpenLN@377 orderState: Filled AverageFillPrice: 82.85 Filled: 1
      2023-11-28 09:50:44:715|1|16|1258047328 | DoPositionUpdateEvent | position: Long
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00023-53 | orderName: CloseLNPft@384-ROpenLN@377 orderState: CancelPending AverageFillPrice: 0 Filled: 0 | limitPrice: 83.22 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00023-53 | orderName: CloseLNPft@384-ROpenLN@377 orderState: CancelSubmitted AverageFillPrice: 0 Filled: 0 | limitPrice: 83.22 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00023-53 | orderName: CloseLNPft@384-ROpenLN@377 orderState: Cancelled AverageFillPrice: 0 Filled: 0 | limitPrice: 83.22 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00024-53 | orderName: CloseLNStp@385-ROpenLN@378 orderState: Filled AverageFillPrice: 82.85 Filled: 1 | limitPrice: 0 | stopPrice: 82.86
      2023-11-28 09:50:44:715|1|16|1258047328 | OnExecutionUpdate | executionId: NT-00017-53 | orderId: NT-00024-53 | orderName: CloseLNStp@385-ROpenLN@378 orderState: Filled AverageFillPrice: 82.85 Filled: 1
      2023-11-28 09:50:44:715|1|16|1258047328 | DoPositionUpdateEvent | position: Long
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00025-53 | orderName: CloseLNPft@386-ROpenLN@378 orderState: CancelPending AverageFillPrice: 0 Filled: 0 | limitPrice: 83.32 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00025-53 | orderName: CloseLNPft@386-ROpenLN@378 orderState: CancelSubmitted AverageFillPrice: 0 Filled: 0 | limitPrice: 83.32 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00025-53 | orderName: CloseLNPft@386-ROpenLN@378 orderState: Cancelled AverageFillPrice: 0 Filled: 0 | limitPrice: 83.32 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00026-53 | orderName: CloseLNStp@387-ROpenLN@379 orderState: Filled AverageFillPrice: 82.85 Filled: 1 | limitPrice: 0 | stopPrice: 82.86
      2023-11-28 09:50:44:715|1|16|1258047328 | OnExecutionUpdate | executionId: NT-00018-53 | orderId: NT-00026-53 | orderName: CloseLNStp@387-ROpenLN@379 orderState: Filled AverageFillPrice: 82.85 Filled: 1
      2023-11-28 09:50:44:715|1|16|1258047328 | DoPositionUpdateEvent | position: Long
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00027-53 | orderName: CloseLNPft@388-ROpenLN@379 orderState: CancelPending AverageFillPrice: 0 Filled: 0 | limitPrice: 83.42 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00027-53 | orderName: CloseLNPft@388-ROpenLN@379 orderState: CancelSubmitted AverageFillPrice: 0 Filled: 0 | limitPrice: 83.42 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00027-53 | orderName: CloseLNPft@388-ROpenLN@379 orderState: Cancelled AverageFillPrice: 0 Filled: 0 | limitPrice: 83.42 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00028-53 | orderName: CloseLNStp@389-ROpenLN@380 orderState: Filled AverageFillPrice: 82.85 Filled: 1 | limitPrice: 0 | stopPrice: 82.86
      2023-11-28 09:50:44:715|1|16|1258047328 | OnExecutionUpdate | executionId: NT-00019-53 | orderId: NT-00028-53 | orderName: CloseLNStp@389-ROpenLN@380 orderState: Filled AverageFillPrice: 82.85 Filled: 1
      2023-11-28 09:50:44:715|1|16|1258047328 | DoPositionUpdateEvent | position: Flat
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00029-53 | orderName: CloseLNPft@390-ROpenLN@380 orderState: CancelPending AverageFillPrice: 0 Filled: 0 | limitPrice: 83.52 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00029-53 | orderName: CloseLNPft@390-ROpenLN@380 orderState: CancelSubmitted AverageFillPrice: 0 Filled: 0 | limitPrice: 83.52 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | OnOrderUpdate | orderId: NT-00029-53 | orderName: CloseLNPft@390-ROpenLN@380 orderState: Cancelled AverageFillPrice: 0 Filled: 0 | limitPrice: 83.52 | stopPrice: 0
      2023-11-28 09:50:44:715|1|16|1258047328 | ProcessStrategy | StrategyState: CheckingForSignalReversal_Long_CheckStopTriggered | barTime: 08.13.2023 06:42:01.086 PM


      Could you please explain why it was drawn 2 bars after the event happened?
      Last edited by savaa; 12-01-2023, 12:13 AM.

      Comment


        #4
        Hello savaa,

        From your custom output I wouldn't be able to say what may be happening, have you tried to print the executions time that is in the passed in parameters in OnExecutionUpdate? That would be the specific time that the fill occured. Depending on what time you are printing in your custom output you may not be seeing accurate timestamps for when the fill actually occurred.

        Comment


          #5
          Hi NinjaTrader_Jesse,

          We have added the executions time in OnExecutionUpdate. See code attached [ATTACH]n1279807[/ATTACH] .
          We see now that the orders were filled 2 bars after, but we do not understand why the information in the previous bar wasn't used - according to the historical data (screenshot attached), there was 1 tick at 82.86 price at 06:41:55.071 PM. The stop price was set to 82.86, so our expectation is that at least 1 order could get filled on the previous tick.
          Could you please explain why it wasn't used?
          Click image for larger version

Name:	MicrosoftTeams-image (15).png
Views:	136
Size:	43.8 KB
ID:	1279806

          Comment


            #6
            Hello savaa,

            I wouldn't be able to provide a specific explanation on the innerworkings of the fill algorithm as that is closed source. The information that we have about that algorithm is located in the links in post 2. The execution time that comes through OnExecutionUpdate would let you know when the fill algorithm filled the order and then that will be displayed in the backtest chart within the bar where that fills timestamp falls in.


            Comment

            Latest Posts

            Collapse

            Topics Statistics Last Post
            Started by NullPointStrategies, Yesterday, 05:17 AM
            0 responses
            54 views
            0 likes
            Last Post NullPointStrategies  
            Started by argusthome, 03-08-2026, 10:06 AM
            0 responses
            130 views
            0 likes
            Last Post argusthome  
            Started by NabilKhattabi, 03-06-2026, 11:18 AM
            0 responses
            71 views
            0 likes
            Last Post NabilKhattabi  
            Started by Deep42, 03-06-2026, 12:28 AM
            0 responses
            44 views
            0 likes
            Last Post Deep42
            by Deep42
             
            Started by TheRealMorford, 03-05-2026, 06:15 PM
            0 responses
            49 views
            0 likes
            Last Post TheRealMorford  
            Working...
            X