• C#
  • Java
  • VB
  • C++
  • Python
Contact us
Market Depth (Level II)

Market depth data, also known as level II, represents an instrument's order book. Via the TWS API it is possible to obtain this information with the IBApi.EClient.reqMarketDepth function (Note: it is named reqMktDepth in Java, C++, and Python). Unlike Top Market Data (Level I), market depth data is sent without sampling nor filtering, however we cannot guarantee that every price quoted for a particular security will be displayed when you invoke IBApi.EClient.reqMarketDepth. In particular, odd lot orders are not included. Beginning in API v974 and TWS v974 it is possible to Smart-route a IBApi.EClient.reqMarketDepth request to receive aggregated data from all available exchanges, similar to the TWS BookTrader display.

Requesting

The IBApi.EClient.reqMarketDepth method receives a request identifier (or ticker Id) with which to identify the incoming data, the IBApi.Contract for which we want to pull this information and the number of rows or depth level that is required. In case the market depth is smaller than the requested number of rows, the TWS will simply return the available entries.

  • client.reqMarketDepth(2001, ContractSamples.EurGbpFx(), 5, false, null);
  • client.reqMktDepth(2001, ContractSamples.EurGbpFx(), 5, false, null);
  • client.reqMarketDepth(2001, ContractSamples.EurGbpFx(), 5, False, Nothing)
  • m_pClient->reqMktDepth(2001, ContractSamples::EurGbpFx(), 5, false, TagValueListSPtr());
  • 1  self.reqMktDepth(2001, ContractSamples.EurGbpFx(), 5, False, [])

Market Maker or Exchange

Market depth will be returned via the IBApi.EWrapper.updateMktDepth or the IBApi.EWrapper.updateMktDepthL2 callback. The two functions differ in that when there is a market maker or exchange identifier to be returned market depth data will be relayed back through IBApi.EWrapper.updateMktDepthL2. Otherwise it is returned to IBApi.EWrapper.updateMktDepth. For example, ARCA only has ARCA itself as a Market Maker. Therefore when requesting market depth data from ARCA, the data will be relayed back via IBApi.EWrapper.updateMktDepth. On the other hand, with ISLAND (the ECN for NASDAQ) market maker information is provided, so the market maker MPID will be relayed back via IBApi.EWrapper.updateMktDepthL2. The market maker MPID is reported in the 'marketMaker' string argument of the callback function. If the isSmartDepth boolean (available with API v974+) is True, the marketMaker field will indicate the exchange from which the quote originates. Otherwise it indicates the MPID of a market maker.

To check which exchanges offer deep book data, the function IBApi::EClient::reqMktDepthExchanges can be invoked. It will return a list of exchanges from where market depth is available if the user has the appropriate market data subscription.

  • client.reqMktDepthExchanges();
  • client.reqMktDepthExchanges();
  • client.reqMktDepthExchanges()
  • m_pClient->reqMktDepthExchanges();
  • 1  self.reqMktDepthExchanges()

API 'Exchange' fields for which a market depth request would return market maker information and result in a callback to IBApi::EWrapper::updateMktDepthL2 will be indicated in the results from the IBApi::EWrapper::mktDepthExchanges field by a 'True' value in the 'isL2' field:

  • public void mktDepthExchanges(DepthMktDataDescription[] depthMktDataDescriptions)
    {
    Console.WriteLine("Market Depth Exchanges:");
    foreach (var depthMktDataDescription in depthMktDataDescriptions)
    {
    Console.WriteLine("Depth Market Data Description: Exchange: {0}, Security Type: {1}, Listing Exch: {2}, Service Data Type: {3}, Agg Group: {4}",
    depthMktDataDescription.Exchange, depthMktDataDescription.SecType,
    depthMktDataDescription.ListingExch, depthMktDataDescription.ServiceDataType,
    depthMktDataDescription.AggGroup != Int32.MaxValue ? depthMktDataDescription.AggGroup.ToString() : ""
    );
    }
    }
  • @Override
    public void mktDepthExchanges(DepthMktDataDescription[] depthMktDataDescriptions) {
    for (DepthMktDataDescription depthMktDataDescription : depthMktDataDescriptions) {
    System.out.println("Depth Mkt Data Description. Exchange: " + depthMktDataDescription.exchange() +
    ", ListingExch: " + depthMktDataDescription.listingExch() +
    ", SecType: " + depthMktDataDescription.secType() +
    ", ServiceDataType: " + depthMktDataDescription.serviceDataType() +
    ", AggGroup: " + depthMktDataDescription.aggGroup()
    );
    }
    }
  • Public Sub mktDepthExchanges(depthMktDataDescriptions As DepthMktDataDescription()) Implements EWrapper.mktDepthExchanges
    Console.WriteLine("Market Depth Exchanges:")
    Dim aggGroup As String
    For Each depthMktDataDescription In depthMktDataDescriptions
    If depthMktDataDescription.AggGroup <> Integer.MaxValue Then
    aggGroup = depthMktDataDescription.AggGroup
    Else
    aggGroup = ""
    End If
    Console.WriteLine("Depth Market Data Descriprion. Exchange: " & depthMktDataDescription.Exchange &
    " Security Type: " & depthMktDataDescription.SecType &
    " Listing Exch: " & depthMktDataDescription.ListingExch &
    " Service Data Type: " & depthMktDataDescription.ServiceDataType &
    " Agg Group: " & aggGroup)
    Next
    End Sub
  • void TestCppClient::mktDepthExchanges(const std::vector<DepthMktDataDescription> &depthMktDataDescriptions) {
    printf("Mkt Depth Exchanges (%lu):\n", depthMktDataDescriptions.size());
    for (unsigned int i = 0; i < depthMktDataDescriptions.size(); i++) {
    printf("Depth Mkt Data Description [%d] - exchange: %s secType: %s listingExch: %s serviceDataType: %s aggGroup: %s\n", i,
    depthMktDataDescriptions[i].exchange.c_str(),
    depthMktDataDescriptions[i].secType.c_str(),
    depthMktDataDescriptions[i].listingExch.c_str(),
    depthMktDataDescriptions[i].serviceDataType.c_str(),
    depthMktDataDescriptions[i].aggGroup != INT_MAX ? std::to_string(depthMktDataDescriptions[i].aggGroup).c_str() : "");
    }
    }
  • 1  def mktDepthExchanges(self, depthMktDataDescriptions:ListOfDepthExchanges):
    2  super().mktDepthExchanges(depthMktDataDescriptions)
    3  print("MktDepthExchanges:")
    4  for desc in depthMktDataDescriptions:
    5  print("DepthMktDataDescription.", desc)

Receiving

Initially, all requested/available rows will be delivered to the client application. As market moves however these rows will inevitably change. To keep the client's order book consistent, the TWS will send updates not only informing which row is to be updated but also the operation to perform in the row: insert (0), update (1) or remove (2).

  • public class EWrapperImpl : EWrapper
    {
    ...
    public virtual void updateMktDepth(int tickerId, int position, int operation, int side, double price, long size)
    {
    Console.WriteLine("UpdateMarketDepth. " + tickerId + " - Position: " + position + ", Operation: " + operation + ", Side: " + side + ", Price: " + price + ", Size: " + size);
    }
    ...
    public virtual void updateMktDepthL2(int tickerId, int position, string marketMaker, int operation, int side, double price, long size, bool isSmartDepth)
    {
    Console.WriteLine("UpdateMarketDepthL2. " + tickerId + " - Position: " + position + ", Operation: " + operation + ", Side: " + side + ", Price: " + price + ", Size: " + size + ", isSmartDepth: " + isSmartDepth);
    }
  • public class EWrapperImpl implements EWrapper {
    ...
    @Override
    public void updateMktDepth(int tickerId, int position, int operation,
    int side, double price, long size) {
    System.out.println("UpdateMarketDepth. "+tickerId+" - Position: "+position+", Operation: "+operation+", Side: "+side+", Price: "+price+", Size: "+size+"");
    }
    ...
    @Override
    public void updateMktDepthL2(int tickerId, int position,
    String marketMaker, int operation, int side, double price, long size, boolean isSmartDepth) {
    System.out.println("UpdateMarketDepthL2. "+tickerId+" - Position: "+position+", Operation: "+operation+", Side: "+side+", Price: "+price+", Size: "+size+", isSmartDepth: "+isSmartDepth);
    }
  • Public Class EWrapperImpl
    Implements EWrapper
    ...
    Public Sub updateMktDepth(tickerId As Integer, position As Integer, operation As Integer, side As Integer, price As Double, size As Long) Implements IBApi.EWrapper.updateMktDepth
    Console.WriteLine("UpdateMarketDepth. " & CStr(tickerId) & " - Position: " & CStr(position) & ", Operation: " & CStr(operation) & ", Side: " & CStr(side) &
    ", Price: " & CStr(price) & ", Size: " & CStr(size))
    End Sub
    ...
    Public Sub updateMktDepthL2(tickerId As Integer, position As Integer, marketMaker As String, operation As Integer, side As Integer, price As Double, size As Long, isSmartDepth As Boolean) Implements IBApi.EWrapper.updateMktDepthL2
    Console.WriteLine("UpdateMarketDepthL2. " & CStr(tickerId) & " - Position: " & CStr(position) & ", Operation: " & CStr(operation) & ", Side: " & CStr(side) &
    ", Price: " & CStr(price) & ", Size: " & CStr(size) & ", isSmartDepth: " & CStr(isSmartDepth))
    End Sub
  • class TestCppClient : public EWrapper
    {
    ...
    void TestCppClient::updateMktDepth(TickerId id, int position, int operation, int side,
    double price, long long size) {
    printf( "UpdateMarketDepth. %ld - Position: %d, Operation: %d, Side: %d, Price: %g, Size: %lld\n", id, position, operation, side, price, size);
    }
    ...
    void TestCppClient::updateMktDepthL2(TickerId id, int position, const std::string& marketMaker, int operation,
    int side, double price, long long size, bool isSmartDepth) {
    printf( "UpdateMarketDepthL2. %ld - Position: %d, Operation: %d, Side: %d, Price: %g, Size: %lld, isSmartDepth: %d\n", id, position, operation, side, price, size, isSmartDepth);
    }
  • 1 class TestWrapper(wrapper.EWrapper):
    ...
    1  def updateMktDepth(self, reqId: TickerId, position: int, operation: int,
    2  side: int, price: float, size: int):
    3  super().updateMktDepth(reqId, position, operation, side, price, size)
    4  print("UpdateMarketDepth. ReqId:", reqId, "Position:", position, "Operation:",
    5  operation, "Side:", side, "Price:", price, "Size:", size)
    ...
    1  def updateMktDepthL2(self, reqId: TickerId, position: int, marketMaker: str,
    2  operation: int, side: int, price: float, size: int, isSmartDepth: bool):
    3  super().updateMktDepthL2(reqId, position, marketMaker, operation, side,
    4  price, size, isSmartDepth)
    5  print("UpdateMarketDepthL2. ReqId:", reqId, "Position:", position, "MarketMaker:", marketMaker, "Operation:",
    6  operation, "Side:", side, "Price:", price, "Size:", size, "isSmartDepth:", isSmartDepth)
    7 

Canceling

To cancel an active market depth request simply invoke the IBApi.EClient.cancelMktDepth passing in the request's identifier.

  • client.cancelMktDepth(2001, false);
  • client.cancelMktDepth(2001, false);
  • client.cancelMktDepth(2001, False)
  • m_pClient->cancelMktDepth(2001, false);
  • 1  self.cancelMktDepth(2001, False)
    2  self.cancelMktDepth(2002, True)

Limitations

Given the potentially high amount of data being sent, market depth request's are much more limited. Just as with historical data requests, the amount of active depth requests is related to the amount of market data lines, with a minimum of three and maximum of 60:

Number of linesMax. Requests
0 - 3993
400 - 4994
500 - 5995
600 - 6996
etc...


Please note that there is a limit of only 10 quote boosters packs per account and rest of the market data lines are allocated using equity and commissions. Also note that each quote booster pack provides 100 market data lines.