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
- Publish the indicator. An alert needs a published version; on a draft the chart says "Publish the Indicator before adding an alert." (Publishing).
- 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.
- 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:
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
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.
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(...).
// 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_upsends the words the file wrote intocross_wordson the bar of the cross, such as "Fast EMA 103.71 crossed above slow EMA 103.7".still_abovefires 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
- The message the user types in the alert dialog, when there is one. Its placeholders fill in as always.
- Otherwise the
textslot's words on the fired bar. A bar that wrote nothing there, or an empty line, falls through. - 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/awhen the bar has no value.{{close}}and the other placeholders above keep their own meaning, even when an output has the same name. - 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).
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
2hor45m, and aformingview or1wcandle 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_volatilityandskewincluded, and fortrade_volume_by_sizecells. - "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.