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

# Математика Stable AMM

> Бинарный поиск и линейная интерполяция для получения цен из таблицы поиска. Применение комиссий идентично AMM v4. Чистый учёт пула AMM после отделения от OpenBook.

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

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

## Кривая таблицы поиска

Stable AMM заменяет формулу x·y=k на **разреженную таблицу поиска** кортежей (x, y, price). При определении цены свопа программа:

1. Вычисляет текущее соотношение пула из резервов.
2. **Выполняет бинарный поиск** в таблице, чтобы найти две записи, которые охватывают это соотношение.
3. **Линейно интерполирует** между ними, чтобы получить промежуточную цену.
4. Применяет комиссии и возвращает котировку.

Этот подход обменивает детерминизм формулы на **гибкость администратора** в формировании цены, и он достаточно эффективен, чтобы уместиться в бюджет вычислений Solana.

## Макет таблицы и бинарный поиск

`ModelDataInfo` содержит до 50 000 записей `DataElement`, индексируемых администратором. Активны только первые `valid_data_count`. Каждая запись:

```
DataElement {
  x: u64,      // X координата (масштабированная сумма на стороне монеты)
  y: u64,      // Y координата (масштабированная сумма на стороне ПК)
  price: u64,  // price = x/y, масштабировано множителем
}
```

Чтобы найти цену при текущих резервах пула (x\_real, y\_real):

1. Вычислите соотношение: `target_ratio = (x_real * multiplier) / y_real`.
2. Выполните бинарный поиск записей, где `(element.x * multiplier) / element.y` охватывает `target_ratio`.
3. Когда найден диапазон `[min_idx, max_idx]`, выполните интерполяцию.

Код бинарного поиска программы занимает \~150 строк в `state.rs::ModelDataInfo::get_mininum_range_by_xy_real`. Ключевой инвариант: **записи должны быть отсортированы** (x по возрастанию, y по убыванию, price по возрастанию) для работы поиска.

## Линейная интерполяция

Когда две точки таблицы охватывают соотношение, интерполяция вычисляет промежуточную цену и пару резервов:

```
target = (x_real * multiplier) / y_real

[x1, y1, p1] = table[min_idx]
[x2, y2, p2] = table[max_idx]

// Интерполяция цены
p = p1 + (p2 - p1) * (target - ratio1) / (ratio2 - ratio1)

// Интерполяция резерва
x = x1 + (x2 - x1) * (target - ratio1) / (ratio2 - ratio1)
y = y1 + (y2 - y1) * (target - ratio1) / (ratio2 - ratio1)
```

Результат — кусочно-линейная кривая, которая плавно соединяет точки таблицы.

## Масштабирование: множитель

Резервы пула и цены хранятся в разных масштабах. Поле `multiplier` на `ModelDataInfo` это учитывает. Типичный паттерн:

* Монета имеет 6 десятичных знаков, ПК имеет 18 десятичных знаков.
* Множитель = 10^6 (или аналогично).
* Записи таблицы хранятся в уменьшенном масштабе, чтобы соответствовать границам u64.

Программа перемасштабирует при чтении/записи через:

```
real_value = table_value * ratio / multiplier
table_value = real_value * multiplier / ratio
```

## Определение цены свопа: `SwapBaseIn` и `SwapBaseOut`

### SwapBaseIn (точный ввод)

Дан входной объём `amount_in`:

1. Получите текущее соотношение из `(coin_vault, pc_vault)`.
2. Найдите охватывающие записи таблицы и интерполируйте, чтобы получить соотношение в пространстве таблицы.
3. Преобразуйте ввод в пространство таблицы: `dx_table = amount_in * multiplier / ratio`.
4. Запросите таблицу по новой X-координате, чтобы найти новую Y.
5. `dy_table = y_old - y_new`.
6. Преобразуйте обратно: `dy_real = dy_table * ratio / multiplier`.
7. Примените торговую комиссию: `dy_output = dy_real - (dy_real * trade_fee_numerator / trade_fee_denominator)`.
8. Верните `dy_output`.

### SwapBaseOut (точный вывод)

Симметрично: дан желаемый `amount_out`, решите для требуемого `amount_in`.

Оба пути читают эффективные резервы непосредственно из хранилищ пула. Пул не держал открытые ордера OpenBook годами, поэтому нечего расчищать в первую очередь — балансы хранилища — это вся история. (Обновление [2026-06-22](/ru/reference/changelog) удалило оставшийся код маркета.)

## Применение комиссий

Идентично AMM v4: см. [`products/amm-v4/math`](/ru/products/amm-v4/math) для полного вывода.

```
gross_fee = amount_in * (swap_fee_numerator / swap_fee_denominator)    // например, 0.25%
lp_portion = gross_fee - (gross_fee * pnl_numerator / pnl_denominator) // например, 0.22%
pnl_portion = gross_fee * (pnl_numerator / pnl_denominator)            // например, 0.03%
```

`pnl_portion` идёт в `need_take_pnl_*` и выводится администратором через `WithdrawPnl`. `lp_portion` остаётся в хранилище, увеличивая `k` и принося пользу держателям токенов LP.

## Учёт активов пула

Формула исторически добавляла средства, которые пул держал как открытые ордера в своём счёте OpenOrders OpenBook. Этот термин был нулевым на практике с тех пор, как пул перестал размещать ордера, и обновление 2026-06-22 полностью удалило его из формулы, оставив только расчёт на основе хранилища:

```
Старое: total assets = vault balances + open-order funds (native_coin_total / native_pc_total) − pending PnL (need_take_pnl)
Новое: total assets = vault balances − pending PnL (need_take_pnl)
```

Это значение, которое математика кривой рассматривает как эффективные резервы (накопленная, но не выведенная часть `need_take_pnl` физически находится в хранилище, но исключена из определения цены). Код котировок и индексаторы, которые ранее читали балансы OpenOrders, должны отбросить этот термин.

## MonitorStep (удалено)

`MonitorStep` была **инструкцией крана**, которая расчищала ожидающие заполнения OpenBook, пересчитывала `AmmInfo.target_orders` и переразмещала сетку лимитных ордеров, полученную из таблицы поиска. Пул перестал размещать ордера в OpenBook годами назад, поэтому кран больше не имел ничего делать; он был **удалён в обновлении 2026-06-22**. Интеграторам не нужно запускать кран для пулов Stable.

## Резюме: почему это работает

Дизайн таблицы поиска + интерполяция **эффективен и гибок**:

* **Эффективность:** Бинарный поиск — это O(log 50,000) ≈ 16 итераций, каждая \~ 300–500 CU. Интерполяция — это несколько умножений/делений. Общая стоимость котировки \~ 5k–15k CU, намного дешевле, чем пересчёт формулы при каждом свопе.
* **Гибкость:** Администратор может закодировать любую кусочно-линейную кривую. Пары стейблкойнов получают высокую плотность около 1:1; обеспеченные пары получают пользовательские кривые.
* **Самодостаточная ликвидность:** Все средства находятся в хранилищах пула, и определение цены читает их напрямую — без крана, без внешней книги ордеров, меньше счётов за транзакцию.

Для глубокого погружения в логику интерполяции см. `raydium-stable/program/src/state.rs`, методы `get_data_by_x`, `get_data_by_y`, `get_dy_by_dx_base_in` и т. д.

## Что дальше

* [Accounts](/ru/products/stable/accounts) — справочник полей `ModelDataInfo` и `DataElement`.
* [Instructions](/ru/products/stable/instructions) — вызываемый набор (swap, deposit, withdraw, `WithdrawPnl`) и удалённые инструкции.
* [Fees](/ru/products/stable/fees) — применение комиссий и `WithdrawPnl`.
* [`products/amm-v4/math`](/ru/products/amm-v4/math) — для логики определения цены ордеров OpenBook с учётом комиссий.

Источники:

* `raydium-stable/program/src/state.rs` (реализации интерполяции и бинарного поиска)
* `raydium-stable/program/src/math.rs` (утилиты калькулятора)
