Take Profit & Stop Loss
A take-profit (TP) and stop-loss (SL) are price levels you attach to an open position. They are not separate orders — just two numbers stored on the position itself. Once the verified oracle mark crosses a level, a keeper closes the whole position at market, and the trigger is re-checked on-chain so it can never fire early.
What take-profit and stop-loss are
A take-profit and a stop-loss are two optional trigger prices attached to a single open position. Each leg is per-position and independent: you can set a TP, an SL, both, or neither. A take-profit is the price at which you want the position closed in profit; a stop-loss is the price at which you want a losing trade cut before it runs further. Neither does anything while the position is open — they are standing instructions that only act once the mark reaches them.
The important thing to understand is that TP and SL are price levels, not orders. On Levant they are stored as two fields directly on your position (takeProfit and stopLoss). Setting them does not create a second position, does not escrow any extra collateral, and does not place a resting order anywhere. This is different from a limit or stop order used to *open* a trade — those escrow collateral on-chain as a pending intent. A TP/SL on a live position is just two numbers the keeper watches.
Setting them with setTpSl
You attach or change TP and SL by calling setTpSl(positionId, takeProfit, stopLoss) on an open position. Only the position owner can call it. Because the levels live on the position itself, they can only be set once the position exists — the open intent has no TP/SL field. In practice that means the moment your open fills: you can fire setTpSl right away, or add and adjust the levels any time the trade is live. A level of 0 disables that leg.
setTpSl(positionId, takeProfit, stopLoss)
// owner-only; levels are oracle prices (1e18 scale on-chain)
takeProfit = 0 -> take-profit OFF
stopLoss = 0 -> stop-loss OFF
// every call writes BOTH legs at onceWhich side each level goes
A trigger only makes sense on the correct side of your entry, because a TP closes in profit and an SL closes in loss. For a long, price rising is profit — the TP sits above entry, the SL below. For a short, price falling is profit, so the sides invert: the TP sits below entry, the SL above.
| Position | Take-profit (closes in profit) | Stop-loss (closes in loss) |
|---|---|---|
| Long | Above entry price | Below entry price |
| Short | Below entry price | Above entry price |
On-chain, the keeper fills the position the instant the verified oracle mark satisfies either condition. The comparison is inclusive, and a leg set to 0 never fires:
Long TP: mark >= takeProfit SL: mark <= stopLoss
Short TP: mark <= takeProfit SL: mark >= stopLoss
mark = verified oracle mid, re-checked on-chain (inclusive)
a leg set to 0 never fires; the fill then adds the close spreadHow a trigger fires
The keeper is an off-chain service that watches the verified oracle mark for every position carrying a TP or SL. When the mark crosses one of your levels, it submits a signed price report to executeCloseTrigger, which closes the position. Only the keeper can call executeCloseTrigger — unlike liquidation, which anyone can trigger.
- 1You set a TP and/or SL with setTpSl. The levels are stored on your position; no funds move.
- 2The keeper watches the verified oracle mark for every position that has a trigger set.
- 3The mark crosses your level. The keeper submits a signed oracle price report to executeCloseTrigger.
- 4The contract re-checks the trigger against the verified mark. If the mark has genuinely crossed, the whole position closes and settles; if not, the call reverts with TriggerNotHit.
The on-chain re-check is what makes this trust-minimized. The keeper decides *when* to submit, but it cannot close your position early through this path: executeCloseTrigger independently recomputes the condition against the verified mark and reverts TriggerNotHit if it is not met. The price is a signed oracle report the keeper cannot forge, and a position with no TP or SL set at all cannot be trigger-closed. The keeper controls timing only — never the price, and never a close before your level is reached.
The close executes at market
A triggered close settles exactly like a market close of the full position. The trigger is tested on the raw oracle mid, but the fill then applies the deterministic close spread on top — closing a long is sell-side (fills below mid), closing a short is buy-side (fills above mid) — so your realized exit is always a touch past the level itself. The 0.08% close fee (on adjusted size = notional +/- realized PnL) and any accrued funding are settled, and profit is still subject to the 900%-of-margin max-profit cap. There is no slippage cap on a close, unlike on an open.
Full close vs scaling out
A TP/SL trigger always closes the entire position — executeCloseTrigger runs the full-close path, and there is no partial take-profit or partial stop on-chain. To scale out at a target instead, use a partial close: call closePartial for a slice (1 to 9,999 bps, i.e. 0.01%–99.99%), which the keeper settles through the same close path. A partial close realizes proportional PnL and fees and releases proportional reserved liquidity, while the remainder keeps its entry price, funding snapshot, leverage, and — importantly — its TP and SL settings.
| Mechanism | Closes | Triggered by | Price basis |
|---|---|---|---|
| Take-profit / stop-loss | Whole position | Keeper, once the mark crosses your level (re-checked on-chain) | Market: mid + close spread, close fee, funding |
| Manual close | Whole position | You (closePosition), then keeper fills | Market: mid + close spread, close fee, funding |
| Partial close | A slice (0.01%–99.99%) | You (closePartial), then keeper fills | Market on the slice; remainder keeps entry & TP/SL |
| Liquidation | Whole position | Anyone (permissionless), at the 10% maintenance margin | Raw mark, no spread; you forfeit all collateral |
TP/SL vs liquidation
A stop-loss and a liquidation are not the same thing. Even with no SL set, a position is liquidated automatically once its equity falls to the 10% maintenance margin — a loss of 90% of collateral — at which point anyone can force-close it at the raw mark, you forfeit all collateral, and the liquidator earns a 5% fee. A stop-loss is a softer, trader-chosen exit that sits *above* that liquidation price: a well-placed SL closes you on your own terms, returning your remaining margin, before the position ever reaches the heavier liquidation outcome.
Editing and clearing
- Set — attach a TP and/or SL to any open position you own with setTpSl.
- Update — move a level at any time; remember every call writes both legs, so re-pass the leg you want to keep.
- Clear — pass 0 for a leg to disable it and let the position run with no automatic exit on that side.
- Preserved on partial close — closing part of a position leaves the remainder's TP and SL exactly as they were.
- No protocol fee to set — attaching or changing TP/SL only costs the gas of the setTpSl transaction; nothing is charged until a close actually settles.
