Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Feature Request: Realistic Queue Positioning for limit order fills.

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

    Feature Request: Realistic Queue Positioning for limit order fills.

    I have been kicking the tires on NT 8 for a while now, and after doing some extensive testing I think the only thing that I see as a major flaw in the simulation engine is that it lacks the ability to realistically determine when a limit order fill should take place based on when you joined the line vs. when you actually got filled.

    For example: Let's say that a bid / ask level just changed and price moved up by 1 tick. Typically the first side to get served will be the line of Bids. So let's say that I join the line immediately after the level change and run a GetCurrentBidVolume() to see the number of cars ahead of me. The GetCurrentBidVolume in this example is 200. So I place a limit order in the same code block, so I will assume that I immediately become car # 201. Now we wait. In theory 200 cars ahead of me have to move before I get my number 201 called and I get a fill.

    In most cases I have tested however, I will get a fill prior to the bid volume declining (Implies the 200 cars ahead of me got filled), and in around 50% of cases I tested I will see the same exact GetCurrentCurrentBidVolume() when I initially submit my order, when I actually get filled and even on the next bar update after. This implies that the 200 cars ahead of me haven't even gotten filled yet, but it shows that I am filled.

    Obviously there are cases where the volumes rise and fall, and you can say it's fair that I would have gotten a fill, but the scenario I am describing should not ever happen and it does quite often.

    So the behavior that I would like to see before a fill can be given is this:

    1. Snapshot of the CurrentBidVolume or CurrentAskVolume at the time that an order is placed (Depending on if I am short or long). This gives a count of how many contracts are ahead of me and must clear before I get a fill. I know in the real market some of these will cancel, and there are complex rules where orders with larger size can skip ahead, but I would be satisfied if we just start with a snapshot of how many contracts are ahead when an order is placed and use this as step 1.

    2. Only after the volume declines by a magnitude = to the number of contracts ahead of me when I ran CurrentBidVolume or CurrentAskVolume should I be able to get my order filled. This figure would calculate net of any changes in the level 1 volumes (New contracts added and transacted existing contracts) So in this example if there were 100 transacted bids since I joined the queue but 50 more cars that joined the queue after me. I would move from 201 to 101 and expect to see the next GetCurrentBidVolume update at 151 with my position being 101 in line.

    3. We will need to assume that any added volume goes behind me in the queue and my position moves up as transacted volume increases towards my number. I realize that larger size can cut in line, but that will add a whole other level of sophistication that I don't think will be easy to implement, so for now we will keep it simple.

    I hope I am making this request clear, but let me know if you have any questions, or if you need me to provide a code that could highlight this and recreate the scenario I am describing and show how this current logic is not in place.

    Thanks,

    Ian

    #2
    Hello iantg,

    Thank you for your post.

    I will forward your request to our development team.

    Comment


      #3
      Hello iantg,

      Thank you for your patience.

      In my testing I am only seeing the Limit Order fill if it's position is moved to the front for the fill or in the case the price drops past my Buy Limit price level.

      Please take a look at my test script and advise if this is the same behavior you are testing or if I need to adjust my testing or script.
      Attached Files

      Comment


        #4
        Patrick,

        Thank you for following up with me.

        Can you please provide me with your email? I will send you some information that will allow you to see the behavior the I am referring to.

        Thanks,

        Ian

        Comment


          #5
          Hello iantg,

          Thank you for your response.

          You can reach me at platformsupport[at]ninjatrader[dot]com. Please put 'ATTN: Patrick H' in the subject line and reference this thread in the body of the email.

          I look forward to assisting you further.

          Comment


            #6
            Hello iantg,

            Thank you for your patience and providing examples of this item.

            I have submitted this feature request to our development team.

            Comment


              #7
              Patrick,

              Thanks for the ongoing support. So were you able to use the code I provided to recreate this and see what I am referring to? To be fair, I would say that 50% of the time, the fills occur on level changes which implies it would really occur in the real market. And even within the same level the majority of the time fills using occur once volume has declined then risen, but I was able to build a code to target fills that occur without supporting volume. I think these cases are rare, but still present.

              I am looking forward to 8.12! Hopefully this item makes it in the next update!

              Thanks,

              Ian

              Comment


                #8
                Hello iantg,

                Thank you for your response.

                Yes, I was able to see this occur with your script and my own once I tested mine over more trades.

                I spoke with development on this as well. While the simulation engine is proprietary and I cannot go into detail, I can say your suggestion has been forwarded to development for consideration in a future release.

                Please let me know if I may be of further assistance.

                Comment


                  #9
                  Hello iantg,

                  Thank you for your suggestion.

                  The feature request has been assigned the internal tracking id SFT-2955.

                  Please let me know if you have any questions.

                  Comment


                    #10
                    Hello iantg,

                    I spoke further with our development team and the Simulator does actually use a placement in the queue and it is conservative to the volume at the time of placement.

                    In both your test script and my own we were running our checks of the Bid and Ask information in OnBarUpdate() or only when the order was submitted or filled. This is not ideal as we want the Bid and Ask information as it updates and not it's last reference when a bar closed.
                    So we need to use OnMarketData() more so that we are printing out the values of the Bid and Ask prices and volume through MarketDataEvents: https://ninjatrader.com/support/help...aeventargs.htm

                    Also please ensure you do not have the 'Enforce immediate fills' enabled under Tools > Options > Trading.

                    Please let me know if you have any questions on this matter.

                    Comment


                      #11
                      Patrick,

                      Thanks for following up with me. I just wanted to give you a little further insight into exactly what my code was looking at and how it was assessing the volumes.

                      1. Regarding the Printing to the Output Window. The Entry Bar Print was ran on the OnMarketData(MarketDataEventArgs marketDataUpdate event handler, and the entry is being submitted on the OnMarketData(MarketDataEventArgs marketDataUpdate event not the OnBarUpdate event.

                      2. The second data point (When the fill actually occurred) is being ran on the OnExecutionUpdate event handler.

                      In between these two events (When the entry was submitted, and when the fill occurred) there may have been other prints made by the OnBarUpdate event handler, but in most cases the volumes did not change between the first print from OnMarketData and the OnExcecutionUpdate. This was the issue, and why I believe there were not supporting volumes to justify the fills.

                      Also, I was targeting a special case entry where I submit to the weak side that had no prior queue in place. So the exact moment that a level clears and price moves up, I was submitting a buy bid, or alternatively if the level cleared down, I would submit a sell ask. This "weak side" has no prior queue at all because limit orders can not be placed above the market (for buys) and below the market (for sells). (I was targeting this level change with the OnMarketData event handler comparing the GetiCurrent bid or ask to a a variable where I held the prior bid or ask price, the instant it changed, i would submit my order. So in theory I was sumbitting at the earliest possible time.

                      This is what I observed that I believe is flawed:

                      1. When I submit my order (Let's say buy bid), I would sample the bid and ask volumes and see something to the effect of Bid = 200 and Ask = 50.
                      2. Then I would wait, presumably in a queue until the 200 bid volumes cleared before I would get filled. I may see a bar update or not before my fill, but if I did see a bar update, it would show that the price level was unchanged and volumes were unchanged.
                      3. Finally I would get my fill from the OnExcecutionUpdate event, and I would once again sample the volumes and see 200 Bid and 50 Ask. So the volumes did not change at all between when I submitted and when I was filled.
                      4. The first time there was a level change it was due to the 50 Asks getting filled prior to the 200 bids getting filled, and the level moved up accordingly. So I am instantly up 1 tick and I am to believe that getting in line behind 200 bid contracts, I was somehow filled prior to the 50 Ask contracts when I see that the resulting level change tells me that all the asks got filled but not all the bids got filled and the level moved up accordingly. This describes the problem scenario where I believe the SIM engine is off.

                      As I mentioned previously, this was a special case and does not occur every time by any means, but these are the cases where I see issues. I think if you submit to the strong side (level goes down, and you submit a buy bid, or level goes up and you submit a sell ask) you will see the queue at work and be treated according. But submitting to the weak side where there is no queue is where I see the issue.

                      There were definitely extraneous parts of my code and prints from the OnBarUpdate at various intervals for other reasons, and it was likely not clear when and how certain things were being measured. But I believe I have captured these events as clean and granular as possible. Unless the replay data via Kinetic is somehow filtered and missing chunks of volume history then this is likely as granular as it gets.

                      Also just to confirm I am using tick replay, and not checking fill limit orders on touch or immediately. I have observed this behavior on version 8.6, 8.10, and 8.11.

                      I can give you more specifics if needed.

                      Thanks,

                      Ian

                      Comment


                        #12
                        Hi Ian,

                        Thanks for your thorough thoughts on the simulation engine. I am the original author of this and its quite simple how it works.

                        For a buy example:

                        - When limit price == bid price mark the current bid volume (X)
                        - Start tracking trades that go off at the bid (Y)
                        - Once Y > X --> Start filling the limit order

                        This is how it has worked since 2003 and is the most conservative approach IMO.
                        RayNinjaTrader Customer Service

                        Comment


                          #13
                          Originally posted by NinjaTrader_Ray View Post
                          Hi Ian,

                          Thanks for your thorough thoughts on the simulation engine. I am the original author of this and its quite simple how it works.

                          For a buy example:

                          - When limit price == bid price mark the current bid volume (X)
                          - Start tracking trades that go off at the bid (Y)
                          - Once Y > X --> Start filling the limit order

                          This is how it has worked since 2003 and is the most conservative approach IMO.
                          Ray,

                          I appreciate the team over there looking into this for me. And to be fair, the logic you described I believe is present in most all cases. But nonetheless I have found what I believe to be exceptions to these cases that I would like the team to review. I provided Patrick something similar before, but I have since cleaned up my code and examples so it will be easier to follow.

                          I am enclosing the following:

                          1. A ninja-scrip strategy for NT 8 that will reproduce the behavior I have described.
                          2. An output that comes from the print statement of this strategy. I have highlighted the individual trade sequences that I see as having the issues. I ran this on just a few hours and captured around 20 or so trades that I believe are problematic because of the following:

                          A: The applicable bid / ask volume when the order was submitted = the same volume when the order got filled. I am running the volumes from the OnMarketData event handler for submitting the order, and using the OnExecutionUpdate event handler to confirm the fill and printing from each respectively.

                          B: I have highlighted the special cases where not only does the volume not change from submitting to fill, but the price level actually changes in the opposite direction after the fill. So this implies that not all of the pending orders would have filled.

                          As I mentioned previously this strategy targets the "weak side" of each price level. (When the price level moves up, there will be resting Ask limit orders from the previous level, but the bid limit orders do not initially come from a queue from another price level because bid limit orders can not be submitted above the market and likewise ask limit orders can not be submitted below the market. It is this "weak side" of each price level that the SIM engine has this behavior.

                          I would appreciate it if someone from the team can review my enclosed information and provide me with some feedback. I am happy to explain further detail if needed and happy to take this offline as well.

                          Thanks,

                          Ian
                          Attached Files

                          Comment


                            #14
                            Hello iantg,

                            Thank you for your response.

                            The issue with my test and your's is we printed out the GetCurrentBid, GetCurrentAsk, GetCurrentBidVolume, and the GetCurrentAskVolume. These pull from the bar and are not accurate to the actual MarketDataTypes for Ask and Bid in OnMarketData. Essentially we are looking at the Ask and Bid on the Bar which may not have updated if the last did not update. So no matter where in your strategy you are printing you need to be pulling the MarketDataTypes for Ask and Bid in OnMarketData for comparison in this case.

                            You can assign the MarketDataTypes for Ask and Bid in OnMarketData to doubles that you then call in OnBarUpdate or OnExecutionUpdate.

                            Please visit the following link on the MarketDataEventArgs: https://ninjatrader.com/support/help...aeventargs.htm

                            Please let me know if you have any questions. If needed I can provide a simplified version of your script with these adjustments as well.

                            Comment


                              #15
                              Patrick,

                              Thanks for your response and explanation. I am somewhat familiar with the data you mentioned. I developed the following simple code from the NT examples and played around with it some:


                              if (marketDataUpdate.MarketDataType == MarketDataType.Last)
                              {
                              if (marketDataUpdate.Price >= marketDataUpdate.Ask)
                              {
                              MDaskV = marketDataUpdate.Volume;
                              MDaskP = marketDataUpdate.Ask;
                              }


                              if (marketDataUpdate.Price >= marketDataUpdate.Bid)
                              {
                              MDbidV = marketDataUpdate.Volume;
                              MDbidP = marketDataUpdate.Bid;
                              }


                              lastprice = marketDataUpdate.Price;
                              }

                              I can work with these variables fairly easily, but my interpretation of these may be off. When I tested the marketDataUpdate.Volume variables I assumed this was showing a picture of the most recent transacted volume at either the bid or ask depending. So I can see how in retrospective you can see the past from a very granular level. But what I really need to be able to see somehow is the resting (not transacted volumes) as they update in the most granular form possible.

                              My previous understanding was GetCurrentBidVolume() would show a snapshot of all resting bid volumes out there, whereas calling a variable like MDbidV using my example code would show me the last transacted bid contract volume and not necessarily be the total aggregate resting volume for all bids. Maybe I am off here in my understanding of how to get the most granular total aggregate resting volumes. But any insight you have into this would be helpful.

                              Once I can get the most granular view of the total aggregate resting volumes, I can retest everything.

                              Thanks,

                              Ian

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by DJ888, 04-16-2024, 06:09 PM
                              6 responses
                              18 views
                              0 likes
                              Last Post DJ888
                              by DJ888
                               
                              Started by Jon17, Today, 04:33 PM
                              0 responses
                              1 view
                              0 likes
                              Last Post Jon17
                              by Jon17
                               
                              Started by Javierw.ok, Today, 04:12 PM
                              0 responses
                              6 views
                              0 likes
                              Last Post Javierw.ok  
                              Started by timmbbo, Today, 08:59 AM
                              2 responses
                              10 views
                              0 likes
                              Last Post bltdavid  
                              Started by alifarahani, Today, 09:40 AM
                              6 responses
                              41 views
                              0 likes
                              Last Post alifarahani  
                              Working...
                              X