Блог им. evgeniym_956
Пишу сканер аномальных объёмов по MOEX. Логика простая: считаем норму объёма
для инструмента, сравниваем с текущим, превышение — сигнал. Всё работало.
Сигналы приходили, ложных было немного, я был доволен.
Потом полез в логи разбираться, почему на очевидном движении не было сигнала.
И выяснил, что система его видела. Посчитала. И сама же выбросила.
## Почему EMA казалась хорошей идеей
Норму я считал через экспоненциальную скользящую по объёму. Аргумент был
железный: рынок меняется, режимы меняются, статистика за 90 дней устаревает.
EMA подстраивается под текущий режим — если объёмы выросли в целом, порог
поднимется сам, и мы не завалим канал сигналами на ровном месте.
Звучит разумно. Так делают многие. Это и есть ошибка.
## В чём проблема
EMA обновляется той же свечой, с которой вы её сравниваете.
Прилетает аномальный объём. Система его фиксирует, но этой же свечой EMA идёт
вверх. Следующая аномальная свеча сравнивается уже с задранной нормой —
превышение меньше. Третья ещё меньше. К четвёртой-пятой свече EMA догнала
аномалию, и всплеск стал новой нормой.
Детектор аномалий адаптировался к аномалии. Ровно то, чего он делать не должен.
Особенно больно это било по самому интересному сценарию — растянутому входу
крупного объёма. Разовый выброс на одной минуте система ловила. А набор позиции
в течение дня — нет.
## OZON, 30 июля, часовой таймфрейм
Вот как это выглядит на живых данных. Оба столбца — сырые значения из журнала
сканера за один торговый день. Слева объём на момент замера, справа норма,
с которой система его сравнивала.
```
время оборот норма
07:38 974 млн ₽ 196 млн ₽
09:43 650 млн ₽ 296 млн ₽
10:54 1322 млн ₽ 343 млн ₽
11:53 1081 млн ₽ 411 млн ₽
12:42 930 млн ₽ 458 млн ₽
17:52 728 млн ₽ 510 млн ₽
19:43 750 млн ₽ 747 млн ₽
```
Две оговорки, чтобы цифры можно было сверить. Это рублёвый оборот, а не число
бумаг: в терминале тот же час покажет сотни тысяч акций. И это оборот на момент
замера, а не за час целиком — сканер не ждёт закрытия свечи, иначе сигнал
приходил бы с опозданием до часа. Так что с закрытым часовым баром в терминале
эти числа сойдутся только по порядку величины.
Смотрите не на левый столбец, а на правый.
Объём весь день держится в диапазоне 650–1300 млн ₽. Он не растёт и не падает,
крупный игрок ровно работает с позицией с утра до вечера.
А норма за этот же день выросла со 196 до 747 млн. Почти вчетверо. К вечеру она
догнала объём вплотную — 750 против 747, — и последний замер система засчитала
как совершенно обычный день.
Утренняя норма 196 млн накоплена за предыдущие дни, до начала истории. То есть
объём в 750 млн вечером был выше обычного почти в четыре раза. Система этого
уже не видела. Не потому, что рынок успокоился, — потому что она к нему привыкла.
Старый порог на часовике стоял на +200%. Из семи замеров его прошли два: утренний
и в 10:54. Причём не подряд — между ними замер, который до порога не дотянул.
Вместо растянутой аномалии на весь день система показала два разрозненных
всплеска.
## Почему это не заметно
Худший класс бага. Не падение, не исключение в логе. Система работает, отвечает,
сигналы шлёт — просто не те. Причём фильтр не был «слишком строгим»: строгий
режет всё подряд, и ложные срабатывания тоже, по ним проблему видно. Этот резал
избирательно. Пропускал ровно то, ради чего система писалась, и почти не давал
взамен ложных, по которым можно было бы спохватиться.
Такое не находится, пока не пойдёшь проверять руками конкретный случай, про
который точно знаешь ответ.
## Что теперь
Норма считается через статистику Уэлфорда — среднее и дисперсия обновляются
одним проходом, всю историю хранить не нужно.
Ключевое: **на коротких таймфреймах норма для 10:00 строится только из данных
за 10:00 других дней.** Она не может подтянуться к сегодняшнему всплеску, потому
что сегодняшний всплеск в неё не входит. Он попадёт в выборку завтра,
разбавленный всей накопленной историей. На 4h и 1d разбивки по часам нет —
там считается общее среднее, глубина истории своя для каждого таймфрейма.
EMA осталась, но в роли мягкого пола — отсекать шум на неликвиде, где статистика
по часам сама по себе разболтанная.
## Мораль, если она нужна
Адаптивная норма и детектор аномалий — противоречащие друг другу вещи. Чем
быстрее ваш baseline подстраивается под рынок, тем меньше событий он считает
необычными. В пределе идеально адаптивная норма не находит аномалий вообще:
для неё всё нормально.
Если у вас в системе что-то считается «относительно скользящего среднего» —
проверьте, не обновляется ли это среднее тем же значением, с которым вы его
сравниваете.
Сигналы выкладываю в @Volume_Scanner_MOEX, если интересно посмотреть на результат.
Кто как решает эту задачу? Интересно, есть ли подход лучше, чем разделение
выборки по времени суток.