How to send TradingView alerts to MT5 and trade them automatically
TradingView cannot place trades in MetaTrader by itself. By the end of this guide you will know the three parts every TradingView-to-MT5 setup needs, how to make it safe against duplicates and stale signals, and when a native MT5 EA is the better choice.
Based on our team’s research and live testing since 2018.
Many traders build their strategy in Pine Script and want MetaTrader 5 to execute it. TradingView can send an alert, but it cannot reach into your MT5 terminal and place an order. Something has to carry the alert across and turn it into a trade. This guide explains how that works, what can go wrong, and when you should skip the bridge and convert the strategy into an MT5 Expert Advisor instead.
What are the three parts of a TradingView-to-MT5 setup?
- The signal, in TradingView. A Pine strategy or indicator fires an alert. The alert has a message (ideally JSON) and a webhook URL.
- The receiver, on a server. A small web service accepts the webhook, checks it is genuine, removes duplicates and queues the signal.
- The receiving EA, in MT5. An Expert Advisor on your terminal collects queued signals, maps the symbol, sizes the position and sends the order to your broker.
Commercial bridges package parts 2 and 3 for you. A custom build does the same job but under your control. Either way, the parts are the same, and so are the risks.
What does TradingView require for webhooks?
From TradingView’s own documentation (checked October 2026):
- Webhook alerts need two-factor authentication enabled on your account, and webhooks are a paid-plan feature.
- Only ports 80 and 443 are accepted. In practice, use HTTPS on 443.
- If your server takes longer than three seconds to answer, TradingView cancels the request. So the receiver must reply quickly and do the trading elsewhere.
- If the message is valid JSON, it is sent as
application/json; otherwise as plain text. - TradingView publishes the IP addresses its webhooks come from, so your server can accept only those. IPv6 is not supported.
- You cannot add your own HTTP headers. That means no Authorization header, so the shared secret has to travel inside the message body.
TradingView also warns not to put login credentials or passwords in the message. Use a random secret that only opens your receiver, never your broker password.
What should the alert message look like?
Use JSON with TradingView’s placeholders, which are filled in when the alert fires:
1{2 "secret": "replace-with-a-long-random-string",3 "id": "{{ticker}}-{{interval}}-{{time}}-{{strategy.order.id}}",4 "symbol": "{{ticker}}",5 "action": "{{strategy.order.action}}",6 "position": "{{strategy.market_position}}",7 "price": "{{strategy.order.price}}",8 "sent": "{{timenow}}"9}Three choices in this message matter:
- A unique id. Built from the symbol, timeframe, bar time and order id, so the same signal always produces the same id. The receiver uses it to reject duplicates.
- No lot size. Let the EA size the trade from your risk and stop distance on the MT5 account. TradingView does not know your MT5 balance.
- A send time.
{{timenow}}lets the EA reject signals that arrive too late, for example after a VPS restart.
How does the receiver handle security and duplicate alerts?
Alerts can arrive twice, and an attacker who finds your URL can send fake ones. The receiver deals with both before anything reaches MT5:
1# receiver.py: minimal sketch. Accept alerts, queue each one once.2import hmac, json, os, sqlite3, time3from flask import Flask, request, jsonify4 5app = Flask(__name__)6TV_SECRET = os.environ["TV_SECRET"] # same value as "secret" in the alert7EA_KEY = os.environ["EA_KEY"] # the EA sends this in a header8db = sqlite3.connect("signals.db", check_same_thread=False)9db.execute("CREATE TABLE IF NOT EXISTS signals "10 "(id TEXT PRIMARY KEY, body TEXT, received REAL, taken INTEGER DEFAULT 0)")11 12@app.post("/tv")13def tv_alert():14 alert = request.get_json(silent=True) or {}15 # TradingView cannot add custom headers, so the secret is in the body16 if not hmac.compare_digest(str(alert.pop("secret", "")), TV_SECRET):17 return jsonify(error="unauthorised"), 40118 if "id" not in alert:19 return jsonify(error="no id"), 40020 try:21 db.execute("INSERT INTO signals (id, body, received) VALUES (?, ?, ?)",22 (alert["id"], json.dumps(alert), time.time()))23 db.commit()24 except sqlite3.IntegrityError:25 return jsonify(status="duplicate") # same alert twice: ignore it26 return jsonify(status="queued") # answer fast; no trading here27 28@app.get("/next")29def next_signal():30 if not hmac.compare_digest(request.headers.get("X-EA-Key", ""), EA_KEY):31 return jsonify(error="unauthorised"), 40132 row = db.execute("SELECT id, body FROM signals WHERE taken = 0 "33 "ORDER BY received LIMIT 1").fetchone()34 if row is None:35 return jsonify(status="empty")36 db.execute("UPDATE signals SET taken = 1 WHERE id = ?", (row[0],))37 db.commit()38 return app.response_class(row[1], mimetype="application/json")This is a minimal sketch, not a production server. It checks the secret with a constant-time comparison, stores each id once, and answers immediately. The EA side has its own key, sent as a header, because MT5’s WebRequest() can send headers even though TradingView cannot.
A production version adds HTTPS, a TradingView IP allow-list, logging, and one important change: mark a signal done only after the EA confirms the order. The EA should also remember which ids it has already traded, so a restart can never repeat a trade.
What does the receiving EA do?
The EA polls the receiver about once a second and acts on new signals:
1// Receiving EA (excerpt). Add the URL in Tools > Options > Expert Advisors.2input string InpUrl = "https://signals.example.com/next";3input string InpKey = ""; // same value as EA_KEY on the server4 5int OnInit() { EventSetTimer(1); return INIT_SUCCEEDED; }6 7void OnTimer()8{9 char body[], reply[];10 string replyHeaders;11 string headers = "X-EA-Key: " + InpKey + "\r\n";12 int code = WebRequest("GET", InpUrl, headers, 3000, body, reply, replyHeaders);13 if(code != 200) return; // -1 means check GetLastError()14 string json = CharArrayToString(reply, 0, WHOLE_ARRAY, CP_UTF8);15 if(StringFind(json, "\"action\"") < 0) return; // queue was empty16 HandleSignal(json); // map symbol, check age and id, size from risk, send order17}Per the MQL5 documentation, the URL must be added to the allowed list in the terminal options, WebRequest() blocks the EA until the server answers, and it does not run in the Strategy Tester. Inside HandleSignal, the EA should:
- map the TradingView ticker to the broker’s symbol name (for example a suffix such as
EURUSD.r) - skip signals older than a set number of seconds, and ids it has already traded
- size the position from risk, check the spread, and set the stop loss
- apply daily loss limits and any prop-firm rules on the MT5 side
How fast is a TradingView-to-MT5 bridge?
Each step adds delay: the alert triggering, TradingView sending the webhook, the network to your server, the EA’s polling interval, and the order to the broker. For strategies on closed M15 or H1 bars, that delay rarely matters. For scalping, it does.
Measure your own setup rather than trusting anyone’s number. Log the {{timenow}} value, the time the receiver got the alert, and the time the order filled. Run it on demo for a week, then decide.
Also make the signal itself stable. Alerts that fire before a bar closes can fire for a signal that disappears by the close. Use closed-bar signals, and read how to tell if an indicator repaints before you automate an indicator.
When is a native MT5 EA better than a bridge?
A bridge keeps the strategy in TradingView. A conversion moves it into MT5. Choose a native MT5 EA conversion when:
- you want to backtest on your broker’s real tick data in MT5
- you trade a prop-firm account and want fewer moving parts
- you do not want to pay for a TradingView plan and a server just to execute
- the signals should come from your broker’s prices, not another data feed
Keep the bridge when the logic needs data that only TradingView has, or when you change the Pine script often. Translation details are covered in Pine Script to MQL5, and the backtest differences in why TradingView and MT5 backtests don’t match. For the bridge itself, see broker API integration.
Checklist
- Paid TradingView plan with two-factor authentication on
- Receiver on HTTPS (port 443), answering within three seconds
- Secret in the JSON body; IP allow-list for TradingView’s addresses
- Unique id per signal; duplicates rejected on the server and in the EA
- Stale signals rejected; symbol mapping tested for every pair
- Sizing and loss limits done in the EA; tested on demo first
Quick answers
Can I connect TradingView to MT5 with a free TradingView account?
Not for webhooks. TradingView lists webhook notifications as a paid-plan feature, and webhook alerts also need two-factor authentication turned on. Check the current plan table before you build.
Does TradingView need to stay open in my browser?
No. Alerts run on TradingView’s servers once created. But the alert must stay active, and your receiver and MT5 terminal must be running.
Can I backtest the bridge setup in MT5?
No. The signals come from TradingView, and WebRequest() does not run in the MT5 Strategy Tester. You can only test the strategy in TradingView, or convert it to a native EA and test it on MT5.
Will TradingView and MT5 prices match?
Not exactly. TradingView’s data feed may come from a different source than your broker’s prices, so levels and signals can differ slightly.
Can I use this on a prop-firm account?
Often yes, if the firm allows EAs and the trades follow its rules. Ask the firm, and make sure the EA enforces the loss limits itself.