> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
> Use this file to discover all available pages before exploring further.

# AMM v4 数学

> 恒定乘积不变量与 AMM v4 的费用约定、储备到订单簿价格转换、目标订单网格构造及 PnL 结算步骤。

<Info>
  **本页内容由 AI 自动翻译，所有内容以英文版本为准。**

  [查看英文版 →](/products/amm-v4/math)
</Info>

## 不变量

池维持 `coin_reserve × pc_reserve = k`，其中（在 2026-07 OpenBook 移除后）：

```
coin_reserve = coin_vault_balance - accrued_pnl_coin
pc_reserve   = pc_vault_balance   - accrued_pnl_pc
```

需要注意两点：

1. 储备现在**仅来自金库**。历史上的开放订单项（池作为 OpenBook 限价单托管的代币）已被删除——在移除之前很久就已经是零。你可以直接从链上金库余额计算 `k`。
2. PnL 应计（`need_take_pnl_*`）被减去，以便在管理员扫除费用时曲线保持不变。这与 CPMM 的 `protocol_fees_*` 排除原则相同。

程序通过 `calc_total_without_take_pnl_no_orderbook` 计算此值（旧的订单簿感知变体已消失）。

每个 `Swap*` 操作在将 LP 的费用份额加回储备后都强制 `k' ≥ k`。

## 费用约定

AMM v4 使用**比率费用**（分子/分母对），而不是 CPMM / CLMM 的 `1/1_000_000` 约定。链上 `Fees` 结构体（见程序源代码中的 [`Fees::initialize`](https://github.com/raydium-io/raydium-amm/blob/master/program/src/state.rs)）默认为：

```
Fees {
  min_separate_numerator:    5,
  min_separate_denominator:  10_000,   //  5/10_000 = 0.05%

  trade_fee_numerator:      25,
  trade_fee_denominator:    10_000,    // 25/10_000 = 0.25% — 用于 OpenBook 限价单定价

  pnl_numerator:            12,
  pnl_denominator:          100,       // 12/100   = 12%   — 协议在交换费中的份额

  swap_fee_numerator:       25,
  swap_fee_denominator:     10_000,    // 25/10_000 = 0.25% — AMM 路径交换的总费用
}
```

解释（已发布的主网默认值）：

* **总交换费：** `swap_fee = amount_in × 25 / 10_000 = 0.25%` 的总输入。
* **协议份额：** `pnl_numerator / pnl_denominator = 12 / 100 = 12%` **的交换费**，相当于 `0.25% × 12% = 0.03%` 的交易量。此份额应计到 PnL 计数器中，由 `WithdrawPnl` 扫除。
* **LP 份额：** 交换费的剩余 `88%`，相当于 `0.25% × 88% = 0.22%` 的交易量。保留在池中并增加 `k`。
* **无基金份额。** AMM v4 没有 CPMM/CLMM 的基金费用分割。

注意 `pnl_numerator / pnl_denominator` 是费用的一个分数**而非**交易量——这是对这些字段名的常见误读。

`trade_fee_numerator / trade_fee_denominator`（也是 `25 / 10_000`）历史上由 OpenBook 集成在计算 AMM 限价单网格的费用包含价格时使用。随着 OpenBook 代码的移除，此字段已成为遗留物；活跃的交换费是 `swap_fee_*`。

偏离这些默认值很少见，但在少数遗留池上确实存在；在报价前始终从 `AmmInfo.fees` 读取费用。

## 直接交换数学（AMM 路径）

最简单的情况：用户直接对池的金库交换，不与 OpenBook 交互。池的内部储备（包括账上分配）是分母。

**SwapBaseIn（精确输入）：**

```
amount_after_fee = amount_in − ceil(amount_in × swap_fee_numerator / swap_fee_denominator)
amount_out = amount_after_fee × out_reserve
           / (in_reserve + amount_after_fee)
require(amount_out >= minimum_amount_out)
```

此处使用的储备是金库余额（减去应计 PnL）。历史上公式还添加了 AMM 锁定在 OpenBook 订单中的代币；**该项已被移除**——有效储备现在等于原始金库余额减去待处理 PnL。曾经刷新 OpenBook 端的 `MonitorStep` / 隐式结算路径已被移除。

**SwapBaseOut（精确输出）：**

```
amount_in_after_fee = ceil(in_reserve × amount_out / (out_reserve − amount_out))
amount_in_gross     = ceil(amount_in_after_fee × swap_fee_denominator
                            / (swap_fee_denominator − swap_fee_numerator))
require(amount_in_gross <= maximum_amount_in)
```

## 订单簿交互（历史）

<Note>
  **已移除。** 本节描述的网格构造反映了 AMM v4 *最初*如何将曲线镜像到 OpenBook 市场。OpenBook 集成——包括 `MonitorStep` 曲柄和 `build_orders` 网格逻辑——已从程序中**移除**（2026-07 升级）。下面的数学纯粹作为历史背景保留，说明链上 `target_orders` / `amm_open_orders` 账户曾经的大小。
</Note>

除了用户交换，AMM v4 历史上在 OpenBook 市场上放置了一个**网格**的限价单。网格从 `AmmInfo` 参数计算：

* **`depth`** — 每侧的价格级别数。
* **`amount_wave`** — 每个级别的基本大小单位。
* **`min_size`**、**`coin_lot_size`**、**`pc_lot_size`** — OpenBook 市场约束。
* **`state_data.swap_acc_coin_fee`**、**`swap_acc_pc_fee`** — 自上次 `TakePnl` 以来的累积费用计数器。

程序通过以恒定比率步骤从当前曲线价格走出来推导每个级别的价格：

```
price_level(k) = curve_price × (1.0001 ^ k)       # 概念上
size_level(k)  = amount_wave × f(depth, k)        # 按深度逐渐减小
```

确切的价格和大小由 `build_orders` 中计算的 `target_orders` 确定，并在每个 `MonitorStep` 与 `amm_open_orders` 比较。任何偏差都会导致取消 + 新发布。OpenBook 上新填充的订单在下一个刷新 OpenBook 端的操作时结算到池金库中。

集成商很少需要计算网格——Raydium 守护者维护它——但了解以下内容很有用：

* 具有大量**账上**流动性的池将该流动性贡献给 `k`，而不是闲置。
* 陈旧的 OpenBook 市场（事件队列满、曲柄被阻止）阻止网格更新；AMM 随后可以报价与可见订单簿不同的价格，直到下一次曲柄。

## 结算步骤（PnL）

0.03% 的协议份额应计到 `state_data.need_take_pnl_coin` 和 `state_data.need_take_pnl_pc`。`TakePnl` 将这些金额移出金库到管理员指定的目标，然后将计数器清零。

关键属性：不变量中的储备始终计算**减去**应计 PnL，所以 `TakePnl` 不会移动曲线。这与 CPMM 约定相匹配。

## 工作示例

池状态：

* `coin_reserve = 1_000_000_000_000`（1,000,000 币侧；6 位小数）
* `pc_reserve   = 2_000_000_000_000`（2,000,000 pc 侧；6 位小数）
* 费用：默认 `swap = 25/10_000`、`pnl = 3/10_000`。

用户：`SwapBaseIn` 精确输入 `1_000_000_000` 币（1,000 币）。

```
swap_fee        = ceil(1_000_000_000 * 25 / 10_000)    = 2_500_000
amount_after_fee =                                      997_500_000

amount_out = amount_after_fee * pc_reserve
           / (coin_reserve + amount_after_fee)
           = 997_500_000 * 2_000_000_000_000
           / (1_000_000_000_000 + 997_500_000)
           ≈ 1_995_015_009  (1,995.015 pc)

// 在 2_500_000 交换费中：
pnl_share = 2_500_000 * 3 / 25  = 300_000    (通过 need_take_pnl_coin 进入协议)
lp_share  = 2_500_000 * 22 / 25 = 2_200_000  (保留在 coin_reserve 中)

new coin_reserve = 1_000_000_000_000 + 1_000_000_000                 = 1_001_000_000_000
                   (其中 300_000 是应计 PnL)
  curve coin_reserve = 1_001_000_000_000 − 300_000 = 1_000_999_700_000
new pc_reserve   = 2_000_000_000_000 − 1_995_015_009                 ≈ 1_998_004_984_991

k' = curve_coin_reserve * new_pc_reserve
   ≈ 2.000_002_701E24
k  = 1_000_000_000_000 * 2_000_000_000_000
   = 2.0E24
k' > k   ✓
```

LP 份额（`2_200_000`）没有单独列出——它只是提高 `k'` 的剩余部分。

## 精度规则

* 储备乘法使用 `u128`；最终除法向零舍入。
* `swap_fee` 向上舍入（以便池不会低收费）。
* `SwapBaseOut` 的 `amount_in` 向上舍入（以便用户不会少付）。
* 具有极端储备比率的池在非常小的输入上可能会触发 `ZeroTradingTokens`；与 CPMM 相同的约定。

## 与 CPMM 的限制

* AMM v4 的储备现在仅来自金库，所以你**可以**直接从金库余额报价（减去 `need_take_pnl_*`）——之前添加 `open_orders.free` / `open_orders.locked` 金额的要求不再适用。SDK / API 报价仍然是最简单的选项。
* AMM v4 不公开结构化的链上 TWAP。想要 AMM v4 支持的价格的外部消费者必须从交易日志自己计算。
* 不支持 Token-2022。

## 后续步骤

* [`products/amm-v4/instructions`](/zh/products/amm-v4/instructions) — `SwapBaseIn`、`Deposit` 等的位置。
* [`products/amm-v4/fees`](/zh/products/amm-v4/fees) — 完整的费用机制、`TakePnl` 详情。
* [`algorithms/constant-product`](/zh/algorithms/constant-product) — 共享推导。

来源：

* [Raydium AMM 程序源 — `raydium-io/raydium-amm`](https://github.com/raydium-io/raydium-amm)
* Raydium SDK v2 `Liquidity` 模块
