Я позволил будущему просочиться в прошлое
Был один результат бэктеста, на раннем этапе, настолько хороший, что должен был бы меня обрадовать. Вместо этого, после нескольких минут разглядывания, он вызвал у меня глубокое беспокойство. Прибыли в симуляции были большими, гладкими и слишком уж чистыми — такая результативность не случается на реальном, шумном, враждебном рынке. А не случалась она на реальном рынке потому, что мой тест, тихо и незаметно для меня, мог видеть будущее.
Не нарочно. Я не писал если знаешь, что будет дальше, мухлюй. Будущее просочилось в прошлое через десяток крошечных, безобидных на вид швов, и каждый из них вознаграждал мою систему за своего рода ясновидение, которого у неё никогда, ни за что не будет, когда на кону окажутся реальные деньги.
Самый соблазнительный баг в бэктестинге
Общее название этому — заглядывание в будущее (look-ahead bias), или утечка. Оно возникает всякий раз, когда решению в вашей симуляции разрешается использовать информацию, которой в реальной жизни ещё не существовало в тот момент, когда решение принималось.
С этим стоит посидеть, потому что всё тоньше, чем звучит. Бэктест должен быть достоверным воспроизведением истории: в каждый момент система может знать только то, что действительно было познаваемо в этот момент, и действовать она обязана, опираясь лишь на это. Утечка — это любая трещина в этой стене, любое место, где осколок завтрашней информации проскальзывает в сегодняшнее решение. И в тот самый момент, когда это происходит, ваш бэктест перестаёт измерять «как бы это сработало» и начинает измерять «как бы это сработало, если бы можно было подглядывать».
Как будущее прокрадывается внутрь
Я хочу быть осторожным здесь и не описывать особенности моей собственной системы, поэтому буду держаться на уровне общих форм, которые принимают эти утечки, потому что они на удивление распространены.
Одна классическая форма: использование в решении значения, которое на самом деле было окончательно определено лишь после того момента, в который вы притворяетесь, будто принимаете решение. Другая: подготовка данных через масштабирование или нормализацию с помощью статистик — среднего, размаха, типичной величины — вычисленных по всей истории, включая ту её часть, которая в ваш симулированный момент ещё не произошла. Ещё одна: фильтрация или разметка данных задним числом, когда вы тихо исключаете случаи так, как могли бы делать, только если бы уже знали, чем они кончились.
Каждое из этого — единственная, разумно выглядящая строчка кода. Ни одна из них не ощущается мошенничеством, пока ты её пишешь. И каждая вручает вашей системе небольшой, ровный поток информации из будущего.
Почему это так трудно поймать
Самое жестокое в утечке то, что она не выглядит багом. Код выполняется без ошибок. Ничего не падает. И результат, который он выдаёт, — не тревожный сигнал, а отличное число. Отличные числа у большинства людей не вызывают подозрений. Они вызывают ликование, скриншот, тихое чувство, что ты, возможно, что-то нащупал.
Именно это и делает данный класс багов столь опасным. Он прячется внутри результатов, которые выглядят как успех, а это единственное худшее место, где баг может спрятаться, потому что успех — это тот единственный исход, который ты меньше всего мотивирован допрашивать. Падение требует, чтобы его исправили. Красивая кривая капитала приглашает тебя перестать смотреть.
Признак: результаты слишком хороши, чтобы быть правдой
Долгое время единственной надёжной сигнализацией у меня было моё собственное недоверие. Когда бэктест возвращается резко лучше, чем что-либо, на что можно было разумно рассчитывать, — глаже, прибыльнее, последовательнее, чем рынок вправе позволить, — правильная первая гипотеза не «я нашёл нечто гениальное». Она звучит так: «где-то утекает информация, и мне нужно её найти».
Это по-настоящему полезное предположение, и я пришёл к тому, чтобы держаться его крепко: в бэктесте превосходство куда чаще является симптомом бага, чем доказательством преимущества. Рынок тяжёл. Результаты, намекающие, что он лёгок, обычно рассказывают вам о вашем коде, а не о рынке.
Поиск утечек
Сама работа по охоте на утечки скучна и совершенно неприглядна, и я не нашёл никакого хитрого сокращённого пути, который бы её заменял. Дисциплина такова: возьмите каждое отдельное значение, которое питает решение, и проследите его назад до точного момента, в который оно стало бы известно в реальной жизни. А затем спросите, прямо: «в тот миг, в который я притворяюсь, что действую, мог ли я действительно уже располагать этим числом?»
Большинство утечек, когда вы наконец загоняете их в угол, оказываются мошенничеством ценой в одну временную метку — значение, использованное на мгновение раньше, чем оно по-настоящему существовало бы, вычисление, дотянувшееся на один шаг дальше в данные, чем ему было дозволено. Маленькие швы. Но маленький шов, повторённый на тысячах решений, способен сфабриковать целое воображаемое состояние.
Принцип поглубже: уважай стрелу времени
Под всем этим лежит единственный принцип, к которому я теперь отношусь почти как к священному: уважай стрелу времени. Бэктест — это, по сути, обещание; обещание, что вы всегда использовали лишь прошлое, чтобы действовать в настоящем, и никогда — будущее. Каждая утечка — это маленькое нарушенное обещание, место, где ваш код тихо протянулся вперёд во времени, чтобы помочь самому себе.
Так что построение заслуживающей доверия симуляции оказалось гораздо меньше про умное моделирование и гораздо больше про защиту одной границы: черты между тем, что было известно в момент, и тем, что было лишь познаваемо позже. Бо́льшая часть инженерных усилий, та часть, о которой никто не пишет восторженных тредов, — это просто охрана этой границы от постоянной, небрежной склонности вашего собственного кода подглядывать.
Почему я теперь не доверяю своим лучшим результатам
Долгосрочной переменой от всего этого стало то, что моё отношение к хорошим новостям перевернулось. Разочаровывающий бэктест — это просто разочарование; я смотрю на него, пожимаю плечами и иду дальше. А вот эффектный теперь сначала заставляет меня нервничать и лишь потом радоваться. Чем лучше результат, тем упорнее я ищу утечку, которая его породила.
Звучит безрадостно, и иногда так оно и есть. Но это происходит из тяжело доставшегося уважения к тому, как именно эта игра наказывает за самообман. Самая опасная ложь, которую система когда-либо вам скажет, — это не уродливые провалы. Это великолепные успехи — те, что выглядят в точности как то, что вы надеялись найти, и именно поэтому в них вы последними подумаете усомниться.
— Никаких сигналов, никакой доходности, не инвестиционная рекомендация.