> For the complete documentation index, see [llms.txt](https://ch-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://ch-docs.aftermath.finance/yong-xu-he-yue/architecture/market-makers.md).

# 做市商

Aftermath 永续合约做市商集成指南——重报价循环、数据格式、实时行情与风控。

Aftermath 永续合约没有指定做市商计划，没有特殊返佣，也没有延迟优势。任何人都可以在这里做市。

技术集成问题请加入我们的 [Discord](https://discord.gg/VFqMUqKHF3)，或在 [X](https://x.com/AftermathFi) 上私信我们。

## API 基础信息

| 资源           | URL                                                                                                                        |
| ------------ | -------------------------------------------------------------------------------------------------------------------------- |
| API 前缀       | `https://v2-preview.aftermath.finance/api/...`                                                                             |
| Swagger UI   | [`https://v2-preview.aftermath.finance/docs`](https://v2-preview.aftermath.finance/docs)                                   |
| OpenAPI 规范   | [`https://v2-preview.aftermath.finance/api/openapi/spec.json`](https://v2-preview.aftermath.finance/api/openapi/spec.json) |
| Agent skills | [`github.com/AftermathFinance/skills`](https://github.com/AftermathFinance/skills)                                         |

OpenAPI 规范是字段名和类型的唯一权威来源。请从规范生成类型定义，不要手写请求体。

{% hint style="info" %}
Agent skills 仓库把这些集成模式打包给了编码智能体。如果您用 Claude Code、Codex 或 Cursor 开发，请在智能体编写任何请求体之前先让它读取该仓库。
{% endhint %}

## 推荐的集成方式

我们推荐使用永续合约原生 REST 接口（`/api/perpetuals/account/transactions/*`），而非 CCXT 层。原生接口让您完全掌控 Gas 管理，并可使用 PTB（可编程交易块）等 Sui 原生特性。

所有原生接口都返回一个 `TxKindResponse`，其中包含 base64 编码的 `TransactionKind`。您需要将其解码、包装成完整的 `Transaction`、用钱包签名，再通过您的 Sui 客户端提交。

### 核心接口

**取消并下单**（`POST /api/perpetuals/account/transactions/cancel-and-place-orders`）

这是做市商的主力接口。它在单个 PTB 内原子性地取消现有订单并放置新订单，比分开发送取消和下单交易省下可观的 Gas。两个操作之间不存在中间状态，重报价过程中盘口深度永远不会归零。刷新报价或管理订单网格，就用这个接口。

**放置限价单**（`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。                                  |
| `walletAddress`                  | 必填。接收退回的 Gas 币。                                        |
| `marketId`                       | 必填。报价所在的清算所。                                           |
| `orderIdsToCancel`               | 链上分配的订单 ID，以 BigInt 字符串表示。省略则不取消任何订单。                  |
| `clientOrderIdsToCancel`         | 按**您自己的** ID 取消，而非链上 ID。可与 `orderIdsToCancel` 组合使用。    |
| `shouldAbortOnMissingId`         | 默认为 `false`。见下文——这个默认值正是您想要的。                          |
| `ordersToPlace`                  | `{ side, price, size, clientOrderId? }` 数组。省略则不下任何单。   |
| `orderType`                      | 整批订单的执行类型。见下表。                                         |
| `reduceOnly`                     | 若为 `true`，所下订单永远不会增加仓位规模。                              |
| `hasPosition`                    | 必填。账户当前在该市场是否持有仓位。                                     |
| `expiryTimestamp`                | 可选，所下订单的到期时间（毫秒时间戳）。                                   |
| `leverage`                       | 可选，为所下订单覆盖杠杆设置。                                        |
| `shouldDeallocateFreeCollateral` | 若为 `true`，在同一笔交易内把未支撑仓位的保证金清扫回钱包。                      |
| `sponsor`                        | `{ walletAddress }`，用于 Gas 池代付。                        |
| `builderCode`                    | `{ integratorId, integratorFee }`——仅当您以集成商身份路由订单流时才需要。 |
| `txKind`                         | 已有的 base64 `TransactionKind`，在其基础上扩展，把重报价合入您自己更大的 PTB。 |

### 订单类型

`orderType` 作用于 `ordersToPlace` 中的每一笔订单：

| 值   | 类型        | 行为                              |
| --- | --------- | ------------------------------- |
| `0` | GTC       | 挂在订单簿上，直到成交或被取消。                |
| `1` | FOK       | 必须一次全部成交，否则整单取消。                |
| `2` | Post-Only | 仅在不会立即撮合的情况下才加入订单簿；若会吃掉流动性则被拒绝。 |
| `3` | IOC       | 立即成交能成交的部分，其余取消。                |

做市商应使用 `2` 报价。它保证您永远不会跨越盘口，也永远不用付吃单方 (Taker) 费用。

### 客户端订单 ID

每笔订单都可携带可选的 `clientOrderId`——由您自选的 `u64`，以 BigInt 字符串发送。给报价打上自己的 ID，就能通过 `clientOrderIdsToCancel` 取消，全程不必读回链上分配的 ID。

这从重报价循环中省去了一次往返。不再是下单 → 读回订单 ID → 按链上 ID 取消 → 再下单，而是始终针对策略已知的 ID 报价。客户端订单 ID 也会出现在仓位状态的 `pendingOrders` 和订单数据流中，因此可以直接用自己的账本核对成交。

`place-limit-order` 接受 `clientOrderId`；`place-scale-order` 以 `clientOrderIds` 形式接受。

### 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 字段是带 `n` 后缀的 JSON 字符串——`"123n"`。纯数字 `123` 和 `"123"` 都会失败。响应也以同样的格式返回。并非所有数字都是 BigInt：普通计数器和毫秒时间戳仍是普通 JSON 数字，请遵循各接口的 schema，不要全局转换。

**`accountId` 是数字，不是对象 ID。** 原生接口接收数字账户 ID。CCXT 写入接口在同名字段下接收账户权限凭证对象 ID（`0x...`），CCXT 读取接口则使用 `accountNumber`。在应填数字 `accountId` 的地方传入 `0x...` 权限凭证 ID，是最常见的集成故障。

**预览响应是"成功或错误"联合体。** `/api/perpetuals/account/previews/*` 可能返回 HTTP `200`，但响应体是 `{ "error": "..." }` 并带有响应头 `X-Error-Message: true`。读取成功字段前先检查错误结构——单看 `200` 并不代表预览成功。

**响应是确定性排序的。** 市场按交易对符号排序，仓位按市场 ID 排序，挂单中的买单和卖单各自按订单 ID 排序。您可以对相邻两次账户快照按位置做差异比对，不必每次轮询都重建映射。

## 实时行情数据

所有实时数据流——订单簿、预言机、市场状态、账户更新和 K 线——都复用同一个 WebSocket：

`wss://v2-preview.aftermath.finance/api/perpetuals/ws/updates`

连接 `open` 后，按数据流逐个发送 JSON 帧订阅：

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

报价机器人通常需要的数据流：

| 数据流                     | 用途                                       |
| ----------------------- | ---------------------------------------- |
| `topOfOrderbook`        | 分桶的前 N 档快照。每帧都是完整快照，无需 REST 初始化，也无需维护增量。 |
| `orderbook`             | 全深度增量，用于分桶盘口快照不够用的场景。                    |
| `oracle`                | 该市场的预言机价格更新。                             |
| `userOrders`            | 您账户的订单更新，包含 `clientOrderId`。             |
| `userCollateralChanges` | 您账户的保证金资产变动。                             |
| `marketCandles`         | `1m` … `1mo` 各周期的 OHLCV 更新。              |

完整的帧 schema、错误帧和客户端示例见 [WebSockets](https://github.com/clawd-aftermath/aftermath-docs-translations/tree/zh/for-developers/api/websockets.md) 页面。

{% hint style="warning" %}
**重连后先重新同步，再恢复处理。** 重新拉取快照，原子性地替换本地状态，然后再继续应用增量。绝不要从断连前的内存状态继续。
{% endhint %}

如果您更习惯 SSE（Server-Sent Events），可使用 `/api/ccxt/stream/*` 下的 SSE 数据流。

## 风险控制

**发送前先校验规模。** `POST /api/perpetuals/account/max-order-size` 返回给定 `marketId` 和 `side`（`0` 买、`1` 卖）下，账户在当前风险限额内可下的最大订单。放大报价规模或调整杠杆前先调用它，不要靠交易失败来发现限额。

**没有"死人开关"。** API 不提供定时取消或断连自动过期的接口。进程挂掉后，您的报价会一直留在订单簿上。请构建一个心跳终止开关，在策略循环停滞时取消全部挂单，并同时挂接到 `SIGINT`/`SIGTERM` 和心跳超时。退出前确认取消操作已上链。

**账户状态会立即过期。** 任何成交、取消、存入、提取或杠杆变更之后，先刷新账户和仓位状态，再计算下一次报价。

**串行化一切触及同一 coin 对象的操作。** 并发的已签名交易可能在同一个 USDC coin 或 Gas coin 上竞争，因版本冲突或 equivocation 错误而失败。请串行化保证金资产操作，并给并行提交者各自独立的 Gas coin。

## Gas 优化

最大的 Gas 节省来自交易结构。用 `cancel-and-place-orders` 原子性执行，而不是分开调用，每次订单更新可将 Gas 降低约 7 倍。

**低效：分开的交易**

```
TX 1: cancel_orders              → ~0.0016 SUI
TX 2: place_limit_order (×1)     → ~0.0023 SUI
────────────────────────────────────────────────
Total: ~0.004 SUI for 1 order update
```

**高效：原子化取消并下单 + 批量**

```
TX 1:
  cancel_orders                  ← cancel stale quotes
  place_limit_order (×5)         ← 5 new orders
────────────────────────────────────────────────
Total: ~0.002 SUI for cancel + 5 orders
Gas per order: ~0.0004 SUI
```

### 实用建议

* **始终使用 `cancel-and-place-orders`**——绝不把取消和下单作为两笔交易分开发送。
* **每次调用批量提交 5 笔以上订单**——`ordersToPlace` 数组接受多笔订单。单笔交易订单越多，每笔订单的 Gas 越低。
* **一笔交易报双边**——买单和卖单放进同一个 `ordersToPlace` 数组。
* **使用 Post-Only（`orderType: 2`）**——保证以挂单方 (Maker) 身份成交，避免 Taker 费用。
* **用 `clientOrderId` 标记订单**——按自己的 ID 取消，省去读回环节。
* **使用代理钱包**——通过 `grant-agent-wallet` 把签名委托给代理钱包，主密钥保持冷存储。代理钱包可以交易，但无法提取保证金资产。
* **使用 Gas 池代付**——预先注资 Gas 池（`/api/gas-pool/*`），并传入 `sponsor: { walletAddress }`，让 Gas 成本可预测。
* **用 `txKind` 组合交易**——传入已有的 `TransactionKind`，把重报价折叠进更大的 PTB，而不是发两笔交易。
* **不要自行添加预言机更新**——API 在内部处理预言机数据的新鲜度。
* **不要为插队而多付 Gas**——高优先级 Gas 交易会被加收额外的 Taker 费用（市场参数中的 `priorityTakerFee`），该参数未设置时此类交易会被直接拒绝。参见[参考 Gas 价格](/yong-xu-he-yue/architecture/fees/reference-gas-price.md)。

### CCXT

如果贵司已运行在 CCXT 基础设施上，我们同样支持 CCXT 标准接口，参见 [CCXT 文档](https://github.com/clawd-aftermath/aftermath-docs-translations/tree/zh/for-developers/api/ccxt.md)。注意 CCXT 接口返回的是 `transactionBytes` 和 `signingDigest`，而非原始 `TransactionKind`，因此您对 Gas 和 PTB 组合的控制会更少。请对 `signingDigest` 而非 `transactionBytes` 签名，并提交 `signatures[]`——当发送方与 Gas 拥有者不同时，两个签名都放进该数组。

CCXT 的 `OrderRequest` 也比原生接口更窄：`/api/ccxt/build/createOrders` 不支持 `timeInForce` 和 `postOnly`，即使发送也会被忽略。如果只挂单 (Post Only) 报价对您很重要——对做市商来说理应如此——请使用原生接口。

完整 API 参考见 [Swagger 文档](https://v2-preview.aftermath.finance/docs)。
