Math Expressions
Two fields — stopLossExpression and takeProfitExpression — let you compute SL/TP prices server-side from arithmetic over the entry price. They're the bridge between TradingView's Pine values and TVH's absolute-price SL/TP handling.
- Expressions are evaluated server-side after Pine substitutes placeholders. Allowed operators:
+ - * /and parentheses. - Triggers only when the corresponding type field is
"absolute":stopLossType="absolute"for SL,targetType="absolute"for TP. - TVH auto-flips operator signs based on
orderTypeso the same template works for longs and shorts. - Errors are silently swallowed. If the expression throws,
stopLossortargets[0].pricestays at its previous value (often 0 = no order placed). Test before going live. takeProfitExpressionupdates onlytargets[0].price. Multi-target ladders need explicit prices ontargets[1..n].
Quick reference table
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
stopLossExpression | string | no | "" | Arithmetic expression. Resolves to absolute SL price when stopLossType="absolute". Sign-flipped by orderType. |
takeProfitExpression | string | no | "" | Arithmetic expression. Resolves to absolute price for targets[0] when targetType="absolute". Sign-flipped by orderType. |
Both fields are categorised on the Stop Loss and Take Profit detail pages as well — the expressions live in two homes in the schema because they touch both subsystems.
When expressions evaluate
The server runs its auto-mapping step after parsing your JSON. The math step fires only when all three of these are true:
stopLossType="absolute"(for SL) ortargetType="absolute"(for TP).- The expression field (
stopLossExpression/takeProfitExpression) is non-empty. - The expression contains at least one of
* + - /.
If any of those is missing, the expression is ignored and the regular fields (stopLoss, stopLossPercent, targets[].price) are used as-is.
Supported syntax
| Operator | Example | Result |
|---|---|---|
| Addition | 100 + 5 | 105 |
| Subtraction | 100 - 5 | 95 |
| Multiplication | 100 * 0.02 | 2 |
| Division | 100 / 4 | 25 |
| Parentheses | (100 + 5) * 0.5 | 52.5 |
| Decimal literals | 0.02 * 65000 | 1300 |
Not supported:
- Exponentiation (
^or**). Use repeated multiplication. - Functions (
sqrt,abs,min,max). - Comparisons or boolean logic.
- Identifiers other than substituted Pine placeholders.
Anything else fails to evaluate, and the result is silently discarded.
Pine placeholders inside expressions
The expression is a string, not Pine code. Pine substitutes the placeholders before sending the webhook; by the time TVH evaluates the expression, the curly-brace tokens are gone.
1. Pine source string (what you write in the template):
"{{strategy.order.price}}-0.02*{{strategy.order.price}}"
2. After TradingView substitutes (entry price was 65000):
"65000-0.02*65000"
3. After TVH evaluates the expression:
63700
Common placeholders used in expressions:
| Placeholder | Resolves to |
|---|---|
{{strategy.order.price}} | The exact entry price of the order Pine just submitted. |
{{close}} | Latest bar close. Useful for indicator alerts (no strategy context). |
{{high}} / {{low}} | Bar high / low. Stop placement based on recent extremes. |
{{open}} | Bar open. |
You can mix them. {{strategy.order.price}} is the most common because it lines up exactly with the entry's actual fill.
Smart sign-flipping
The same expression should work for longs and shorts. TVH ensures that by flipping operator signs based on orderType, after placeholder substitution.
Stop loss
orderType | Operator | Auto-flipped to | Rationale |
|---|---|---|---|
"sell" | - | + | SL on a short sits above entry. entry - 0.02*entry would put it below — wrong side. Flip to + to push it above. |
"buy" | + | - | SL on a long sits below entry. Same logic mirrored. |
Take profit
The flip is inverted for TP:
orderType | Operator | Auto-flipped to | Rationale |
|---|---|---|---|
"sell" | + | - | TP on a short sits below entry. |
"buy" | - | + | TP on a long sits above entry. |
One template, both directions
Write the expression as if for a long, and let TVH flip on sells:
stopLossExpression = "{{strategy.order.price}}-0.02*{{strategy.order.price}}"
takeProfitExpression = "{{strategy.order.price}}+0.04*{{strategy.order.price}}"
On a buy: SL = entry × 0.98, TP = entry × 1.04. Risk-reward 1:2.
On a sell: signs auto-flip. SL = entry × 1.02, TP = entry × 0.96. Same 1:2 R:R, mirrored.
Caveat: the flip is a literal string replace of -/+. If your expression has both operators in different roles (e.g. entry - 0.02*entry + 100), the flip replaces both occurrences, which may not be what you want. Keep expressions simple — one risk multiplier per expression.
Common patterns
Fixed % from entry
stopLossExpression: "{{strategy.order.price}}-0.02*{{strategy.order.price}}"
2% from entry. Equivalent to stopLossType="percent", stopLossPercent: 2.0 — but stored as an absolute on submission, which is what some adapters need for native conditional orders.
Risk-reward TP from a known SL
stopLossExpression: "{{strategy.order.price}}-0.01*{{strategy.order.price}}"
takeProfitExpression: "{{strategy.order.price}}+0.02*{{strategy.order.price}}"
SL at 1% below, TP at 2% above. 1:2 R:R.
ATR-based SL (Pine pre-computes ATR)
You can't reference atr(14) inside the expression — TVH doesn't know what that is. Instead, compute the ATR in Pine and pass the concrete number:
//@version=5
strategy("ATR SL example", overlay=true)
atr = ta.atr(14)
slDistance = atr * 1.5
slPrice = strategy.position_avg_price - slDistance
alert_message = '{"exchange":"binance-futures","pair":"' + syminfo.ticker + '","isBuy":true,"stopLossType":"absolute","stopLoss":' + str.tostring(slPrice) + ',"alertTimestamp":"' + syminfo.ticker + '-' + str.tostring(time) + '","token":"<token>"}'
if longCondition
strategy.entry("L", strategy.long, alert_message=alert_message)
Here Pine produces an already-computed stopLoss number. The expression machinery isn't used — you've moved the math to Pine. That's the right pattern when the math depends on indicator values.
Asymmetric SL/TP
stopLossExpression: "{{strategy.order.price}}-150"
takeProfitExpression: "{{strategy.order.price}}+450"
Fixed 150 below entry, 450 above. Works for any price level — the dollar offsets are absolute.
Error handling
The rule is simple: if the expression can't be evaluated, the error is swallowed silently. Invalid syntax, divide-by-zero, an unsupported operator — any of these means stopLoss keeps whatever value it had (usually 0), and the order is submitted with no stop loss.
There is no log of the parse failure on the user's side. The Activity Log shows the trade with stopLoss: 0, which looks like "stop loss not configured" — not "stop loss expression failed to parse".
Test before going live
Three good practices:
- Test on a demo account first. Send the exact payload from your Pine alert template to a demo account and verify the SL/TP arrived at the expected price. See Demo Accounts for the per-exchange setup.
- Echo the resolved expression in
strategyComment. TradingView substitutes placeholders in any string field. Putting"sl_expr":"{{strategy.order.price}}-0.02*{{strategy.order.price}}"in the comment gives you a logged trace of the post-substitution string. - Start with simple expressions. Two-operand expressions like
entry - 0.02*entryare nearly impossible to mistype. Save complex ladder math for when you need it.
takeProfitExpression updates only targets[0]
For multi-target ladders, the expression updates only the first slot. Targets [1..n] keep whatever price value you sent.
If you want all targets computed from the entry price, set them explicitly in Pine:
tp1 = entryPrice + 0.005 * entryPrice
tp2 = entryPrice + 0.010 * entryPrice
tp3 = entryPrice + 0.020 * entryPrice
Then build the targets array in the JSON template directly. The expression field is a single-slot convenience, not a multi-target engine.
Detail per property
stopLossExpression
| Attribute | Value |
|---|---|
| Type | string |
| Required | no |
| Default | "" |
| TV placeholder compatible | yes |
| Auto-mapped | yes — sign-flipped, evaluated server-side, writes to stopLoss |
Arithmetic expression. Only evaluated when stopLossType="absolute" and the string contains + - * /.
takeProfitExpression
| Attribute | Value |
|---|---|
| Type | string |
| Required | no |
| Default | "" |
| TV placeholder compatible | yes |
| Auto-mapped | yes — sign-flipped, evaluated server-side, writes to targets[0].price |
Arithmetic expression. Only evaluated when targetType="absolute" and at least one entry exists in targets.
Common mistakes
stopLossExpressionwithstopLossType="percent". Ignored. Switch to"absolute".- Using
^for exponentiation. Fails to evaluate — silently swallowed. Usex*xfor squares. - Expression with both
+and-in different roles. The sign-flip replaces all occurrences.entry - 100 + 50on a sell becomesentry + 100 - 50. Keep expressions to a single risk operator. takeProfitExpressionon a payload with 3 targets. Onlytargets[0]is updated. The other two keep their originalpricevalues.- Forgetting that the expression must produce a price, not a percent.
0.02 * 65000= 1300 (a number of dollars), not a price. Combine with the entry:65000 - 0.02 * 65000.
Next
Examples — 12 complete Pine + JSON pairs, including math-expression patterns from this page.