> This page is for NT Prop.

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

## Using Tick Charts

### Requesting Tick Charts

To get Tick Chart data, you can use the same process described in the Market Data section. Just like with Market Data, you need to open and authorize a WebSocket first. If you're following the comprehensive JavaScript tutorial, you can find [tick chart examples here](https://github.com/tradovate/example-api-js/tree/main/tutorial/WebSockets/EX-11-Tick-Charts).

Just like requesting regular chart data, you must construct a request body with the `symbol`, `chartDescription`, and `timeRange` fields. However, you need to lock `elementSize` to 1 and set `underlyingType` to `Tick`. For example:

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

You call the standard `md/getChart` endpoint and pass the request to it. The server responds with the standard JSON object schema for chart data. Because an unsubscription request requires the real-time subscription ID sent with this response, store the ID of each subscription you create so you can properly unsubscribe later.

### Data Stream Messages

A typical data stream message has the following structure:

```js
{
    "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.
        }
    ]
};
```

Because tick prices and timestamps are sent as deltas from the packet's base values, you reconstruct them with `actual price = (bp + tick.p) * ts` and `actual time = bt + tick.t`. For example, with a base price `bp` of 11917, a contract tick size `ts` of 0.25, and a tick with `p` of 4, the actual price is `(11917 + 4) * 0.25 = 2980.25`. The deltas stay small because most ticks trade near the same price level, which keeps the payload compact compared to repeating full prices on every tick.

### Using the Tick Stream

The following code snippet is an example of how to process tick chart data stream messages and calculate actual ticks for client consumption.

The function takes a data stream message and converts its packets into a list of actual ticks. Usage of this function assumes that you'll be passing it the message data retrieved from the WebSocket. Because tick stream data can arrive out of chronological order, it is the client's responsibility to store and sort pertinent 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;
}
```

### Ordering Ticks Before Rendering

`processTickChartMessage` decodes each packet into actual ticks, but it doesn't sort them, and tick data can arrive out of chronological order for a few reasons:

* A single WebSocket message can carry multiple packets, each with its own base timestamp (`bt`), and the packets aren't guaranteed to be in order.
* Historical and real-time ticks can interleave around the end-of-history boundary. The `eoh` flag marks the transition, but the boundary isn't always clean.
* Network batching can deliver several packets in one burst, where the order within the burst depends on how the server assembled it.

If you plot ticks as they arrive, your chart can show gaps and false price spikes. Buffer incoming ticks, sort by timestamp with the tick ID as a tiebreaker, and only then render or aggregate them. Sort the historical batch when `eoh` arrives, then keep a small sort window for real-time ticks, which can also arrive slightly out of order within a burst.

```js
let tickBuffer = [];

function onChartMessage(msgData) {
  const ticks = processTickChartMessage(msgData);
  tickBuffer.push(...ticks);
}

function onEndOfHistory() {
  // Sort all historical ticks by timestamp, then by id as a tiebreaker
  tickBuffer.sort((a, b) => a.timestamp - b.timestamp || a.id - b.id);
  renderChart(tickBuffer);
}

function onRealtimeTick(ticks) {
  // Insert and keep the buffer sorted for real-time ticks
  tickBuffer.push(...ticks);
  tickBuffer.sort((a, b) => a.timestamp - b.timestamp || a.id - b.id);
  renderChart(tickBuffer);
}
```

This snippet re-sorts the whole buffer for clarity. A production implementation would bound the real-time sort window rather than re-sorting every tick.

To build N-tick or time-based bars from raw ticks, aggregate them after sorting. For the full charting pipeline (connection sharding, candle warehousing, and overlaying live data), see

[Charting Best Practices](/prop/overview/core-concepts/web-sockets/market-data/charting-best-practices)

.