Take Profit & Multi-Target Ladders
Where stop-loss is mostly a single field, take-profit is an array. You can attach one target or twelve, percent or absolute, with optional per-target trailing. Six top-level TP fields plus the 5-property LimitOrder sub-shape.
- TPs go in the
targetsarray. Each entry is aLimitOrderwithamountplus eithertakeProfitPercentorprice. targetType: "percent"(default) → usetakeProfitPercent.targetType: "absolute"→ useprice.targetAmountInPercent: truemakes eachamounta percent share of the position. With it set, target amounts should sum to 100.takeProfitExpressionupdates onlytargets[0].priceand therefore belongs totargetType: "absolute"payloads.- Bybit-only flags:
setTpToPositionandtargetAssignedToPosition. They control whether TPs attach to the position vs sit as standalone reduce-only orders.
targets[].price (absolute mode), targets[].takeProfitPercent (percent mode), and targets[].amount accept Pine placeholders, as does takeProfitExpression. See Anatomy → Dynamic values via TradingView placeholders.
Quick reference table
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
takeProfitExpression | string | no | — | Arithmetic expression evaluated to produce the first target's absolute price (e.g. "{{strategy.order.price}}+0.02*{{strategy.order.price}}"). Operator sign auto-flips based on orderType. Updates targets[0].price only; it does NOT create the target, so you must also send targetType="absolute" and a targets array. PREFERRED: compute the target price in Pine and send it directly via targets[].price with targetType:"absolute"; this expression is a fallback for when you cannot precompute. |
targets | List<LimitOrder> | no | — | Array of take-profit targets. Each entry is a LimitOrder with amount plus either price (absolute targets) or takeProfitPercent (percent targets), and optional trailing fields. Order matters — fills are processed in array order. |
targetType | string | no | percent | Selects the target value field. percent = use targets[].takeProfitPercent; absolute = use explicit targets[].price levels (supports takeProfitExpression). |
targetAmountInPercent | bool | no | false | When true, each target's amount is a percent share of the position. When false, amount is an absolute quantity in base units. |
targetAssignedToPosition | bool | no | false | Bybit-only. When targets contains exactly one entry, attach it to the position as a TP-on-position instead of a standalone reduce-only order. |
setTpToPosition | bool | no | false | Bybit-specific. Attach take-profit orders to the position rather than as separate reduce-only limits. Required when entering via limit orders on Bybit's UTA. |
LimitOrder fields (used inside targets[])
These properties only appear inside an entry of the targets array. They are not top-level TradeCommand fields.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
idx | int | no | 0 | Position of the target inside the ladder. Used by the UI / activity log to label fills (TP1, TP2, ...). Zero-based. |
price | decimal | yes | 0 | Absolute target price. Use when the parent TradeCommand has targetType=absolute. For targetType=percent, use takeProfitPercent instead. |
amount | decimal | yes | 0 | Size assigned to this target. Percent of position when targetAmountInPercent=true, otherwise absolute base-asset quantity. |
takeProfitPercent | decimal | no | 0 | Target distance in percent from entry. Use when the parent TradeCommand has targetType=percent. For targetType=absolute, use price instead. |
trailingPercent | decimal | no | 0 | Per-target trailing offset. Once price reaches this target, the trailing logic for that slice arms with this distance. |
The LimitOrder sub-shape
Each entry in targets is a LimitOrder object with five fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
idx | int | no | 0 | Zero-based position in the ladder. Used by the UI to label fills (TP1, TP2, …). TVH does not enforce uniqueness — convention is 0, 1, 2, … |
price | decimal | conditional | 0 | Absolute target price. Use when targetType="absolute". |
amount | decimal | yes | 0 | Size of this target. Percent of position when targetAmountInPercent=true, otherwise absolute base-asset quantity. |
takeProfitPercent | decimal | conditional | 0 | Percent distance from entry. Use when targetType="percent". |
trailingPercent | decimal | no | 0 | Per-target trailing offset. Once price reaches this target, the trailing logic for that slice arms with this distance. |
In JSON:
{
"targetType": "percent",
"targets": [
{ "idx": 0, "takeProfitPercent": 1.0, "amount": 50 },
{ "idx": 1, "takeProfitPercent": 2.0, "amount": 30 },
{ "idx": 2, "takeProfitPercent": 3.0, "amount": 20 }
]
}
Order matters. Fills are processed in array order: targets[0] is "TP1", targets[1] is "TP2", and so on.
To lock the stop at break-even once price reaches your TP level, use the trailing-stop companion stopLossToBreakEven and set the trailing activation threshold to that level. See Stop Loss → Trailing break-even. There is no automatic "move to break-even after TP1 fills".
Percent vs absolute target prices
Percent mode (default)
{
"targetType": "percent",
"targets": [
{
"takeProfitPercent": 0.8,
"amount": 50
},
{
"takeProfitPercent": 1.6,
"amount": 50
}
],
"targetAmountInPercent": true
}
Targets at +0.8% and +1.6% from entry on a long. Direction flips for shorts.
Absolute mode
{
"targetType": "absolute",
"targets": [
{ "price": 66500, "amount": 50 },
{ "price": 68000, "amount": 50 }
],
"targetAmountInPercent": true
}
Fixed price levels. Your job to ensure they're on the correct side of entry — TVH does not validate.
Absolute mode with expression (first target only)
{
"targetType": "absolute",
"takeProfitExpression": "{{strategy.order.price}}+0.02*{{strategy.order.price}}",
"targets": [
{ "price": 0, "amount": 100 }
]
}
Server evaluates the expression and writes the result to targets[0].price. For multi-TP ladders, set explicit prices on targets[1..n] — only the first is updated by the expression.
Sign-flip rules: + becomes - on a sell, - becomes + on a buy. Same engine as stopLossExpression. See Math Expressions.
Multi-TP scale-out
Three targets, equal share, percent-spaced:
{
"targetType": "percent",
"targetAmountInPercent": true,
"targets": [
{
"takeProfitPercent": 0.7,
"amount": 33
},
{
"takeProfitPercent": 1.4,
"amount": 33
},
{
"takeProfitPercent": 2.1,
"amount": 34
}
]
}
When the first target fills, 33% of the position closes. Two-thirds remain. When the second fills, another 33% closes. And so on.
Amount summation rule. When targetAmountInPercent=true, the sum of amount should equal 100. TVH does not normalise — sending [50, 30] (sum 80) leaves 20% of the position un-targeted. Sending [50, 60] (sum 110) tries to close more than 100% and the last target will be partially or fully rejected.
Adjust your Pine logic to always hit 100, or build the targets so the final slice picks up the rounding remainder (the 34 in the example above).
Trailing per target
Each target can carry its own trailing offset that arms once that level fills.
{
"targetType": "percent",
"targets": [
{ "takeProfitPercent": 0.8, "amount": 30 },
{ "takeProfitPercent": 1.6, "amount": 30 },
{ "takeProfitPercent": 0, "amount": 40, "trailingPercent": 0.5 }
]
}
The first two fills are flat partial closes at +0.8% and +1.6%. The third "target" has takeProfitPercent: 0 and trailingPercent: 0.5 — it isn't an execution target, it's a "trail the rest of the position at 0.5% offset" instruction. The trailing slice arms after the second target fills.
This mirrors the Trade Command Builder UI's Scale out then trail preset. Available on every exchange that supports trailing.
Bybit-only TP attachment
Bybit UTA allows two different TP delivery models: