> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ninjatrader.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ninjatrader.com/_mcp/server.

# Tick Charts

Tick charts use the same process as the [Market Data](/market-data) API: open and authorize a WebSocket first, then request chart data. The difference is in the chart description.

If you're following the [JavaScript tutorial](https://github.com/tradovate/example-api-js), see the [tick chart examples](https://github.com/tradovate/example-api-js/tree/main/tutorial/WebSockets/EX-11-Tick-Charts).

## Requesting Tick Charts

Build a request body with the `symbol`, `chartDescription`, and `timeRange` fields, as you would for any chart. For tick data, lock `elementSize` to 1 and set `underlyingType` to `"Tick"`:

```js
{
  "symbol": "ESU9",
  "chartDescription": {
    "underlyingType": "Tick",
    "elementSize": 1,
    "elementSizeUnit": "UnderlyingUnits"
  },
  "timeRange": {
    // ...
  }
}
```

Then call the standard `md/getChart` endpoint with this request. The server responds with the standard chart-data schema. Because unsubscribing requires the real-time subscription ID returned with the response, store the ID of each subscription you create so you can unsubscribe later.

## Data Stream Messages

A tick data stream message has the following structure. Tick prices and timestamps are stored relative to each packet's base values, so you reconstruct actual values by adding the tick's relative value to the packet's base:

```js
{
  "charts": [                     // array of packets
    {
      "id": 16335,                // subscription ID (historical or real-time, from the request response)
      "s": "db",                  // source of packet data
      "td": 20210718,             // trade date YYYYMMDD
      "bp": 11917,                // base price of the packet (in integer contract tick sizes);
                                  // tick prices are relative to this
      "bt": 1563421179735,        // base timestamp of the packet;
                                  // tick timestamps are relative to this
      "ts": 0.25,                 // tick size of the contract
      "tks": [                    // array of ticks in this packet
        {
          "t": 0,                 // tick relative timestamp; actual = packet.bt + tick.t
          "p": 0,                 // tick relative price (in tick sizes); actual = packet.bp + tick.p
          "s": 3,                 // tick size (tick volume); not the contract tick size (packet.ts)
          "b": -1,                // bid relative price (optional); actual = packet.bp + tick.b
          "a": 0,                 // ask relative price (optional); actual = packet.bp + tick.a
          "bs": 122.21,           // bid size (optional)
          "as": 28.35,            // ask size (optional)
          "id": 11768401          // tick ID
        }
      ]
    },
    {
      "id": 16335,
      "eoh": true                 // end-of-history flag: historical ticks are loaded,
                                  // and further packets will contain real-time ticks
    }
  ]
}
```

## Processing the Tick Stream

The following snippet converts a data stream message's packets into a list of actual ticks for consumption. Because tick stream data can arrive out of chronological order, it's the client's responsibility to store and sort the relevant portions of this data.

```js
function processTickChartMessage(msg) {
    const result = [];
    if (msg.charts && msg.charts.length) {
        for (let i = 0; i < msg.charts.length; ++i) {
            const packet = msg.charts[i];
            if (packet.eoh) { // end-of-history: historical ticks are loaded
            }
            else if (packet.tks && packet.tks.length) {
                for (let j = 0; j < packet.tks.length; ++j) {
                    const tick = packet.tks[j];

                    const timestamp = packet.bt + tick.t;   // actual tick timestamp
                    const price = packet.bp + tick.p;       // actual tick price

                    const bid = tick.bs && (packet.bp + tick.b);    // actual bid price (if bid size defined)
                    const ask = tick.as && (packet.bp + tick.a);    // actual ask price (if ask size defined)

                    result.push({
                        id: tick.id,
                        timestamp: new Date(timestamp),

                        price: price * packet.ts,           // tick price as contract price
                        size: tick.s,                       // tick size (tick volume)

                        bidPrice: bid && (bid * packet.ts), // bid price as contract price
                        bidSize: tick.bs,

                        askPrice: ask && (ask * packet.ts), // ask price as contract price
                        askSize: tick.as,
                    });
                }
            }
        }
    }
    return result;
}
```