Bid-Ask Volume

I am struggling to understand how I could get bid/ask volume at each traded price during a given period through the API. I am automating a short-term momentum strategy that in part identifies imbalances between orders at the bid/ask. I can view this information on the bid-ask chart type through the Tradovate software so I know it is available.

You can use Realtime Market Data to stream these values to you live. You need to do a few things to open the socket:

  1. Ensure you have API Access and a subscription to realtime market data.

  2. Find a solution for your environment. If you’re programming using JavaScript in a browser, the WebSocket class constructor is a built-in. If you’re using Node.js, use node’s ws package to mimic the standard WebSocket api. If you’re using another language you’ll have to do some research, but as far as I know most languages have an implementation of websockets (if they’ve ever been used on the web anyway). Don’t forget to reference our WebSockets docs here for more details about our websockets API.

  3. Connect a websocket to the Tradovate Market Data API. Typical socket implementations will connect upon construction:

const mySocket = new WebSocket('wss://md.tradovateapi.com/v1/websocket')
  1. Authorize the socket. You do this by using an accessToken that you’ve retrieved using the standard Tradovate REST API.
//imaginary way to get accessToken, not implemented here
const { accessToken } = await myAccessTokenRequestFn(credentials) 
//construct the message. We use newlines as param delimiters
const authMessage= `authorize\n1\n\n${accessToken}`
//send the message to the server.
mySocket.send(authMessage) //=> [{i:1, s:200}] if successful
  1. Implement heartbeats. This is a whole topic in itself, but the idea is send a heartbeat to the server roughly every 2.5s to keep the connection alive. Don’t respond to server heartbeats directly, as when the server is transmitting data it will not emit heartbeats (but you’re still responsible for sending them). Just measure the time since the last message was received and send a heartbeat in the case that it’s been 2.5s or greater. A client side heartbeat looks like this:
mySocket.send('[]')
  1. Now you have a connection that will live for the length of your token’s lifetime. You can retrieve bid and ask data from md/getChart which is detailed here. Look at the bidVolume and offerVolume fields of each incoming chart data bar object. Alternatively, you can use md/subscribeDom to get more details about the bid/ask on a per-price basis. You can learn more about the DOM approach here.

Additional notes:

  • Keep in mind, data is ordered by timestamp. For charts, incoming data with the same timestamp came from the same bar. If you receive data with the same timestamp be sure to replace the previous data that had that timestamp. When data is coming to you live, it comes as a snapshot of what has happened so far - it is pre-aggregated. Therefore you don’t need to aggregate the incoming data, just replace the latest data until you receive a packet with a new timestamp. When this happens, you know you’ve moved into a new candle/bar.

  • Also for chart data, watch for the { eoh: true } end-of-history flag object. In the scenario you’re looking up historical data, this means you’ve either hit the limit of transmitted data, or you’ve reached the end of your request’s timeframe. If you’re requesting asMuchAsElements: n in your timeRange, the eoh flag represents that you’ve retrieved n || max packets of data. For this request type it also signals the start of realtime data.

Thanks for all the information–I’ve had a Node.js script running for a while off of data through the API. I probably should have clarified that my question is more of a conceptual one about how exactly bid and offer volume work towards the volume profile of a given period than the logistics of using the API. In order to get that 1 min or 5 min volume profile like a footprint chart, would I record the bid and offer volumes through getChart at the current price and then just take the most recent values of the bid and offer volume at the most recent time a price was traded during that candle? For instance, if a price trades at a certain level 5 times during a 1-minute candle, would the bidVolume property of that most recently received object when price last traded at that level contain the aggregate bidVolume for every time price has traded at that price in the past minute? I’m essentially trying to mimic the data often seen in the DOM that shows the number of contracts bought and sold at each price but I don’t believe that data is available through subscribeDom.

subscribeDom should update on every tick, so you can compare previous and next data to see what changed. You’ll mostly be able to see stop and limit orders by watching the DOM. You can get a feel for market orders filling those stops and limits by watching the tick stream. Use getChart with an elementSize of 1 and underlyingType of 'Tick' to get raw ticks - the packets look weird by the way. I’d suggest transforming the data into something more useful right away. Here’s a sample of what they might look like raw (from out docs):

{
  "charts": [                     // Array of packets.
    {
      "id": 16335,            // Subscription ID, the same as historical/real-time subscription IDs from request response.
      "s": "db",              // Source of packet data.
      "td": 20210718,         // Trade date YYYYMMDD.
      "bp": 11917,            // Base price of the packet (integer number of contract tick sizes).
                              // Tick prices are calculated as relative from this one.
      "bt": 1563421179735,    // Base timestamp of the packet.
                              // Tick timestamps are calculated as relative from this value.
      "ts": 0.25,             // Tick size of the contract for which the tick chart is requested.
      "tks": [                // Array of ticks of this packet.
        {
          "t": 0,         // Tick relative timestamp.
                          // Actual tick timestamp is packet.bt + tick.t
          "p": 0,         // Tick relative price (in contract tick sizes).
                          // Actual tick price is packet.bp + tick.p
          "s": 3,         // Tick size (seems more proper name should be tick volume).
                          // Please don't confuse with contract tick size (packet.ts).
          "b": -1,        // Bid relative price (optional).
                          // Actual bid price is packet.bp + tick.b
          "a": 0,         // Ask relative price (optional).
                          // Actual ask price is packet.bp + tick.a
          "bs": 122.21,   // Bid size (optional).
          "as": 28.35,    // Ask size (optional).
          "id": 11768401  // Tick ID
        },
        ...
      ]
    },
    // Multiple packets are possible...
    {
      "id": 16335,
      eoh: true               // End of history flag.
                              // If the request time range assumes historical data,
                              // this flag indicates that historical ticks are loaded and
                              // further packets will contain real-time ticks.
    }
  ]
}

Thanks so much, I had no idea the tick stream existed. Took me a while to learn what all the returned data meant but I just now matched my API-generated footprint charts with the ones I can see in the Tradovate software. I was getting the limit orders shown in the as/bs fields confused with market orders at those prices for a while.

1 Like