Защита маркетмейкера (MMP)
Лимиты исполнений в окне и автоматическая отмена заявок для маркетмейкеров.
Все эндпоинты MMP, привязанные к кошельку, требуют аутентификации EIP-712. Запись конфигурации принимает подпись владельца кошелька или авторизованного агента этого кошелька. Чтение доступно только владельцу, подписи авторизованных агентов отклоняются.
MMP применяется к торговому аккаунту, которому принадлежат исполнения. Для менеджера
с портфельной маржой чтение и запись, подписанные кошельком менеджера, относятся к его
торговому аккаунту Account.sol, то есть к тому же аккаунту, через который выставляются заявки.
В ответах этот аккаунт указан в поле wallet_address, и последовательность nonce MMP
привязана именно к нему.
Чтобы гарантировать чтение после записи, передавайте X-Hypercall-Fence-Wait: 1 в любом
изменяющем запросе MMP. Тогда успешный ответ содержит X-Hypercall-Fence; передайте это
значение в заголовке X-Hypercall-Fence в аутентифицированном GET-запросе. Изменяющие запросы без
заголовка ожидания fence намеренно не возвращают.
Обзор
MMP защищает маркетмейкеров от серии быстрых неблагоприятных исполнений:
- Отслеживает накопленные показатели исполнений в скользящем временном окне
- Отменяет заявки при превышении лимитов
- Автоматически отменяет все остальные заявки с включённым MMP для той же пары кошелёк+базовый актив
Конфигурация
Установка конфигурации MMP
Эндпоинт: POST /mmp-config
Запрос: SetMmpConfigRequest
{
"wallet": "0x...",
"currency": "BTC",
"interval_ms": 60000,
"frozen_time_ms": 300000,
"qty_limit": "1000000",
"delta_limit": "10.0",
"vega_limit": "5.0",
"nonce": 1,
"signature": "0x..."
}
Поля:
wallet: адрес кошелькаcurrency: базовый актив (например,"BTC","ETH")interval_ms: длина скользящего окна в миллисекундахfrozen_time_ms: длительность заморозки после срабатывания (в миллисекундах)qty_limit: необязательный лимит по объёму (в контрактах), передаётся десятичной строкойdelta_limit: необязательный лимит по дельте, передаётся десятичной строкойvega_limit: необязательный лимит по веге, передаётся десятичной строкойnonce: nonce для защиты от повторного воспроизведения; ровно на единицу больше последнего зафиксированного nonce, который возвращает аутентифицированное чтениеsignature: подпись EIP-712SetMmpConfigпо всем полям запроса
Ответ: ApiResponse<MmpConfigData>
Получение конфигурации MMP
Эндпоинт: GET /mmp-config?wallet=...¤cy=...
Параметры запроса:
wallet(обязательный)currency(необязательный фильтр)
Обязательные заголовки:
X-Hypercall-Expires-At-Ms: время истечения в миллисекундах Unix, не более чем на 60 секунд вперёдX-Hypercall-Signature: подписьGetMmpConfigпоwallet,currency(пустая строка, если параметр не передан) и времени истечения из заголовка
Восстановленный подписант должен быть владельцем кошелька. Подписи авторизованных агентов для этого чтения не принимаются.
Ответ: MmpConfigResponse
{
"success": true,
"data": [
{
"wallet_address": "0x...",
"currency": "BTC",
"interval_ms": 60000,
"frozen_time_ms": 300000,
"qty_limit": "1000000",
"delta_limit": "10.0",
"vega_limit": "5.0",
"enabled": true,
"nonce": 1
}
]
}
Удаление конфигурации MMP
Эндпоинт: DELETE /mmp-config
Запрос: DeleteMmpConfigRequest
{
"wallet": "0x...",
"currency": "BTC",
"nonce": 1,
"signature": "0x..."
}
Ответ: ApiResponse<String>. Успешная команда отключает числовые параметры
в авторитетном состоянии для пары кошелёк/базовый актив. При заголовке запроса
X-Hypercall-Fence-Wait: 1 ответ содержит X-Hypercall-Fence для
чтения из проекции.
Сброс состояния MMP
Эндпоинт: POST /mmp-config/reset
Запрос: ResetMmpRequest
{
"wallet": "0x...",
"currency": "BTC",
"nonce": 1,
"signature": "0x..."
}
Ответ: ApiResponse<String>. Успешная команда очищает окно исполнений MMP
и состояние заморозки в воспроизводимом авторитетном состоянии. При заголовке запроса
X-Hypercall-Fence-Wait: 1 ответ содержит X-Hypercall-Fence для
чтения из проекции.
Поведение MMP (важно понимать)
Какие исполнения учитываются
Каждое исполнение учитывается в окне MMP каждой стороны, у заявки которой
mmp_enabled=true, независимо от того, стояла ли заявка в книге (мейкер) или
пришла в неё (тейкер):
- Стоящие котировки: когда входящая заявка исполняется против вашей стоящей заявки с включённым MMP, это исполнение добавляется в ваше окно. Ради этого MMP и существует: серия исполнений по вашим котировкам срабатывает MMP и отменяет оставшиеся котировки с включённым MMP по этому базовому активу.
- Входящие заявки: заявка с включённым MMP, забирающая ликвидность, учитывает свои исполнения так же.
- Комбинации и подразумеваемые заявки: каждый защищённый участник пакета учитывает все ноги, которые он торгует, с одной проверкой на пакет.
- Исполнения RFQ и RPI пока не учитываются в MMP. При этом заморозка MMP блокирует новые исполнения RFQ для замороженного поставщика котировок.
Каждое исполнение оценивается с вашей стороны: продажа колла добавляет отрицательную дельту в окно продавца и положительную в окно покупателя.
Если у заявки mmp_enabled=true, но для её кошелька нет конфигурации MMP
по этому базовому активу, MMP срабатывает в безопасную сторону: первое же исполнение вызывает срабатывание и отменяет
заявки этого кошелька с включённым MMP по базовому активу. Настройте MMP до того, как помечать
заявки.
Порядок обработки исполнений
MMP проверяется ПОСЛЕ исполнения:
- Движок сопоставляет заявки и сначала принимает исполнение
- Затем MMP проверяет накопленные показатели для каждой защищённой стороны исполнения
- Если у стороны превышены лимиты:
- MMP срабатывает для кошелька и базового актива этой стороны
- Движок генерирует событие
MmpTriggered - Движок автоматически отменяет все открытые заявки с включённым MMP для этой пары кошелёк+базовый актив
- Если лимит превысила входящая заявка, исполнение её остатка
прекращается, и она получает статус
CANCELEDс причиной"MMP triggered during fill processing" - Если лимит превысил стоящий мейкер, входящая заявка продолжает исполняться против другой ликвидности; остальные котировки мейкера с включённым MMP к этому моменту уже отменены
Следствие: до срабатывания MMP вы можете получить исполнения, которые выведут вас за настроенные пороги.
Условия срабатывания
MMP срабатывает при превышении любого из настроенных лимитов:
- Лимит по объёму:
cumulative_qty > qty_limit - Лимит по дельте:
|cumulative_delta| > delta_limit - Лимит по веге:
|cumulative_vega| > vega_limit
Накопленные показатели считаются по скользящему окну (interval_ms).
Отмена заявок
При срабатывании MMP:
- Входящая заявка, вызвавшая срабатывание: исполнение остатка прекращается, заявка получает статус
CANCELEDс причиной"MMP triggered during fill processing" - Стоящая заявка, вызвавшая срабатывание: исполнение остаётся в силе; остаток заявки, если он есть, отменяется вместе с другими заявками с включённым MMP
- Остальные заявки: все открытые заявки с включённым MMP для той же пары кошелёк+базовый актив автоматически отменяются с причиной
"Order canceled by MMP trigger"
Примечание: отменяются только заявки с включённым MMP. Заявки с mmp_enabled=false не затрагиваются.
Заморозка
После срабатывания MMP:
- Кошелёк и базовый актив замораживаются на
frozen_time_msмиллисекунд - Во время заморозки кошелёк исключается из аукционов RPI и RFQ по этому базовому активу, а исполнения комбинаций или подразумеваемых заявок с его участием отклоняются
- Прямое исполнение заявки с включённым MMP во время заморозки остаётся в силе, после чего MMP срабатывает снова и отменяет заявки этого кошелька с включённым MMP по базовому активу
POST /mmp-config/resetдосрочно снимает заморозку
Расчёт показателей исполнений
Объём
- Источник:
sizeисполнения (в контрактах) - Накопленное значение: сумма всех исполнений в скользящем окне
Дельта
- Источник: рассчитывается по символу исполнения и GreeksCache
- Накопленное значение: сумма дельт всех исполнений в скользящем окне
Вега
- Источник: рассчитывается по символу исполнения и GreeksCache
- Накопленное значение: сумма вег всех исполнений в скользящем окне
Скользящее окно
Вытеснение из окна
Исполнения старше interval_ms автоматически вытесняются из окна:
- Вытеснение выполняется при обработке следующего исполнения, до добавления нового
- При удалении старых исполнений накопленные показатели уменьшаются
Сброс окна
Сброс состояния MMP через POST /mmp-config/reset:
- Очищает все исполнения в окне
- Обнуляет накопленные показатели
- Снимает заморозку с базового актива
События MMP
Событие MmpTriggered
Внутреннее событие (пока не передаётся клиентам WS):
MmpTriggeredMessage {
wallet: WalletAddress,
currency: String,
reason: String, // "MMP triggered during fill processing" | "MMP triggered by a resting order fill"
timestamp: u64,
}
Рекомендации
Конфигурация
- Выставляйте адекватные лимиты: исходя из вашей толерантности к риску и типичного размера исполнений
- Настраивайте конфигурацию по каждому активу: отдельно для каждого базового актива (BTC, ETH и т. д.)
- Следите за срабатываниями MMP: частые срабатывания могут говорить о неверно настроенных лимитах или неблагоприятной рыночной конъюнктуре
Управление заявками
- Включайте MMP выборочно: ставьте
mmp_enabled=trueтолько для заявок, которые нужно защитить - Используйте одинаковый актив: убедитесь, что все заявки по одному базовому активу соответствуют активу в конфигурации MMP
- Обрабатывайте отмены по MMP: реализуйте логику, которая распознаёт отмены по срабатыванию MMP и корректирует котирование
Мониторинг
- Отслеживайте срабатывания MMP: следите за причинами
"MMP triggered during fill processing"и"Order canceled by MMP trigger" - Анализируйте окна исполнений: накопленные показатели окна пока не доступны через API; ведите учёт собственных исполнений по каждому базовому активу
- Корректируйте лимиты: если MMP срабатывает слишком часто, повышайте лимиты; если слишком редко, снижайте
Типичные проблемы
MMP срабатывает слишком часто
Причины:
- Лимиты слишком низкие для типичного размера исполнений
- Неблагоприятная рыночная конъюнктура
- Частое котирование приводит к большому числу исполнений
Решения:
- Повысьте
qty_limit,delta_limitилиvega_limit - Увеличьте
interval_ms, чтобы окно вмещало больше исполнений - Снизьте частоту котирования
MMP не срабатывает
Причины:
- Лимиты слишком высокие
- MMP не включён в заявках (
mmp_enabled=false) - Несовпадение актива (актив в конфигурации MMP != базовый актив заявки)
Решения:
- Снизьте лимиты
- Проверьте, что в заявках стоит
mmp_enabled=true - Проверьте, что актив в конфигурации MMP совпадает с базовым активом заявки
Неожиданные отмены
Симптом: заявки отменены с причиной "Order canceled by MMP trigger", хотя вы этого не ожидали.
Причина: MMP сработал из-за другой заявки той же пары кошелёк+базовый актив.
Решение: при срабатывании проверяйте все заявки с включённым MMP по этому базовому активу.