TradingCharts

Alerts

A ChartScript script talks to the alert system in two ways:

alertcondition(condition, title, message?)

rsi = ta.rsi(close, 14)
alertcondition(ta.crossover(rsi, 70),  "RSI overbought",  "{{ticker}} RSI crossed above 70")
alertcondition(ta.crossunder(rsi, 30), "RSI oversold",    "{{ticker}} RSI crossed below 30")
  • title is what the user sees in the Alerts tab when adding an alert.
  • message is the default text — the user can override it in the dialog. It accepts placeholders (below).
  • Called from script scope only. Only const strings for title/message.

alert(message, freq?)

A dynamic call fires an alert directly from the body:

if volume > ta.sma(volume, 20) * 3
    alert("Volume spike on " + syminfo.ticker + " @ " + str.tostring(close),
          alert.freq_once_per_bar_close)
freq Fires
alert.freq_all Every tick on the forming bar
alert.freq_once_per_bar At most once per bar (on the first evaluation that matches)
alert.freq_once_per_bar_close Once per bar, only on the confirmed (closing) evaluation

Omitting freq defaults to alert.freq_once_per_bar (the reference default). Use freq_once_per_bar_close when a signal must not fire on a forming bar that later reverses.

Placeholders

alertcondition default messages and the message typed into the alert dialog support these {{…}} placeholders. They are substituted by the alert service at delivery time:

Placeholder Replaced with
{{ticker}} Symbol ticker (TCS)
{{exchange}} Exchange (NSE)
{{interval}} Chart interval (5, 1D)
{{time}} Time of the bar that fired (ISO 8601)
{{timenow}} Wall-clock time at delivery (ISO 8601)
{{plot("Title")}} Current value of the plot with that title= (na when the plot has no value)

Anything else inside {{…}} is left verbatim. Bar values (close, volume, …), strategy.* account values and the reference platform's {{plot_0}} / {{strategy.order.*}} forms are not substituted — build such text in the script with str.tostring() and pass it to alert() or alert_message= instead:

if ta.crossover(close, ta.sma(close, 20))
    alert("Long " + syminfo.ticker + " @ " + str.tostring(close, format.mintick), alert.freq_once_per_bar_close)

Example: 3Commas-style strategy alert

BUY_JSON  = '{"action":"buy","bot_id":4635591,"token":"***"}'
SELL_JSON = '{"action":"sell","bot_id":4635591,"token":"***"}'

if ta.crossover(close, ta.sma(close, 20))
    strategy.entry("Long",  strategy.long,  alert_message = BUY_JSON)
if ta.crossunder(close, ta.sma(close, 20))
    strategy.close("Long",                  alert_message = SELL_JSON)

The alert panel can arm a durable rule with trigger = order_fill and a webhookUrl; every fill's alert_message is POSTed verbatim to the webhook.

Durable server-side alerts

A rule armed from the Alerts panel registers with the platform's alert service. The script continues executing with no chart open; alertcondition() fires are delivered through the configured channels (push / email / webhook).

Durable alert fields:

  • scriptId / version — pinned to the version the chart ran
  • inputs — the inputs snapshot the chart used
  • symbol / interval
  • trigger — what fires the rule:
    • alert_condition (default) — an alertcondition() / alert() call; optionally pinned to ONE alertcondition title (conditionName), otherwise any fires
    • order_fill — a strategy order filled; the fill's alert_message is the payload
    • plot_condition — a plot compared against a value, the price, or another script's plot (crossing / crossing up / crossing down / greater than / less than / entering, exiting, inside, outside channel / moving up, moving down, moving up %, moving down %) — no code needed
  • freq — only_once (fires then stops), once_per_bar, once_per_bar_close, once_per_minute, all
  • channels — any combination of push, email, webhook
  • webhookUrl — target for the webhook channel; must be a public http(s) host (private, loopback and link-local addresses are rejected, and re-checked on every delivery)
  • message — overrides the script's default message; supports the placeholders above
  • optional expiry — the rule deactivates itself at the chosen time

Marketplace scripts. A rule on a paid script is tied to your plan: when the subscription expires the alert service deactivates the rule (reason subscription expired — renew it … to re-arm this alert) within ten minutes, or exactly at the plan end when it is known. Renewing does not re-arm it — open the Alerts panel and enable the rule again.

Safety rails: a rule that fires 15 times within 3 minutes is stopped automatically (runaway guard), and a webhook body is posted verbatim — a JSON alert_message arrives byte-for-byte, with Content-Type: application/json when it parses as JSON.

See the alerts tab in the Indicators hub for creating, pausing, deleting rules, and reading the delivery log.

Alerts and strategies

A strategy that uses alert_message= on orders can arm a trigger = order_fill rule — every filled order emits one alert carrying its alert_message. Combine with an alertcondition() or two for pre-fill signals that notify you before a bar closes.

Debugging alerts

  • log.info(message) from inside the body prints to the Logs panel, mirroring exactly what would go into an alert.
  • A test alert can be fired with alert() and freq=alert.freq_all to validate placeholder interpolation.

See also