TradingView can't place futures orders at your broker by itself, but every alert can POST a message to a URL. Put a small server behind that URL, have it read the message and call your broker's API, and your alerts become orders. This post covers how TradingView sends webhooks, how to write the alert message, and the problems a receiver has to handle before you trust it with a live account.
How TradingView sends a webhook
When an alert fires and Webhook URL is ticked in the alert's notifications, TradingView sends an HTTP POST to that URL with the alert's Message as the body. As of October 2026, TradingView's help page on webhooks says:
- Webhook alerts only work with 2-factor authentication turned on for your TradingView account.
- Only ports 80 and 443 are accepted.
https://example.com:8443/hookwon't work. - If your server takes longer than 3 seconds to respond, the request is cancelled.
- If the message is valid JSON, the request has
Content-Type: application/json. Otherwise it'stext/plain. - IPv6 isn't supported, so your hostname needs an A record.
- Requests come from four published IP addresses (52.89.214.238, 34.212.75.30, 54.218.53.128 and 52.32.178.7), which you can allowlist.
- Webhooks can occasionally fail to arrive. The alert log has a Webhook status column for checking.
TradingView also says not to put passwords or other credentials in the message body. That matters later.
Writing the alert message
Make the message JSON, so it arrives as application/json and your server can parse it without guessing. A minimal order looks like this:
{
"action": "buy",
"symbol": "MNQ1!",
"qty": 1,
"orderType": "market"
}
You don't want to hard-code buy and 1 for a strategy that both enters and exits. TradingView fills in placeholders in double curly braces when the alert fires. For a Pine Script strategy(), create the alert on the strategy itself with the trigger Order fills only, and use:
{
"action": "{{strategy.order.action}}",
"symbol": "{{ticker}}",
"qty": {{strategy.order.contracts}},
"orderType": "market"
}
On each fill, strategy.order.action becomes buy or sell, strategy.order.contracts becomes the number of contracts and ticker becomes the chart's symbol. Leave qty unquoted so it arrives as a number.
These strategy.* placeholders don't exist for an indicator(). There you either write fixed values into each alert, or build the whole message in Pine and send it with alert():
msg = '{"action":"' + (isLong ? "buy" : "sell") + '","symbol":"' + syminfo.ticker +
'","qty":' + str.tostring(qty) + ',"orderType":"market"}'
alert(msg, alert.freq_once_per_bar_close)
A minimal receiver
Here's the shape of a receiver in Node.js with Express. The secret is in the URL path rather than the body, so each strategy gets its own URL that you can revoke, and placeOrder stands for your broker's API call:
import express from "express";
const app = express();
app.use(express.json());
const seen = new Map(); // body -> time, to drop duplicate deliveries
app.post("/hook/:token", (req, res) => {
if (req.params.token !== process.env.HOOK_TOKEN) return res.sendStatus(404);
const { action, symbol, qty, orderType = "market" } = req.body ?? {};
if (!["buy", "sell"].includes(action) || !symbol || !Number.isInteger(qty) || qty < 1) {
return res.status(400).json({ error: "bad alert message" });
}
const key = JSON.stringify(req.body);
if (seen.has(key) && Date.now() - seen.get(key) < 5_000) return res.sendStatus(200);
seen.set(key, Date.now());
// Answer TradingView first, then trade: the broker call may take longer than 3 seconds.
res.sendStatus(202);
placeOrder({ action, symbol, qty, orderType }).catch((err) => console.error("order failed", err));
});
app.listen(3000); // behind a reverse proxy on 443 with a real TLS certificate
It's short, but each line answers one of the problems below.
What a receiver has to get right
The 3-second limit. A broker API call that has to log in, refresh a token or wait for an acknowledgement can easily take longer than 3 seconds. Answer TradingView first and place the order afterwards, or better, put it on a queue with retries. Then log the broker's answer somewhere you'll actually look.
Duplicates. Several alerts can fire on the same bar, and a retry can deliver the same message twice. A receiver that turns every POST into an order will one day double your position. The 5-second window above is the bare minimum. A real one records each order it placed, and the broker's own idempotency key if it has one.
Contract rollover. MNQ1! is TradingView's continuous contract, not something a futures broker can trade. Your receiver has to map it to the current front-month contract, such as MNQZ6, and keep doing that correctly after every quarterly roll. Hard-coding the month works until the day it silently sends orders to an expiring contract.
Exits. "Close my position" is less simple than it sounds. Flattening the whole account also cancels resting orders and closes positions in other symbols that your alert never mentioned. Decide which one you mean, and to exit one symbol, send the opposite side for that quantity.
Brackets. If you send a take-profit and a stop-loss with the entry, they have to be linked as one-cancels-other (OCO) at the broker. Otherwise, when the target fills, the stop is still working and can open a new position the wrong way.
Accounts that stop existing. Prop-firm evaluation accounts get reset, breached or replaced. The alert keeps firing at an account the broker no longer reports, and every order is rejected. If nothing tells you, you find out at the end of the day.
Auth and logging. Use HTTPS, keep the token out of the message body, and log every alert next to the order it produced, or the error it got. When a trade looks wrong, that log is the only way to tell whether TradingView, your receiver or the broker caused it.
Test on a simulated account first. Run the whole path, from alert to fill, on a demo or sim account until the logs match your strategy's trades.
The shortcut: a hosted webhook
If you'd rather not run and maintain that server, this is what we built into Signal Trade App, a futures trade copier for Tradovate, Rithmic, NinjaTrader and ProjectX accounts. You create a webhook token on the Signals page, paste its URL into the TradingView alert, and use the same JSON as above:
{
"action": "{{strategy.order.action}}",
"symbol": "{{ticker}}",
"qty": {{strategy.order.contracts}},
"orderType": "market",
"takeProfit": 4550.00,
"stopLoss": 4490.00
}
It handles the problems above like this:
- Continuous symbols such as
MNQ1!orES1!resolve to the current front-month contract, including after rollovers. -
takeProfitandstopLosswork with market, limit and stop entries, and the two exit legs are linked as OCO at the broker. -
{"action": "close"}flattens the whole account. If you also name a symbol while the account holds a different one, the close is refused rather than closing a position the alert never mentioned. - A token is tied to one account. If the broker stops reporting that account, the Signals page flags the token instead of failing silently.
- If the token points at a leader account, the order is copied to its follower accounts with each follower's own ratio and risk limits.
There are limits: one take-profit and one stop-loss per entry, no trailing or breakeven stops from a single webhook, and no OCO between two separate entry orders. For those you'd use the REST API. The TradingView trade copier page has the full setup.
Whichever route you take, keep the alert message as JSON, test on a sim account first, and keep a log of every alert next to the order it produced.
Automated trading can lose money quickly. Nothing here is trading advice.
Top comments (0)