Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Best practices on capturing Level 2 order book data through Ninjascript?

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

    Best practices on capturing Level 2 order book data through Ninjascript?

    I'm currently working in Ninjatrader 8. I have Level 2 Market Data on a security like IWM and I can see a market depth map in the background on a volumetric chart.

    Ideally, I want to capture this market depth map data in variables using Ninjascript/C# so I can utilize them in custom strategies. From what I've been able to gather, I can use something like this to get started:
    https://ninjatrader.com/support/help...vel_ii_dat.htm

    Another example is from the NT8 Help Guide on MarketDepth:
    https://ninjatrader.com/support/help...arketdepth.htm

    Beyond these, does anyone have any recommendations or best practices on how to capture market depth/order book related data?


    Thanks in advance!



    Last edited by Spiderbird; 02-02-2023, 11:11 PM. Reason: Forgot to add tags

    #2
    Hello Spiderbird,

    Those samples show how to capture the market depth/order book data. Could you clarify what specific data you are trying to capture if that sample is not doing what you wanted?

    The first link is the suggested example for working with level 2 data and the orderbook. That collects to a List and then its used from OnBarUpdate. You wouldn't need anything additional beyond that to work with the orderbook.



    JesseNinjaTrader Customer Service

    Comment


      #3
      Hi Jesse,

      After getting it up and running, it doesn't look like the sample code that was given​ (SampleLevel2Book_NT8.zip) in the "Creating your own Level II data book (Accessing market depth)" help page isn't quite working for me.

      Here's a screen recording of my experience. The output window on the left shows some entries in the Order Book for both bid and ask, along with the current price in real time (this was recorded during a live session). The chart on the right is cut off, but shows a blueprint chart bar with the Order Book information on the right. From what I can tell, the two sets of numbers don't match.



      I'm assuming there's some sort of setting in the indicator that's not configured correctly. I did a direct copy/paste from the SampleLevel2Book file and it builds the lists out as coded. But they aren't matching what the indicator is showing.

      For reference, I'm only looking at one security (IWM) for this example.

      Let me know what I should be double checking.

      Comment


        #4
        Hello Spiderbird,

        You won't be able to compare the output window against a chart for live data, they have different refresh rates. The script would see each update using the override which you may or may not see in the chart because it has a fixed refresh rate.

        JesseNinjaTrader Customer Service

        Comment


          #5
          Originally posted by NinjaTrader_Jesse View Post
          Hello Spiderbird,

          You won't be able to compare the output window against a chart for live data, they have different refresh rates. The script would see each update using the override which you may or may not see in the chart because it has a fixed refresh rate.
          I see. So which has the most up-to-date information against the order book? Should I look at the generated code or the chart?

          Comment


            #6
            Hello Spiderbird,

            They both use the same information/event, the chart just visually refreshes at a certain rate so you won't see as many updates as you would in the output window.

            If your doing something in code you would look at the output window to dictate what you program. The chart will show whatever you plot or render so it will ultimately be subject to whatever your code is rendering. The chart will then display the most recent data as its rendered it just wont directly match the output window which is forced to show every print you make.

            JesseNinjaTrader Customer Service

            Comment


              #7
              I understand. I'm more concerned about placing trades based on accurate market data and what I'm able to understand from the Order Book. So while the chart is a nice visual that gives a snapshot of the order book at a particular time (based on a fixed refresh rate), I'm ultimately wanting to use what I'm seeing in the output window (or the Level 2 window) as the closest representation of the order book at any given time.

              Would that be an accurate summary?

              Comment


                #8
                Hello Spiderbird,

                There is no discrepancy in accuracy here, they use the same information/events for the data. If you are programming something based on the order book you would use conditions in your code to detect whatever it is you wanted to use as a signal. The output window is forced to show every print that you make, each time you call print in code that's going to the output window so you have the full context there. The chart has a fixed interval of refresh so its going to show the values at the time of the refresh, that's still based on the underlying code which is using all of the level 2 events. There is no difference, what you are comparing is just not able to be compared visually because one is a fixed rate and the other is not.

                JesseNinjaTrader Customer Service

                Comment


                  #9
                  Originally posted by NinjaTrader_Jesse View Post
                  Hello Spiderbird,

                  There is no discrepancy in accuracy here, they use the same information/events for the data. If you are programming something based on the order book you would use conditions in your code to detect whatever it is you wanted to use as a signal. The output window is forced to show every print that you make, each time you call print in code that's going to the output window so you have the full context there. The chart has a fixed interval of refresh so its going to show the values at the time of the refresh, that's still based on the underlying code which is using all of the level 2 events. There is no difference, what you are comparing is just not able to be compared visually because one is a fixed rate and the other is not.
                  Interestingly enough, and after a couple of weeks of wrestling with this, I figured it out. I'm putting this here for others to use if they stumble upon it and to give you some perspective.

                  In terms of getting an exact match from the Level 2 book and it's data, you have to modify the sample code that NT provides in their Help Guide (Creating your own Level II data book (Accessing market depth)​ - SampleLevel2Book_NT8.zip) so that an event is fired *every time* there is an update to the order book.

                  Yes, it can be CPU intensive and slow things down, but that wasn't my experience. I had about a .05 sec lag time at the most around 10:00 AM this morning, and the output window was reflecting the Level 2 window bid/ask columns perfectly. (I recorded about 30 seconds of video to double check since my I can't track the updates that fast with my eyes. )

                  The next step down is getting Level 2 updates is using the example code (again, SampleLevel2Book_NT8.zip) that utilizes OnBarUpdate. This is all fine and well, but volumes start getting inaccurate in the bid/ask books around the bid and ask points. It's a snapshot of the Level 2 book at a particular time, so it has some accurate information but it's a mixed batch.

                  Finally, there's the Order Flow Market Depth Map on a volumetric chart. It's good for visually figuring out how the depth map is spread out over prices, and has lots of options to modify visuals and such. But in terms of representing the most up-to-date price/volume information from the Level 2 Book, the information presented through the indicator is either slow (via the 200ms refresh default) or outright incorrect (via lag).

                  Even when you adjust the screen refresh to 10ms (which I found through an add-on called 'TickRefresh'), there's still a bit of inaccuracy in the presented data. I ultimately used the indicator to figure out how to code my trade targets, but that was the only utility it had for me.

                  Bottom line is that if you want the most accurate representation of Level 2 data outside a Level 2 generated window, you have to tweak the sample code mentioned above to fire on every update/add event in the order book and print to an output window (or some UI equivalent).

                  Here's what I ended up with:
                  Code:
                  protected override void OnMarketDepth(MarketDepthEventArgs e)
                          {
                              // protect e.Instrument.MarketDepth.Asks and e.Instrument.MarketDepth.Bids against in-flight changes
                              lock (e.Instrument.SyncMarketDepth)
                              {
                                  List<LadderRow> rows    = (e.MarketDataType == MarketDataType.Ask ? askRows: bidRows);
                                  LadderRow row            = new LadderRow { Price = e.Price, Volume = e.Volume };
                  
                                  if (e.Operation == Operation.Add || (e.Operation == Operation.Update
                                      && (rows.Count == 0 || rows.Count <= e.Position)))
                                  {
                                      if (rows.Count <= e.Position)
                                          rows.Add(row);
                                      else
                                          rows.Insert(e.Position, row);
                                  }
                                  else if (e.Operation == Operation.Remove && rows.Count > e.Position)
                                  {
                                      rows.RemoveAt(e.Position);
                                  }
                                  else if (e.Operation == Operation.Update)
                                  {
                                      if (rows[e.Position] == null)
                                      {
                                          rows[e.Position] = row;
                                      }
                                      else
                                      {
                                          rows[e.Position].Price            = e.Price;
                                          rows[e.Position].Volume            = e.Volume;
                                      }
                                  }
                  
                                  print_marketDepthInfo();
                              }
                          }
                  
                          private void print_marketDepthInfo()
                          {
                              if (askRows.Count >= 6 && bidRows.Count >= 6)
                              {
                                  // Prints the L2 Ask Book we created. Cycles through the whole List and prints the contained objects.
                                  Print("Ask Book:");
                                  Print("There are " + askRows.Count + " entries in the ask book.");
                  
                                  max_askIndex = Math.Min(askRows.Count - 1, 7);
                                  for (int idx = max_askIndex; idx >= 0; idx--)
                                  {  Print("Ask Price=" + askRows[idx].Price + " Volume=" + askRows[idx].Volume + " Position=" + idx);  }
                  
                                  askGap = Math.Round(askRows[0].Price - GetCurrentAsk(),2);
                                  bidGap = Math.Round(GetCurrentAsk() - bidRows[0].Price,2);
                  
                                  Print("-----");
                                  Print("Ask Gap: " + askGap);
                                  Print("Ask: " + GetCurrentAsk());
                                  Print("-----");
                                  Print("Price: " + Close[0]);
                                  Print("-----");
                                  Print("Bid: " + GetCurrentBid());
                                  Print("Bid Gap: " + bidGap);
                                  Print("-----");
                  
                                  // Prints the L2 Bid Book we created. Cycles through the whole List and prints the contained objects.
                                  Print("Bid Book");
                                  Print("There are " + bidRows.Count + " entries in the bid book.");
                  
                                  max_bidIndex = Math.Min(bidRows.Count - 1, 6);
                                  for (int idx = 0; idx <= max_bidIndex; idx++)
                                  { Print("Bid Price=" + bidRows[idx].Price + " Volume=" + bidRows[idx].Volume + " Position=" + idx); }
                              }
                          }​

                  Hope this helps!
                  Last edited by Spiderbird; 03-09-2023, 11:39 PM. Reason: Better grammar and wording.

                  Comment

                  Latest Posts

                  Collapse

                  Topics Statistics Last Post
                  Started by jxs_xrj, 01-12-2020, 09:49 AM
                  6 responses
                  3,290 views
                  1 like
                  Last Post jgualdronc  
                  Started by Touch-Ups, Today, 10:36 AM
                  0 responses
                  9 views
                  0 likes
                  Last Post Touch-Ups  
                  Started by geddyisodin, 04-25-2024, 05:20 AM
                  11 responses
                  62 views
                  0 likes
                  Last Post halgo_boulder  
                  Started by Option Whisperer, Today, 09:55 AM
                  0 responses
                  8 views
                  0 likes
                  Last Post Option Whisperer  
                  Started by halgo_boulder, 04-20-2024, 08:44 AM
                  2 responses
                  25 views
                  0 likes
                  Last Post halgo_boulder  
                  Working...
                  X