Первый алерт на активность, который пишет большинство команд: «сообщи, если транзакций за час больше N». Или наоборот, «меньше N», то есть контракт затих. Это просто, и для большинства контрактов это неверно. Эта часть объясняет почему, чем заменить и где фиксированное число всё же правильный инструмент.
Она продолжает часть 10: там мы следили за значением, которое никогда не должно быть определённым числом. Здесь следим за значением, которое меняется всегда, и вопрос в том, что считать «необычным».
У контракта есть день и ночь
Ончейн-активность идёт за людьми. Кредитный пул, пара на DEX или игровой контракт загружены, когда пользователи не спят, и тихи, когда спят. Один и тот же час повторяется изо дня в день. В недельных данных большого кредитного пула в BNB Chain около 120 транзакций в час были нормой в 20:00 UTC, а около 500 — нормой в полдень1.
Теперь выберите одно число.
- Поставить под день. «Больше 500 в час» днём почти молчит. В 20:00 час должен вырасти вчетверо, чтобы сработало, и необычный вечер проходит незамеченным.
- Поставить под ночь. «Больше 170 в час» ловит скачок в 20:00 и срабатывает почти каждый час суток, потому что днём норма выше. Это уже не алерт, а журнал.
- Вариант «затих» («меньше 150») каждую ночь срабатывает на обычной тишине, а когда в полдень контракт падает с 500 до 200, не видит ничего.
Числа, которое верно и для полудня, и для 20:00, нет. Любой выбор меняет ночную слепоту на дневной шум.
Вторая половина проблемы тяжелее. Порог ставят один раз. Через квартал у контракта больше пользователей, норма сместилась, алерт срабатывает весь день. Команда его глушит, и с этого момента мониторинга нет. Так чаще всего и умирают алерты, и никакая настройка в первый день этого не предотвращает.
Идея: сравнивать час с тем же часом
Перестаньте сравнивать с константой. Сравнивайте каждый час с тем, каким этот час бывает обычно.
Для сегодняшних 20:00 берём значение в 20:00 за каждый из предыдущих семи дней. Их описывают два числа:
- обычное значение — их медиана;
- обычный разброс — медиана того, насколько каждое значение отстоит от медианы (MAD), умноженная на 1,4826, чтобы масштаб совпал со стандартным отклонением.
Правило срабатывает, когда сегодняшнее значение дальше от медианы больше чем на *k* разбросов. Граница получается медиана + k × 1,4826 × MAD. В полдень медиана высокая и полоса широкая. В 20:00 низкая и узкая. Порог идёт за часами и за контрактом: когда пул вырос, медианы следующей недели растут вместе с ним.
В SmartAlert это условие называется «отклонение от нормы». Это описанная формула, а не обученная модель: её можно пересчитать вручную по тем же данным. Мы считаем, что алерту, который можно проверить, доверяют, а алерт, которому не доверяют, глушат.
Почему медиана, а не среднее
Семидневное окно часто содержит то самое, что вы хотите поймать: прошлую аварию, эирдроп, взбесившегося бота. Среднее и стандартное отклонение этот день впитывают. Полоса расширяется, и следующий такой же инцидент выглядит нормой. Правило, выучившее инцидент, перестаёт видеть инциденты.
Медиана и MAD одного плохого дня из семи почти не замечают. Один выброс сильно двигает среднее и почти не двигает медиану, поэтому даже испорченная история даёт пригодную норму.
Нижняя граница разброса
На очень ровном ряду MAD бывает нулевым. Тогда любое отличие от медианы выглядит бесконечно необычным, и тихий контракт срабатывал бы на каждой транзакции. Поэтому у разброса есть нижняя граница в абсолютных единицах: большее из 1 и 10% медианы. В вечерний час из примера выше ширину полосы задаёт именно эта граница, а не данные.
Единственная ручка: чувствительность
*k* — это чувствительность, по умолчанию 3, в SmartAlert от 1 до 10. Меньше *k* — уже полоса и больше алертов. Больше *k* — меньше алертов, и нужен более крупный скачок.
Угадывать её не нужно. Помогают две вещи.
- Бэктест. До запуска правила посмотрите на метрику, полосу и точки, в которых правило сработало бы за прошлые дни. Двигайте *k*, пока отметки не станут теми, о которых вы хотели бы знать.
- От своей терпимости. Скажите «просыпаться готов не чаще двух раз в неделю», и инструмент подберёт *k*, при котором на выбранной истории будет не больше такого числа алертов. Свою терпимость к шуму человек называет легко. Сколько транзакций в час «нормально», он обычно не знает.
Ещё два параметра важны на практике.
- Направление. Для числа свопов скачок часто безразличен, а падение до нуля — инцидент. Выберите только «ниже», только «выше» или оба.
- Свой порог для часа. Если вы знаете, что в 03:00 UTC работает пакет кипера, можно задать для этого часа свой порог, и он заменит норму в этом часе.
Тот же час прошлых дней или тот же день недели
У недели тоже есть форма. DEX может быть тихим в воскресенье и оживлённым во вторник. Базу сравнения можно выбрать:
| База | С чем сравнивает | Точек |
|---|---|---|
| тот же час, прошлые 7 дней | тот же час за каждый из последних 7 дней | 7 |
| тот же час, прошлые 28 дней | тот же час за последние 28 дней | 28 |
| тот же день недели и час, прошлые 4 недели | например, воскресенье 20:00 за последние 4 воскресенья | 4 |
По умолчанию — семь дней. Она быстро подстраивается, но выходные сравниваются с буднями. База по дню недели это исправляет, но точек для сравнения всего четыре. Четыре меньше стандартного минимума в пять, поэтому выбирайте её вместе с меньшим минимумом.
Когда фиксированный порог — правильный инструмент
Норма не заменяет фиксированные числа. Она заменяет их там, где граница берётся из статистики. Фиксированное число верно, когда граница приходит извне:
- коэффициент обеспечения ниже 150%;
- баланс ниже суммы завтрашних выплат;
- комиссия выше записанной в договоре.
Здесь значение может месяцами стоять у опасной границы. Если взять «необычно для этого часа», опасная граница станет нормой, и правило замолчит ровно там, где должно кричать. Итак: граница из договора, кода или регламента — фиксированный порог. Граница «не как обычно» — норма.
Где норма пока не работает
- Нет истории — нет нормы. Норме нужны данные из прошлого: для базы за 7 дней — семь дней ряда. Пока их нет, правило сохранено, но не вычисляется, а интерфейс показывает дату, с которой норма появится. На этот период можно задать запасной порог.
- Запуск и быстрый рост. Если контракт запускается или идёт эирдроп, норма отстаёт, и правило говорит «выше нормы» час за часом. Поставьте правилу «Тишина до…» на 1, 3, 7 дней или до даты: оно продолжит считать, запишет срабатывания как «не отправлено: тишина» и включится само.
- Атаки, которые выглядят нормально. Эксплойт, двигающий $10M несколькими транзакциями, не меняет число транзакций загруженного контракта. Нормы активности ловят изменение частоты, а не одну транзакцию. Для этого нужен порог в долларах на выводе, а он по природе фиксированный.
Как поймать
- Для частоты берите норму. Рецепты transfer-spike, swap-spike и contract-went-quiet — это норма со средней чувствительностью: первые два срабатывают выше неё, последний ниже. В своём правиле выбирайте «ниже» для контракта, который не должен останавливаться, и «выше» для того, который не должны долбить.
- Бэктест до включения. Откройте правило на 30 днях истории, затем двигайте *k* или задайте число алертов в неделю, которое готовы терпеть.
- Для денежных границ берите фиксированные числа в долларах. large-transfer-usd, treasury-below-usd. Фиксированный и баланс кошелька, который платит: keeper-gas-runway уже переводит траты в дни запаса.
- Пересмотрите через неделю. Если правило срабатывает на каждом обычном часе, поднимите *k*. Если не срабатывает никогда, проверьте на бэктесте, что оно вообще могло.
Источники
- 1.SmartAlert, Aave V3 Pool в BNB Chain, транзакции в час, неделя октября 2026. Числа в тексте — типичный уровень каждого часа, округлённый. ↩