# EliteXCharts — custom indicator format (complete reference)

This page is written so that an AI assistant can read it and produce a working indicator file. Give it this link, describe the
indicator you want, and import the `.json` it returns in EliteXCharts: **Indicators** (chart header) → **Import indicator** (bottom
of the window). The indicator then shows up under the **Custom** category. Importing a file with the same `id` again updates it.

Portuguese version: https://elitexcharts.com/docs/indicadores-customizados.md · Human-friendly manual: https://elitexcharts.com/docs/#indicadores-customizados

## 1. What the file is

- One JSON object, **declarative**: it describes what to calculate and how to paint. It never runs code, so it cannot touch files,
  accounts or the network.
- Comments (`//`, `/* */`) and trailing commas are accepted. Property names are case-insensitive.
- The indicator is drawn **on top of the price candles** (lines, a band fill and candle colors). There is no separate pane for
  custom indicators.
- An invalid file is **rejected at import** with the reason; it never breaks a chart.

## 2. Top-level fields

| Field | Required | Meaning |
|---|---|---|
| `schema` | yes | Must be `1`. |
| `name` | yes | Name shown in the platform. |
| `id` | recommended | Stable identifier (`"my-indicator"`). Re-importing the same `id` updates the indicator. |
| `version`, `author`, `description` | no | Shown in the indicator's "About". |
| `inputs` | no | Settings the user can change (section 3). |
| `series` | no | The calculation pipeline (section 4). |
| `plots` | no | Series drawn as lines (section 5). |
| `fill` | no | A fill between two series (section 5). |
| `candleColors` | no | Rules that paint the candles (section 6). |
| `requires` | no | Native indicators this one relies on (section 7). |
| `source` | no | Calculate on another timeframe by default (section 8). |

The file must draw something: at least one `plots` entry **or** one `candleColors` rule.

## 3. `inputs` — the settings screen

Each input becomes a row in the indicator's settings. Anywhere in the file, the string `"$id"` reads the input's current value.

| Field | Meaning |
|---|---|
| `id` | Required. Referenced as `"$id"`. |
| `type` | `"int"` (whole number) · `"number"` (decimals) · `"color"` (`"#rrggbb"`) · `"bool"` (checkbox). Anything else is rejected. |
| `label` | What the user sees. |
| `default` | Starting value (a number, `"#rrggbb"` for colors, `true`/`false` for bool). |
| `min`, `max`, `step` | Limits of the numeric control. **Defaults: `min` 0, `max` 1000, `step` 1** — set `max` explicitly for larger ranges. |

## 4. `series` — the calculation

Steps run **in order**, and each step may only reference base sources or series defined **before** it (no cycles are possible).
Each step becomes a named series with one value per candle.

Base sources, always available: `open`, `high`, `low`, `close`, `volume`, `hl2`, `hlc3`, `ohlc4`.

| `fn` | Parameters | Result |
|---|---|---|
| `src` | `src` | Copy of a source or series. |
| `sma`, `ema`, `rma` | `src`, `len` | Simple, exponential and Wilder moving average. |
| `stdev` | `src`, `len` | Standard deviation over `len` bars. |
| `highest`, `lowest` | `src`, `len` | Highest / lowest value over `len` bars. |
| `atr` | `len` | Average True Range of the candles. |
| `rsi` | `src` (default `close`), `len` | RSI (Wilder). |
| `add`, `sub`, `mul`, `div`, `max`, `min` | `a`, `b` | Element-wise operation. `div` by zero gives an empty value. |
| `abs` | `src` | Absolute value. |
| `shift` | `src`, `len` | The value `len` bars back. |

Rules that trip up generated files:
- **Inside `series`, every value is a string**: `"len": "20"`, `"b": "2"`, `"len": "$period"`. A bare number (`"len": 20`) makes
  the import fail.
- Operands (`src`, `a`, `b`) accept a series id, a base source, a number as a string or an input (`"$mult"`).
- `len` missing = 14.
- Warm-up bars (not enough history yet) are empty: nothing is drawn there and conditions on them never match.
- Limits: 64 series and 32 candle-color rules per indicator.

## 5. `plots` and `fill`

```json
"plots": [ { "series": "basis", "color": "$colMid", "width": "$bandW", "dash": "solid", "label": "Basis" } ],
"fill":  { "from": "upper", "to": "lower", "color": "$colBand", "alpha": 0.07 }
```

- `plots[].series` must name an existing series. `color` is `"#rrggbb"` or `"$input"`; `width` is a number or `"$input"`;
  `dash` is `"solid"`, `"dash"` or `"dot"`.
- `fill.from` / `fill.to` must name existing series; `alpha` (0–1) is a number or `"$input"`.
- Outside `series` (plots, fill, inputs, comparisons), numbers are plain JSON numbers.

## 6. `candleColors` — painting the candles

Rules are evaluated top to bottom and **the first one that matches wins**. A rule with `"else": true` catches every remaining candle.

```json
"candleColors": [
  { "when": { "all": [ "isUp",   { "lt": [ "low",  "lower" ] } ] }, "color": "$colUp" },
  { "when": { "all": [ "isDown", { "gt": [ "high", "upper" ] } ] }, "color": "$colDown" },
  { "else": true, "color": "$colNeutral" }
]
```

Each rule: `when` (a condition) or `else: true`, `color` (`"#rrggbb"` or `"$input"`), and optional `"wicks": false` to paint only
the body (the wick keeps the theme color; the default `true` paints both).

Conditions:
- **Words:** `"isUp"`, `"isDown"` (candle direction) · `"isReversal"`, `"isReversalUp"`, `"isReversalDown"` (the candle changed
  direction versus the previous one; in Renko, the first brick of the other side) · `"hasBuyBigTrade"`, `"hasSellBigTrade"`,
  `"hasBigTrade"` (a Big Trades bubble fell on this candle; needs `requires`, section 7) · `"always"`.
- **Comparisons:** `{ "lt": [x, y] }`, `gt`, `lte`, `gte`, `eq` — two operands, each a series id, a base source, a number or
  `"$input"`. If either side is empty (warm-up), the comparison is false. `eq` compares with a 1e-9 tolerance.
- **Logic:** `{ "all": [ … ] }` (and), `{ "any": [ … ] }` (or), `{ "not": condition }`.
- An unknown word is simply false.

To compare against "the previous candle", build it in `series` with `shift` (e.g. `{ "id": "prevClose", "fn": "shift", "src": "close", "len": "1" }`).

## 7. `requires` — relying on a native indicator

The big-trade conditions need the native **Big Trades** running. Declare it and the platform turns it on when the indicator is added:

```json
"requires": { "bigTrades": { "minVolume": 75, "windowMs": 10 } }
```

`minVolume` (contracts, floor 20, default 75) and `windowMs` (default 10). If Big Trades is already on the chart with a higher
minimum, the platform lowers the minimum so no bubble is missed; the rest of the user's Big Trades settings is never overwritten.
`days` is accepted for compatibility and has no effect (Big Trades covers the chart's whole period).

## 8. `source` — calculating on another timeframe (multi-timeframe)

A custom indicator can be **calculated on another chart type or timeframe** and drawn on the current chart, the same feature the
native indicators have. Example: trading on Renko 14 with the indicator calculated on Renko 44.

```json
"source": { "type": "renko", "param": 44, "smooth": true }
```

| Field | Meaning |
|---|---|
| `type` | `"chart"` (default: the current chart) · `"candle"` (time; `"time"` also works) · `"renko"` · `"tick"`. Delta is only available on screen. |
| `param` | `candle`: **minutes** (5 = 5-minute candles, 60 = hourly) · `renko`: **brick size in ticks** · `tick`: **ticks per candle**. Default 120. |
| `smooth` | `true` (default): the lines are interpolated between the source's closes · `false`: steps, holding the value until the source candle closes. |

How it works:
- `source` is only the **default** chosen by the author. The user can change it any time under **Calculation source** in the
  indicator's settings on the chart; a yellow badge ("◄ Renko 44", "◄ 5min") shows the source in the legend.
- **Everything is calculated on the source's candles**: every series, every comparison and every candle-color rule. The result is
  then brought to the chart on screen. Candle colors are never interpolated: each candle on screen takes the color of the source
  candle that contains it.
- **History:** the user does not need to have opened that timeframe before. Today's session is built from the same Rithmic ticks
  the chart already loaded; previous days come ready-made from the platform's history server (the source's own product, for the
  same days the chart loaded). If the server is still building that product, the legend shows the progress and the indicator
  recalculates when it arrives. It needs a connected Rithmic account. In Replay, the source uses only the replay ticks.
- **Live:** after the first load, only new ticks enter the source series; history is not recalculated on every trade.
- **When not to use it:** if the logic depends on what happens *inside* the candle (volume, delta, aggression), a bigger timeframe
  changes what the number means. Prefer the current chart in that case.

## 9. Complete examples

### Bollinger 20/2 that paints band breaks

```json
{
  "schema": 1,
  "id": "elitex-bollinger",
  "name": "EliteXBollinger",
  "version": "1.0.0",
  "description": "Bollinger 20/2 that paints the candle when it breaks a band.",
  "inputs": [
    { "id": "period", "type": "int",    "label": "Period",            "default": 20, "min": 2,   "max": 400, "step": 1 },
    { "id": "mult",   "type": "number", "label": "Deviations",        "default": 2,  "min": 0.5, "max": 5,   "step": 0.1 },
    { "id": "colUp",      "type": "color", "label": "Broke the lower band", "default": "#22c55e" },
    { "id": "colDown",    "type": "color", "label": "Broke the upper band", "default": "#ef4444" },
    { "id": "colNeutral", "type": "color", "label": "Other candles",        "default": "#7a7a85" },
    { "id": "colMid",  "type": "color",  "label": "Basis",     "default": "#f5a623" },
    { "id": "colBand", "type": "color",  "label": "Bands",     "default": "#4a90e2" },
    { "id": "bandW",   "type": "number", "label": "Thickness", "default": 1, "min": 0.5, "max": 6, "step": 0.5 }
  ],
  "series": [
    { "id": "basis", "fn": "sma",   "src": "close", "len": "$period" },
    { "id": "dev",   "fn": "stdev", "src": "close", "len": "$period" },
    { "id": "devx",  "fn": "mul",   "a": "dev",     "b": "$mult" },
    { "id": "upper", "fn": "add",   "a": "basis",   "b": "devx" },
    { "id": "lower", "fn": "sub",   "a": "basis",   "b": "devx" }
  ],
  "plots": [
    { "series": "basis", "color": "$colMid",  "width": "$bandW" },
    { "series": "upper", "color": "$colBand", "width": "$bandW" },
    { "series": "lower", "color": "$colBand", "width": "$bandW" }
  ],
  "fill": { "from": "upper", "to": "lower", "color": "$colBand", "alpha": 0.07 },
  "candleColors": [
    { "when": { "all": [ "isUp",   { "lt": [ "low",  "lower" ] } ] }, "color": "$colUp" },
    { "when": { "all": [ "isDown", { "gt": [ "high", "upper" ] } ] }, "color": "$colDown" },
    { "else": true, "color": "$colNeutral" }
  ]
}
```

### Renko reversal against a big trade (colors only, no lines)

```json
{
  "schema": 1,
  "id": "elitex-reversal-flow",
  "name": "EliteXReversalFlow",
  "requires": { "bigTrades": { "minVolume": 75, "windowMs": 10 } },
  "inputs": [
    { "id": "colDown",    "type": "color", "label": "Down reversal with a big buy",  "default": "#ef4444" },
    { "id": "colUp",      "type": "color", "label": "Up reversal with a big sell",   "default": "#22c55e" },
    { "id": "colNeutral", "type": "color", "label": "Other candles",                 "default": "#7a7a85" }
  ],
  "candleColors": [
    { "when": { "all": [ "isReversalDown", "hasBuyBigTrade"  ] }, "color": "$colDown" },
    { "when": { "all": [ "isReversalUp",   "hasSellBigTrade" ] }, "color": "$colUp" },
    { "else": true, "color": "$colNeutral" }
  ]
}
```

### Multi-timeframe: average distance on Renko 44, painting the candles of the chart on screen

```json
{
  "schema": 1,
  "id": "stretch-r44",
  "name": "Stretch from the Renko 44 average",
  "description": "Paints the candle when price is more than X% away from the 25-period average of Renko 44.",
  "source": { "type": "renko", "param": 44, "smooth": true },
  "inputs": [
    { "id": "len",  "type": "int",    "label": "Average period", "default": 25, "min": 2, "max": 400 },
    { "id": "pct",  "type": "number", "label": "Distance (%)",   "default": 0.2, "min": 0.01, "max": 5, "step": 0.01 },
    { "id": "colAbove", "type": "color", "label": "Stretched above", "default": "#7c3aed" },
    { "id": "colBelow", "type": "color", "label": "Stretched below", "default": "#c6f24e" },
    { "id": "colRest",  "type": "color", "label": "Other candles",   "default": "#8b8b95" }
  ],
  "series": [
    { "id": "ma",    "fn": "sma", "src": "close", "len": "$len" },
    { "id": "frac",  "fn": "div", "a": "$pct",  "b": "100" },
    { "id": "dist",  "fn": "mul", "a": "ma",    "b": "frac" },
    { "id": "upper", "fn": "add", "a": "ma",    "b": "dist" },
    { "id": "lower", "fn": "sub", "a": "ma",    "b": "dist" }
  ],
  "plots": [
    { "series": "ma",    "color": "#3b82f6", "width": 1.6 },
    { "series": "upper", "color": "#7c3aed", "width": 1, "dash": "dash" },
    { "series": "lower", "color": "#c6f24e", "width": 1, "dash": "dash" }
  ],
  "candleColors": [
    { "when": { "gte": [ "high", "upper" ] }, "color": "$colAbove" },
    { "when": { "lte": [ "low",  "lower" ] }, "color": "$colBelow" },
    { "else": true, "color": "$colRest" }
  ]
}
```

## 10. Checklist before returning the file

1. `"schema": 1` and a `name`.
2. Every `series` step uses a known `fn` and only references base sources, earlier series, numbers or `"$inputs"`.
3. Every value inside `series` is a **string** (`"len": "20"`).
4. Every `plots[].series` and `fill.from/to` exists.
5. At least one `plots` entry or one `candleColors` rule; the last color rule is usually `{ "else": true, … }`.
6. Inputs use `int`, `number`, `color` or `bool`, and numeric inputs set `max` when it should go above 1000.
7. `source.type` is `chart`, `candle`, `renko` or `tick`; `param` is minutes / brick ticks / ticks per candle.
8. At most 64 series and 32 color rules.
