Eugen Soloviov
Инженер торговых систем
Разработка торговых ботов с 2017 года: межбиржевой арбитраж (подключал до 30 бирж), парный арбитраж на коинтеграции между спотом и фьючерсами, скальпинг, фронтраннинг, торговля по новостям, сентиментный анализ, трендовые алгоритмы, а также алгоритмы управления и балансировки портфелей. Делает выставление ордеров до 1 мс, warehouse для big data, бэктестинг-движки, AI-агентов и интерфейсы для ботов (в т.ч. open-source profitmaker.cc). Стек: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, архитектура.
Статьи
TWAP против VWAP против POV: выбор бенчмарка исполнения (и когда каждый из них вас обманывает)
TWAP, VWAP и POV — это ставки на прогноз объема. Разбираем скрытые допущения каждого планировщика, строим внутридневные кривые объема для крипты и сравниваем все три алгоритма на переигранных L2-данных.
Алмгрен-Чрисс без размахивания руками: оптимальное исполнение, которое можно реализовать за один вечер
Полный вывод модели оптимального исполнения Алмгрена-Чрисса: линейный импакт, траектория sinh/cosh, эффективная граница и рабочий Python для калибровки eta, gamma и sigma по данным стакана L2 и сделок Binance.
Честный негативный результат: десятки тысяч бэктестов, пять мажоров, никакого робастного edge
Финал арки про поиск и оверфиттинг — и он заканчивается негативным результатом, то есть правильным. Односимвольный поиск на ETHUSDT с двумя таймфреймами нашел конфигурацию на +16.35% out-of-sample и +2.62% на нетронутом holdout; Deflated Sharpe Ratio, учитывающий ~37,000 испытаний, дефлировал ее до 0.00. Кросс-инструментальный проход по пяти мажорам (ETH/BTC/SOL/BNB/XRP, ~1.18M минутных баров каждый) с отбором по медиане out-of-sample добивает окончательно: двойной таймфрейм DSR 0.24 / PBO 0.264, тройной DSR 0.14 / PBO 0.327 — оба не проходят гейты. Чемпион прибылен на 1 из 5 символов и убыточен на остальных. Именно для этого анти-оверфит аппарат и существует: чтобы не дать вам отгрузить лучший из шума как альфу.
Доказываем отсутствие look-ahead в мультитаймфреймовых бэктестах: возмущаем будущее, доказываем, что прошлое его не видит
Мультитаймфреймовые бэктесты пропускают будущее через формирующийся бар старшего таймфрейма, чей финальный close еще не существует. Уверенность нельзя получить одним лишь ревью кода — это надо тестировать. Мы воспроизводим правило closed-bar живого бота один в один, а затем доказываем отсутствие утечки с помощью пробы со сдвинутым будущим: возмущаем каждый будущий бар и проверяем, что каждый прошлый сигнал и сделка побитово не изменились. 25 из 25 проверок паритета, и проба не беззубая.
Когда GPU окупается: roofline перебора параметров, где заголовочные 167x — на самом деле 27x алгоритма, умноженные на 6.2x железа
Отрыв GPU от CPU растет с размером батча — от 54.5x при одной комбинации на вызов до 359.6x при 61 на нашем мультитаймфреймовом предрасчете индикаторов, — потому что маленький перебор не может амортизировать накладные расходы на запуск ядер и пересылку данных. Мы раскладываем заголовочные 167x на алгоритмический выигрыш 27x, который ускоряет и CPU, и аппаратный выигрыш 6.2x, показываем, что истинный отрыв GPU от лучшего CPU — лишь 3.2x на одном таймфрейме и 6.2x на нескольких, и даем руководство к решению: насколько широким должен быть перебор, чтобы GPU стоил вложений.
Ловушка точности GPU: как fp32-бэктест на Apple Metal незаметно возвращает мусор
GPU Apple Metal не имеет float64. Наивно перенесите на него векторизованный бэктест — и соблазнительная WMA на префиксных суммах переполняет fp32: максимальная относительная ошибка 211× — но при этом все отрабатывает и выдает правдоподобные числа. Решение — не в дополнительной точности, а в другой формуле: прямая оконная свертка, безопасная для fp32 с точностью до 8×10⁻⁷ и быстрее однопоточной numba в 55.9 раза. Ловушка, арифметика и как доказать, что вы в нее не попали.
Фиделити-гейт: поиск от грубого к точному обманывает быстрее, если дешевый прокси не ранжирует так же, как дорогой
Drill-down / мультификсиделити-поиск (ASHA, successive halving, Hyperband) дешево отсеивает тысячи конфигураций и продвигает к дорогой полной оценке только выживших. Это настоящее ускорение — но оно тихо ломается, если ранжирование на низкой фиделити расходится с ранжированием на высокой. Мы измерили корреляцию рангов по фолдам: на одном фолде спирменовское ρ может быть 0.03 (ранги почти случайны), поднимаясь до 0.43, 0.67, 0.78, 0.91 по мере накопления фолдов. Решение — один обязательный гейт: сначала измерить ρ(дешевое, полное), а затем автоматически поднять минимальную фиделити до первой ступени, где ρ ≥ 0.5.
Случайный поиск против умного: переломный момент задает стоимость оценки, а не алгоритм
Когда один бэктест дешев, тупой скремблированный Sobol выигрывает по чистой пропускной способности — умные сэмплеры (TPE, CMA-ES, ASHA) платят Python-налог на ask/tell, который замедляет их в 20 раз, поэтому при равном wall-clock они оценивают намного меньше точек и проигрывают. Сделайте каждую оценку дорогой (multi-TF + walk-forward фолды) — и точка пересечения переворачивается. Мы измерили оба режима и разобрались, почему fold-rank fidelity (ρ@1 растет с 0.03 до 0.43) — необходимое условие, чтобы прунинг окупался.
Двухосевое пространство параметров: почему большая часть вашего перебора должна быть почти бесплатной
Не все параметры одинаково дорого перебирать. Параметры стратегии делятся на дорогую ось (индикаторы — пересчитываются по всему ряду) и дешевую ось (пороги принятия решений — проход O(n) по уже посчитанным сигналам). Поскольку индикаторы не зависят от порогов, их можно посчитать один раз и перебрать тысячи конфигураций порогов на скорости ~5600 конфиг/с — примерно в 1600 раз дешевле, чем пересчитывать на каждую конфигурацию. Переоценка проклятия размерности.
Налог на фреймворк: когда ваша библиотека для бэктестов медленнее наивного цикла на pandas
Мы прогнали восемь движков для бэктестов через один и тот же перебор параметров — 150 тысяч баров, 80 комбинаций HMA-пересечения, паритет по числу сделок закреплен на 2707. Два самых популярных событийно-ориентированных фреймворка оказались медленнее, чем написанный вручную цикл на pandas, а векторизованный/компилируемый движок выполнил ту же работу примерно в 13 000 раз быстрее. Исследование накладных расходов на каждый бар, которые популярные библиотеки никогда не были рассчитаны амортизировать.
Probability of Backtest Overfitting: обыграл ли ваш поиск подбрасывание монеты?
Deflated Sharpe Ratio оценивает победившую стратегию; PBO оценивает сам поиск, который ее выбрал. Combinatorially Symmetric Cross-Validation прогоняет C(16,8) = 12,870 train/test-разбиений по матрице результатов 1000x200 и спрашивает: попадает ли in-sample победитель в нижнюю половину out of sample? Ловушка, которую почти все упускают, — ноль PBO равен 0.5, а не 1. На 200 стратегиях с нулевым edge лучший in-sample годовой Sharpe 1.98 схлопывается до 0.06 out of sample, а PBO = 0.476: подбрасывание монеты, полный оверфит. Заложите реальный edge (годовой Sharpe 2.38) — и PBO падает до 0.001, а in-sample 3.73 сохраняется до out-of-sample 2.34. У сетки moving average на чистом случайном блуждании тоже нет out-of-sample мастерства — PBO 0.463 в среднем по 60 матрицам, статистически неотличимо от нуля, — а на одной представительной матрице мираж особенно нагляден: лучший in-sample Sharpe 2.33 схлопывается до медианного out-of-sample -0.22, PBO 0.573, 63% шанс убытка.
IPC-налог: спрячьте бэктест-движок за сокетом и потеряйте 13% — при этом сокет почти ни при чем
Мы перенесли numba-ядро бэктеста построчно на Rust и вызывали его через границу процесса четырьмя способами, а проверка эквивалентности подтвердила идентичный PnL вплоть до последней сделки. Передача всего ценового ряда в 1.2 MB через Unix-сокет стоит ~2 мс — около 0.1% всей работы. JSON-кодирование того же payload стоит 1348x дороже, чем сырые байты, болтливые вызовы на каждую комбинацию пересылают данные заново 80 раз, а паттерн вызова на каждый бар обошелся бы в 2.1 с чистого IPC на задаче длиной 2.0 с. Граница дешева; налог — в том, как вы ее пересекаете.