Eugen Soloviov

Eugen Soloviov

Инженер торговых систем

Разработка торговых ботов с 2017 года: межбиржевой арбитраж (подключал до 30 бирж), парный арбитраж на коинтеграции между спотом и фьючерсами, скальпинг, фронтраннинг, торговля по новостям, сентиментный анализ, трендовые алгоритмы, а также алгоритмы управления и балансировки портфелей. Делает выставление ордеров до 1 мс, warehouse для big data, бэктестинг-движки, AI-агентов и интерфейсы для ботов (в т.ч. open-source profitmaker.cc). Стек: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, архитектура.

Статьи

Честный негативный результат: десятки тысяч бэктестов, пять мажоров, никакого робастного edge

Честный негативный результат: десятки тысяч бэктестов, пять мажоров, никакого робастного 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 в мультитаймфреймовых бэктестах: возмущаем будущее, доказываем, что прошлое его не видит

Доказываем отсутствие look-ahead в мультитаймфреймовых бэктестах: возмущаем будущее, доказываем, что прошлое его не видит

Мультитаймфреймовые бэктесты пропускают будущее через формирующийся бар старшего таймфрейма, чей финальный close еще не существует. Уверенность нельзя получить одним лишь ревью кода — это надо тестировать. Мы воспроизводим правило closed-bar живого бота один в один, а затем доказываем отсутствие утечки с помощью пробы со сдвинутым будущим: возмущаем каждый будущий бар и проверяем, что каждый прошлый сигнал и сделка побитово не изменились. 25 из 25 проверок паритета, и проба не беззубая.

Когда GPU окупается: roofline перебора параметров, где заголовочные 167x — на самом деле 27x алгоритма, умноженные на 6.2x железа

Когда 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: как 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

Налог на фреймворк: когда ваша библиотека для бэктестов медленнее наивного цикла на pandas

Мы прогнали восемь движков для бэктестов через один и тот же перебор параметров — 150 тысяч баров, 80 комбинаций HMA-пересечения, паритет по числу сделок закреплен на 2707. Два самых популярных событийно-ориентированных фреймворка оказались медленнее, чем написанный вручную цикл на pandas, а векторизованный/компилируемый движок выполнил ту же работу примерно в 13 000 раз быстрее. Исследование накладных расходов на каждый бар, которые популярные библиотеки никогда не были рассчитаны амортизировать.

Probability of Backtest Overfitting: обыграл ли ваш поиск подбрасывание монеты?

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% — при этом сокет почти ни при чем

IPC-налог: спрячьте бэктест-движок за сокетом и потеряйте 13% — при этом сокет почти ни при чем

Мы перенесли numba-ядро бэктеста построчно на Rust и вызывали его через границу процесса четырьмя способами, а проверка эквивалентности подтвердила идентичный PnL вплоть до последней сделки. Передача всего ценового ряда в 1.2 MB через Unix-сокет стоит ~2 мс — около 0.1% всей работы. JSON-кодирование того же payload стоит 1348x дороже, чем сырые байты, болтливые вызовы на каждую комбинацию пересылают данные заново 80 раз, а паттерн вызова на каждый бар обошелся бы в 2.1 с чистого IPC на задаче длиной 2.0 с. Граница дешева; налог — в том, как вы ее пересекаете.

Deflated Sharpe Ratio: сколько "победителей" бэктеста переживают множественное тестирование?

Deflated Sharpe Ratio: сколько "победителей" бэктеста переживают множественное тестирование?

Поиск параметров — это машина по производству удачи. На чистом шуме — 1,000 стратегий с нулевым истинным edge — лучший годовой Sharpe в среднем составляет 1.63, а наивный тест значимости фиксирует открытие в 100% случаев. Мы строим контролируемую истину и показываем, что Deflated Sharpe Ratio, haircut Harvey-Liu и Reality Check Уайта восстанавливают честность: доля ложных открытий падает с 1.000 до 0.001-0.057, настоящие edge выше потолка шума сохраняются с power ~1 — и одна реальная ловушка (коррелированные сетки), где сырой DSR передефлирует, а вердикт нужно читать по всей полосе оценок эффективного числа испытаний, а не по одной цифре.

Дизайн целевой функции: метрика, которую вы оптимизируете, тайно выбирает вашу стратегию

Дизайн целевой функции: метрика, которую вы оптимизируете, тайно выбирает вашу стратегию

Чтобы искать "лучшую" стратегию, нужно сначала определить "лучшее" — и этот скаляр тайно выбирает победителя. На синтетических данных с известным edge (600 seeds, T=2000, 80 порогов) наивный per-trade Sharpe коронует лотерею: он выбирает победителя с exposure ниже 5% в 56% seeds и вырождается в 57% случаев — на самом ярком seed 8 сделок дают in-sample Sharpe 21.09, который схлопывается до 0.13 out of sample. Честное исправление почти скучно: измерять на полном таймлайне, который никогда не вырождается (out-of-sample 1.71). Trade-count shrinkage (conf_k) и exposure floor способны доработать per-trade метрику, но даже полностью исправленные они лишь сравниваются с full-timeline Sharpe (1.70 против 1.71) — никогда не превосходят его. Закон Гудхарта в бэктесте, с контролируемой истиной.