# Cancel all orders

Cancel every open order that matches a filter, with an answer at acceptance and idempotent retries.

[`OrdersService.CancelAllOrders`](https://testnet.polyester.com/docs/api-docs/connect/orders.v1.OrdersService/CancelAllOrders) ([`POST /v1/orders/cancel-all`](https://testnet.polyester.com/docs/api-docs/rest/POST/v1/orders/cancel-all)) cancels every open order of an account that matches a filter. Use it as a kill switch, or to clear a Pair or Side before requoting.

## Which orders are canceled

The filter selects:

- the account: the Subaccount in `subaccount_id`; when it is omitted, the caller's Main Account, or the bound Subaccount for a Subaccount-scoped API key
- optional Pairs, up to 100; an empty list matches every Pair
- optional `side`; when it is omitted, both sides match

An unknown Pair rejects the request. Cancel-all accepts every Pair status, so you can clear your orders on a disabled or halted Pair.

Polyester fixes the set of matching orders when it accepts the request. Every order that is pending or open at that moment is canceled, including orders whose cancellation is already in progress. An order modified after that moment is still canceled.

Orders that Polyester accepts after that moment are not affected, even if they were sent shortly before the cancel-all.

### What happens to triggers

> **Triggers are not canceled**
>
> Cancel-all does not cancel standalone triggers, such as stops, TWAPs, and ladders; cancel them separately. Open orders that a trigger already placed, such as TWAP slices and ladder levels, are canceled like any other matching order. Canceling an attached take-profit, stop-loss, or trailing stop exit order ends that leg, and with `oco` set the order's other legs too.

The triggers themselves keep their reserved funds and behave as follows after a cancel-all:

- A stop-loss, take-profit, or trailing stop that has not fired stays armed and places its order when its condition is met.
- A TWAP keeps running and places its next slice when it is due.
- A ladder ends once none of its levels remains open: `STATUS_CANCELED` with `TRIGGER_CANCEL_REASON_USER_REQUEST` when none of them filled, `STATUS_COMPLETED` when any did.
- An attached leg whose working exit is canceled ends `STATUS_CANCELED` with `TRIGGER_CANCEL_REASON_USER_REQUEST`. Attached legs without a working exit keep protecting the parent's filled position, even when the parent order is canceled; they end only if the parent is canceled before any fill.
- A paused trigger stays paused.

### Stop all automated trading

To stop every order and trigger, cancel each live trigger first, then cancel all orders. A trigger that fires before its cancel places an order that the cancel-all then cancels.

1. List the live triggers, attached legs included, with [`TriggersService.ListTriggers`](https://testnet.polyester.com/docs/api-docs/connect/triggers.v1.TriggersService/ListTriggers) and the statuses `STATUS_CREATED`, `STATUS_ARMED`, `STATUS_RUNNING`, and `STATUS_PAUSED`. Over REST, use `GET /v1/triggers?status=CREATED,ARMED,RUNNING,PAUSED`. Follow the next page token until it is empty.
2. Cancel each one with [`TriggersService.CancelTrigger`](https://testnet.polyester.com/docs/api-docs/connect/triggers.v1.TriggersService/CancelTrigger), or [`DELETE /v1/triggers/{trigger_id}`](https://testnet.polyester.com/docs/api-docs/rest/DELETE/v1/triggers/trigger_id) over REST. Canceling a TWAP or a ladder also cancels its open child orders.
3. Send the cancel-all.

The [dead-man switch](https://testnet.polyester.com/docs/developer-docs/shared-concepts/dead-man-switch) cancels what a cancel-all with the same scope cancels, so a client that stopped responding leaves its triggers live.

## Request examples

## REST JSON

Send the filter to `POST /v1/orders/cancel-all`. REST takes Pair symbols in `symbols`:

```json
{
	"symbols": ["BTC-USDT"],
	"side": "BUY",
	"requestId": "ca-1"
}
```

## ConnectRPC ProtoJSON

Call `/orders.v1.OrdersService/CancelAllOrders`. ConnectRPC takes the Pair IDs from `GetSpotConfig` in `symbol_ids`:

```json
{
	"symbolIds": [1],
	"side": "BUY",
	"requestId": "ca-1"
}
```

## Response fields

Polyester answers as soon as it has durably recorded the cancel-all, before the orders are canceled. A matched order can still execute before its cancel takes effect, so read order updates or order reads for each final outcome. The full schema is in the API reference; these are the rules behind each field.

| Protobuf field      | Meaning                                                                                                                                                                                                                                                                               |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`            | `SUBMITTED`, or `DRY_RUN` for a dry run                                                                                                                                                                                                                                               |
| `matched_orders`    | Open orders that matched the filter when the request was accepted, including orders whose cancellation was already in progress and open orders that triggers placed. Triggers themselves are never counted. Unless `dry_run` is set, equals `submitted_cancels` plus `failed_cancels` |
| `submitted_cancels` | Matched orders for which cancellation was requested, including orders whose cancellation was already in progress. Zero for a dry run                                                                                                                                                  |
| `failed_cancels`    | Matched orders that could not be submitted because the cancel-all could not be recorded for their Pair. This request does not cancel them. Zero for a dry run                                                                                                                         |
| `cancelled_orders`  | Orders whose cancellation was requested, oldest first, at most 200. Each lists `order_id` and `client_order_id`. Empty for a dry run                                                                                                                                                  |
| `total_cancelled`   | Number of orders whose cancellation was requested, including those not listed. Equals `submitted_cancels`                                                                                                                                                                             |
| `truncated`         | `true` when more orders were canceled than `cancelled_orders` lists                                                                                                                                                                                                                   |
| `listing_status`    | `INCLUDED` on the first answer. `NOT_RETAINED_READ_OPEN_ORDERS` on a retry, whose `cancelled_orders` is empty                                                                                                                                                                         |
| `ts`, `ts_ns`       | Server time of the response                                                                                                                                                                                                                                                           |

REST returns the same fields in camelCase, such as `matchedOrders` and `cancelledOrders`, with each `orderId` as a public ID.

```json
{
	"status": "SUBMITTED",
	"matchedOrders": 2,
	"submittedCancels": 2,
	"failedCancels": 0,
	"cancelledOrders": [
		{ "orderId": "2NEpo7TZRRrLZSi2U", "clientOrderId": "mm-bid-17" },
		{ "orderId": "2NEpo7TZRRrLZSi2V", "clientOrderId": "mm-ask-17" }
	],
	"totalCancelled": 2,
	"truncated": false,
	"listingStatus": "INCLUDED"
}
```

When `truncated` is `true`, read the remaining state with [`GET /v1/orders/open`](https://testnet.polyester.com/docs/api-docs/rest/GET/v1/orders/open). A retry with the same `request_id` and filter within 120 seconds returns the original status, counts, and timestamp, but not the list: `cancelled_orders` is empty and `listing_status` is `NOT_RETAINED_READ_OPEN_ORDERS`. An empty list on a retry does not mean nothing was canceled; read open orders for the current state.

## Completion

Each submitted order then moves to `CANCELED` asynchronously, unless it fills first. Observe the result through the [private WebSocket](https://testnet.polyester.com/docs/developer-docs/connectrpc/private-websocket) or [`GetOpenOrders`](https://testnet.polyester.com/docs/api-docs/connect/orders.v1.OrdersReadService/GetOpenOrders).

An accepted cancel-all completes even if the service restarts.

> **Handle failed cancels**
>
> If `failed_cancels` is not zero, or matching orders are still open afterwards, send a new cancel-all with a new `request_id`.

## Retries and idempotency

`request_id` is required and makes the call idempotent. It contains 1 to 64 ASCII letters, numbers, `.`, `_`, `:`, `/`, or `-`, and is scoped to the account.

- A retry with the same `request_id` and the same filter returns the original outcome and counts and does not cancel again. Polyester retains the result for at least two minutes.
- Reusing a `request_id` with a different filter, or with a different `dry_run` value, is rejected with `CONFLICT_IDEMPOTENCY_KEY_REUSE`.
- A `request_id` reused after the retention window starts a new cancel-all. Use a new `request_id` for every new cancel-all.

## Errors

Each entry gives the stable code and the ConnectRPC code, then the condition and what to do.

- **`UNAVAILABLE`** · `Unavailable`\
  The service is busy. The cancel-all may still be processed. Retry with the same `request_id`, then confirm the result.
- **`ORDER_CANCEL_OUTCOME_UNKNOWN`** · `DeadlineExceeded`\
  The cancel-all was not confirmed in time. Some or all matching orders may be canceled. Read open orders, then retry with the same `request_id` if needed.
- **`CONFLICT_IDEMPOTENCY_KEY_REUSE`** · `AlreadyExists`\
  Same `request_id`, different filter. Use a new `request_id` for a different cancel-all.
- **`UNKNOWN_SYMBOL`** · `InvalidArgument`\
  Unknown Pair. Correct the filter.
- **`VALIDATION_ERROR` (REST)** · `InvalidArgument`\
  More than 100 Pairs. Split the request, or omit Pairs to match every Pair.
- **`RATE_LIMIT_EXCEEDED`** · `ResourceExhausted`\
  Rate limited. Retry with the same `request_id` after backoff.

REST returns `503`, `504`, `409`, `400`, and `429` for these conditions.

If the request can be recorded for only some Pairs, the call succeeds and the orders it could not submit are counted in `failed_cancels`.

## Dry run

With `dry_run` set, the response counts the matching orders in `matched_orders` and nothing is canceled. A dry run uses its `request_id`; send the live cancel-all with a new one.
