> ## 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

> Инвариант constant-product с соглашением о комиссиях AMM v4, преобразование цены резервов в ордербук, построение целевой сетки ордеров и шаг расчёта PnL.

<Info>
  **Эта страница переведена с помощью ИИ. За эталон принимается английская версия.**

  [Открыть английскую версию →](/products/amm-v4/math)
</Info>

## Инвариант

Пул поддерживает `coin_reserve × pc_reserve = k`, где:

```
coin_reserve = coin_vault_balance
             + orders_posted_on_openbook.base
             + pending_coin_fill_not_yet_settled
pc_reserve   = pc_vault_balance
             + orders_posted_on_openbook.quote
             + pending_pc_fill_not_yet_settled
             - accrued_pnl_pc
```

На что нужно обратить внимание:

1. Резервы включают **суммы, зафиксированные на OpenBook**. Лимит-ордера AMM остаются частью его ликвидности — они не «теряются» в ордербуке, а просто там хранятся. Вычисление `k` только по остаткам на цепи недооценивает резервы.
2. Начисление PnL (`need_take_pnl_*`) вычитается, чтобы сохранить кривую, когда администратор снимает комиссии. То же самое в CPMM — исключение `protocol_fees_*`.

Каждая операция `Swap*` гарантирует `k' ≥ k` после добавления доли комиссии LP обратно в резервы.

## Соглашение о комиссиях

AMM v4 использует **пропорциональные комиссии** (пары числитель/знаменатель) вместо соглашения `1/1_000_000` CPMM/CLMM. Структура `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; по умолчанию равно `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)
```

Резервы, используемые здесь, — это **эффективные** резервы. Исторически это было `coin_vault_balance + coin_posted_on_openbook + ...` (хранилище AMM плюс токены, заблокированные в ордерах OpenBook). **После деактивации OpenBook баланс на ордербуке равен нулю**, поэтому эффективные резервы равны сырым остаткам хранилища. Путь `MonitorStep` / неявного расчёта, который раньше обновлял сторону OpenBook, больше не требуется на практике.

**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 деактивирована; пулы больше не выставляют и не поддерживают ордера на OpenBook. Математика ниже сохранена для контекста — она объясняет, для чего имели размер учётные записи `target_orders` / `amm_open_orders` и почему программа всё ещё проверяет параметры, связанные с `MonitorStep`, хотя киппер больше их не обновляет.
</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)        # сужается по depth
```

Точные цены и размеры определяются `target_orders`, вычисляемыми в `build_orders` и сравниваемыми с `amm_open_orders` при каждом `MonitorStep`. Любое расхождение приводит к отмене + новому выставлению. Недавно заполненные ордера на 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 на стороне coin; 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` coin (1,000 coin).

```
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` округляется вверх (чтобы пул не недополучил).
* `amount_in` для `SwapBaseOut` округляется вверх (чтобы пользователь не заплатил меньше).
* Пулы с экстремальными соотношениями резервов могут получить `ZeroTradingTokens` на очень малых входах; то же соглашение, что и в CPMM.

## Ограничения vs CPMM

* Резервы AMM v4 включают выделение на OpenBook, поэтому интегратор не может котировать правильно только от `getTokenAccountBalance`. Всегда получайте полное состояние (хранилища + `open_orders.free` + `open_orders.locked`), или используйте квотировку SDK / API.
* AMM v4 не предоставляет структурированный TWAP на цепи. Внешние потребители, которые хотят цену, поддерживаемую AMM v4, должны вычислить её сами из логов торговли.
* Token-2022 не поддерживается.

## Что дальше

* [`products/amm-v4/instructions`](/ru/products/amm-v4/instructions) — где включаются `SwapBaseIn`, `Deposit` и т. д.
* [`products/amm-v4/fees`](/ru/products/amm-v4/fees) — полная механика комиссий, детали `TakePnl`.
* [`algorithms/constant-product`](/ru/algorithms/constant-product) — общий вывод.

Источники:

* [Исходники программы Raydium AMM — `raydium-io/raydium-amm`](https://github.com/raydium-io/raydium-amm)
* Модуль `Liquidity` SDK Raydium v2
