Fathom Zones
See where the market pushes hard. Volume and time spent in each bar bring effort zones to the surface.
Computed on 40-tick range bars: the trade that exceeds the range opens the next bar, and so does a new session. A run of at least two valid effort bars (moderate delta, bounded effort, short duration) forms a zone when it breaks. The zone extends for 20 bars, or until a bar body crosses it. Publishes closed bars, the 20-period exponential moving average and the zones. The computation restarts every week.
- Identifier
effort-zones- Calculation
- Since the start of the week
- Historical cost
- 1 unit per trade
Subscribe
The same params work in real time, on historical data and in exports. Any you leave out take their default value.
{
"t": "subscribe",
"sub": "effort-zones",
"instrument": "NQ.front",
"indicator": "effort-zones",
"params": {},
"mode": "live",
"from": "live"
}Parameters
This indicator has no parameters. Send "params": {}.
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 complete
Closed range bar (index counted from the start of the week).
Format of the id: bar:<weekStart>:<index>. Each object is sent once, already complete (final: true).
| Field | Type | Unit | Present | Description |
|---|---|---|---|---|
close | integer | ticks | always | Close. |
high | integer | ticks | always | High. |
low | integer | ticks | always | Low. |
open | integer | ticks | always | Open. |
start | string | timestamp (ns) | always | ts_event of the bar’s first trade. |
ema sent complete
Moving average point at the close of bar index.
Format of the id: ema:<weekStart>:<index>. Each object is sent once, already complete (final: true).
| Field | Type | Unit | Present | Description |
|---|---|---|---|---|
value | number | ticks | always | 20-period exponential moving average of the closes, taken at the close of bar index. |
zone updated continuously
Zone; provisional while it is active.
Format of the id: zone:<weekStart>:<startIndex>. While it changes, the object is sent with final: false, and each new version fully replaces the previous one. Its last version carries final: true.
| Field | Type | Unit | Present | Description |
|---|---|---|---|---|
active | boolean | — | always | The zone is still being extended. |
end | integer | bars | sometimes | Index of the last bar; absent while the zone is active. |
high | number | ticks | always | Top of the zone. |
low | number | ticks | always | Bottom of the zone. |
side | string · buy | sell | — | always | Side of the effort that formed the zone. |
start | integer | bars | always | Index of the range bar that confirmed the zone. |
Calculation warm-up
The calculation starts over at the beginning of every week (Saturday 00:00 UTC). During the weekend close it keeps the state of the past week; objects still open are finalized on the first trade of the new week. If nobody is using your parameters yet, the server first recomputes the week since its start (status warming), then goes live (status live).
Historical data follows the same rules: a query over a past period returns exactly what the real-time stream published.
Example messages
Messages received with the subscription parameters above. Over WebSocket, each message also carries sub and cursor.
{
"t": "upsert",
"id": "bar:2026-09-19:2173",
"final": true,
"ts": "1790208062570437091",
"data": {
"start": "1790207999116757165",
"open": 123012,
"high": 123044,
"low": 123004,
"close": 123006
}
}{
"t": "upsert",
"id": "ema:2026-09-19:2173",
"final": true,
"ts": "1790208062570437091",
"data": {
"value": 123066.85128768138
}
}{
"t": "upsert",
"id": "zone:2026-09-19:2348",
"final": false,
"ts": "1790230140362667235",
"data": {
"side": "buy",
"high": 122357,
"low": 122258,
"active": true,
"start": 2348
}
}{
"t": "upsert",
"id": "zone:2026-09-19:2348",
"final": true,
"ts": "1790231072263560767",
"data": {
"side": "buy",
"high": 122357,
"low": 122258,
"active": false,
"start": 2348,
"end": 2369
}
}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/effort-zones/params.json",
"type": "object",
"additionalProperties": false,
"properties": {}
}Schema of the data field
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://fathomcharts.com/schemas/effort-zones/data.json",
"anyOf": [
{
"title": "bar",
"type": "object",
"additionalProperties": false,
"required": [
"start",
"open",
"high",
"low",
"close"
],
"properties": {
"close": {
"type": "integer",
"description": "Close."
},
"high": {
"type": "integer",
"description": "High."
},
"low": {
"type": "integer",
"description": "Low."
},
"open": {
"type": "integer",
"description": "Open."
},
"start": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "`ts_event` of the bar’s first trade."
}
}
},
{
"title": "ema",
"type": "object",
"additionalProperties": false,
"required": [
"value"
],
"properties": {
"value": {
"type": "number",
"description": "20-period exponential moving average of the closes, taken at the close of bar `index`."
}
}
},
{
"title": "zone",
"type": "object",
"additionalProperties": false,
"required": [
"side",
"high",
"low",
"active",
"start"
],
"properties": {
"active": {
"type": "boolean",
"description": "The zone is still being extended."
},
"end": {
"type": "integer",
"description": "Index of the last bar; absent while the zone is active."
},
"high": {
"type": "number",
"description": "Top of the zone."
},
"low": {
"type": "number",
"description": "Bottom of the zone."
},
"side": {
"type": "string",
"enum": [
"buy",
"sell"
],
"description": "Side of the effort that formed the zone."
},
"start": {
"type": "integer",
"description": "Index of the range bar that confirmed the zone."
}
}
}
]
}