Fathom Footprint
Bid and ask volume at each price level, bar by bar.
What the indicator shows
- Each bar is split into price levels. At each level, the volume of aggressive selling, filled at the bid, and the volume of aggressive buying, filled at the ask; on the Fathom chart, the bid is on the left and the ask on the right.
- The POC is the level with the most volume traded in the bar; the Fathom chart frames it.
- A level’s delta (ask minus bid) shows which side was more aggressive at that price. The sum over the levels gives the bar’s delta.
How to use it
- Spot absorption: heavy aggressive volume at an extreme without price moving on shows passive orders soaking up the aggression.
- Follow where the POC sits: at the top of the bar, volume traded at the highs; at the bottom, at the lows.
- Compare bid and ask on neighboring levels to see where one side takes over. The
imbalancesindicator keeps only those zones. - Raise
groupTicksfor a more condensed reading on a fast instrument.filterMinandfilterMaxonly count trades of a given size, for example large lots.
How it is computed
- Bars last
timeframeseconds, from 1 second to 1 hour, and start on multiples oftimeframefrom midnight UTC (9:30, 9:35… with 5 minutes). A bar with no counted trade is not sent. - Levels are aligned on multiples of
groupTicksticks: a level’s price is the bottom of its group, so withgroupTicks: 4the first level can be below the bar’slow. levelsvolumes only count trades whose volume is betweenfilterMinandfilterMax. The OHLC (open,high,low,close) is computed on all the bar’s trades, counted or not.- A level’s bid volume comes from aggressive sellers (hitting the bid), its ask volume from aggressive buyers. Level volume =
l[1] + l[2] + (l[3] ?? 0); delta =l[2] − l[1].
When it updates
- The bar is sent once, final, on the first trade of a later bar; nothing is sent while it is in progress. The last bar before the 5:00 pm New York break therefore only arrives at the 6:00 pm reopen, and Friday’s last bar on Sunday evening.
- No reset: each bar is independent.
- Identifier
footprint- Objects received
bar- Historical cost
- 1 compute unit per trade
Subscribe
The same params work in real time, on historical data and in exports. Parameters you omit take their default values.
{
"t": "subscribe",
"sub": "footprint",
"instrument": "NQ.front",
"indicator": "footprint",
"params": {
"filterMax": 0,
"filterMin": 0,
"groupTicks": 1,
"timeframe": 15
},
"mode": "live",
"from": "live"
}Parameters
As soon as a parameter differs from its default, the subscription counts as a custom configuration. To change a parameter, open a new subscription.
| Parameter | Type | Default | Allowed values | Unit | Description |
|---|---|---|---|---|---|
filterMax | integer | 0 | 0 to 1,000,000 | contracts | Maximum volume for a trade to be counted; 0 = unlimited, otherwise at least filterMin. |
filterMin | integer | 0 | 0 to 1,000,000 | contracts | Minimum volume for a trade to be counted; 0 = no minimum. |
groupTicks | integer | 1 | 1 to 100 | ticks | Number of ticks grouped into each price level. |
timeframe | integer | 15 | 1 · 5 · 10 · 15 · 30 · 60 · 120 · 180 · 300 · 600 · 900 · 1800 · 3600 | — | Bar duration in seconds: from 1 second (1) to 1 hour (3600). |
Objects received
Each object keeps the same id from one update to the next. Its data is in the data field. Prices are in ticks: multiply them by the instrument’s tick size (GET /v1/instruments) to get points.
bar sent final
Finished bar that starts at barStartNs, sent on the first trade of a later bar.
id format: <barStartNs>. Each object is sent once, final (final: true).
| Field | Type | Unit | Present | Description |
|---|---|---|---|---|
close | integer | ticks | always | Close: price of the bar’s last trade, counted or not. |
high | integer | ticks | always | High of the bar, over all its trades. |
levels | array | — | always | Levels by ascending price: [price (ticks), bid volume (contracts), ask volume (contracts)], plus a 4th element when the level holds trades whose aggressor is not identified (their volume, in contracts): [122008, 14, 3, 2] counts 14 contracts at the bid, 3 at the ask and 2 without an identified aggressor. Bid volume comes from aggressive sellers (hitting the bid), ask volume from aggressive buyers. A level’s price is the bottom of its groupTicks-tick group. Level volume = l[1] + l[2] + (l[3] ?? 0); delta = l[2] − l[1]. |
low | integer | ticks | always | Low of the bar, over all its trades. |
open | integer | ticks | always | Open: price of the bar’s first trade, counted or not. |
poc | integer | ticks | always | Price of the bar’s highest-volume level. On a tie: the lowest of the tied levels at or above the open’s level, otherwise the highest of those below it. |
Example messages
Messages as the WebSocket sends them to the subscription above, taken from a real NQ session. Historical data returns the same objects, with the same cursor, without the sub field.
{
"sub": "footprint",
"cursor": "20720.0.0",
"t": "upsert",
"id": "1790207985000000000",
"final": true,
"ts": "1790208000058746563",
"data": {
"open": 123032,
"high": 123032,
"low": 123012,
"close": 123014,
"levels": [
[
123012,
1,
0
],
[
123014,
0,
1
],
[
123015,
1,
0
],
[
123016,
2,
0
],
[
123017,
1,
0
],
[
123018,
1,
0
],
[
123020,
3,
1
],
[
123022,
1,
0
],
[
123024,
1,
0
],
[
123025,
0,
1
],
[
123026,
0,
1
],
[
123027,
0,
2
],
[
123032,
0,
1
]
],
"poc": 123020
}
}JSON schemas
To validate or type your data: the JSON schemas of the parameters and of data, also available from GET /v1/indicators. The SDK can generate types from them.
Parameters schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://fathomcharts.com/schemas/footprint/params.json",
"type": "object",
"additionalProperties": false,
"properties": {
"filterMax": {
"description": "Maximum volume for a trade to be counted; 0 = unlimited, otherwise at least `filterMin`.",
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 1000000
},
"filterMin": {
"description": "Minimum volume for a trade to be counted; 0 = no minimum.",
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 1000000
},
"groupTicks": {
"description": "Number of ticks grouped into each price level.",
"type": "integer",
"default": 1,
"minimum": 1,
"maximum": 100
},
"timeframe": {
"description": "Bar duration in seconds: from 1 second (`1`) to 1 hour (`3600`).",
"type": "integer",
"default": 15,
"enum": [
1,
5,
10,
15,
30,
60,
120,
180,
300,
600,
900,
1800,
3600
]
}
}
}data field schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://fathomcharts.com/schemas/footprint/data.json",
"title": "bar",
"type": "object",
"additionalProperties": false,
"properties": {
"close": {
"description": "Close: price of the bar’s last trade, counted or not.",
"type": "integer"
},
"high": {
"description": "High of the bar, over all its trades.",
"type": "integer"
},
"levels": {
"description": "Levels by ascending price: `[price (ticks), bid volume (contracts), ask volume (contracts)]`, plus a 4th element when the level holds trades whose aggressor is not identified (their volume, in contracts): `[122008, 14, 3, 2]` counts 14 contracts at the bid, 3 at the ask and 2 without an identified aggressor. Bid volume comes from aggressive sellers (hitting the bid), ask volume from aggressive buyers. A level’s price is the bottom of its `groupTicks`-tick group. Level volume = `l[1] + l[2] + (l[3] ?? 0)`; delta = `l[2] − l[1]`.",
"type": "array",
"items": {
"type": "array",
"maxItems": 4,
"minItems": 3,
"prefixItems": [
{
"type": "integer"
},
{
"type": "integer"
},
{
"type": "integer"
},
{
"type": "integer"
}
]
}
},
"low": {
"description": "Low of the bar, over all its trades.",
"type": "integer"
},
"open": {
"description": "Open: price of the bar’s first trade, counted or not.",
"type": "integer"
},
"poc": {
"description": "Price of the bar’s highest-volume level. On a tie: the lowest of the tied levels at or above the open’s level, otherwise the highest of those below it.",
"type": "integer"
}
},
"required": [
"open",
"high",
"low",
"close",
"levels",
"poc"
]
}