Pine Script stop loss and take profit

How to set multiple take profits on TradingView with Pine Script

Use separate strategy.exit() calls for each take-profit target, with unique exit IDs, the same from_entry ID, and explicit quantities. Give each slice its own stop. Earlier calls reserve quantity, so a separate stop submitted last may cover only the unreserved remainder. These are Pine strategy simulations; broker order-ticket features depend on the connected broker.

Build partial-exit rules

Set the quantities before placing the targets

Start with a written allocation. In the example below, all quantities refer to one 100-unit long entry. Each bracket has a profit target and a stop for the same slice. qty overrides qty_percent when both are supplied; use one sizing method consistently.

Exit IDQuantityTargetStop coverage
TP140 unitsEntry + 1R40 units at entry - 1R
TP235 unitsEntry + 2R35 units at entry - 1R
TP325 unitsEntry + 3R25 units at entry - 1R
Total100 unitsThree targets100 units before any exit

Why a separate full-position stop can miss most of the trade

An exit call cannot use quantity already reserved by an earlier exit call. Changing OCA names does not remove that reservation rule. For this 100-unit allocation, placing three target-only exits first leaves no quantity for a fourth stop-only exit.

Submission orderRequested quantityAvailable quantity
Target-only TP14040 reserved; 60 left
Target-only TP23535 reserved; 25 left
Target-only TP32525 reserved; 0 left
Separate stop submitted last1000 left to reserve
Correction used below40 / 35 / 25Attach a stop to each target bracket

Worked example: reconcile every partial fill

Illustrative arithmetic, not a backtest: assume 100 shares bought at $100, a $0.01 tick, a 200-tick risk distance ($2), no costs, no gaps, and sequential fills on separate bars. Keep break-even disabled. The targets are $102, $104, and $106; every initial stop is $98.

  • The last two rows are alternative paths after TP2, not consecutive events.
  • A stop before any target would close 40 + 35 + 25 units for a gross loss of $200 under these assumptions.
  • Do not infer the fill sequence from a bar that touches both a target and a stop. Review the emulator assumptions and lower-timeframe detail.
EventExit sizePosition leftRemaining stop ordersRealized gross P/L
Entry fills at $100010040 + 35 + 25 at $98$0
TP1 fills at $102406035 + 25 at $9840 × $2 = $80
TP2 fills at $104352525 at $98$80 + 35 × $4 = $220
Then price reaches $98250None$220 - 25 × $2 = $170
Alternative: TP3 fills at $106250None$220 + 25 × $6 = $370

Pine Script v6: three targets with stops for every slice

This long-only teaching strategy submits brackets with the entry order. Risk is measured in ticks from the eventual entry fill. Each bracket has a separate OCA name so one slice does not reduce another slice. The script places each bracket once per entry and leaves it pending; it does not recreate a filled target on later bars. The 100-unit size is for checking the allocation, not a sizing recommendation.

Original example: 40 / 35 / 25 units, fixed tick risk, no break-evenpinescript
//@version=6
strategy("Three targets with reserved stops", overlay = true, initial_capital = 1000000, pyramiding = 0)

channelLength = input.int(20, "Breakout length", minval = 2)
riskTicks = input.int(200, "Risk distance in ticks", minval = 1)
channelHigh = ta.highest(high, channelLength)[1]
enterLong = ta.crossover(close, channelHigh)

if enterLong and strategy.position_size == 0
    strategy.entry("Long", strategy.long, qty = 100)
    strategy.exit("TP1", "Long", qty = 40, profit = riskTicks, loss = riskTicks, oca_name = "Slice1")
    strategy.exit("TP2", "Long", qty = 35, profit = riskTicks * 2, loss = riskTicks, oca_name = "Slice2")
    strategy.exit("TP3", "Long", qty = 25, profit = riskTicks * 3, loss = riskTicks, oca_name = "Slice3")

plot(channelHigh, "Breakout level", color = color.blue)
plot(strategy.position_size, "Remaining units", display = display.data_window)

Choose an order interface and OCA behavior

strategy.exit() uses OCA reduce behavior by default; explicit oca_name values separate groups. Within a bracket, a filled target cancels its paired stop. A custom strategy.order() reduce group needs cumulative quantities: a fill subtracts its quantity from the other orders. For example, target quantities 40, 75, 100 and stop quantity 100 become 35, 60, 60 after a 40-unit TP1 fill. That is a different design from the independent 40 / 35 / 25 brackets above.

  • OCA cancel removes sibling orders after a fill; OCA reduce subtracts the fill quantity; OCA none leaves siblings independent.
  • The three same-price stops in the example remain separate orders and can produce separate fill alerts. Any external execution system must handle their quantities.
InterfaceUseQuantity responsibility
strategy.exit()Price-based target/stop bracketsExplicit slices; reservation applies
strategy.order()Custom net-position ordersManage OCA, quantities and reversal risk yourself
strategy.close()Market exit for an entry IDNo resting take-profit price
strategy.close_all()Market exit for the whole positionNo target ladder

Keep the stop-loss basics: price, ticks, percent and ATR

stop and limit specify prices; loss and profit specify ticks. Convert a price distance to ticks with distance / syminfo.mintick. Keep the initial risk definition explicit when you compare fixed-percent and ATR settings.

RuleLong-side calculationWhat to hold fixed
Fixed percentEntry price × (1 - percent / 100)Percent and entry reference
Fixed ticksEntry price - ticks × minimum tickTick distance
ATR stopEntry price - captured ATR × multiplierCaptured ATR and multiplier
Break-evenMove stop to entry after a chosen triggerActivated state until the position closes

Pine Script v6: ATR stop with a persistent break-even state

This separate single-target example retains the original Donchian, ATR stop and break-even workflow. It freezes ATR on the signal bar, rounds risk up to whole ticks, and submits the initial bracket with the entry. A close at or above 1R activates break-even for subsequent execution; the stop cannot revert on a later bar. The 1R trigger is a demonstration choice. An entry-price stop does not cover fees or guarantee an entry-price fill.

Original corrected example: signal-bar ATR, 2R target, optional close-confirmed break-evenpinescript
//@version=6
strategy("ATR stop and persistent break-even", overlay = true, initial_capital = 10000, pyramiding = 0, default_qty_type = strategy.percent_of_equity, default_qty_value = 10)

channelLength = input.int(20, "Donchian length", minval = 2)
atrLength = input.int(14, "ATR length", minval = 1)
atrMultiplier = input.float(1.5, "ATR multiplier", minval = 0.1)
rewardMultiple = input.int(2, "Target in R", minval = 2)
enableBreakEven = input.bool(true, "Enable break-even at a 1R close")
channelHigh = ta.highest(high, channelLength)[1]
atrValue = ta.atr(atrLength)
enterLong = ta.crossover(close, channelHigh)
var int tradeRiskTicks = na
var bool breakEvenActive = false

if strategy.position_size == 0
    breakEvenActive := false
    if enterLong and not na(atrValue)
        tradeRiskTicks := math.max(1, int(math.ceil(atrValue * atrMultiplier / syminfo.mintick)))
        strategy.entry("Long", strategy.long)
        strategy.exit("Exit", "Long", loss = tradeRiskTicks, profit = tradeRiskTicks * rewardMultiple)

float activeStop = na
float targetPrice = na
if strategy.position_size > 0 and not na(tradeRiskTicks)
    entryPrice = strategy.position_avg_price
    riskDistance = tradeRiskTicks * syminfo.mintick
    if enableBreakEven and close >= entryPrice + riskDistance
        breakEvenActive := true
    activeStop := breakEvenActive ? entryPrice : entryPrice - riskDistance
    targetPrice := entryPrice + riskDistance * rewardMultiple
    strategy.exit("Exit", "Long", stop = activeStop, limit = targetPrice)

plot(channelHigh, "Channel high", color = color.blue)
plot(activeStop, "Active stop", color = color.red, style = plot.style_linebr)
plot(targetPrice, "Target", color = color.green, style = plot.style_linebr)

Check the trade ledger before changing the exit rules

For the three-target example, check the exit IDs and sizes against the allocation table. Inspect Remaining units in the Data Window after each fill. Test a target-first path and a stop-first path, then add commission and slippage assumptions. Syntax validation alone cannot establish that these scenarios behave as intended on your chosen chart.

  • If you add break-even to the partial-exit example, update only brackets whose slices remain open. Track actual exit fills; touching a chart price is not sufficient evidence of a fill.
  • Export the TradingView trade list as CSV for Pineify Backtest Deep Report. Use the chart and strategy ledger to reconcile remaining quantity; the CSV does not describe the unfilled order book.
  • For shorts, reverse the direction of stop and target prices and review the quantity rules again. Neither example implements short entries or pyramiding.
Where Pineify fits

Pine Script AI Coding Agent

Describe your entry ID, target allocation, stop policy and break-even trigger. Generate Pine Script rules that you can inspect and test in TradingView.

Also useful: Backtest Deep Report. Analyze your exported TradingView trade CSV after you reconcile partial fills and remaining position sizes in the strategy ledger.

Build partial-exit rules

Frequently asked questions

Educational code and hypothetical arithmetic, not investment advice or a tested trading recommendation. Pine strategies simulate execution. Costs, gaps, order timing and broker behavior can change outcomes.

Sources and verification