Indicator

Fathom Zones

Directional effort zones on 40-tick range bars, with the 20-period EMA of the closes.

What the indicator shows

  • A buy zone marks a fast upward push backed by buying delta; a sell zone, a fast downward push backed by selling delta. On the Fathom chart, buy zones are blue and sell zones coral.
  • The range bars and the 20-period EMA of their closes are published with the zones, to put them in the context of the trend.

How to use it

  • When price comes back into a zone, watch how it reacts: a buy zone often acts as support, a sell zone as resistance.
  • Place the zone against the EMA: a buy zone above the EMA goes with the trend, and so does a sell zone below it.
  • A zone crossed by a bar body ends: the level did not hold.

How it is computed

  • 40-tick range bars, with no parameter: a bar runs until the trade that would take its high-low spread beyond 40 ticks, and that trade opens the next bar. A new bar also opens at every session open (6:00 pm New York time). Bars are numbered from 0 from the start of the week.
  • A finished bar is an effort bar when four conditions hold: its body goes the way of its delta (ask − bid; close ≥ open for a positive or zero delta, close < open for a negative one); the absolute delta is 10% to 40% of the bar’s volume; the delta per body tick, delta / (|close − open| + 1) truncated, is at most 14 contracts in absolute value; the bar formed in 45 seconds at most (duration truncated to the second), from its first trade to the trade that opens the next one.
  • As soon as two consecutive effort bars go the same way, a push starts: its bounds start from the high and low of these two bars, then widen with each further effort bar the same way.
  • The first bar that is not an effort bar that way ends the push and creates the zone, whose start is that bar’s index. The zone covers the half of the push where it started: the lower half for a buy zone, the upper half for a sell zone, so its bounds can fall on a half tick.
  • The 20-period EMA of the closes starts at the bar with index 21: its first value is that bar’s close, then EMA = previous EMA + (close − previous EMA) × 2/21.
  • The three object kinds have no field telling them apart in data: read the id prefix (bar:, ema:, zone:). <weekSunday> is the YYYY-MM-DD date of the week’s Sunday reopen; the index of a bar or an EMA value is the last segment of its id.

When it updates

  • Each range bar is sent once, final, when it closes, with its EMA value from index 21 on.
  • A zone is sent in progress as soon as it is created, then final (active: false, with its end) on the first of these events: a bar body crosses it (below its bottom for a buy zone, above its top for a sell zone), it has covered 21 bars (start to start + 20), or a new session opens (6:00 pm New York time). A zone can end on the very bar that creates it.
  • The snapshot keeps the last 32 final objects of all kinds together: the more numerous bars and EMA values can leave no room for any final zone.
  • Weekly reset: on the first trade of the Sunday reopen (6:00 pm New York time), the week’s last bar is sent, zones still in progress are sent final (active: false, with their end), then bars, indices and the EMA start over; nothing carries over to the new week.
Identifier
effort-zones
Objects received
bar, ema, zone
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.

Subscribe message
{
  "t": "subscribe",
  "sub": "effort-zones",
  "instrument": "NQ.front",
  "indicator": "effort-zones",
  "params": {},
  "mode": "live",
  "from": "live"
}

Parameters

This indicator has no parameters: omit params or send {}.

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 range bar. <weekSunday>: YYYY-MM-DD date of the week’s Sunday reopen; <index>: number of the bar from the start of the week (0, 1, 2…), read from the id (data has no index field).

id format: bar:<weekSunday>:<index>. Each object is sent once, final (final: true).

FieldTypeUnitPresentDescription
closeintegerticksalwaysClose.
highintegerticksalwaysHigh.
lowintegerticksalwaysLow.
openintegerticksalwaysOpen.
startstringtimestamp (ns)alwaysTimestamp of the bar’s first trade. To place a zone in time, read the start of the bar whose index equals the zone’s start (or end); the bar of the end is only published when it closes.

ema sent final

EMA value at the close of bar <index> (the same index as in that bar’s id).

id format: ema:<weekSunday>:<index>. Each object is sent once, final (final: true).

FieldTypeUnitPresentDescription
valuenumberticksalways20-period EMA of the closes, at the close of bar <index>. It starts at the bar with index 21, on its close, then EMA = previous EMA + (close − previous EMA) × 2/21. No ema object exists for bars 0 to 20 of the week.

zone sent in progress

Zone; in progress while it is active. <startIndex> is its start.

id format: zone:<weekSunday>:<startIndex>. The object is sent in progress (final: false), then sent again on every change; each version fully replaces the previous one. Once it can no longer change, its last version carries final: true, except for objects whose description says they are removed without a final version.

FieldTypeUnitPresentDescription
activeboolean—alwaystrue while the zone is extended; false once it is final.
endintegerbarssometimesIndex of the range bar after the end of the zone: the bar still in progress when the zone ends, only published when it closes; at the end of the week, the index that would follow the week’s last bar, which no bar carries. On expiry, end = start + 21. Absent while the zone is active.
highnumberticksalwaysTop of the zone; can fall on a half tick.
lownumberticksalwaysBottom of the zone; can fall on a half tick.
sidestring · buy | sell—alwaysDirection of the push that formed the zone: buy for a positive or zero delta, sell for a negative delta.
startintegerbarsalwaysIndex of the range bar that created the zone, counted from the start of the week like the index of bar ids (not a timestamp).

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.

Final object
{
  "sub": "effort-zones",
  "cursor": "20720.110.0",
  "t": "upsert",
  "id": "bar:2026-09-20:2939",
  "final": true,
  "ts": "1790208062570437091",
  "data": {
    "start": "1790207999116757165",
    "open": 123012,
    "high": 123044,
    "low": 123004,
    "close": 123006
  }
}
Final object
{
  "sub": "effort-zones",
  "cursor": "20720.110.1",
  "t": "upsert",
  "id": "ema:2026-09-20:2939",
  "final": true,
  "ts": "1790208062570437091",
  "data": {
    "value": 123066.85128768138
  }
}
Object in progress
{
  "sub": "effort-zones",
  "cursor": "20720.32179.2",
  "t": "upsert",
  "id": "zone:2026-09-20:3114",
  "final": false,
  "ts": "1790230140362667235",
  "data": {
    "side": "buy",
    "high": 122357,
    "low": 122258,
    "active": true,
    "start": 3114
  }
}
Final object
{
  "sub": "effort-zones",
  "cursor": "20720.34641.2",
  "t": "upsert",
  "id": "zone:2026-09-20:3114",
  "final": true,
  "ts": "1790231072263560767",
  "data": {
    "side": "buy",
    "high": 122357,
    "low": 122258,
    "active": false,
    "start": 3114,
    "end": 3135
  }
}

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
JSON
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://fathomcharts.com/schemas/effort-zones/params.json",
  "type": "object",
  "additionalProperties": false,
  "properties": {}
}
data field schema
JSON
{
  "$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,
      "properties": {
        "close": {
          "description": "Close.",
          "type": "integer"
        },
        "high": {
          "description": "High.",
          "type": "integer"
        },
        "low": {
          "description": "Low.",
          "type": "integer"
        },
        "open": {
          "description": "Open.",
          "type": "integer"
        },
        "start": {
          "description": "Timestamp of the bar’s first trade. To place a zone in time, read the `start` of the bar whose index equals the zone’s `start` (or `end`); the bar of the `end` is only published when it closes.",
          "type": "string",
          "pattern": "^[0-9]+$"
        }
      },
      "required": [
        "start",
        "open",
        "high",
        "low",
        "close"
      ]
    },
    {
      "title": "ema",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "value": {
          "description": "20-period EMA of the closes, at the close of bar `<index>`. It starts at the bar with index 21, on its close, then `EMA = previous EMA + (close − previous EMA) × 2/21`. No `ema` object exists for bars 0 to 20 of the week.",
          "type": "number"
        }
      },
      "required": [
        "value"
      ]
    },
    {
      "title": "zone",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "active": {
          "description": "`true` while the zone is extended; `false` once it is final.",
          "type": "boolean"
        },
        "end": {
          "description": "Index of the range bar after the end of the zone: the bar still in progress when the zone ends, only published when it closes; at the end of the week, the index that would follow the week’s last bar, which no bar carries. On expiry, `end` = `start` + 21. Absent while the zone is active.",
          "type": "integer"
        },
        "high": {
          "description": "Top of the zone; can fall on a half tick.",
          "type": "number"
        },
        "low": {
          "description": "Bottom of the zone; can fall on a half tick.",
          "type": "number"
        },
        "side": {
          "description": "Direction of the push that formed the zone: `buy` for a positive or zero delta, `sell` for a negative delta.",
          "type": "string",
          "enum": [
            "buy",
            "sell"
          ]
        },
        "start": {
          "description": "Index of the range bar that created the zone, counted from the start of the week like the index of bar `id`s (not a timestamp).",
          "type": "integer"
        }
      },
      "required": [
        "side",
        "high",
        "low",
        "active",
        "start"
      ]
    }
  ]
}