Раздел 30 · Web3 на Rust

База финансов: рынок, ликвидность и риск

senior~55 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

База финансов: рынок, ликвидность и риск

Предыдущий блок закончился на том, что доверие никуда не исчезает, а переезжает. Начинается самая денежная часть раздела: мы строим биржу. Но перед первой строкой контракта нужно разобраться, что вообще происходит на рынке, иначе получится красивый код, который теряет деньги на каждой сделке. Этот урок без блокчейна: рынок, ликвидность, маркетмейкер, плечо и опцион. Дальше всё это будет записано в контракте.

Рынок это механизм сведения встречных желаний

Начнём с простого наблюдения. Один хочет продать, другой хочет купить, и оба ищут цену, при которой согласны оба. Всё остальное в бирже это ответ на вопрос, как свести их быстро, честно и с понятными правилами.

Исторически способов было два.

Аукцион по времени. Все заявки копятся, в назначенный момент считается одна цена, при которой сойдётся максимальный объём, и все сделки проходят по ней. Так до сих пор открываются и закрываются классические биржи.

Непрерывный двойной аукцион. Заявки стоят в очереди и исполняются, как только встретили встречную по подходящей цене. Так работают биржи в течение дня, так работает ордербук-DEX, и именно эту модель мы будем реализовывать в уроке 49.

Третий способ появился уже в крипте: цену считает формула, а контрагентом выступает общий пул. Это AMM, и ему посвящены уроки 46 и 47. Сначала общие понятия, они одинаковы для всех трёх механизмов.

Стакан: очередь намерений

Стакан хранит два вида заявок.

Лимитная заявка говорит: купи мне 10 штук, но не дороже 101. Если сейчас никто столько не просит, заявка встаёт в очередь и ждёт. Она даёт ликвидность: другие смогут исполниться о неё.

Рыночная заявка говорит: купи 10 штук по любой доступной цене прямо сейчас. Она забирает ликвидность: съедает чужие лимитные заявки.

Отсюда два имени участников. Тот, кто стоит в стакане, это мейкер. Тот, кто исполняется об него, это тейкер. Мейкер платит меньше и ждёт, тейкер платит больше и получает сразу. Это первая развилка любой торговли: скорость против цены.

Цена: не одно число, а как минимум три

Спроси, какая сейчас цена, и честный ответ будет: смотря для чего.

  • Лучшая покупка (bid): максимум, который кто-то готов заплатить.
  • Лучшая продажа (ask): минимум, за который кто-то готов отдать.
  • Середина (mid): среднее между ними, удобная условность.

Расстояние между bid и ask это спред, и это плата за мгновенность. Купил по ask, тут же продал по bid: минус спред, при том что рынок не двинулся.

Есть и четвёртая цена, которую знают не все. Если на покупку стоит гора, а на продажу почти ничего, следующая сделка вероятнее пройдёт вверх. Взвешивание середины объёмами противоположных сторон даёт микроцену:

/// Двусторонняя котировка: лучшая покупка и лучшая продажа с их объёмами.
pub struct Quote {
    pub bid: f64,
    pub ask: f64,
    pub bid_size: f64,
    pub ask_size: f64,
}

impl Quote {
    pub fn mid(&self) -> f64 {
        (self.bid + self.ask) / 2.0
    }

    /// Спред в базисных пунктах от середины: так спреды разных инструментов
    /// становятся сравнимыми.
    pub fn spread_bps(&self) -> f64 {
        (self.ask - self.bid) / self.mid() * 10_000.0
    }

    /// Микроцена: середина, взвешенная объёмами с обратным весом.
    pub fn microprice(&self) -> f64 {
        let total = self.bid_size + self.ask_size;
        if total == 0.0 {
            return self.mid();
        }
        (self.bid * self.ask_size + self.ask * self.bid_size) / total
    }
}

Обрати внимание на веса: цена покупки умножается на объём продажи, и наоборот. Это не опечатка. Большой объём на покупку означает, что пробить эту сторону трудно, значит цена скорее уйдёт в другую, и оценка смещается туда.

Спред меряем в базисных пунктах, а не в деньгах: только так спред акции за 3000 рублей сравним со спредом токена за четыре цента.

Ликвидность это не объём, а глубина

Слово «ликвидность» звучит на каждом углу, но полезное определение только одно: сколько ты можешь купить или продать, не сдвинув цену сильно. Это свойство не одного числа, а всей лестницы заявок.

Проверить просто: пройти стакан своей заявкой и посчитать среднюю цену.

pub struct BookLevel {
    pub price: f64,
    pub size: f64,
}

/// Пройти стакан заявкой на `quantity`.
/// Уровни обязаны идти от лучшей цены к худшей.
pub fn walk_book(levels: &[BookLevel], quantity: f64) -> Option<Execution> {
    let best = levels.first()?.price;
    let mut left = quantity;
    let mut notional = 0.0;
    for level in levels {
        if left <= 0.0 {
            break;
        }
        let taken = left.min(level.size);
        notional += taken * level.price;
        left -= taken;
    }
    let filled = quantity - left;
    if filled <= 0.0 {
        return None;
    }
    let vwap = notional / filled;
    Some(Execution {
        filled,
        vwap,
        slippage_bps: (vwap - best).abs() / best * 10_000.0,
    })
}

Возьмём стакан из трёх уровней: 5 штук по 101, 10 штук по 102, 50 штук по 105.

ЗаявкаИсполненоСредняя ценаПроскальзывание
55101.000 bps
1515101.6766 bps
2020102.50149 bps
5050104.00297 bps

Разница между ценой, которую ты видел, и ценой, которую получил, называется проскальзыванием. Пятьдесят штук на этом рынке стоят почти на три процента дороже, чем показывала лучшая цена. Рынок ликвиден для пяти штук и неликвиден для пятидесяти, хотя рынок один и тот же.

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

премиум-урок

Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.

Оплата пока не подключена: премиум и курсы открываются промокодом.