Skip to main content

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.

TL;DR
  • TPs go in the targets array. Each entry is a LimitOrder with amount plus either takeProfitPercent or price.
  • targetType: "percent" (default) → use takeProfitPercent. targetType: "absolute" → use price.
  • targetAmountInPercent: true makes each amount a percent share of the position. With it set, target amounts should sum to 100.
  • takeProfitExpression updates only targets[0].price and therefore belongs to targetType: "absolute" payloads.
  • Bybit-only flags: setTpToPosition and targetAssignedToPosition. They control whether TPs attach to the position vs sit as standalone reduce-only orders.
TradingView placeholders

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

PropertyTypeRequiredDefaultDescription
takeProfitExpressionstringnoArithmetic 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.
targetsList<LimitOrder>noArray 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.
targetTypestringnopercentSelects the target value field. percent = use targets[].takeProfitPercent; absolute = use explicit targets[].price levels (supports takeProfitExpression).
targetAmountInPercentboolnofalseWhen true, each target's amount is a percent share of the position. When false, amount is an absolute quantity in base units.
targetAssignedToPositionboolnofalseBybit-only. When targets contains exactly one entry, attach it to the position as a TP-on-position instead of a standalone reduce-only order.
setTpToPositionboolnofalseBybit-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.

PropertyTypeRequiredDefaultDescription
idxintno0Position of the target inside the ladder. Used by the UI / activity log to label fills (TP1, TP2, ...). Zero-based.
pricedecimalyes0Absolute target price. Use when the parent TradeCommand has targetType=absolute. For targetType=percent, use takeProfitPercent instead.
amountdecimalyes0Size assigned to this target. Percent of position when targetAmountInPercent=true, otherwise absolute base-asset quantity.
takeProfitPercentdecimalno0Target distance in percent from entry. Use when the parent TradeCommand has targetType=percent. For targetType=absolute, use price instead.
trailingPercentdecimalno0Per-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:

FieldTypeRequiredDefaultDescription
idxintno0Zero-based position in the ladder. Used by the UI to label fills (TP1, TP2, …). TVH does not enforce uniqueness — convention is 0, 1, 2, …
pricedecimalconditional0Absolute target price. Use when targetType="absolute".
amountdecimalyes0Size of this target. Percent of position when targetAmountInPercent=true, otherwise absolute base-asset quantity.
takeProfitPercentdecimalconditional0Percent distance from entry. Use when targetType="percent".
trailingPercentdecimalno0Per-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:

Model A — standalone reduce-only limits (default)

Each target becomes a separate reduce-only limit order on the order book. Works on every exchange. Default behaviour.

Model B — TP attached to the position

{
"exchange": "bybit",
"targetType": "percent",
"setTpToPosition": true,
"targets": [
{ "takeProfitPercent": 0.8, "amount": 100 }
]
}

The single target attaches directly to the position rather than living as a standalone order. Required when entering via limit orders on Bybit UTA because the position doesn't exist yet at the moment the entry limit is placed — TVH must wait, then attach.

targetAssignedToPosition is the single-target shortcut: when targets contains exactly one entry and you want it on the position, set this instead of setTpToPosition. They differ in subtle Bybit-API details — prefer setTpToPosition unless you have a reason otherwise.

Both flags only apply to Bybit. Setting them on Binance, OKX, KuCoin, Coinbase, or BitMEX is silently ignored.

Detail per property

targets

AttributeValue
TypeList<LimitOrder>
Requiredno
Default[]
TV placeholder compatibleonly inner fields (price, takeProfitPercent, amount)

Array of take-profit targets. Each entry has the LimitOrder shape documented above.

targetType

AttributeValue
Typestring
Requiredno
Allowed values"percent", "absolute"
Default"percent"

Selects the target value field: percent uses targets[].takeProfitPercent; absolute uses targets[].price.

takeProfitExpression

AttributeValue
Typestring
Requiredno
Default""
Auto-mappedyes — sign-flipped by orderType, evaluated server-side
TV placeholder compatibleyes

Arithmetic expression that resolves to an absolute price for targets[0]. Only evaluated when targetType="absolute" and at least one target exists in the array.

For multi-target ladders in absolute mode, set explicit price values on targets [1..n]. The expression updates only the first slot.

targetAmountInPercent

AttributeValue
Typebool
Requiredno
Defaultfalse

When true, each target's amount is a percent share of the position. When false, amount is an absolute base-asset quantity.

The Trade Command Builder UI defaults to true because percent-of-position is the more common pattern.

setTpToPosition

AttributeValue
Typebool
Requiredno
Defaultfalse

Bybit-only. Attach take-profit orders to the position rather than as separate reduce-only limits. Required when entering via limit orders on Bybit UTA.

targetAssignedToPosition

AttributeValue
Typebool
Requiredno
Defaultfalse

Bybit-only. Single-target shortcut: when targets has exactly one entry, attach it to the position. Prefer setTpToPosition for new payloads.

Per-exchange TP limits

ExchangeNative multi-TPMax targetsNotes
Binance Futures USDT-Myes (reduce-only limits)unlimited within order count capCounts against the open-orders-per-pair limit.
Binance Futures COIN-Myesunlimited within capSame as USDT-M.
Binance Spotyes (one OCO at a time per pair)1 per OCOTVH emulates multi-TP by chaining OCO orders.
Bybit UTAyesunlimited (standalone) / 1 (attached)setTpToPosition limits to 1 TP on the position.
OKXyes (algo orders)unlimited within capHedge mode needs positionSide.
KuCoin Futuresyesunlimited within cap
KuCoin Spotpartial (stop-limit only)1
Coinbase Perpsyes (algo)unlimited within cap
Coinbase Spotemulated1TVH polls and submits.
BitMEXyes (algo)unlimited within cap

Worked examples

Two-target scale-out with trailing break-even

{
"exchange": "binance-futures",
"pair": "BTCUSDT",
"apiKey": "binance-futures-main",
"isBuy": true,
"isMarket": true,
"leverage": 5,
"unitsType": "percent",
"unitsPercent": 3,
"stopLossType": "percent",
"stopLossPercent": 1.5,
"useTrailingStopLoss": true,
"trailingStopLossActivationPercent": 1.0,
"stopLossToBreakEven": true,
"targetType": "percent",
"targetAmountInPercent": true,
"targets": [
{
"takeProfitPercent": 1.0,
"amount": 50
},
{
"takeProfitPercent": 2.0,
"amount": 50
}
],
"token": "<token>"
}

3% of balance at 5x leverage, SL at −1.5%, two TPs at +1% and +2% (half the position each). With trailing on and the activation threshold at +1%, the stop locks at break-even once price reaches +1%, the level of TP1. Binance Futures and Bybit only.

Single TP attached to Bybit UTA position

{
"exchange": "bybit",
"pair": "BTCUSDT",
"apiKey": "bybit-uta-main",
"isBuy": true,
"isLimit": true,
"price": 63800,
"chaseLimitOrder": true,
"units": 0.05,
"stopLossType": "percent",
"stopLossPercent": 1.0,
"targetType": "percent",
"targets": [
{
"takeProfitPercent": 1.5,
"amount": 100
}
],
"setTpToPosition": true,
"token": "<token>"
}

Limit-chase entry, single TP at +1.5%, attached to the position so it survives the limit-order chase.

Common mistakes

  • Forgetting targetAmountInPercent. Without it, amount: 50 is interpreted as 50 base-asset units, not 50% of the position. On a 0.05 BTC position that's a 1000× over-sell. TVH does not auto-detect — set the flag explicitly when you mean percent.
  • takeProfitExpression on a multi-TP ladder. It only updates targets[0].price in absolute mode. Targets [1..n] keep whatever absolute price you sent. Either set explicit prices everywhere or use a single-target payload.
  • Bybit setTpToPosition: true with targets.length > 1. Only the first target attaches; the rest are dropped. Bybit's position-side TP allows one TP value.
  • Sum of amounts ≠ 100. Easy to hit when the strategy generates fractional sizes. Always reconcile at the last entry.

Next

Futures Options — leverage, margin mode, hedge mode, the postOnly default trap.