26 Августа 2026 · рыночные данные

Анатомия одной 1-минутной свечи: чего OHLCV не рассказывает вашему бэктесту

Анатомия одной 1-минутной свечи: чего OHLCV не рассказывает вашему бэктесту

Перед вами одна свеча. Бессрочный контракт Binance USDⓈ-M, BTCUSDT, минута, начинающаяся в 13:46:00 UTC. С эндпоинта klines она пришла в виде простого массива из двенадцати значений и представляет собой наиболее распространённую единицу входных данных в розничных и близких к ним количественных исследованиях. Почти каждая стратегия, которую мы генерируем, работает с чем-то подобным.

Давайте разберём её по индексам и посмотрим, сколько всего бэктест интерпретирует неправильно.

ИндексЗначениеНазвание
01773495960000время открытия (мс)
1"84120.50"открытие
2"84177.90"максимум
3"84098.10"минимум
4"84163.40"закрытие
5"38.417"объём в базовой валюте (BTC)
61773496019999время закрытия (мс)
7"3232108.94"объём в котируемой валюте (USDT)
81204число сделок
9"21.883"объём покупок taker в базовой валюте
10"1841203.55"объём покупок taker в котируемой валюте
11"0"игнорировать

[0] и [6]: какая это минута и чьи часы это определяют

Время открытия — 1773495960000, время закрытия — 1773496019999. Обратите внимание на второе: оно заканчивается на 19999, то есть за одну миллисекунду до открытия следующей свечи. Интервал полуоткрытый, и биржа явно вам об этом сообщает. Половина ошибок в данных, с которыми мне доводилось разбираться, начиналась с того, что кто-то считал обе границы включительными и дважды учитывал сделку на границе либо выравнивал ресемплинг так, что каждая 5-минутная свеча заимствовала миллисекунду у соседней.

Временные метки — это время matching engine биржи. Не ваши часы, не часы получения данных вашим поставщиком и не те же часы, которые используются снимком funding. Когда вы соединяете ряд свечей с рядом ставок funding или открытого интереса, полученным с другого эндпоинта, вы объединяете источники, которые большую часть времени согласуются с точностью примерно до секунды. Для 1-минутной стратегии это ошибка синхронизации 1,7% в каждой объединённой строке. Для всего, что короче минуты, это фатально.

Кроме того, эта свеча снабжена меткой открытия. Содержащуюся в ней информацию невозможно узнать раньше 13:46:59.999. Любой вопрос о соглашении по индексации — сдвигать ли ряд на одну позицию, использовать ли время закрытия или открытия свечи как время события — на самом деле является одним и тем же вопросом о заглядывании вперёд, просто в другом наряде.

[1] «84120.50»: открытие, по которому нельзя торговать

Открытие — это цена первой сделки, которая прошла внутри интервала. Это завершённая транзакция между двумя другими участниками. К тому моменту, когда свеча появляется строкой в вашем dataframe, этой цене уже шестьдесят секунд.

Предыдущая свеча закрылась на 84109.80, поэтому в промежутке между двумя соседними 1-минутными свечами скрыто движение цены на $10.70. Это 1,3 базисного пункта, незаметно на графике и примерно четверть комиссии taker. Ничего страшного. Но посмотрите на тот же ряд по альткоиновому perp во время публикации CPI в США — такие разрывы между свечами составляют 15–40 б.п. Бэктест, который генерирует сигнал на закрытии свечи и исполняет его на открытии следующей, молча предполагает, что разрыв равен нулю, причём нулю он оказывается ровно тогда, когда это имеет значение.

[2] и [3]: максимум «84177.90», минимум «84098.10»

Именно здесь большинство движков исполнения отправляется на тот свет, поэтому этот раздел будет длинным.

Максимум и минимум — это экстремальные цены, которых коснулся рынок. Они не содержат информации об объёме или длительности. В необработанной ленте сделок за ту же минуту всё, что прошло по цене 84100.00 или ниже, составило 0.62 BTC в девяти сделках за интервал в 1,4 секунды. Поэтому стоп в бэктесте на 84100 «исполняется», а если ваша позиция составляет 3 BTC, по пути вниз вы поглотили весь видимый стакан, и оставшийся объём исполнился где-то в обратном движении 84105–84130. Свеча говорит, что минимум был 84098.10. Но она не говорит, что по этой цене проторговалось всего $52,000 номинального объёма.

1204сделок за минуту
0.032 BTCсредний размер сделки (~$2.7k)
0.62 BTCвсего проторговано по цене не выше минимальной
57%объёма инициировано покупателями

Обратная ситуация ещё хуже, потому что она создаёт у вас ложное ощущение успеха. Допустим, у вас стояла лимитная заявка на продажу по 84175. Максимум свечи — 84177.90, поэтому наивный движок исполнит вашу заявку по 84175 и начислит maker-rebate вместо комиссии taker. Произошло ли это исполнение на самом деле, зависит от позиции в очереди на этом ценовом уровне — а свеча этого не знает, и вы, скорее всего, никогда не записывали такую информацию. Касание — это не исполнение.

Правило, на котором мы остановились в нашем движке исполнения: отложенная лимитная заявка исполняется только если свеча проходит сквозь этот уровень, а не просто касается его. Исполнения на точном экстремальном тике требуют подтверждения объёмом на уровне цены из ленты сделок; в противном случае они отклоняются. Это исключило около 6% сделок из типичной книги mean-reversion и снизило бэктестовый Sharpe одного кандидата с 1.9 до 1.1. Этот кандидат никогда не был настоящим; правило исполнения просто первым оказалось достаточно честным, чтобы это показать.

Связанная ловушка: внутрисвечной путь. Если диапазон свечи охватывает и ваш стоп, и take-profit, OHLCV не может сказать, что сработало раньше. Каждый движок вынужден выбрать соглашение. Наш всегда предполагает, что первым срабатывает стоп: это пессимистично, иногда ошибочно, но никогда не создаёт фиктивную прибыль. Если ваш движок предполагает, что первым сработал take-profit, свечи с широким диапазоном будут производить прибыль из неоднозначности, а именно такие свечи сильнее всего влияют на распределение вашего PnL.

[4] «84163.40»: наименее надёжное число в свече

Закрытие — это последняя сделка в интервале. И всё. Это может быть нечётный лот в 0.002 BTC от бота, который закрыл остаток позиции в 13:46:59.8. Именно этот единственный, структурно случайный тик большинство исследовательских конвейеров использует для расчёта каждого сигнала, оценки каждой позиции и проверки каждого выхода.

Для BTC perp это почти не имеет значения; для малоликвидного альткоинового perp в 04:00 UTC — имеет огромное, и разница между закрытиями двух площадок за одну и ту же минуту может превысить весь ваш доход на сделку. Когда PnL стратегии зависит именно от закрытия, мы повторно запускаем её, оценивая позиции по mark price биржи, который выводится из индекса и гораздо устойчивее к манипуляциям. Если результаты расходятся, стратегия торговала артефактом.

[5] и [7]: «38.417» и «3232108.94», объём в чём именно

Объём в базовой валюте — BTC, объём в котируемой — USDT. Здесь присутствуют оба, что является любезностью, которую оказывают не все площадки. Это важно при агрегации данных по площадкам. Контракты с расчётом в монете котируются в контрактах по $100 номинала. Некоторые потоки по акциям сообщают объём круглыми лотами. Площадки рынков прогнозов сообщают число долей, где каждая доля — бинарное требование, номинированное в долларах. Суммирование «объёма» по смешанной вселенной без нормализации к единой единице номинала создаёт рейтинг ликвидности, который является чистой бессмыслицей, причём бессмыслицей стабильного вида, способной пережить ревью.

Нормализуйте всё до номинала в котируемой валюте на этапе загрузки. Сохраняйте исходное поле, но никогда не показывайте его стратегии.

[8] 1204: поле, которое должно определять вашу модель ценового воздействия

Деление базового объёма на число сделок даёт среднюю сделку размером 0.032 BTC, то есть около $2,700. Если ваша стратегия-кандидат хочет войти сразу на $250,000, она запрашивает объём примерно в 92 раза больше типичной сделки в эту минуту. Именно это число, а не какая-нибудь универсальная константа проскальзывания в 5 б.п., должно определять ваш член воздействия с корнем квадратным. Мы рассчитываем participation rate для каждой свечи как отдельную полноценную колонку и отклоняем стратегии, чья медианная заявка на вход превышает несколько процентов номинала свечи, потому что всё, что следует за этим, — выдумка.

Число сделок также дёшево выявляет странные минуты. Объём обычный, а число сделок упало до 11? Кто-то провёл крупную сделку. Объём обычный, а число сделок достигло 9,000? Это каскад ликвидаций, который разбирают на крошечные части.

[9] и [10]: «21.883», поле, которое выбрасывают все

Объём покупок taker в базовой валюте. В этой свече 21.883 из 38.417 BTC были инициированы покупателями, поэтому знаковая дельта объёма составляет +5.349 BTC, а соотношение агрессоров — 57/43 в пользу покупателей. Биржа бесплатно передаёт вам дисбаланс потока заявок в поле, которое большинство людей никогда не читает, потому что pandas не присвоил ему название колонки.

Я не утверждаю, что это само по себе предсказывает доходность; наивные стратегии по дельте — один из наиболее надёжных способов пожертвовать деньги комиссии. Но это действительно другое измерение по сравнению с ценой, оно доступно в том же запросе, который вы уже делали, и позволяет отличить рост, купленный участниками, от роста, произошедшего потому, что продавцы отошли. На OHLC эти ситуации выглядят одинаково, а через десять минут ведут себя по-разному. Наш исследовательский агент считает, что гипотеза, игнорирующая разделение taker на площадке, где оно публикуется, оставляет свидетельства без внимания.

[11] «0»: ignore — и всё остальное, чего здесь нет

Индекс 11 — устаревшее поле, навсегда равное нулю. Гораздо интереснее список того, чего эта свеча не содержит: ни bid, ни ask, ни спреда, ни глубины стакана, ни funding rate, ни открытого интереса, ни ликвидаций, ни mark price, ни index price. И, что особенно важно, никакой возможности узнать, была бы ваша заявка maker или taker — а это разница между уплатой 0.045% и заработком 0.01% на этой площадке.

Итак, любая модель комиссий, построенная только на klines, — это предположение, переодетое под число. Мы решаем проблему, заставляя каждую стратегию заранее объявлять стиль исполнения, а затем начисляя комиссию taker на всё, что не может доказать обратное.

Свеча, которая никогда не появилась

Последняя деталь — и та, которая больнее всего бьёт за пределами крупных инструментов. Минута с нулём сделок вообще не создаёт kline. Поставщики данных и библиотеки обычно заполняют её вперёд: open = high = low = close = предыдущее закрытие, объём 0. Ваш индикатор спокойно считает. Ваша стратегия видит корректную строку и может сгенерировать сигнал в минуту, когда никто в мире не торговал этим инструментом.

В одном perp средней капитализации, который мы загрузили, 4.1% 1-минутных свечей за двенадцатимесячный период не содержали ни одной сделки. Стратегия mean-reversion-кандидат по этому символу размещала 38% входов на синтетических свечах, потому что плоские синтетические цены — магнит для всего, что измеряет отклонение от скользящей средней. На бэктесте она выглядела великолепно. На самом деле она торговала пробелами в данных.

Поэтому теперь слой загрузки хранит для каждой свечи булево поле synthetic, которое передаётся через каждый ресемплинг, а проверочный конвейер отклоняет любую стратегию, чьи сделки группируются на таких свечах. Дешёвая колонка. Она погубила больше кандидатов, чем любой индикатор, который мы когда-либо написали.

Двенадцать значений. Четыре из них регулярно интерпретируются неверно, два регулярно выбрасываются, а целая категория отсутствует в строке и додумывается движком. Перед следующим бэктестом возьмите одну необработанную свечу из собственного хранилища и вслух прочитайте каждое поле, сопоставляя его с тем, что предполагает ваша логика исполнения. Это упражнение на двадцать минут, и я ещё ни разу не видел, чтобы кто-то выполнил его и не обнаружил ничего.

ohlcvмикроструктура рынкаинженерия данныхбэктестингкрипто-фьючерсы
ПоделитьсяXLinkedInFacebookRedditHacker NewsWhatsAppTelegramEmail
← Все статьи