> For the complete documentation index, see [llms.txt](https://docs.aftermath.finance/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aftermath.finance/ru/bessrochnye-fyuchersy/architecture/market-makers.md).

# Маркет-мейкеры

Руководство по интеграции для маркет-мейкеров на Aftermath Perpetuals — цикл перекотировки, форматы данных, рыночные данные в реальном времени и контроль рисков.

Специальной программы для назначенных маркет-мейкеров нет, и ни у кого нет преимуществ по задержке. Заниматься маркет-мейкингом на Aftermath Perpetuals может любой желающий.

С техническими вопросами по интеграции заходите в наш [Discord](https://discord.gg/VFqMUqKHF3) или пишите нам в [X](https://x.com/AftermathFi).

## Базовые адреса API

| Ресурс               | URL                                                                                                  |
| -------------------- | ---------------------------------------------------------------------------------------------------- |
| Префикс API          | `https://aftermath.finance/api/...`                                                                  |
| Swagger UI           | [`https://aftermath.finance/docs`](https://aftermath.finance/docs)                                   |
| Спецификация OpenAPI | [`https://aftermath.finance/api/openapi/spec.json`](https://aftermath.finance/api/openapi/spec.json) |
| Навыки для агентов   | [`github.com/AftermathFinance/skills`](https://github.com/AftermathFinance/skills)                   |

Спецификация OpenAPI — источник истины для имён и типов полей. Генерируйте типы из неё, а не пишите тела запросов вручную.

{% hint style="info" %}
Репозиторий навыков для агентов упаковывает эти шаблоны интеграции для ИИ-агентов, пишущих код. Если вы работаете с Claude Code, Codex или Cursor, укажите агенту этот репозиторий до того, как он начнёт писать тела запросов.
{% endhint %}

## Рекомендуемая интеграция

Мы рекомендуем использовать нативные REST-эндпоинты бессрочных фьючерсов (`/api/perpetuals/account/transactions/*`), а не слой CCXT. Нативные эндпоинты дают полный контроль над управлением газом и доступ к возможностям, специфичным для Sui, таким как программируемые блоки транзакций (PTB).

Все нативные эндпоинты возвращают `TxKindResponse` с `TransactionKind` в кодировке base64. Вы декодируете его, оборачиваете в полную `Transaction`, подписываете своим кошельком и отправляете через свой клиент Sui.

### Основные эндпоинты

**Отмена и размещение ордеров** (`POST /api/perpetuals/account/transactions/cancel-and-place-orders`)

Это основной эндпоинт для маркет-мейкеров. Он атомарно отменяет существующие ордера и размещает новые в одном PTB, что обходится дешевле по газу, чем отдельные транзакции отмены и размещения. Промежуточного состояния между двумя операциями нет, поэтому глубина ваших котировок в стакане никогда не падает до нуля посреди перекотировки. Используйте его для обновления котировок или управления сетками ордеров.

**Размещение лимитного ордера** (`POST /api/perpetuals/account/transactions/place-limit-order`)

Для размещения отдельных ордеров. Поддерживает прикрепление SL/TP в той же транзакции.

**Размещение рыночного ордера** (`POST /api/perpetuals/account/transactions/place-market-order`)

Для немедленного исполнения по рыночной цене. Также поддерживает прикрепление SL/TP.

**Размещение масштабированного ордера** (`POST /api/perpetuals/account/transactions/place-scale-order`)

Распределяет объём по ценовому диапазону в одной транзакции. Используйте его для входов в стиле DCA, лестниц ликвидности или распределения риска по нескольким ценовым уровням.

**TWAP-ордера** (`POST /api/perpetuals/account/transactions/create-twap-orders`, `edit-twap-orders`, `cancel-twap-orders`)

Планирует исполнение объёма во времени. Запланированные и обработанные объёмы можно прочитать через `POST /api/perpetuals/account/twap-order-datas`. Используйте их, чтобы разгрузить накопленный инвентарь, не платя спред сразу целиком.

{% hint style="info" %}
У каждого маршрута транзакций аккаунта выше есть аналог для хранилища в `/api/perpetuals/vault/transactions/*`, включая `cancel-and-place-orders`. Если вы котируете из хранилища, а не с личного аккаунта, формат запросов тот же: замените `accountId` на `vaultId`.
{% endhint %}

## Цикл перекотировки

Вы будете вызывать `cancel-and-place-orders` тысячи раз в день, поэтому изучите каждое поле.

| Поле                             | Примечания                                                                                                                                                                   |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accountId`                      | Числовой ID аккаунта, передаётся как строка BigInt (`"123n"`). При котировании из хранилища используйте вместо него `vaultId`.                                               |
| `accountCapId`                   | Необязательный ID объекта capability аккаунта, авторизующего транзакцию.                                                                                                     |
| `walletAddress`                  | Обязательное. Получает возвращённые монеты газа.                                                                                                                             |
| `marketId`                       | Обязательное. Клиринговая палата (clearing house), на которой выставляются котировки.                                                                                        |
| `orderIdsToCancel`               | Присвоенные блокчейном ID ордеров в виде строк BigInt. Не указывайте, если отменять ничего не нужно.                                                                         |
| `clientOrderIdsToCancel`         | Отмена по **вашим собственным** ID вместо ID блокчейна. Можно сочетать с `orderIdsToCancel`.                                                                                 |
| `shouldAbortOnMissingId`         | По умолчанию `false`. См. ниже — именно это значение по умолчанию вам и нужно.                                                                                               |
| `ordersToPlace`                  | Массив `{ side, price, size, clientOrderId? }`. Не указывайте, если размещать ничего не нужно.                                                                               |
| `orderType`                      | Тип исполнения для всего пакета. См. таблицу ниже.                                                                                                                           |
| `reduceOnly`                     | Если `true`, размещённые ордера никогда не увеличивают размер позиции.                                                                                                       |
| `hasPosition`                    | Обязательное. Есть ли у аккаунта сейчас позиция на этом рынке.                                                                                                               |
| `expiryTimestamp`                | Необязательный срок действия размещаемых ордеров в миллисекундах эпохи Unix.                                                                                                 |
| `leverage`                       | Необязательное переопределение кредитного плеча для размещаемых ордеров.                                                                                                     |
| `shouldDeallocateFreeCollateral` | Если `true`, в той же транзакции возвращает в кошелёк маржу, не обеспечивающую позицию.                                                                                      |
| `sponsor`                        | `{ walletAddress, bytes, signature, gasBudget? }` для спонсирования через GasPool. `bytes` — это base64 от `Aftermath Terms and Conditions`; `gasBudget` указывается в MIST. |
| `builderCode`                    | `{ integratorId, integratorFee }` — нужно, только если вы направляете поток ордеров как интегратор.                                                                          |
| `txKind`                         | Существующий `TransactionKind` в base64 для расширения, чтобы встроить перекотировку в собственный более крупный PTB.                                                        |

### Типы ордеров

`orderType` применяется ко всем ордерам в `ordersToPlace`:

| Значение | Тип       | Поведение                                                                                       |
| -------- | --------- | ----------------------------------------------------------------------------------------------- |
| `0`      | GTC       | Остаётся в стакане до исполнения или отмены.                                                    |
| `1`      | FOK       | Должен исполниться полностью за одно сопоставление, иначе отменяется весь ордер.                |
| `2`      | Post-Only | Добавляется в стакан, только если не исполнится сразу. Отклоняется, если забрал бы ликвидность. |
| `3`      | IOC       | Немедленно исполняет всё, что может, а остаток отменяет.                                        |

Маркет-мейкерам следует котировать с `2`. Это гарантирует, что ваш ордер никогда не пересечёт спред и вы никогда не заплатите комиссию тейкера.

### Клиентские ID ордеров

Каждый размещаемый ордер принимает необязательный `clientOrderId`: выбранное вами значение `u64`, передаваемое как строка BigInt. Помечайте котировки собственными ID — и вы сможете отменять их через `clientOrderIdsToCancel`, не считывая ID, присвоенный блокчейном.

Это убирает один круговой запрос из цикла перекотировки. Вместо цепочки «разместить → прочитать ID ордеров → отменить по ID блокчейна → разместить снова» вы продолжаете котировать по ID, которые ваша стратегия уже знает. Клиентские ID ордеров также возвращаются в `pendingOrders` в состоянии позиции и в потоках ордеров, поэтому исполнения можно сверять напрямую с собственной книгой.

`clientOrderId` принимается в `place-limit-order`, а в виде `clientOrderIds` — в `place-scale-order`.

### Отсутствующие ID по умолчанию не считаются ошибкой

Ордер, который вы собираетесь отменить, может исполниться или истечь между моментом решения и попаданием транзакции в сеть. С `shouldAbortOnMissingId: false` (по умолчанию) транзакция допускает отсутствующий ID: остальные отмены и все новые ордера всё равно проходят.

Устанавливайте `true`, только если частично применённая перекотировка для вас хуже, чем отсутствие перекотировки. Для большинства стратегий котирования это не так: прерванная транзакция оставляет в стакане устаревшие котировки — ровно ту ситуацию, которую вы пытались исправить.

### Пример запроса

```json
POST /api/perpetuals/account/transactions/cancel-and-place-orders

{
  "accountId": "123n",
  "accountCapId": "0x...",
  "walletAddress": "0x...",
  "marketId": "0x...",
  "clientOrderIdsToCancel": ["1001n", "1002n", "1003n"],
  "ordersToPlace": [
    { "side": 0, "price": "7250000000000n", "size": "100000000n", "clientOrderId": "1004n" },
    { "side": 0, "price": "7249000000000n", "size": "100000000n", "clientOrderId": "1005n" },
    { "side": 1, "price": "7260000000000n", "size": "100000000n", "clientOrderId": "1006n" },
    { "side": 1, "price": "7261000000000n", "size": "100000000n", "clientOrderId": "1007n" },
    { "side": 1, "price": "7262000000000n", "size": "100000000n", "clientOrderId": "1008n" }
  ],
  "orderType": 2,
  "reduceOnly": false,
  "hasPosition": true,
  "shouldAbortOnMissingId": false
}
```

`side` равно `0` для бида и `1` для аска.

## Правила формата данных

На эти четыре правила приходится большинство неудачных первых интеграций.

**Полям BigInt нужен завершающий `n`.** Нативные поля BigInt — это JSON-строки с суффиксом `n`, например `"123n"`. И просто `123`, и `"123"` вызовут ошибку. Ответы возвращаются в том же формате. Не каждое число — BigInt: обычные счётчики и метки времени в миллисекундах остаются обычными числами JSON, поэтому следуйте схеме каждого эндпоинта, а не преобразуйте всё подряд.

**`accountId` — число, а не ID объекта.** Нативные эндпоинты принимают числовой ID аккаунта. Эндпоинты записи CCXT принимают под тем же именем ID объекта capability аккаунта (`0x...`), а эндпоинты чтения CCXT используют `accountNumber`. Передача ID capability `0x...` туда, где ожидается числовой `accountId`, — самая частая ошибка интеграции.

**Ответы предпросмотра — объединение успеха и ошибки.** `/api/perpetuals/account/previews/*` может вернуть HTTP `200` с телом `{ "error": "..." }` и заголовком `X-Error-Message: true`. Проверяйте наличие ошибки, прежде чем читать поля успешного ответа. Сам по себе код `200` не означает, что предпросмотр прошёл успешно.

**Ответы упорядочены детерминированно.** Рынки сортируются по символу, позиции — по ID рынка, а ожидающие биды и аски — каждый по ID ордера. Последовательные снимки аккаунта можно сравнивать по позициям, не перестраивая словарь при каждом опросе.

## Рыночные данные в реальном времени

Все потоки в реальном времени (стакан, оракул, состояние рынка, обновления аккаунта и свечи) мультиплексируются через один WebSocket:

`wss://aftermath.finance/api/perpetuals/ws/updates`

Подписка оформляется для каждого потока отдельно: после `open` отправьте JSON-фрейм:

```json
{
  "action": "subscribe",
  "subscriptionType": {
    "topOfOrderbook": {
      "marketId": "0x...",
      "priceBucketSize": 0.0001,
      "bucketsNumber": 10
    }
  }
}
```

Потоки, которые обычно нужны боту-котировщику:

| Подписка/хелпер                  | Назначение                                                                                                                                  |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `topOfOrderbook`                 | Снимки top-N с группировкой по ценовым корзинам. Каждый фрейм — полный снимок, поэтому не нужна начальная загрузка через REST и учёт дельт. |
| `orderbook`                      | Дельты полной глубины, если сгруппированной верхушки стакана недостаточно.                                                                  |
| `oracle`                         | Обновления цены оракула для рынка.                                                                                                          |
| `subscribeUserOrders`            | Хелпер SDK для обновлений ордеров аккаунта, включая `clientOrderId`.                                                                        |
| `subscribeUserCollateralChanges` | Хелпер SDK для движений обеспечения аккаунта.                                                                                               |
| `marketCandles`                  | Обновления OHLCV с интервалами от `1m` до `1mo`.                                                                                            |

Полные схемы фреймов, фреймы ошибок и примеры клиентов — на странице [WebSockets](https://docs.aftermath.finance/for-developers/api/websockets) (на английском).

{% hint style="warning" %}
**После переподключения сначала синхронизируйтесь, затем продолжайте.** Повторно запросите снимки, атомарно замените локальное состояние и только потом снова применяйте дельты. Никогда не продолжайте работу с состоянием в памяти, сформированным до разрыва соединения.
{% endhint %}

Если вы предпочитаете server-sent events, потоки SSE доступны в `/api/ccxt/stream/*`.

## Контроль рисков

**Проверяйте размер перед отправкой.** `POST /api/perpetuals/account/max-order-size` возвращает максимальный ордер, который аккаунт может разместить с учётом текущих лимитов риска, для заданных `marketId` и `side` (`0` — бид, `1` — аск). Вызывайте его перед увеличением размера котировок или изменением плеча, а не узнавайте лимит по неудавшейся транзакции.

**Аварийного выключателя (dead man's switch) нет.** В API нет эндпоинта для отложенной отмены или автоматического истечения ордеров при отключении. Если ваш процесс упадёт, котировки останутся в стакане. Сделайте аварийный выключатель на основе heartbeat, который отменяет все открытые ордера, когда цикл стратегии зависает, и привяжите его к `SIGINT`/`SIGTERM`, а также к тайм-ауту heartbeat. Перед выходом убедитесь, что отмены прошли.

**Состояние аккаунта сразу устаревает.** После любого исполнения, отмены, внесения, вывода или изменения плеча обновите состояние аккаунта и позиций, прежде чем рассчитывать следующую котировку.

**Выполняйте последовательно всё, что затрагивает один и тот же объект монеты.** Параллельные подписанные транзакции могут конкурировать за одну и ту же монету USDC или монету газа и завершаться ошибками версии или эквивокации. Выполняйте операции с обеспечением последовательно и выделяйте параллельным отправителям собственные монеты газа.

## Оптимизация газа

Наибольшая экономия газа достигается за счёт структуры транзакций. Атомарное использование `cancel-and-place-orders` избавляет от оплаты отдельных транзакций и убирает разрыв между отменой устаревших котировок и размещением новых.

**Неэффективно: отдельные транзакции**

```
TX 1: cancel_orders              ← cancel stale quotes
TX 2: place_limit_order (×1)     ← place the replacement
────────────────────────────────────────────────
Result: two transactions, two computation minimums, and a gap between them
```

**Эффективно: атомарная отмена и размещение с пакетированием**

```
TX 1:
  cancel_orders                  ← cancel stale quotes
  place_limit_order (×5)         ← 5 new orders
────────────────────────────────────────────────
Result: one atomic transaction and one computation minimum for the entire quote refresh
```

Каждая транзакция Sui оплачивает минимум вычислений в 1,000 единиц, что составляет 100,000 MIST (0.0001 SUI) при текущей эталонной цене газа 100 MIST. В этот минимум помещается примерно до 24 действий с ордерами (размещений или отмен), поэтому объединение отмен и размещений в один вызов `cancel-and-place-orders` распределяет его на все действия в PTB. PTB отмены и размещения с двумя отменами и двумя новыми лимитными ордерами стоит около **103,000 MIST (0.000103 SUI) в сумме**, или около **0.0000515 SUI за новый ордер**.

Это наблюдаемые медианы в Sui mainnet при эталонной цене газа 100 MIST, измеренные 2026-10-27 после протокола Sui 139. Это не фиксированные комиссии. Чистый газ — это вычисления плюс стоимость хранения минус возврат за хранение (storage rebate), а вычислительная часть линейно зависит от цены газа. Стоимость одного действия с ордером в зависимости от размера пакета см. в разделе [Экономика маркет-мейкинга](/ru/bessrochnye-fyuchersy/architecture/market-makers/market-maker-economics.md#gas-costs).

### Советы

* **Всегда используйте `cancel-and-place-orders`.** Никогда не отправляйте отмену и размещение отдельными транзакциями.
* **Объединяйте отмены и размещения в один вызов.** Массив `ordersToPlace` принимает несколько ордеров. Стоимость одного действия с ордером резко падает примерно до 24 действий в PTB. Каждое действие сверх этого добавляет около 4,300 MIST.
* **Рассчитывайте бюджет газа с учётом депозитов.** Каждый новый ордер требует депозита за хранение, который возвращается при отмене или исполнении ордера, но бюджет газа должен покрывать его заранее. Большому пакету нужен больший бюджет, но не большие чистые расходы после отмены или исполнения его ордеров. `sponsor.gasBudget` указывается в MIST.
* **Котируйте обе стороны в одной транзакции.** Размещайте биды и аски в одном массиве `ordersToPlace`.
* **Используйте Post-Only (`orderType: 2`).** Это гарантирует исполнение в роли мейкера и избавляет от комиссий тейкера.
* **Помечайте ордера через `clientOrderId`.** Отменяйте по собственным ID и пропускайте круговой запрос на чтение.
* **Используйте агентские кошельки.** Делегируйте подписание агентскому кошельку через `grant-agent-wallet`, чтобы основной ключ оставался в холодном хранении. Агентские кошельки могут торговать, но не могут выводить обеспечение.
* **Используйте спонсирование через gas pool.** Заранее пополните gas pool (`/api/gas-pool/*`) и передавайте кошелёк спонсора вместе с его многоразовой подписью условий — так расходы на газ будут предсказуемыми.
* **Компонуйте с `txKind`.** Передайте существующий `TransactionKind`, чтобы встроить перекотировку в более крупный PTB вместо отправки двух транзакций.
* **Не добавляйте собственные обновления оракула.** API самостоятельно следит за актуальностью оракула.
* **Не переплачивайте за газ, чтобы пролезть без очереди.** Транзакции с приоритетным газом облагаются дополнительной комиссией тейкера (`priorityTakerFee` в параметрах рынка) и сразу отклоняются, если этот параметр не задан. См. [Эталонная цена газа](/ru/bessrochnye-fyuchersy/architecture/fees/reference-gas-price.md).

### CCXT

Если ваша компания уже работает на инфраструктуре CCXT, мы также поддерживаем стандартный интерфейс CCXT. См. [документацию CCXT](https://docs.aftermath.finance/for-developers/api/ccxt) (на английском). Учтите, что эндпоинты CCXT возвращают `transactionBytes` и `signingDigest`, а не сырой `TransactionKind`, поэтому контроль над газом и компоновкой PTB у вас меньше. Подписывайте `signingDigest`, а не `transactionBytes`, и отправляйте `signatures[]`. Если отправитель и владелец газа различаются, обе подписи помещаются в этот массив.

`OrderRequest` в CCXT также уже нативного интерфейса: `timeInForce` и `postOnly` не поддерживаются в `/api/ccxt/build/createOrders` и игнорируются, если переданы. Маркет-мейкерам следует котировать в режиме post-only, поэтому используйте нативные эндпоинты.

Полный справочник API — в [документации Swagger](https://aftermath.finance/docs).
