> For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/special-order-types/order-types/trailing.md).

# Trailing Stops

Make a trigger follow the market with trailingStopBps — on a stop-loss, a take-profit, or an OCO's stop leg.

**One field — `trailingStopBps` — turns any trigger order into a trailing one.** What it *does* depends on the order type, and the three behaviours are genuinely different. Getting them mixed up is the easiest way to ship a trailing UI that lies to the user.

| Order type    | What `trailingStopBps` does                                       | Render the live trigger from            |
| ------------- | ----------------------------------------------------------------- | --------------------------------------- |
| `stop_loss`   | The trigger **ratchets up** as the market makes new highs         | `currentTriggerPrice` (server-computed) |
| `take_profit` | `triggerPrice` becomes an **arm level** instead of a sell level   | compute it client-side                  |
| `oco`         | The **stop-loss leg** ratchets; the take-profit ceiling stays put | `currentTriggerPrice` (server-computed) |

`trailingStopBps` is an integer between `1` and `9999` — basis points of pullback from the tracked peak, so `500` is 5%.

Prices below are written as dollar figures for readability. On the wire, every value is a fixed-point string on the order's [price basis](/titan/developer-doc/special-order-types/order-types/stop-loss-take-profit.md#price-basis) — a pair ratio by default, or USD per input token with `priceBasis: "usd"`.

## Trailing stop-loss

Set `trailingStopBps` on a `stop_loss` order and the trigger follows the market up. Whenever the pair makes a new high, the trigger is re-anchored to `peak × (1 − trailingStopBps/10000)`. It **only ever rises** — a trail wider than the current market-to-stop gap leaves the trigger sitting at your `triggerPrice`, never below it. The order fires when the price falls back to whatever the trigger is at that moment.

With `triggerPrice` at $90 and `trailingStopBps: 500`:

| Market does   | Trigger becomes |
| ------------- | --------------- |
| Sits at $100  | $95             |
| Runs to $120  | $114            |
| Falls to $114 | **Fills**       |

Without a run-up it behaves exactly like a static $90 stop, because the trigger never ratchets below where you set it.

On reads, `currentTriggerPrice` is the live trigger and `highestObservedPrice` is the peak seen so far (absent until the first new high). `currentTriggerPrice` is **always present** on a stop-loss — it equals `triggerPrice` until the first ratchet — so you can bind your UI to it unconditionally for every stop-loss, trailing or not.

{% hint style="danger" %}
Render `currentTriggerPrice`, not `triggerPrice`, once an order is trailing. `triggerPrice` is the initial stop and stays frozen at its original value forever — showing it to a user whose trail has ratcheted to $114 tells them their stop is still at $90.
{% endhint %}

### There's no separate "pure trailing" type

`triggerPrice` is always both the initial stop and the floor, so a pure trail is something you construct rather than a type you request. Seed `triggerPrice` from the current market — `market × (1 − trailingStopBps/10000)`, where `market` is the pair ratio on a `pair` order or the input token's USD price on a `usd` order — and set `trailingMode: "pure"` so your UI can tell the two intentions apart on a later read.

`trailingMode` accepts `"pure"` (you derived `triggerPrice` from the market as the trail seed) or `"stop_and_trail"` (the user chose a hard stop and you added a trail on top). It requires `trailingStopBps`, is `stop_loss` only, and is stored and echoed back on reads. It **never affects execution** — it exists purely so your UI can reconstruct what the user meant.

## Trailing take-profit

This one inverts the meaning of `triggerPrice`, which is why it deserves its own mental model. Set `trailingStopBps` on a `take_profit` order and reaching `triggerPrice` no longer sells — it **arms the trail**. From that point the order tracks the peak and fires when the price pulls back `trailingStopBps` from it. The derived trigger is floored at `triggerPrice`, so the fill can only improve on what the static order would have taken, never undercut it.

With `triggerPrice` at $110 and `trailingStopBps: 500`:

| Market does      | Order does                               |
| ---------------- | ---------------------------------------- |
| Rises to $110    | Arms the trail — **no sale**             |
| Runs to $130     | Derived trigger becomes $123.50          |
| Falls to $123.50 | **Fills**, \~12% above the static target |

If instead the market stalls right at $110 and slips back, the floor fires at \~$110 — roughly the static fill, one tick later.

The trade-off is worth stating plainly to users: you give up selling at the exact first touch of `triggerPrice` in exchange for uncapped upside when the move keeps going. A touch-and-crash fills at \~`triggerPrice` on the way down, with normal slippage.

On reads, `trailingActivated` tells you whether the trail is armed and `highestObservedPrice` is the tracked peak (absent until armed). There's no `currentTriggerPrice` on a take-profit — compute the live derived trigger yourself:

```typescript
const derivedTrigger = (order: {
  highestObservedPrice?: string;
  triggerPrice: string;
  trailingStopBps: number;
}): bigint => {
  const floor = BigInt(order.triggerPrice);
  if (!order.highestObservedPrice) return floor;
  const trailed =
    (BigInt(order.highestObservedPrice) * BigInt(10_000 - order.trailingStopBps)) / 10_000n;
  return trailed > floor ? trailed : floor;
};
```

Without `trailingStopBps`, `triggerPrice` sells on first touch as documented in [Stop-Loss & Take-Profit](/titan/developer-doc/special-order-types/order-types/stop-loss-take-profit.md).

## Trailing an OCO's stop-loss leg

Set `trailingStopBps` on an `oco` order and the stop-loss leg ratchets identically to a standalone trailing stop, anchored on `stopLossPrice` instead of `triggerPrice`. Reads expose the same `currentTriggerPrice` and `highestObservedPrice`, where `currentTriggerPrice` equals `stopLossPrice` until the first ratchet.

The take-profit leg is unaffected and stays static. Because the take-profit is evaluated first, the trailing trigger can never ratchet past `takeProfitPrice` — as the market approaches the ceiling, the two simply form a tightening bracket.

## Pause and resume

A paused trailing order isn't price-watched, so it can't ratchet while paused. State survives: on resume, a trailing stop keeps its ratcheted trigger and a trailing take-profit keeps its armed state and its peak.

## Modifying a trailing order

`trailingStopBps` can be changed or cleared with a parameter-only `PATCH`. Trailing state is recomputed from the observed peak rather than reset: widening a trail moves the live trigger down, narrowing it moves it up, and lowering `triggerPrice` alone does not loosen a stop that has already ratcheted above it. Raising a trailing take-profit's target above the observed peak disarms it. The full rules are in [Modify a trigger order](/titan/developer-doc/special-order-types/guides/managing-orders.md#trailing-state).

## Related pages

* [Stop-Loss & Take-Profit](/titan/developer-doc/special-order-types/order-types/stop-loss-take-profit.md) — the static behaviour and the full config table
* [OCO Brackets](/titan/developer-doc/special-order-types/order-types/oco.md) — the two-leg order the trail attaches to
* [Order & Execution Schema](/titan/developer-doc/special-order-types/reference/schema.md) — every trailing field on a read response
