pipsocketDOCS
Guides / TradingView

TradingView alerts

Trade your MT5 accounts from TradingView alerts, through a hook.

A hook is a webhook URL that trades. You create it in the dashboard, point it at one or more of your MT5 accounts, and paste its URL and a message into a TradingView alert. Each time the alert fires, TradingView posts the message to the hook, and Pipsocket turns it into trades on every account the hook targets, with a full trace of what it did and why in the alert log.

Hooks are part of the base price: nothing extra per hook or per alert.

What you need

  • A paid TradingView plan, Essential or higher. TradingView sends webhooks only from paid plans.
  • Two-factor sign-in on your TradingView account. TradingView won't send webhooks without it.
  • An owner or admin of the workspace, with a verified email, to create the hook. Members can read the alert log.
  • An MT5 account connected in the dashboard. Try a demo account first.

Create a hook

  1. Open TradingView in the dashboard and create a hook. Give it a name: usually one hook per TradingView strategy or indicator.
  2. Add accounts. A hook trades 1 to 20 accounts of the workspace, called its targets. Each target has its own settings (below).
  3. Build the message. The hook's message builder writes the JSON for each action you want (open, close, strategy mode and the rest) with the right TradingView placeholders, and the steps for the kind of alert you use. Alert messages describes every field.
  4. Send a test. A test is a dry run: Pipsocket checks the message against each account as it is now (symbol, size, stops, margin) and trades nothing. See dry runs.

The URL is the credential

https://hooks.pipsocket.com/t/hk_.../whk_...

The URL carries the hook's secret: anyone who has it can send alerts to your accounts. Keep it in TradingView and nowhere else.

  • Reveal it again. The dashboard shows the URL masked. Owners and admins can reveal and copy it again after entering their password, so adding another TradingView alert never needs a new URL. Every reveal is recorded in the workspace's audit log.
  • Rotate it if it may have leaked. The old URL stops working (it answers 404) as soon as the hook has the new secret, usually at once. Until then the old URL still works, the dashboard doesn't show the new one yet, and Pipsocket keeps applying the change. Paste the new URL into every TradingView alert that used the old one.
  • TradingView addresses only. A hook setting, off by default: when on, the hook accepts posts only from TradingView's published addresses. TradingView can change them, so check its list if alerts stop arriving.

Target settings

Each account a hook trades has its own settings. Defaults are in brackets.

SettingWhat it does
Sizing (as sent)As sent: the size the message asks for. Multiplier: that size times your number. Fixed: always your number of lots, whatever the message says. See sizing.
Default lots (none)The size when a message names none. Without it, a message with no size is refused, unless the target sizes fixed (its fixed lots are used).
Maximum lots (none)A size above it is refused above_max_lots, or cut down to it if you allow clipping.
Risk basisBalance or equity: what risk.percent is a percent of.
Strategy sizingFixed or scaled, for strategy mode.
Pyramiding (0)How many more entries on the same side an open may add while the lane already holds one. 0 refuses an open while one is there.
Opposite side (refuse)What an open does while the lane holds the other side: refuse, close first (runs a reverse), or hedge (hedging accounts only).
Heal drift (off)Strategy mode: open up to the strategy's position when the account has drifted from it.
Allowed actions (all but kill_switch)Actions the hook may take on this account.
Allowed symbols (all)Symbols the hook may trade on this account.
Maximum positions (none)A cap on positions; an open over it is refused max_positions.
Trading hours (none)Days and times, in a time zone you choose, when the hook may trade the account.
Slippage (10 points)The deviation for market orders, unless the message sets slippage.

The hook itself has a name, an age limit for entries (30 seconds, see age limits), the TradingView addresses setting, and email notifications.

Set up the alert in TradingView

  1. Create the alert on your chart, with the condition for the kind of alert you use (below).
  2. In the alert's notifications, turn on Webhook URL and paste the hook's URL.
  3. Paste the message from the builder into Message.

TradingView posts the message as it is, after filling in its placeholders. A message that is a JSON object is what Pipsocket reads; anything else is recorded and refused invalid_json.

How Pipsocket answers TradingView

TradingView waits 3 seconds for an answer and then gives up. Pipsocket answers as soon as the alert is safely recorded, before any trading starts, so trading time never counts against those 3 seconds.

AnswerMeaning
200Recorded. Refusals are recorded too, and live in the alert log, not in the answer.
404No such hook, or the wrong secret (a rotated URL, for example).
413The message is over 8 KB.
429Over the hook's limit (see limits), or too many wrong URLs from one address.
503The alert could not be recorded.

TradingView sends an alert again after a 5xx answer (except 504), 5 seconds later, up to 3 times. That only happens when the alert was not recorded, and an alert that arrives twice is recognised by its event_id and refused duplicate, so a resend never trades twice.

The three kinds of alert

TradingView has three ways for a script to raise an alert, and they treat placeholders such as {{ticker}} differently. The builder gives you the message and steps for each.

Order fills (strategies)

A strategy's alert fires on each order fill. TradingView fills in placeholders in the alert's message, including the strategy's: {{strategy.market_position}}, {{strategy.market_position_size}}, {{strategy.prev_market_position}}, {{strategy.order.id}} and the rest. That makes strategy mode a good fit: the message says where the strategy's position was and where it is now, and Pipsocket brings each account's position there.

//@strategy_alert_message in the script sets the default text of the alert's message box, so you don't paste it each time you create the alert:

//@version=6
//@strategy_alert_message {"action":"strategy","src":"k3f9","event_id":"k3f9-ema-{{exchange}}:{{ticker}}-{{interval}}-{{strategy.order.id}}-{{time}}-{{strategy.market_position}}-{{strategy.market_position_size}}","sent_at":"{{timenow}}","symbol":"{{exchange}}:{{ticker}}","tag":"ema","position":{"from":"{{strategy.prev_market_position}}","from_size":"{{strategy.prev_market_position_size}}","to":"{{strategy.market_position}}","size":"{{strategy.market_position_size}}"},"sl":{"pips":25},"tp":{"pips":50}}
strategy("EMA cross", overlay = true)

fast = ta.ema(close, 9)
slow = ta.ema(close, 21)

// Each cross reverses the strategy: long to short, short to long.
if ta.crossover(fast, slow)
    strategy.entry("Long", strategy.long)
if ta.crossunder(fast, slow)
    strategy.entry("Short", strategy.short)

Create the alert on the strategy (condition: the strategy's order fills) and the message box already holds the message.

An order's alert_message argument is passed through as {{strategy.order.alert_message}}, but TradingView doesn't fill in placeholders inside it: keep the placeholders in the message box, and use alert_message for values computed in Pine. A strategy that can fill more than once inside one bar needs that: a counter from Pine, so its alerts get different event_ids (see ids and duplicates). TradingView evaluates alert_message when the order executes, so the counter goes up with each order the script places:

//@version=6
//@strategy_alert_message {"action":"strategy","src":"k3f9","event_id":"k3f9-ema-{{exchange}}:{{ticker}}-{{interval}}-{{strategy.order.id}}-{{time}}-{{strategy.market_position}}-{{strategy.market_position_size}}-{{strategy.order.alert_message}}","sent_at":"{{timenow}}","symbol":"{{exchange}}:{{ticker}}","tag":"ema","position":{"from":"{{strategy.prev_market_position}}","from_size":"{{strategy.prev_market_position_size}}","to":"{{strategy.market_position}}","size":"{{strategy.market_position_size}}"}}
strategy("EMA cross, every tick", overlay = true, calc_on_every_tick = true)

fast = ta.ema(close, 9)
slow = ta.ema(close, 21)

// Counts the orders the script places. `varip` keeps the count across the ticks of a bar (`var`
// would go back to the bar's start on each tick), so each order in a bar gets its own number.
varip int orders = 0

if ta.crossover(fast, slow)
    orders += 1
    strategy.entry("Long", strategy.long, alert_message = str.tostring(orders))
if ta.crossunder(fast, slow)
    orders += 1
    strategy.entry("Short", strategy.short, alert_message = str.tostring(orders))

alertcondition() (indicators)

An indicator declares its conditions with alertcondition(), and you create one alert per condition. Placeholders work in the alert's message, so the message can use {{exchange}}, {{ticker}}, {{interval}}, {{time}} and {{timenow}}. The message given to alertcondition() is the default text of the message box:

//@version=6
indicator("EMA cross signals", overlay = true)

fast = ta.ema(close, 9)
slow = ta.ema(close, 21)

alertcondition(ta.crossover(fast, slow), "Buy", '{"action":"open","src":"q7m2","event_id":"q7m2-ema-{{exchange}}:{{ticker}}-{{interval}}-open-buy-{{time}}","sent_at":"{{timenow}}","symbol":"{{exchange}}:{{ticker}}","side":"buy","tag":"ema","risk":{"percent":1},"sl":{"pips":25},"tp":{"pips":50}}')
alertcondition(ta.crossunder(fast, slow), "Close buys", '{"action":"close","src":"q7m2","event_id":"q7m2-ema-{{exchange}}:{{ticker}}-{{interval}}-close-buy-{{time}}","sent_at":"{{timenow}}","symbol":"{{exchange}}:{{ticker}}","side":"buy","tag":"ema"}')

Create two alerts on the indicator, one with the condition Buy and one with Close buys, both with the hook's URL. Choose Once per bar close: the event_ids assume at most one alert per bar for each action.

alert() (any script)

alert() sends a message a script builds while it runs, and TradingView does not fill in placeholders in it: {{ticker}} would arrive as the text {{ticker}}. Build every value in Pine instead, with str.tostring() for numbers, syminfo.prefix and syminfo.ticker for the symbol, timeframe.period for the interval, and str.format_time() for the time. One alert, with the condition Any alert() function call, carries every message the script sends.

Because the script builds the message, it can also send source_seq, which orders alerts exactly (see ids, duplicates and order). Pine's timenow is the time the script ran, in milliseconds. Every alert from one run shares it, and two runs inside the same millisecond would too, so it works as source_seq only when each of a lane's alerts comes from its own run, well apart: here the alerts fire once per bar close on a time-based chart, and the open and the close can't both fire on one bar. A script that alerts several times inside a bar should leave source_seq out, and order then comes from sent_at and the order Pipsocket received them in (a varip counter would do for event_ids, as in the order fills example, but a count kept by the script isn't guaranteed to keep growing when TradingView restarts the alert, for example when you pause and restart it).

//@version=6
indicator("Breakout", overlay = true)

length = input.int(20, "Length")
upper = ta.highest(high, length)[1]
lower = ta.lowest(low, length)[1]

symbolText = syminfo.prefix + ":" + syminfo.ticker

// The hook's message for one action, every value written out by Pine.
message(action, side, rest) =>
    eventId = "b8x1-brk-" + symbolText + "-" + timeframe.period + "-" + action + "-" + side + "-" + str.tostring(time)
    sentAt = str.format_time(timenow, "yyyy-MM-dd'T'HH:mm:ss'Z'", "UTC")
    '{"action":"' + action + '","src":"b8x1","event_id":"' + eventId + '","source_seq":' + str.tostring(timenow) + ',"sent_at":"' + sentAt + '","symbol":"' + symbolText + '","side":"' + side + '","tag":"brk"' + rest + '}'

if close > upper
    alert(message("open", "buy", ',"lots":0.10,"sl":{"price":' + str.tostring(lower, format.mintick) + '}'), alert.freq_once_per_bar_close)
if close < lower
    alert(message("close", "buy", ""), alert.freq_once_per_bar_close)

plot(upper, "Upper")
plot(lower, "Lower")

Lanes and tags

A hook only ever touches the positions and pending orders it opened itself, never your manual or API trades, and never another hook's. Inside a hook, ownership is per lane: the account, the hook, the message's tag and the symbol. Two strategies can share one hook and one account by sending different tags, and each then closes only its own positions.

Orders a hook sends carry a magic number from Pipsocket's reserved range (2^52 to 2^53 - 1) and the comment ps:<tag>, so you can tell them apart in MT5 and in your broker's history. Your own API orders can't use that range (422 magic_reserved).

Hedging and netting accounts

MT5 accounts are hedging (several positions per symbol, each with its own stops) or netting (one position per symbol, with one stop loss and take profit). Hooks work with both, with fewer features on netting:

HedgingNetting
Open, close, close all, cancel, reverse, modify, strategy modeYesYes
Pending entries, trailing stops, break-evenYesYes
Take-profit ladders (market entries only)YesRefused
Opposite side: hedgeYesRefused
PyramidingYesRefused
Other trades on the same symbolAllowed: the lane touches only its ownRefused symbol_shared: the lane must own the symbol's whole position

On a netting account, a lane may add exposure to a symbol only when the symbol has no position or order other than the lane's own, and holds the whole position for as long as it lasts. If anything else trades that symbol meanwhile (an API call, another terminal, the broker's app, another EA), the lane's ownership is lost: closes, changes and managed stops on that symbol are suspended, and alerts are refused ownership_lost, until you release the ownership or close the position in the dashboard. The broker's own stop loss, take profit and stop out are normal, and don't count.

On a hedging account, other trades on the symbol are simply left alone: the lane acts only on its own positions.

Symbols

TradingView names symbols as EXCHANGE:TICKER (OANDA:XAUUSD); your broker may call it XAUUSD.r. Pipsocket finds the account's symbol by:

  1. The account's symbol map: an exact EXCHANGE:TICKER, then the TICKER.
  2. The ticker exactly, if the account has a symbol of that name (case matters).

Otherwise the alert is refused unknown_symbol: Pipsocket never guesses. Edit the map on the dashboard's Symbols page, which lists the account's symbols with their specifications and the tickers your alerts sent that had no match.

Each mapping has a pip size, used for distances in pips. It starts at 10 points for 3- and 5-digit symbols and 1 point for the rest. Metals, indices and crypto are flagged to confirm until you save them, as their pips vary by broker.

The kill switch and alerts

The account's kill switch blocks new exposure from every source, hooks included: while it is engaged, alerts that would open or add are refused kill_switch, and closes still run. A sweep closes everything on the account, the hook's positions included, and while the switch stays engaged it also closes anything opened later, trades placed by hand in the broker's app included.

An alert can engage it with {"action": "kill_switch"}, only on targets whose allowed actions include it (off by default). It engages at once, without waiting behind the hook's other trades on the account, and works while the terminal is disconnected: the block holds, and the sweep runs when the terminal connects. An alert never lifts it: lift it in the dashboard or with the API.

Pause, change, rotate, delete

  • Pause stops a hook. Alerts that arrive while it's paused are recorded as paused and not delivered. Alerts already on their way to an account send no further trade that would open or add; exits already planned go ahead. A trade already sent to the broker before the pause can't be called back: it settles as usual, and may fill. The hook shows pausing until every account's host has confirmed, then paused. Resume lets new alerts through; what the pause stopped stays stopped.
  • Changes to targets and settings apply to alerts received after them. A change that narrows what a hook may do (turning a target off, removing an action or symbol, lowering a limit) takes effect on alerts already on their way too, the same way a pause does.
  • Rotate replaces the URL: the old one answers 404 once the hook has the new secret, usually at once, and keeps working until then.
  • Delete stops the hook. Positions it opened stay owned, with their trailing stops and break-even, until they close: the account's page lists them as from a deleted hook.

Pausing or deleting a hook doesn't stop the trailing stops and break-even of positions it already opened.

Limits

LimitPerWhen it's reached
30 alerts in 10 seconds, 600 an hourHook429. Exits (close, close all, cancel, strategy mode reductions) have a second allowance of the same size, so a flood of entries never uses up the room a close needs. Refused alerts are counted per minute in the alert log.
8 KBMessage413.
20 accountsHookA hook can't target more.
200 alerts waitingAccount of a hookRefused queue_full.
20 wrong URLs a minuteSending address429 for a minute. TradingView's own addresses are never blocked this way.
30 daysAlert logAlerts and their traces are deleted 30 days after they end. One still watched after it ended (an exit watching for late fills: see late_fill_closed) is kept until that is over, and 30 days more.

These are Pipsocket's limits, separate from your broker's and from the limits you set on each target.

Notifications and permissions

  • Email digests, per hook (on by default, a hook setting): at most every 15 minutes, a summary of alerts that ended refused, partial, unknown or undelivered, and of strategy drift. See alert outcomes.
  • Owners and admins create, change, rotate, pause, delete, test and resend, and edit symbol maps. Members read the alert log.