Indicator

Fathom Walls

Liquidity walls defending repeated highs or lows.

What the indicator shows

  • A sell wall sits on a high tested several times, where aggressive buying stalls without breaking the level; the Fathom chart marks it with an S.
  • A buy wall sits on a low tested several times, where aggressive selling stalls; the Fathom chart marks it with a B.
  • In both cases, passive orders absorb the aggression: the level is defended.

How to use it

  • A wall gives a level to trade for a bounce while it holds, or to watch for a breakout if it gives way.
  • Start with the looser settings described below (about one signal per hour on NQ), then tighten the criteria to keep only the clearest walls. Stricter criteria do not guarantee that a level will hold.

How it is computed

  • The test runs when each timeframe-second bar closes. A bar is a candidate for a sell wall when its high reaches those of the two previous bars (its low, for a buy wall).
  • The tested group (cluster) starts at a recent candidate bar and gathers every later candidate up to the closed bar, at least minimumBars bars in all. Its first bar is less than nearnessBars bars old, and the closed bar does not exceed that bar’s high by more than tickBreakoutMargin ticks: a candidate that no longer meets these conditions is dropped. The closed bar’s high must be less than tickGrouping ticks from the group’s high. Groups are tried from the oldest candidate to the most recent.
  • A cell is one price level of one bar of the group, within the tickGrouping ticks below the group’s high (above its low, for a buy wall).
  • A wall is signaled when at least minimumBars cells each reach minPerBarVolume contracts, the cells’ total volume exceeds minClusterVolume and the share of aggressive volume stalling on the wall (sideVolume / volume) exceeds minDeltaPercent%; these last two thresholds are exclusive, so with minDeltaPercent: 100 no wall is possible. Despite their names, minimumBars also counts cells, minPerBarVolume applies to each cell, and minDeltaPercent is not a delta but that share of aggressive volume.
  • The defaults are strict: on NQ or ES, they give few walls, sometimes none over a whole session. For about one signal per hour on NQ, set minPerBarVolume: 10, minClusterVolume: 30, minDeltaPercent: 40 and tickGrouping: 4, keeping the other parameters at their defaults (these are the parameters of the examples). The stricter the parameters, the fewer the walls.

When it updates

  • At most one signal per bar, sent final when the bar closes; the sell wall is tested first.
  • Weekly reset: at the Sunday reopen (6:00 pm New York time), the previous week’s highs and lows no longer count.
Identifier
liquidity-walls
Objects received
signal
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": "liquidity-walls",
  "instrument": "NQ.front",
  "indicator": "liquidity-walls",
  "params": {
    "minClusterVolume": 30,
    "minDeltaPercent": 40,
    "minPerBarVolume": 10,
    "minimumBars": 2,
    "nearnessBars": 20,
    "tickBreakoutMargin": 1,
    "tickGrouping": 4,
    "timeframe": 60
  },
  "mode": "live",
  "from": "live"
}

These example parameters differ from the defaults: minClusterVolume 30 (default 400), minDeltaPercent 40 (default 70), minPerBarVolume 10 (default 100), tickGrouping 4 (default 1). This subscription therefore counts as a custom configuration; the Sandbox allows 1.

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.

ParameterTypeDefaultAllowed valuesUnitDescription
minClusterVolumeinteger4000 to 10,000,000contractsTotal volume the wall’s cells must exceed (exclusive).
minDeltaPercentinteger700 to 100%Minimum share (exclusive) of the wall’s cell volume made up of aggressive volume stalling against it: buying for a sell wall, selling for a buy wall.
minPerBarVolumeinteger1000 to 1,000,000contractsMinimum volume for a cell (one price level of one bar of the wall) to count.
minimumBarsinteger22 to 100—Minimum number of cells whose volume reaches minPerBarVolume, and of candidate bars in the cluster.
nearnessBarsinteger201 to 1,000barsMaximum age of a high or low in the cluster, in bars.
tickBreakoutMargininteger10 to 100ticksHow far price may go past a high (or a low) before it stops counting for the wall.
tickGroupinginteger11 to 100ticksDepth of the wall, below the cluster’s high (or above its low); the tested bar must reach this band.
timeframeinteger601 · 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.

signal sent final

Signal of the finished bar that starts at barStartNs.

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

FieldTypeUnitPresentDescription
cellsinteger—alwaysNumber of cells (one price level of one bar of the wall) whose volume reaches minPerBarVolume.
priceintegerticksalwaysHigh (sell wall) or low (buy wall) of the bar that tests the wall.
sidestring · buy | sell—alwayssell: sell wall at the high; buy: buy wall at the low.
sideVolumeintegercontractsalwaysAggressive volume that stalled on the wall, in those cells: buying (ask) for a sell wall, selling (bid) for a buy wall.
volumeintegercontractsalwaysTotal volume of the wall’s cells.

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": "liquidity-walls",
  "cursor": "20720.13976.0",
  "t": "upsert",
  "id": "1790217900000000000",
  "final": true,
  "ts": "1790217960047941663",
  "data": {
    "price": 122988,
    "side": "sell",
    "volume": 186,
    "sideVolume": 162,
    "cells": 4
  }
}

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/liquidity-walls/params.json",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "minClusterVolume": {
      "description": "Total volume the wall’s cells must exceed (exclusive).",
      "type": "integer",
      "default": 400,
      "minimum": 0,
      "maximum": 10000000
    },
    "minDeltaPercent": {
      "description": "Minimum share (exclusive) of the wall’s cell volume made up of aggressive volume stalling against it: buying for a sell wall, selling for a buy wall.",
      "type": "integer",
      "default": 70,
      "minimum": 0,
      "maximum": 100
    },
    "minPerBarVolume": {
      "description": "Minimum volume for a cell (one price level of one bar of the wall) to count.",
      "type": "integer",
      "default": 100,
      "minimum": 0,
      "maximum": 1000000
    },
    "minimumBars": {
      "description": "Minimum number of cells whose volume reaches `minPerBarVolume`, and of candidate bars in the cluster.",
      "type": "integer",
      "default": 2,
      "minimum": 2,
      "maximum": 100
    },
    "nearnessBars": {
      "description": "Maximum age of a high or low in the cluster, in bars.",
      "type": "integer",
      "default": 20,
      "minimum": 1,
      "maximum": 1000
    },
    "tickBreakoutMargin": {
      "description": "How far price may go past a high (or a low) before it stops counting for the wall.",
      "type": "integer",
      "default": 1,
      "minimum": 0,
      "maximum": 100
    },
    "tickGrouping": {
      "description": "Depth of the wall, below the cluster’s high (or above its low); the tested bar must reach this band.",
      "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": 60,
      "enum": [
        1,
        5,
        10,
        15,
        30,
        60,
        120,
        180,
        300,
        600,
        900,
        1800,
        3600
      ]
    }
  }
}
data field schema
JSON
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://fathomcharts.com/schemas/liquidity-walls/data.json",
  "title": "signal",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "cells": {
      "description": "Number of cells (one price level of one bar of the wall) whose volume reaches `minPerBarVolume`.",
      "type": "integer"
    },
    "price": {
      "description": "High (sell wall) or low (buy wall) of the bar that tests the wall.",
      "type": "integer"
    },
    "side": {
      "description": "`sell`: sell wall at the high; `buy`: buy wall at the low.",
      "type": "string",
      "enum": [
        "buy",
        "sell"
      ]
    },
    "sideVolume": {
      "description": "Aggressive volume that stalled on the wall, in those cells: buying (ask) for a sell wall, selling (bid) for a buy wall.",
      "type": "integer"
    },
    "volume": {
      "description": "Total volume of the wall’s cells.",
      "type": "integer"
    }
  },
  "required": [
    "price",
    "side",
    "volume",
    "sideVolume",
    "cells"
  ]
}