База финансов: рынок, ликвидность и риск
Предыдущий блок закончился на том, что доверие никуда не исчезает, а переезжает. Начинается самая денежная часть раздела: мы строим биржу. Но перед первой строкой контракта нужно разобраться, что вообще происходит на рынке, иначе получится красивый код, который теряет деньги на каждой сделке. Этот урок без блокчейна: рынок, ликвидность, маркетмейкер, плечо и опцион. Дальше всё это будет записано в контракте.
Рынок это механизм сведения встречных желаний
Начнём с простого наблюдения. Один хочет продать, другой хочет купить, и оба ищут цену, при которой согласны оба. Всё остальное в бирже это ответ на вопрос, как свести их быстро, честно и с понятными правилами.
Исторически способов было два.
Аукцион по времени. Все заявки копятся, в назначенный момент считается одна цена, при которой сойдётся максимальный объём, и все сделки проходят по ней. Так до сих пор открываются и закрываются классические биржи.
Непрерывный двойной аукцион. Заявки стоят в очереди и исполняются, как только встретили встречную по подходящей цене. Так работают биржи в течение дня, так работает ордербук-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.
| Заявка | Исполнено | Средняя цена | Проскальзывание |
|---|---|---|---|
| 5 | 5 | 101.00 | 0 bps |
| 15 | 15 | 101.67 | 66 bps |
| 20 | 20 | 102.50 | 149 bps |
| 50 | 50 | 104.00 | 297 bps |
Разница между ценой, которую ты видел, и ценой, которую получил, называется проскальзыванием. Пятьдесят штук на этом рынке стоят почти на три процента дороже, чем показывала лучшая цена. Рынок ликвиден для пяти штук и неликвиден для пятидесяти, хотя рынок один и тот же.
Отсюда правило, которое пригодится нам в проекте: размер сделки это часть цены. Любой расчёт доходности, который берёт лучшую цену и умножает на объём, врёт в свою пользу.
премиум-урок
Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.
Оплата пока не подключена: премиум и курсы открываются промокодом.