# Trailing stop

Create trailing stops that follow the best price on either side, with a distance in price or basis points, an optional activation price, and a capped child order.

A trailing stop follows the best price since it started tracking and fires when the price reverses by a set distance. Unlike a fixed stop, its trigger level moves with the market in your favor and never moves back. Other venues call the distance a callback rate. Create one with [`CreateTrigger`](https://testnet.polyester.com/docs/api-docs/connect/triggers.v1.TriggersService/CreateTrigger) ([`POST /v1/triggers`](https://testnet.polyester.com/docs/api-docs/rest/POST/v1/triggers)) and a `trailingStop` strategy.

## How it tracks the price

- **`SELL`** · Tracks the highest price, its peak\
  Trigger level: peak minus the distance. Fires when the price falls to or below the level.
- **`BUY`** · Tracks the lowest price, its trough\
  Trigger level: trough plus the distance. Fires when the price rises to or above the level.

The price is the one named by `trigger_price_source`: the last traded price by default, or the index price. Activation, the peak or trough, and the fire all follow that price.

A distance in basis points is applied to the current peak or trough, so the level keeps the same percentage as the price moves. A distance in ticks is a fixed price amount.

`trailing.peak_price_ticks` or `trailing.trough_price_ticks` shows the tracked extreme, and `trailing.trigger_price_ticks` the current level. The level is absent until the trailing stop starts tracking.

## Activation

Without `activation_price_ticks`, the trailing stop starts tracking as soon as it is installed.

With an activation price, it waits in `CREATED` until the price reaches it: at or above it for a sell, at or below it for a buy. Polyester then records `EVENT_ACTIVATED`, the status becomes `ARMED`, and tracking starts from that price. Use activation to start trailing only once a position is in profit.

The activation price must be one the watched price has not reached yet when the trailing stop is installed. A create whose activation price an earlier price already reached ends `FAILED` with `TRIGGER_WOULD_TRIGGER_IMMEDIATELY`: it never activates, places no order, and releases its reserved funds. To start trailing from the market at once, leave the activation price unset.

## Child order

| Side   | Without a slippage cap                    | With a slippage cap                                                                              |
| ------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `SELL` | A market order                            | An IOC limit order at the fire price minus the cap, rounded up to the tick size                  |
| `BUY`  | An IOC limit order at `limit_price_ticks` | An IOC limit order at the fire price plus the cap, or `limit_price_ticks` if lower, rounded down |

The fire price is the trigger level when the trailing stop fired. A buy trailing stop never pays more than `limit_price_ticks`, its maximum price. A rise that fires it above that maximum still fires it, and the child then fills only at or below the maximum, or not at all.

A sell's market child is still price-protected without a cap: it uses the Pair's default market slippage for a sell, or 200 basis points when the Pair has no positive default, measured from the fire price. On a gap past that limit it can fill partly or not at all. See [Market order price protection](https://testnet.polyester.com/docs/developer-docs/shared-concepts/market-order-price-protection).

> **A trailing stop is not a guaranteed exit**
>
> An IOC child takes only the liquidity available within its price at that moment. Any remainder is canceled, and the trailing stop does not re-arm.

## Fields and validation

| Field                                                | Rule                                                                                                  |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `side`                                               | Required: `BUY` or `SELL`                                                                             |
| `trailing_distance_ticks` or `trailing_distance_bps` | Required, exactly one. Ticks positive; basis points `1` to `10000`                                    |
| `activation_price_ticks`                             | Optional. Positive and on the Pair's tick size                                                        |
| `max_slippage_ticks` or `max_slippage_bps`           | Optional, at most one. Ticks positive; basis points `1` to `9999` on either side                      |
| `limit_price_ticks`                                  | Required for a buy, on the tick size, and at least `activation_price_ticks`. Must be unset for a sell |
| `trigger_price_source`                               | Optional: `LAST_PRICE` (default) or `INDEX_PRICE`                                                     |

A buy placed without an activation price must have `limit_price_ticks` at or above the price it watches when it is installed. Otherwise it fails with `LIMIT_PRICE_INVALID` and places nothing.

A positive distance in price is accepted whether or not it is a multiple of the Pair's tick size.

In REST, the distance in price is `trailingDistance` and the prices are `activationPrice` and `limitPrice`, all decimal strings. Basis-point and tick slippage fields keep their integer form.

## Funds reserved at creation

A sell trailing stop reserves its quantity in the base Asset. A buy trailing stop reserves `limit_price_ticks` times the quantity, plus the fee, in the quote Asset. When it fires, the reserve moves to the child order, and any part the child does not need is released.

A create whose child could never pass the Pair's order limits is rejected with that check's code, such as `MIN_NOTIONAL`: a buy is valued at its maximum price, and a sell, whose fire price has no upper bound, is checked on its quantity only (`MIN_QUANTITY`, `STEP_SIZE`).

## Examples

A sell trailing stop on 0.5 BTC that starts tracking once BTC-USDT reaches 70,000 and fires after a 2% fall from the highest price since then. Its market child is capped at 1% below the fire price.

## REST JSON

`POST /v1/triggers`

```json
{
	"trigger": {
		"symbol": "BTC-USDT",
		"qty": "0.5",
		"clientTriggerId": "ts-btc-1",
		"trailingStop": {
			"side": "SELL",
			"trailingDistanceBps": 200,
			"activationPrice": "70000",
			"maxSlippageBps": 100
		}
	}
}
```

## ConnectRPC ProtoJSON

`/triggers.v1.TriggersService/CreateTrigger`, assuming Pair ID `1` and a `base_quantity_scale` of 8.

```json
{
	"trigger": {
		"symbolId": 1,
		"qtyScaled": "50000000",
		"clientTriggerId": "ts-btc-1",
		"trailingStop": {
			"side": "SELL",
			"trailingDistanceBps": 200,
			"activationPriceTicks": "70000000000000",
			"maxSlippageBps": 100
		}
	}
}
```

### Worked example: a sell trailing stop

With the example above:

1. BTC-USDT trades at 68,000. The trailing stop is `CREATED` and waits for 70,000.
2. A trade at 70,000 activates it. The peak is 70,000 and the level is 68,600.
3. The price rises to 75,000. The peak follows, and the level moves up to 73,500.
4. The price falls to 73,500 and the trailing stop fires at that level. Its child sells 0.5 BTC with an IOC limit at 72,765, 1% below the fire price.

### A buy trailing stop

Buys 0.5 BTC after the price rises 150 USDT from its lowest point, paying at most 66,000.

```json
{
	"trigger": {
		"symbol": "BTC-USDT",
		"qty": "0.5",
		"clientTriggerId": "ts-btc-buy-1",
		"trailingStop": {
			"side": "BUY",
			"trailingDistance": "150",
			"limitPrice": "66000"
		}
	}
}
```

Without a cap, the child is an IOC limit order at 66,000. With `maxSlippageBps`, it would be placed at the fire price plus the cap, if that is below 66,000.

## Modify, pause, and resume

[`ModifyTrigger`](https://testnet.polyester.com/docs/api-docs/connect/triggers.v1.TriggersService/ModifyTrigger) changes the distance, the activation price, and the slippage cap. Set `activation_price_ticks` or the cap to `0` to clear it. The side, quantity, price source, and `limit_price_ticks` cannot change; setting `limit_price_ticks` is refused with `TRIGGER_CHILD_PRICE_NOT_MODIFIABLE`.

A change that would make the trailing stop act at once is refused with `TRIGGER_WOULD_TRIGGER_IMMEDIATELY`, and nothing changes:

- a new activation price, on a trailing stop not yet activated, that the watched price has already reached;
- a new distance, on an activated trailing stop, that puts its trigger price at or past the latest price.

Clearing the activation price (`0`) is accepted and starts trailing from the market at once. An activation price off the Pair's tick size is refused with `PRICE_TICK_SIZE` and a `tick_size` violation on `activation_price_ticks` (REST `activationPrice`), with or without the optional `symbol_id`. A change is also refused with `MIN_QTY`, `QTY_STEP_SIZE`, or `MIN_NOTIONAL` when the child order would fall below the Pair's current minimums, which can happen only if they rose after the create. A resumed trailing stop is checked against the latest price at once, so it can activate or fire straight away.

## Attached trailing stops

A trailing stop can be attached to an order as its stop leg. It starts tracking once the order fills, or once its activation price is reached, and exits on the opposite side of the order:

- On a **buy** order, the trailing stop sells. Leave `limit_price_ticks` unset; an order that sets it is rejected with `BAD_PRICE`.
- On a **sell** order, the trailing stop buys back and requires `limit_price_ticks`, its maximum price, at or above any activation price. An order without it is rejected with `TRIGGER_BUY_REQUIRES_LIMIT`.

The violation names the attached field, `order.attached_risk.trailing_stop.limit_price_ticks` (REST `order.attachedRisk.trailingStop.limitPrice`). Order reads return that maximum in the order's `attached_risk.trailing_stop.policy.limit_price_ticks` (REST `attachedRisk.trailingStop.policy.limitPrice`).

On an order with a fill, the activation and distance rules above apply to the attached trailing stop too when you change it with `ModifyTrigger`. `ModifyOrder` replaces the leg instead, so these rules judge only the new leg's activation price. A new distance on a leg without an activation price is accepted, and the new leg starts trailing from the latest price.

See [Attached take-profit and stop-loss](https://testnet.polyester.com/docs/developer-docs/shared-concepts/triggers/attached-tp-sl).

## Errors

- **`TRIGGER_BUY_REQUIRES_LIMIT`**\
  A buy trailing stop has no `limit_price_ticks`.
- **`BAD_PRICE`**\
  A sell set `limit_price_ticks`, or a buy's maximum is below its activation price.
- **`VALIDATION_ERROR`**\
  The distance is missing, not positive, or outside `1` to `10000` bps.
- **`MAX_SLIPPAGE_INVALID`**\
  The slippage cap is `10000` bps.
- **`PRICE_TICK_SIZE`**\
  The activation or maximum price is not on the Pair's tick size.
- **`TRIGGER_WOULD_TRIGGER_IMMEDIATELY`**\
  A modify would move the activation price or the trigger price onto a price already reached. A create with such an activation price ends `FAILED` with this reason instead.
- **`TRIGGER_PRICE_SOURCE_UNSUPPORTED`**\
  The source is `MARK_PRICE` or not a defined source.
- **`INSUFFICIENT_FUNDS`**\
  The available balance cannot cover the reserve.
- **`MIN_QUANTITY`, `STEP_SIZE`, `MIN_NOTIONAL`**\
  The child order could never pass the Pair's order limits.
- **`MIN_QTY`, `QTY_STEP_SIZE`, `MIN_NOTIONAL`** on a modify\
  The change would leave the child order below the Pair's minimums. The trailing stop is unchanged.

See [Error codes](https://testnet.polyester.com/docs/developer-docs/error-handling/error-codes) for HTTP and ConnectRPC statuses.
