---
title: "Alerts"
description: "An alert runs your published indicator in OpenMarket's cloud and tells you when a condition on one of its outputs, or a signal its file declares, comes true."
order: 59
section: "functions"
---

<!-- source: docs/indicators/functions/alerts.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Alerts

An alert runs your published indicator in OpenMarket's cloud and tells
you when a condition on one of its outputs, or a signal its file declares,
comes true.

You set it from the chart: pick an output or a declared signal, choose a
condition, and OpenMarket's alerts engine evaluates it on the chart's
market with the overlay's settings. There are two ways in: every drawn
output is a value a condition can follow, and
`alert(name, { when, message })` in the file names a ready-made signal.

## Set an alert from the chart

1. Publish the indicator. An alert needs a published version; on a draft
   the chart says "Publish the Indicator before adding an alert."
   ([Publishing](publishing.md)).
2. Add it to a chart and open the alert dialog from the indicator's bell in
   the legend, from the right-click menu, or from **Alert on** >
   **Indicators** in the sidebar's alert menu.
3. Pick the output or a declared signal, pick the condition, and save. The
   alert runs the version and the settings the overlay carries.

The conditions on an output:

```text
Crossing   Crosses above, Crosses below, Greater than, Less than
Channel    Entering channel, Exiting channel, Inside channel, Outside channel
Moving     Moving up, Moving down, Moving up %, Moving down %
```

A data-only output (declared `none`) is not offered as a condition target;
give it a declared signal instead, as below.

## Declare a signal in the file

```typescript sample=fn-alert-golden-cross
param("fast", 21, { min: 1, max: 200, description: "Fast EMA length" });
param("slow", 55, { min: 2, max: 400, description: "Slow EMA length" });
output("fast", line, overlay, { color: "#2563eb", description: "Fast EMA of the close" });
output("slow", line, overlay, { color: "#f97316", description: "Slow EMA of the close" });
// Data-only: 1 on the bar the fast EMA crosses above the slow one, else 0.
const goldenCross = output("golden_cross", none);
// A ready-made signal in the chart's alert dialog: it fires when the gate turns from 0 to 1.
alert("golden_cross_up", { when: goldenCross, message: "{{symbol}} golden cross at {{close}}", description: "Fast EMA crosses above slow EMA" });

let fast = new Ema(21);
let slow = new Ema(55);
let cross = new Cross();

function onStart(): void {
  fast = new Ema(i32(p_fast()));
  slow = new Ema(i32(p_slow()));
  cross = new Cross();
}

function onBar(): void {
  const close = bar.close();
  const fastValue = fast.update(close);
  const slowValue = slow.update(close);
  const crossed = cross.update(fastValue, slowValue);
  if (isNaN(slowValue)) return;
  out_fast(fastValue);
  out_slow(slowValue);
  out_golden_cross(crossed == 1 ? 1.0 : 0.0);
}
```

`alert(name, { when, message?, description?, text?, every_bar?, title? })`
declares a signal over an output handle bound by a top-level `const`. It
fires on the bar the `when` output turns from false to true (true is
finite and nonzero; a `NaN` row is false and re-arms it), `message` is
the text the alert sends (placeholders such as `{{symbol}}` and
`{{close}}` fill in when it fires, and `{{<output>}}` reads a declared
output's value on the fired bar), and `description` is the line under
the signal in the alert dialog. Both are at most 200 characters. The
`when` output may be data-only, as here: the file writes `golden_cross`
from `onBar()` like any other output, and the declaration adds nothing
to the compiled module.

Declared signals appear in the chart's alert dialog beside the conditions,
and an indicator that declares only signals offers only those. The grammar
is on [Declarations and the sheet](../reference/declarations.md#alert).

## Alert words and every bar

An alert can send words your file writes on the bar it fires, and it can
fire on every bar its condition holds instead of only the first. Both are
options on `alert(...)`.

```typescript sample=fn-alert-live-words
// Two alerts on an EMA pair: one writes its own message on the cross, one fires on every bar the fast line stays above.
param.int("fast", 21, { min: 1, max: 200 });
param.int("slow", 55, { min: 2, max: 400 });
output("fast", line, overlay, { color: "#2563eb" });
output("slow", line, overlay, { color: "#f97316" });
// Data-only gates: 1 on the bar the condition is true, else 0.
const crossedUp = output("crossed_up", none);
const above = output("above", none);
// The slot the file writes the cross message into.
const crossWords = string("cross_words", { max_bytes: 96 });

// Fires once, on the bar of the cross, with the words the file wrote on that bar. The dialog lists it as "EMA cross up".
alert("cross_up", { when: crossedUp, text: crossWords, title: "EMA cross up", description: "Fast EMA crosses above slow EMA" });
// Fires on every bar the gate is true. {{fast}} and {{slow}} read those outputs on the fired bar.
alert("still_above", { when: above, message: "{{symbol}}: fast {{fast}} is above slow {{slow}}", every_bar: true, description: "Every bar the fast EMA is above the slow EMA" });

let fast = new Ema(21);
let slow = new Ema(55);
let cross = new Cross();

function onStart(): void {
  fast = new Ema(i32(p_fast()));
  slow = new Ema(i32(p_slow()));
  cross = new Cross();
}

function onBar(): void {
  const close = bar.close();
  const fastValue = fast.update(close);
  const slowValue = slow.update(close);
  const crossed = cross.update(fastValue, slowValue);
  if (isNaN(slowValue)) return;
  out_fast(fastValue);
  out_slow(slowValue);
  out_crossed_up(crossed == 1 ? 1.0 : 0.0);
  out_above(fastValue > slowValue ? 1.0 : 0.0);
  // Write the words on the bar the alert fires. On a bar that writes nothing, the alert sends its message, else its name.
  if (crossed == 1) {
    sb_clear();
    sb_text("Fast EMA ");
    sb_auto(fastValue);
    sb_text(" crossed above slow EMA ");
    sb_auto(slowValue);
    str_cross_words_sb();
  }
}
```

- `cross_up` sends the words the file wrote into `cross_words` on the bar
  of the cross, such as "Fast EMA 103.71 crossed above slow EMA 103.7".
- `still_above` fires on every bar the fast line is above the slow one, at
  most once per bar. Its message fills `{{fast}}` and `{{slow}}` with those
  outputs' values on the fired bar.

### Options

| Option | What it does | Default |
| --- | --- | --- |
| `when` | the output that turns the alert on: true is finite and not 0 | required |
| `text` | a string slot; its words on the fired bar are the message | none |
| `message` | fixed words with placeholders: `{{symbol}}`, `{{exchange}}`, `{{interval}}`, `{{open}}`, `{{high}}`, `{{low}}`, `{{close}}`, `{{volume}}`, `{{value}}`, `{{time}}`, `{{alert.name}}`, and `{{<output>}}` for any output you declared | the alert's name |
| `every_bar` | `true` fires on every bar `when` is true, once per bar at most; `false` fires only on the bar it turns true | `false` |
| `description` | the line under the signal in the alert dialog | none |
| `title` | the words the alert dialog, an armed alert and its notification show for the signal: any characters, spaces included, 1 to 120 of them on one line (the name itself takes letters, digits, `.`, `_` and `-` only); a newline, a control character or an invisible one (a bidi control, a zero-width space) is refused, as in a text setting's default | the alert's name |

### Which words are sent

1. The message the user types in the alert dialog, when there is one. Its
   placeholders fill in as always.
2. Otherwise the `text` slot's words on the fired bar. A bar that wrote
   nothing there, or an empty line, falls through.
3. Otherwise `message`. Each `{{<output>}}` becomes that output's value on
   the fired bar: a whole number as it is, any other number to 10
   significant digits, `n/a` when the bar has no value. `{{close}}` and the
   other placeholders above keep their own meaning, even when an output has
   the same name.
4. Otherwise the alert's name.

Words written by the file arrive as plain text. They are one line of at
most 200 characters (longer words end with an ellipsis), a link in them is
never clickable and never previewed, and mentions such as `@everyone` and
`@here` are removed. They are never read as a template, so a `{{close}}`
the file writes arrives exactly as written.

### Every bar, and the user's choice

The alert dialog shows the file's choice under **Fire**: "When it turns
true" or "Every bar while true". The user can change it for one alert
there ("The script's author set the default; your choice applies to this
alert."). The repeat and the tick-or-close choices still apply, so a tick
alert can fire on the forming bar before it closes.

### Limits and refusals

| Rule | What the build says |
| --- | --- |
| `text` names a string slot | `alert 'cross_up' option 'text' references output 'above' where a string slot is needed` |
| `text` names something declared | `alert 'cross_up' option 'text' references 'ghost', which is neither a declared output nor a declared string slot; bind the output first (const h = output("...", ...)) or the slot (const s = string("...", { max_bytes: 32 })) and pass that const, or name it as a string literal` |
| `every_bar` is `true` or `false` | `option 'every_bar' takes true or false` |
| `message` and `description` are at most 200 characters | `message must be at most 200 characters` |
| `title` is 1 to 120 characters | `alert 'cross_up' title is 121 characters; it takes at most 120`, `alert 'cross_up' title is empty; it names the alert in the dialog (leave it out to show the name)` |
| `title` is one line of plain text | `alert 'cross_up' title carries a newline, a control or an invisible character; shown text is stripped of bidi controls, zero-width characters and control characters, so a title cannot carry them` |
| 64 alerts per indicator | `alerts must declare at most 64 entries` |

## Strategy alerts

An indicator that trades adds four choices of its own to the alert dialog:
"Strategy order placed", "Strategy trade opened or closed", "Strategy
position" and "Strategy equity" ([Strategies overview](../strategies/overview.md)).

## What an alert can evaluate

An alert evaluates the indicator on the chart's market at the chart's
interval, which must be 1m, 5m, 15m, 30m, 1h, 4h or 1d ("Alerts support
1m, 5m, 15m, 30m, 1h, 4h and 1d intervals. Switch the chart interval to
arm this indicator."). It serves these inputs:

| The indicator reads | An alert serves |
| --- | --- |
| Feeds | `ohlcv`, `trades`, `oi`, `liquidations`, `funding` and `time`, plus `book` and `volume_profile` cells |
| Another market | its candles, through a secondary `ohlcv` input pinned with `symbol` and `exchange`: a crypto venue, a stock or ETF on `POLYGON`, forex, gold or silver on `FX_OTC` |
| A coarser timeframe | a secondary `ohlcv`, `funding` or `oi` input pinned to `1m`, `5m`, `15m`, `30m`, `1h`, `4h`, `1d` or `1w`, a whole multiple of the chart's interval; a pinned candle counts only once it has closed |
| A view of a pin | `confirmed` (the default), `forming` and `is_new_period` |

An indicator it cannot evaluate is refused by name when you save the
alert:

- "This Indicator is pinned to a different interval than the chart." for a
  pin on the first input, a pin finer than the chart's interval or not a
  whole multiple of it, a custom timeframe such as `2h` or `45m`, and a
  `forming` view or `1w` candle too long for the 600 bars an alert keeps
  (below).
- "This Indicator reads a data source alerts cannot evaluate yet." for
  every feed the table does not list, `odds`, `implied_volatility` and
  `skew` included, and for `trade_volume_by_size` cells.
- "Alerts cannot use the Timeframe setting yet. Set it back to its default
  to arm." for a source, timeframe or symbol setting moved off its default;
  the sentence names the setting.
- "This Indicator reads a market alerts cannot evaluate yet (an index)."
  for an input pinned to an index (`POLYGON_INDICES`); pin an ETF on the
  same market instead (`SPY/USD`).
- "Indicator alerts are not allowed on this venue." for an indicator whose
  input pins a CME market.

On CME markets only price alerts are available for now ("Only price alerts
are available on CME markets right now."), and where wrun indicator alerts are
switched off the chart says "Indicator alerts are not enabled yet."

`intrabar` cells, `candles` streams, `options_chain` cells and an `offset`
view are refused as well.

### How much history an alert reads

An alert evaluates over at most 600 bars of the chart's interval. The
window is the larger of the warm-up the file declares and the largest
maximum among its settings, counted up to 500. A warm-up is one top-level
line, `warmup({ terms: [{ param: length, times: 2 }], min: 40 })`: with
`length` a setting's handle, it asks for twice that setting's value in
bars, and at least 40.

An indicator that reads a coarser pinned timeframe gets all 600 bars,
even when it reads only that pin's `is_new_period` pulse; one that also
reads `book` or `volume_profile` cells keeps the window its warm-up and
settings give it. 600 chart bars hold 600 / (leg / chart) candles of a
pin: a 4h pin holds 150 candles on a 1h chart, 37 on 15m and 12 on 5m. A
higher-timeframe average longer than that arms but stays empty, so arm
such alerts on a coarser chart: an average of 20 4h candles fills on 15m
and stays empty on 5m.

A `forming` view needs its whole candle inside the 600 bars, so a `1d`
one needs a 5m chart or coarser and a `1w` one 30m or coarser, and a `1w`
pin's closed candle needs 1h or coarser; on a finer chart the alert is
refused with "This Indicator is pinned to a different interval than the
chart."

An indicator that builds a higher timeframe from the chart's own bars,
with a `Resampler` and no pin, must declare `warmup()` long enough for it:
20 1h candles on a 5m chart are 20 x 12 = 240 bars. Otherwise its alert
can arm and stay empty.

## Next

- **Publishing:** the published version an alert runs ([Publishing](publishing.md))
- **Declarations and the sheet:** the full `alert(...)` grammar ([Declarations and the sheet](../reference/declarations.md#alert))
- **Multi-source:** pin a stock, forex or gold so an alert reads it ([Multi-source](../core-concepts/multi-source.md#stocks-forex-and-gold))
- **Execution model:** where each part of an indicator runs ([Execution model](../core-concepts/execution-model.md))
