TradingView Placeholders for Dynamic Payloads
{{ticker}}, {{close}}, {{strategy.order.action}} — those curly-brace tokens are TradingView's placeholders. TradingView replaces each one with a live value just before posting the webhook body. By the time TVH receives the request, the braces are gone and you have real strings and numbers in the JSON.
Use them so a single JSON template works on any chart, on any timeframe, for any direction.
- Placeholders are substituted by TradingView, not TVH. TVH only ever sees the resolved values.
{{ticker}}makes one alert work on every symbol.{{strategy.order.action}}makes one alert handle both directions.- Wrap string-typed placeholders in quotes (
"pair":"{{ticker}}"). Numbers can go either way — keep quotes for consistency. - Most TVH fields accept placeholders. The exceptions are booleans and
apiKey/token.
This is the complete placeholder reference for every alert type. If you specifically want one strategy alert to handle entry, exit, and close, start with Strategy Alerts, which walks through the dynamic command end to end.
How placeholders work
Two things to remember:
- TVH never sees the curly braces. If a placeholder is misspelled, TradingView leaves it literal — TVH then fails to parse
"price": "{{cl0se}}"as a number. - You can substitute inside string values OR inside number positions. TradingView does not check types; it just performs string replacement on the body.
Full placeholder reference
The placeholders TVH supports — i.e. that resolve to something you can usefully consume in a trade command:
| Placeholder | Resolves to | Typical TradeCommand field |
|---|---|---|
{{ticker}} | Symbol-only (e.g. BTCUSDT, XAUUSD). No exchange prefix. | pair |
{{exchange}} | Chart's exchange (e.g. BINANCE, BITMEX). | Usually unused — TVH's exchange is an identifier, not TradingView's exchange code. |
{{interval}} | Chart timeframe in TradingView format (60, 1D, 4H). | Metadata. Useful as part of an idempotency key. |
{{time}} | UTC ISO-8601 string of the bar's open time (e.g. "2026-05-21T12:00:00Z"). | alertTimestamp (with prefix for uniqueness). |
{{timenow}} | UTC ISO-8601 string of the time the alert fired (e.g. "2026-05-21T12:00:03Z"). | alertTimestamp. |
{{open}} | Bar open price. | price, math-expression operand. |
{{high}} | Bar high. | stopLoss, math expressions. |
{{low}} | Bar low. | stopLoss, math expressions. |
{{close}} | Bar close. | price, stopLoss, targets[].price for absolute targets. Use takeProfitPercent for percent targets. |
{{volume}} | Bar volume. | Metadata; conditional logic on the Pine side. |
{{strategy.order.action}} | "buy" or "sell" (lowercase). | orderType for strategy-driven alerts. |
{{strategy.order.contracts}} | Order size in Pine's strategy units. | units when Pine sizes the trade. |
{{strategy.order.price}} | Fill price of the order that fired. | price, inside math expressions. |
{{strategy.order.comment}} | The comment= argument of the order. | strategyComment (drives the auto-close logic). |
{{strategy.order.alert_message}} | The alert_message= argument of the triggering order. | Whole Message field for strategy dispatch. |
{{strategy.market_position}} | long, short, or flat. | Conditional logic, audit trail. |
{{strategy.position_size}} | Current position size in base units. | Metadata; rarely useful at receive time. |
{{plot("Name")}} / {{plot_0}} | A plotted series value, by plot title or index. | stopLoss, targets[].price (absolute). |
{{strategy.order.alert_message}} is special — it is the entire body. The others are field-level substitutions you embed inside a larger JSON template.
Placeholders inside TradeCommand fields
Five patterns cover almost every real use case:
Universal pair
{ "pair": "{{ticker}}" }
The same alert now works on BTCUSDT, ETHUSDT, XAUUSD — anywhere you attach it.
Limit order at the current close
{
"isLimit": true,
"price": "{{close}}",
"postOnly": false,
"limitPriceType": "fixedPrice"
}
Note postOnly:false — the default is true, which would reject the order if the close happens to be touching the spread. See Gotchas #1.
Strategy-driven dispatch
{
"orderType": "{{strategy.order.action}}",
"strategyComment": "{{strategy.order.comment}}"
}
One alert handles entry (buy/sell) and close (when the comment contains close|exit|flat). The closed-source pattern in full: Strategy Alerts → closed-source automation.
Idempotency-safe key
{ "alertTimestamp": "{{ticker}}-{{timenow}}" }
{{timenow}} resolves to a UTC ISO-8601 timestamp string; combined with the ticker it deduplicates retries across multi-symbol setups. The lock is permanent — see Timing & Idempotency.
Pine-sized trade
{
"units": "{{strategy.order.contracts}}",
"unitsType": "absolute",
"price": "{{strategy.order.price}}"
}
Pine computes the size via strategy.entry(qty=...); TVH executes that exact quantity at that exact price.
Plotted values for dynamic SL/TP
Reference any series plotted on the chart with {{plot("Title")}} (by plot title) or {{plot_0}} (by index). TradingView substitutes the plotted number at fire time, so you can feed strategy-computed stop and target prices straight into absolute fields:
{
"stopLossType": "absolute",
"stopLoss": "{{plot("Strategy SL Price")}}",
"targetType": "absolute",
"targets": [{ "price": "{{plot("Strategy TP Price")}}", "amount": "100" }]
}
This works even with a closed-source strategy, as long as it plots the SL/TP series with a title you can reference. Worked example: Strategy Alerts → full dynamic command.
Computing SL/TP in Pine and injecting them
When you control the Pine code, you can compute the stop and target prices in the script and bake them straight into the JSON string. No TradingView placeholder is involved: Pine builds the body, converting each number with str.tostring(), and alert() ships it.
//@version=5
indicator("ATR bracket — TVH", overlay=true)
atr = ta.atr(14)
slPrice = close - atr * 1.5 // long stop, 1.5 ATR below
tpPrice = close + atr * 3.0 // long target, 3 ATR above
longSignal = ta.crossover(ta.ema(close, 9), ta.ema(close, 21))
if longSignal
cmd = '{"exchange":"binance-futures","pair":"' + syminfo.ticker + '"' +
',"apiKey":"bin-fut","isBuy":true,"isMarket":true' +
',"unitsType":"percent","unitsPercent":5' +
',"stopLossType":"absolute","stopLoss":' + str.tostring(slPrice, format.mintick) +
',"targetType":"absolute","targets":[{"price":' + str.tostring(tpPrice, format.mintick) + ',"amount":100}]' +
',"token":"YOUR_TOKEN"}'
alert(cmd, alert.freq_once_per_bar_close)
Notes:
str.tostring(value)writes the number unquoted, which is what JSON wants forstopLossandprice. Passformat.mintick(or a pattern like"#.##") so the price rounds to the symbol's tick size instead of emitting 8 decimals.- TVH never sees a
{{...}}token here. The value is already a literal number by the time the alert fires. - This is the most flexible option, but it needs Pine access. For a closed-source script you cannot edit, use
{{plot("...")}}above instead.
Which method for dynamic SL/TP?
| Method | Who computes the value | Needs Pine access |
|---|---|---|
{{plot("Title")}} | TradingView reads a plotted series | No (works on closed-source that plots it) |
str.tostring() in alert() | Your Pine script | Yes |
stopLossExpression (see below) | TVH evaluates a formula server-side | No, but you compose the formula |
Placeholders inside math expressions
stopLossExpression and takeProfitExpression (see Math Expressions) accept any numeric placeholder. The expression is evaluated server-side after TradingView has substituted the placeholders, so you compose arithmetic in plain text:
{
"stopLossType": "absolute",
"stopLossExpression": "{{strategy.order.price}}-0.02*{{strategy.order.price}}"
}
That stop sits 2 % below the entry. TVH's auto-flip handles the direction — write - once and the same template works for both longs and shorts (the operator is flipped for sells; the SL ends up above entry on a short).
A take-profit equivalent driven by the bar close:
{
"targetType": "absolute",
"takeProfitExpression": "{{close}}*1.05"
}
The TP sits 5 % above the close. The sign-flip for TPs is the inverse of SLs — TVH adjusts accordingly.
Caveat: the expression must resolve to a parseable number. Placeholders that return strings ({{ticker}}, {{strategy.market_position}}, {{strategy.order.comment}}) do not work inside math expressions. They render as literal text and the expression fails to evaluate, silently. The stopLoss field stays at its previous value (often 0, meaning no stop is placed). Test every expression against a real alert before going live.
Ready-to-use templates
Five patterns you can copy into the Pine alert() call or static alert Message field:
1. Universal long-market alert
{
"exchange": "binance",
"pair": "{{ticker}}",
"apiKey": "binance-main",
"isBuy": true,
"isMarket": true,
"unitsType": "percent",
"unitsPercent": 5,
"alertTimestamp": "{{ticker}}-{{timenow}}",
"token": "YOUR_TOKEN"
}
2. Limit at the close, post-only off
{
"exchange": "bybit",
"pair": "{{ticker}}",
"apiKey": "bybit-main",
"isBuy": true,
"isLimit": true,
"price": "{{close}}",
"postOnly": false,
"unitsType": "percent",
"unitsPercent": 5,
"alertTimestamp": "{{ticker}}-{{timenow}}",
"token": "YOUR_TOKEN"
}
3. ATR-based SL (Pine computes ATR, sends as concrete number)
Pine assembles the JSON with a runtime ATR value. The placeholder is never seen by TVH; only the concrete number arrives.
atr = ta.atr(14) * 2
sl = close - atr
cmd = str.format('{{"exchange":"binance-futures","pair":"{0}","isBuy":true,"stopLossType":"absolute","stopLoss":{1},"unitsType":"percent","unitsPercent":5,"token":"YOUR_TOKEN"}}', syminfo.ticker, sl)
alert(cmd, alert.freq_once_per_bar_close)
4. Strategy auto-dispatch (closed-source friendly)
{
"exchange": "binance-futures",
"pair": "{{ticker}}",
"apiKey": "bin-fut-main",
"leverage": 5,
"marginType": "cross",
"isMarket": true,
"unitsType": "percent",
"unitsPercent": 5,
"orderType": "{{strategy.order.action}}",
"strategyComment": "{{strategy.order.comment}}",
"alertTimestamp": "{{ticker}}-{{timenow}}",
"token": "YOUR_TOKEN"
}
5. Idempotent retry-safe
{
"exchange": "okx",
"pair": "{{ticker}}",
"apiKey": "okx-main",
"isBuy": true,
"isMarket": true,
"unitsType": "percent",
"unitsPercent": 5,
"alertTimestamp": "{{ticker}}-{{interval}}-{{timenow}}",
"token": "YOUR_TOKEN"
}
If TradingView retries the webhook (network blip, edge case), the second call hits the dedup lock and is rejected as a duplicate. The permanent lock is documented on Timing & Idempotency.
Placeholders that do not help TVH
A few placeholders exist in TradingView but are not useful for trade commands:
- HTML-formatting placeholders — TVH only consumes plain JSON.
{{strategy.position_avg_price}}and other strategy-state placeholders are partially supported by TradingView; check the TradingView docs before using one. Pine-computed values are usually safer.
Quote handling around placeholders
The JSON parser TVH uses is tolerant. Both of these resolve to the same value:
"price": "{{close}}" // string
"price": {{close}} // raw number
The raw-number form is technically correct JSON only when the placeholder resolves to a number with no quotes. The string form survives any resolution — including the edge case where TradingView prefixes a sign or unit.
Recommendation: wrap every placeholder in quotes. The JSON parser converts string-to-number where the field type requires it. The mental model is easier and the failure modes are fewer.
Cross-references
- Full list of which fields accept placeholders: the
tv_placeholder_compatiblecolumn in Parameter Reference. - Server-side arithmetic on placeholder values: Math Expressions.
- Auto-close behaviour triggered by
{{strategy.order.comment}}: Anatomy. - Idempotency contract for
alertTimestamp: Timing & Idempotency.