Ось одна свічка. Безстроковий контракт Binance USDⓈ-M, BTCUSDT, хвилина, що починається о 13:46:00 UTC. Вона надійшла з ендпойнта klines як звичайний масив із дванадцяти значень і є найпоширенішою одиницею вхідних даних у роздрібних та суміжних кількісних дослідженнях. Майже кожна стратегія, яку ми генеруємо, працює з чимось подібним.
Тож розберімо її поелементно й подивімося, скільки всього бектест трактує неправильно.
| Індекс | Значення | Назва |
|---|---|---|
| 0 | 1773495960000 | час відкриття (мс) |
| 1 | \"84120.50\" | відкриття |
| 2 | \"84177.90\" | максимум |
| 3 | \"84098.10\" | мінімум |
| 4 | \"84163.40\" | закриття |
| 5 | \"38.417\" | обсяг базового активу (BTC) |
| 6 | 1773496019999 | час закриття (мс) |
| 7 | \"3232108.94\" | обсяг котирувального активу (USDT) |
| 8 | 1204 | кількість угод |
| 9 | \"21.883\" | обсяг базового активу в угодах taker buy |
| 10 | \"1841203.55\" | обсяг котирувального активу в угодах taker buy |
| 11 | \"0\" | ігнорувати |
[0] і [6]: яка це хвилина і чиї годинники це визначають
Час відкриття — 1773495960000, час закриття — 1773496019999. Зверніть увагу на друге значення: воно закінчується на 19999, тобто на одну мілісекунду раніше за відкриття наступної свічки. Вікно напіввідкрите, і біржа прямо вам про це повідомляє. Половина помилок у даних, з якими я стикався, починалася з того, що хтось вважав обидві межі включними й двічі рахував угоду на межі або вирівнював ресемплінг так, що кожна 5-хвилинна свічка позичала мілісекунду в сусідньої.
Таймстемпи — це час матчинг-рушія біржі. Не час на вашому годиннику, не час надходження даних до вашого постачальника і не той самий час, який використовує знімок funding. Коли ви об’єднуєте ряд клайнів із рядом funding rate або open interest, отриманим з іншого ендпойнта, ви поєднуєте джерела, які здебільшого узгоджуються з точністю приблизно до секунди. Для 1-хвилинної стратегії це помилка синхронізації 1,7% у кожному об’єднаному рядку. Для всього, що коротше за хвилину, це фатально.
І ця свічка має таймстемп на моменті відкриття. Інформацію, що міститься в ній, неможливо знати до 13:46:59.999. Кожне питання щодо конвенції індексації — чи зсувати на один, чи використовувати час закриття або відкриття свічки як час події — насправді є тим самим питанням про lookahead, лише в іншому вбранні.
[1] \"84120.50\": відкриття, за яким ви не можете торгувати
Відкриття — це ціна першої угоди, укладеної всередині вікна. Це завершена транзакція між двома іншими людьми. На той момент, коли свічка з’являється рядком у вашому dataframe, цій ціні вже шістдесят секунд.
Попередня свічка закрилася на 84109.80, тож у проміжку між двома сусідніми 1-хвилинними свічками приховано рух ціною на $10.70. Це 1,3 базисного пункту — на графіку непомітно й приблизно чверть комісії taker. Гаразд. Але подивіться на той самий ряд для alt 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.
Інший напрямок ще гірший, бо він створює приємну для вас ілюзію. Припустімо, у вас стоїть лімітний ордер на продаж за 84175. Максимум свічки — 84177.90, тож наївний рушій виконує ваш ордер за 84175 і записує maker rebate замість витрат taker. Чи відбулося це виконання насправді, залежить від позиції в черзі на цьому ціновому рівні — а цього свічка знати не може, і ви, ймовірно, ніколи не записували ці дані. Дотик до ціни — не виконання.
Правило, на якому ми зупинилися у своєму рушії виконання: відкладений лімітний ордер виконується лише якщо свічка проходить крізь цей рівень, а не просто торкається його. Виконання точно на екстремальному тіку потребує підтвердження обсягом за ціною з потоку угод; інакше ми його відхиляємо. Це прибрало близько 6% угод із типової mean-reversion книги й знизило бектестовий Sharpe одного кандидата з 1.9 до 1.1. Той кандидат ніколи не був реальним; правило виконання просто виявилося першим, достатньо чесним, щоб це сказати.
Пов’язана пастка: внутрішньосвічковий шлях. Якщо діапазон свічки охоплює і ваш стоп, і тейк-профіт, OHLCV не може сказати, що спрацювало першим. Кожен рушій мусить обрати конвенцію. Наш завжди припускає, що першим спрацьовує стоп: це песимістично, іноді неправильно, але ніколи не створює фальшивої перемоги. Якщо ваш рушій припускає, що першим спрацьовує тейк-профіт, свічки з широким діапазоном генеруватимуть прибуток із неоднозначності, а саме такі свічки й визначають розподіл вашого PnL.
[4] \"84163.40\": найменш надійне число у свічці
Закриття — це остання угода у вікні. І все. Це може бути непарний лот у 0.002 BTC від бота, який о 13:46:59.8 закривав залишок позиції. Саме цей структурно випадковий тік більшість дослідницьких конвеєрів використовує для обчислення кожного сигналу, маркування кожної позиції та оцінювання кожного виходу.
На BTC perp це майже не має значення; на малоліквідному altcoin perp о 04:00 UTC це має величезне значення, і різниця між цінами закриття на двох майданчиках за ту саму хвилину може перевищити всю вашу перевагу на одну угоду. Коли PnL стратегії залежить саме від закриття, ми запускаємо її повторно, маркуючи позиції за mark price біржі — вона визначається індексом і значно важче піддається маніпуляції. Якщо результати розходяться, стратегія торгувала артефактом.
[5] і [7]: \"38.417\" та \"3232108.94\", обсяг у чому саме
Обсяг базового активу — BTC, обсяг котирувального активу — USDT. Тут присутні обидва, що є люб’язністю, яку пропонують не всі майданчики. Причина, чому це важливо, — агрегація між майданчиками. Контракти з маржею в монетах котируються в контрактах із номіналом $100. Деякі джерела даних щодо акцій повідомляють обсяги круглими лотами. Майданчики prediction market повідомляють кількість часток, де одна частка є бінарною вимогою, номінованою в доларах. Сумування «обсягу» для змішаного набору активів без нормалізації до єдиної одиниці номіналу породжує рейтинг ліквідності, який є цілковитою нісенітницею — причому нісенітницею стабільного вигляду, що переживає рев’ю.
Нормалізуйте все до номіналу котирувального активу під час завантаження. Сире поле теж зберігайте, але ніколи не показуйте його стратегії.
[8] 1204: поле, яке має визначати вашу модель впливу
Якщо поділити базовий обсяг на кількість угод, середній розмір угоди становить 0.032 BTC, тобто близько $2,700. Якщо ваша кандидатна стратегія хоче одразу увійти на $250,000, вона просить приблизно у 92 рази більший обсяг, ніж типова транзакція в цю хвилину. Саме це число, а не якийсь універсальний коефіцієнт прослизання 5 б.п., має визначати ваш член впливу з квадратним коренем. Ми обчислюємо коефіцієнт участі для кожної свічки як повноцінну колонку й відхиляємо стратегії, чий медіанний вхід перевищує кілька відсотків номіналу свічки, бо все, що йде далі, — вигадка.
Кількість угод також дешево сигналізує про дивні хвилини. Обсяг нормальний, кількість угод впала до 11? Хтось провів block trade. Обсяг нормальний, кількість угод зросла до 9,000? Це каскад ліквідацій, який розгризають на крихітні частини.
[9] і [10]: \"21.883\Я\", поле, яке викидають усі
Обсяг базового активу в угодах taker buy. У цій свічці 21.883 із 38.417 BTC ініціювали покупці, тож дельта підписаного обсягу становить +5.349 BTC, а розподіл агресорів — 57/43 на користь покупців. Біржа безкоштовно передає вам дисбаланс потоку ордерів у полі, яке більшість ніколи не читає, бо pandas не дала колонці назви.
Я не стверджую, що це саме по собі прогнозує дохідність; наївні стратегії на дельті — один із найнадійніших способів поповнити реєстр комісій. Але це справді інше вимірювання, ніж ціна, воно доступне в тому самому запиті, який ви й так уже робили, і дає змогу відрізнити ралі, куплене покупцями, від ралі, що сталося тому, що продавці відійшли. На OHLC вони виглядають однаково, але через десять хвилин поводяться по-різному. Наш дослідницький агент вважає, що гіпотеза, яка ігнорує розподіл taker на майданчику, де його публікують, залишає докази невикористаними.
[11] \"0\": ігнорувати — і все інше, чого тут немає
Індекс 11 — застаріле поле, яке завжди дорівнює нулю. Цікавішим є список того, чого ця свічка не містить: ні bid, ні ask, ні спреду, ні глибини стакана, ні funding rate, ні open interest, ні ліквідацій, ні mark price, ні index price. І, що особливо важливо, жодного способу дізнатися, чи був би ваш ордер maker або taker — а це різниця між сплатою 0.045% і заробітком 0.01% на цьому майданчику.
Отже, будь-яка модель комісій, побудована лише на клайнах, — це припущення, вдягнене в одяг числа. Ми розв’язуємо цю проблему, змушуючи кожну стратегію заздалегідь оголосити свій стиль виконання, а потім нараховуючи комісію taker на все, що не може довести протилежне.
Свічка, яка ніколи не з’явилася
Останній момент, і той, що найболючіше кусається поза мейджорами. Хвилина з нульовою кількістю угод узагалі не створює клайну. Постачальники та бібліотеки часто заповнюють її вперед: open = high = low = close = попереднє закриття, volume 0. Ваш індикатор щасливо обчислюється. Ваша стратегія бачить дійсний рядок і може згенерувати сигнал у хвилину, коли ніхто у світі не торгував цим інструментом.
Для одного perp із активом середньої капіталізації, який ми завантажували, 4.1% 1-хвилинних свічок у дванадцятимісячному вікні мали нуль угод. Кандидат mean-reversion на цьому символі відкривав 38% своїх позицій на синтетичних свічках, бо пласкі синтетичні ціни — справжня приманка для всього, що вимірює відхилення від ковзного середнього. На бектесті він виглядав чудово. Він торгував прогалини в даних.
Саме тому на рівні завантаження тепер зберігається булеве поле synthetic для кожної свічки, яке проходить через кожен ресемплінг, а перевірочний конвеєр провалює будь-яку стратегію, чиї угоди концентруються на таких свічках. Дешева колонка. Вона знищила більше кандидатів, ніж будь-який індикатор, який ми коли-небудь написали.
Дванадцять значень. Чотири з них регулярно трактують неправильно, два регулярно відкидають, а ціла категорія відсутня в рядку й вигадується рушієм. Перед наступним бектестом дістаньте зі свого сховища одну сиру свічку й уголос прочитайте кожне поле, зіставляючи його з тим, що ваш рушій виконання припускає про неї. Це двадцятихвилинна вправа, і я ще жодного разу не бачив, щоб хтось її виконав і не знайшов нічого.
← Усі статті