# How to send a chart alert to a trading bot

A TradingView alert can POST to a bot that places the order. What the webhook costs, what it does not promise, and where the secret ends up.

*https://cryptomarkets.tools/how-to/send-a-chart-alert-to-a-bot · next to Crypto Charting Platforms & Libraries*

**Answer:** Create a TradingView alert with a webhook URL — a paid feature from Essential up, with two-factor authentication switched on — and point it at a bot that accepts one: 3Commas or Altrady hosted, OctoBot or Gunbot on your own server, or a receiver you write around CCXT. TradingView sends one POST, cancels it after three seconds and documents no retry, so the bot has to reject stale, repeated and unauthenticated messages by itself.

## Approaches

*In the author’s order. Paid placement does not affect it.*

1. [3Commas](https://cryptomarkets.tools/tools/3commas.md) — Signal bots take a JSON body carrying the bot's secret, plus an optional max_lag that refuses a signal older than you allow. Signal bots start on Pro.
2. [Altrady](https://cryptomarkets.tools/tools/altrady.md) — A hosted signal bot on every tier, fed JSON carrying the bot's key and secret. Its test flag works on open signals only; any other signal goes live.
3. [OctoBot](https://cryptomarkets.tools/tools/octobot.md) — Self-hosted, with its own KEY=VALUE alert format, reached through ngrok or a proxy you run — and a token check that ships switched off.
4. [Gunbot](https://cryptomarkets.tools/tools/gunbot.md) — A self-hosted licence that reads a space-separated alert starting with your passphrase, and must listen publicly on port 443.
5. [CCXT](https://cryptomarkets.tools/tools/ccxt.md) — Write the receiver yourself — check the secret, drop repeats, then place the order through one client. The only option where every check is yours to see.

## The short way

[TradingView](https://cryptomarkets.tools/tools/tradingview) does not place orders from an alert. It sends one: when an alert
fires, it POSTs the message you wrote to a URL you gave it, and whatever answers that URL does the
trading. So the job is three settings on the TradingView side and one bot that accepts webhooks on
the other.

On the TradingView side: a paid plan, because the pricing table marks webhook notifications as
absent on Basic and present from Essential upward; two-factor authentication on the account,
because webhook alerts are refused without it; and a URL on port 80 or 443 over IPv4, the only
ports and the only protocol the help centre accepts. Then write the alert message in whatever
format the receiving bot reads — usually JSON, which TradingView sends as `application/json` when
it parses, and as `text/plain` when it does not.

On the other side, the least work is a hosted signal bot. A [3Commas](https://cryptomarkets.tools/tools/3commas) signal bot
gives you a URL and a message template to paste into the alert; the message carries your bot's
identifiers and a secret, and TradingView's placeholders fill in the rest when it fires:

```json
{
  "secret": "YOUR_BOT_SECRET",
  "max_lag": "300",
  "timestamp": "{{timenow}}",
  "trigger_price": "{{close}}",
  "tv_exchange": "{{exchange}}",
  "tv_instrument": "{{ticker}}",
  "action": "enter_long",
  "bot_uuid": "YOUR_BOT_UUID"
}
```

Two of those fields are doing more work than the rest. `secret` is the only thing standing between
the URL and anyone who learns it. `max_lag` is the largest delay you will accept between
`timestamp` and execution, in seconds — anything from 10 to 86,400, 300 by default — and a signal
past it is treated as invalid. It is the one field in this chain that guards against an order
placed on a price from five minutes ago. Keep both. The
rest of this page is about why.

## What the options are

**Hosted, JSON in.** 3Commas, above, runs signal bots from the Pro plan; the free plan cannot trade
at all. [Altrady](https://cryptomarkets.tools/tools/altrady) is the same shape on every tier: a webhook builder generates the
JSON, which carries the `api_key` and `api_secret` from the bot's settings, and the message names the action —
open, increase, reduce, close or reverse — the exchange and the market. Both run on the vendor's
servers, so they keep working with your machine off, and both hold your exchange keys to do it.

**Self-hosted, text in.** [OctoBot](https://cryptomarkets.tools/tools/octobot) reads its own format, one `KEY=VALUE` per line
or separated by semicolons:

```text
EXCHANGE={{exchange}}
SYMBOL={{ticker}}
SIGNAL=BUY
TOKEN=YOUR_OCTOBOT_TOKEN
```

The webhook server listens on port 9000 by default, which TradingView will not call, so the bot
either opens an ngrok tunnel — built in, with your ngrok token — or sits behind a proxy you run on
443. [Gunbot](https://cryptomarkets.tools/tools/gunbot) takes a single space-separated line, passphrase first:

```text
YOUR_PASSPHRASE binance buy USDT-BTC 0.1 0
```

— exchange, side, pair, amount and a price, where `0` means a market order — and its documentation
requires the bot itself to be publicly reachable on 443. Both keep the exchange keys on your own
machine, which is the reason to choose them; the network exposure is the cost.

**Your own receiver.** A few dozen lines of any web framework, with [CCXT](https://cryptomarkets.tools/tools/ccxt) behind them
to place the order on whichever venue you trade. This is the only version in which every check
described below is code you can read: the secret comparison, the age check, the duplicate filter,
and what happens to a close that arrives when there is nothing open. It is also the only one in
which all of that is your bug.

## Where this breaks

**The secret travels in the message body.** TradingView's webhook sends the message you typed and
nothing else you control: the help page describes no custom header and no signing. So every bot on
this page authenticates the same way — a value inside the body. 3Commas calls it `secret`, Altrady
`api_secret`, OctoBot `TOKEN`, Gunbot a passphrase. That puts the credential in the alert
definition, in the alert log, in any screenshot of either, and in any script that builds the
message from inputs and gets shared. TradingView's own page asks you not to put "sensitive
information such as login credentials or passwords in the webhook body", which is advice no
receiver here lets you follow. What you can do is limit what the secret opens: a bot-scoped value
rather than anything reused, exchange keys with withdrawals disabled, and — if the receiver is
yours — a check that the request came from one of the four IPv4 addresses TradingView publishes.

**One self-hosted bot ships with the check switched off.** OctoBot generates a token when the
TradingView service is configured, but `require-token` defaults to `false`, so until you turn it
on any POST to the URL in the right format is a signal. On a URL that has been pasted into a tunnel
dashboard, a chat or a support thread, that is the whole of the security.

**There is no retry, and three seconds is the deadline.** TradingView cancels a request the server
takes longer than three seconds to answer, says webhooks "may occasionally fail to reach the
specified URL", and points you at a status column in the alert log. Nothing on the page promises a
second attempt. A receiver that places the order before responding can blow the deadline on a slow
exchange call; one that is restarting when the alert fires simply misses it. A missed entry costs
an opportunity. A missed exit leaves a position open that the chart believes is closed — which is
why Altrady recommends a single reverse signal over a close followed by an open, noting that with
separate signals "the timing of the close is unknown".

**The alert fires later than the bar closes.** With "once per bar close", TradingView waits for the
first trade of the next bar before it can be sure the previous one has closed; on a thin market that
can be several seconds, and with no trades at all the server closes the bar itself one minute after
the formal close. Add the exchange call on the far side, and the order is priced by the market that
exists then, not by `{{close}}`. That is what `max_lag` is for; a receiver of your own should compare
`{{timenow}}` with its own clock and refuse old signals the same way.

**A signal that repaints can trade and then vanish.** The default frequency for a script's
`alert()` is once per bar, which fires on the first call during the live bar — before the bar is
confirmed. An indicator whose condition is true mid-bar and false at the close has sent a webhook
by then, and the chart afterwards shows no signal where your bot traded. Pine's documentation
defines repainting as historical and real-time calculations behaving differently, and says the
simplest way to avoid it in alerts is to trigger only on bar close, at the price of immediacy.
Values requested from another timeframe are the other usual cause: on real-time bars they can be
unconfirmed.

**An alert runs the script as it was when you created it.** TradingView saves a copy of the script,
its inputs, the symbol and the timeframe; later edits "will not affect running alerts previously
created from them". Fix a bug in the indicator and the bot keeps trading the buggy version until
the alert is deleted and made again.

**An alert that fires too often is stopped.** More than 15 triggers in three minutes and TradingView
halts it. A condition that flickers on a fast chart can switch off your bot's only input without
anything on the bot's side noticing.

## If you outgrow this

If the problem is **that a missed webhook is a missed trade**, stop treating the alert as the order.
Make it a notification that the bot reconciles against: on each message, the receiver reads the
position from the exchange and moves it toward what the signal says it should be, so the next
message repairs whatever the last lost one broke. A hosted signal bot cannot be made to work this
way; your own receiver can.

If the problem is **that the logic lives in someone else's charting language**, the durable fix is
to move it next to the orders. [Freqtrade](https://cryptomarkets.tools/tools/freqtrade), [Hummingbot](https://cryptomarkets.tools/tools/hummingbot) and
OctoBot compute their signals from the exchange's own feed inside the bot process, which removes the
webhook, the three-second deadline and the secret in the body at once — and brings a backtest that
runs the same code, which a Pine alert never had. The costs of that are covered in
[what a crypto backtest silently assumes](https://cryptomarkets.tools/guides/what-a-crypto-backtest-assumes).

If the problem is **that the chart is not TradingView**, check the webhooks line on the card before
building anything. Of the nine charting cards in this catalogue that alert at all, three list
webhooks — TradingView, [CoinAnk](https://cryptomarkets.tools/tools/coinank) and [GoCharting](https://cryptomarkets.tools/tools/gocharting),
where alert webhooks sit on a paid plan — and the rest notify a person rather than a program. The
same caution applies at the far end: [Tealstreet](https://cryptomarkets.tools/tools/tealstreet) has a local listener but no
hosted path for an alert to reach it, and [Bitsgap](https://cryptomarkets.tools/tools/bitsgap) accepts no webhooks at all.
The [trading bots](https://cryptomarkets.tools/categories/trading-bots) listing is where to compare the rest.

## FAQ

### Do TradingView webhook alerts need a paid plan?

Yes. The plan comparison marks webhook notifications as unavailable on the free Basic plan and included from Essential upward. Separately from the plan, the account needs two-factor authentication switched on, and the receiving URL has to be on port 80 or 443 over IPv4.

### Does TradingView retry a webhook that failed?

Nothing in its documentation says so. The help page says a request is cancelled if the server takes more than three seconds to answer, that webhooks may occasionally fail to reach the URL, and that delivery can be checked in the webhook status column of the alert log. Build the receiver on the assumption that a message can be lost.

### How do I keep someone else from triggering my bot's webhook?

TradingView cannot add headers or sign a request, so the check has to be inside the message — the secret, token or passphrase each bot defines. Make sure it is actually enforced (OctoBot's is off by default), keep it out of shared scripts and screenshots, use exchange keys without withdrawal rights, and on a receiver you run, accept requests only from the four IPv4 addresses TradingView publishes.

### Why did my bot trade on a signal that is not on the chart?

Usually because the alert fired during a live bar and the condition was no longer true when the bar closed. A script's alert() defaults to once per bar, which fires on the first qualifying tick. Setting the alert to once per bar close waits for confirmation — at the cost of firing later, sometimes seconds and occasionally a minute after the close.

## Sources

1. [How to configure webhook alerts](https://www.tradingview.com/support/solutions/43000529348-about-webhooks/) — TradingView, read 2026-09-27
2. [Compare plans](https://www.tradingview.com/pricing/) — TradingView, read 2026-09-27
3. [Delays in OncePerBarClose alerts](https://www.tradingview.com/support/solutions/43000773948-delays-in-onceperbarclose-alerts/) — TradingView, read 2026-09-27
4. [Alert was triggered too often and stopped](https://www.tradingview.com/support/solutions/43000690939-alert-was-triggered-too-often-and-stopped/) — TradingView, read 2026-09-27
5. [Pine Script v6 User Manual — Alerts](https://www.tradingview.com/pine-script-docs/concepts/alerts/) — TradingView, read 2026-09-27
6. [Pine Script v6 User Manual — Repainting](https://www.tradingview.com/pine-script-docs/concepts/repainting/) — TradingView, read 2026-09-27
7. [Signal bot: JSON file in Custom signal type](https://help.3commas.io/en/articles/8894481-signal-bot-json-file-in-custom-signal-type) — 3Commas, 2026-07-31
8. [Webhook Signal Reference: Open, Increase, Reduce, Close, and Reverse](https://help.altrady.com/en/tradingview-webhooks/webhook-signal-json-reference) — Altrady, read 2026-09-27
9. [Services/Services_bases/trading_view_service/trading_view.py](https://github.com/Drakkar-Software/OctoBot-Tentacles/blob/master/Services/Services_bases/trading_view_service/trading_view.py) — Drakkar-Software, read 2026-09-27
10. [Services/Services_bases/webhook_service/webhook.py](https://github.com/Drakkar-Software/OctoBot-Tentacles/blob/master/Services/Services_bases/webhook_service/webhook.py) — Drakkar-Software, read 2026-09-27
11. [Trading/Mode/trading_view_signals_trading_mode/trading_view_signals_trading.py](https://github.com/Drakkar-Software/OctoBot-Tentacles/blob/master/Trading/Mode/trading_view_signals_trading_mode/trading_view_signals_trading.py) — Drakkar-Software, read 2026-09-27
12. [octobot_services/constants.py](https://github.com/Drakkar-Software/OctoBot-Services/blob/master/octobot_services/constants.py) — Drakkar-Software, read 2026-09-27
13. [Webhook alerts](https://www.gunbot.com/support/docs/trading-alerts/webhook-alerts/) — Gunbot, read 2026-09-27

*Last updated 2026-09-27. Corrected in place — an endpoint that moves is a bug here, not a new post.*
