Полезный тест бэктеста никак не связан с поиском более удачного параметра: остановите процесс на середине, восстановите его и завершите то же воспроизведение. При одинаковых входных данных и контролируемом симуляторе исполнения решения, ордера и капитал должны совпасть с непрерывным запуском.
Если это не так, вы нашли проблему управления состоянием. Стратегия зависит от данных, которые вы не сохранили и не смогли восстановить. Эта зависимость важна, когда исследовательские запуски продолжаются после паузы, рабочие процессы заменяются или в сервис бумажной торговли выкладывают новый код.
Мне нравится этот тест, потому что здесь необычайно ясно, каким должен быть результат. Не нужно спорить о том, изменился ли рынок. В обоих запусках используются одни и те же рыночные данные.
Вот 3 неправильных способа перезапустить процесс. Числа приведены для примера; каждый сбой возможен даже в полностью детерминированной системе.
1. Загрузить несколько баров и считать индикаторы прогретыми
Предположим, стратегия использует экспоненциальную скользящую среднюю с периодом 100. Она обновляется так:
alpha = 2 / 101
ema_next = alpha * close + (1 - alpha) * ema_previous
Непрерывно работающий процесс сохраняет накопленное значение EMA. После перезапуска процесс загружает 100 баров, задаёт первое закрытие как начальное значение EMA и исходит из того, что индикатору с периодом 100 нужно 100 наблюдений.
Это предположение смешивает параметр сглаживания индикатора с окном конечной длины. EMA сохраняет затухающую долю начального значения. Если в начале значение EMA в двух версиях отличается на 10 ценовых единиц, то при одинаковых последующих ценах разница уменьшается так:
| Обновлений с момента инициализации | Оставшаяся разница | Доля начальной ошибки |
|---|---|---|
| 100 | 1.353 | 13.53% |
| 250 | 0.0674 | 0.674% |
| 500 | 0.000454 | 0.00454% |
Расчёт такой: 10 * (99 / 101)^k. Если загрузить 100 баров, получится всего 99 обновлений, если первое наблюдение используется как начальное значение.
Проблема обычно проявляется на границе принятия решения. В одном запуске цена выше EMA, в другом — ниже. Небольшая численная разница приводит к лишней сделке. После этого могут разойтись периоды ожидания, доступные средства и последующие решения.
Сохраняйте рекурсивное состояние индикатора, статус его инициализации и время последнего обработанного события. Или воспроизводите данные заново, начиная с известного начального состояния. Более длинный прогрев может дать приемлемое приближение, но выбирайте его длительность исходя из явно заданной допустимой ошибки и проверяйте, не повлияет ли эта ошибка на решения. «Период, умноженный на 5» — это общепринятое правило, а не доказательство.
И индикаторами история не исчерпывается. Скользящему перцентилю нужно окно данных. Онлайн-модели может требоваться состояние оптимизатора. Правило, которое ждёт 3 бара после убытка, должно помнить и об убытке, и о счётчике.
2. Сохранить позиции и забыть об ордерах в работе
Целевая позиция — 10 единиц. Ордер на покупку 10 единиц исполнен частично: куплено 4, ещё 6 остаются в заявке. Вы сохраняете позицию размером 4, перезапускаете процесс и выставляете новый ордер на недостающие 6.
Если исполнятся и остаток исходного ордера, и новый ордер, у вас окажется 16 единиц.
В бэктесте эта ошибка часто остаётся незаметной: после перезапуска движок исполнения молча удаляет активные ордера. В бумажной торговле симулятор или внешний сервис может их сохранить. Тогда один и тот же код восстановления приведёт к разной экспозиции в зависимости от того, какой компонент продолжил работу.
| На момент перезапуска | Фактическое состояние | Что видит восстановление только по позиции |
|---|---|---|
| Целевая позиция | 10 | 10 |
| Исполненная позиция | 4 | 4 |
| Объём ордера на покупку в работе | 6 | 0 |
| Сколько ещё нужно купить | 0 | 6 |
Проблема проявляется в виде внезапного потока ордеров сразу после восстановления. Иногда экспозиция удваивается. Иногда закрывается позиция, хотя защитный ордер всё ещё активен и позднее может открыть новую позицию.
В контрольной точке нужно сохранять идентификаторы и статусы ордеров наряду с позициями. Перед созданием новых заявок при восстановлении необходимо сверить эти записи с системой исполнения. Если судьба ордера неизвестна, это нужно выяснить; считать, что «нет сохранённого подтверждения» означает «ордер не отправляли», — верный способ породить дубликаты.
Постоянные клиентские идентификаторы ордеров помогают выяснить, что произошло. Они предотвращают дублирование только в том случае, если принимающая система действительно обеспечивает необходимую уникальность или идемпотентность. Сохраняйте и идентификаторы обработанных исполнений, чтобы повторно полученное исполнение не увеличило позицию дважды.
У меня слабость к скучному экрану со статусами ордеров. В день перезапуска его неприметные строчки вдруг становятся самым интересным интерфейсом во всём офисе.
3. Восстановить позицию и начать вести новый реестр P&L
Рассмотрим пример спотовой торговли без плеча и комиссий. Начните с $10,000 наличных, купите 10 единиц по $100 и сохраните состояние, когда цена достигнет $110.
Правильное состояние: $9,000 наличных и позиция стоимостью $1,100, то есть капитал $10,100. Если при восстановлении вернуть 10 единиц, но сбросить наличные к исходным $10,000, система покажет $11,100. Перезапуск процесса создал $1,000 из ничего.
Бывают и менее заметные варианты. При восстановлении общий капитал остаётся прежним, но цена входа сбрасывается до $110. Общий капитал верен, а распределение между реализованным и нереализованным результатом меняется. Если стоп или условие выхода зависит от цены входа, бухгалтерский обходной путь уже влияет на торговое поведение.
Или система забывает предыдущий максимум капитала. Допустим, капитал достиг $10,600, а затем снизился до $10,100. Просадка составляет около 4.72%. Если при восстановлении сбросить максимум, стратегия вдруг решит, что просадки нет. Любой контроль риска на основе просадки только что получил несанкционированный сброс.
Проблема может проявиться в скачке капитала, подозрительно уменьшившейся просадке или в том, что после развёртывания перестаёт срабатывать правило риска. Сохраняйте реестр и все зависящие от учёта состояния стратегии: движения денежных средств, позиции, применимую себестоимость, накопленные комиссии и сборы, а также память системы контроля риска. Сверяйте восстановленный капитал с реестром на тот же момент оценки.
Контрольная точка должна фиксировать согласованное состояние. Если сохранить наличные после исполнения, а объём позиции — до него, получится состояние, которого никогда не существовало. Фиксируйте связанные состояния вместе или записывайте надёжную последовательность событий, из которой их можно восстановить. Сохраняйте вместе с состоянием и указатель события, чтобы при восстановлении исполнение не было ни пропущено, ни учтено дважды.
В исследовательском тестовом стенде я бы оставил проверку с одним непрерывным эталонным воспроизведением, а затем перезапускал бы второй запуск в заведомо неудобные моменты: во время инициализации индикатора, после частичного исполнения и при активном лимите риска. Используйте тот же порядок событий и сохраняйте состояние генератора случайных чисел симулятора. Сравнивайте первое решение после восстановления, записи об ордерах и исполнениях, а также динамику капитала. Одно лишь совпадение конечного баланса может скрыть ошибки, которые взаимно компенсировались.
Чтобы проверить сбой после отправки ордера, но до подтверждения, стенд также должен отдельно сохранять состояние сервиса исполнения. Иначе он удалит именно ту неопределённость, которую вы пытаетесь проверить.
Спецификация стратегии включает в себя то, что она помнит. Опишите эту память достаточно явно, чтобы можно было остановить процесс на середине воспроизведения и показать, как он вернётся к работе.
← Все статьи


