4 вересня 2026 · бектестування

І стоп, і тейк-профіт були в межах однієї свічки. Який із них спрацював у вашому бектесті?

І стоп, і тейк-профіт були в межах однієї свічки. Який із них спрацював у вашому бектесті?

Лонг від 84,120. Стоп на 84,036, тейк-профіт на 84,271. Надходить свічка наступної хвилини: відкриття 84,118, максимум 84,290, мінімум 84,010, закриття 84,240.

Обидва рівні — у межах цієї свічки. Стоп було зачеплено, і тейк-профіт було зачеплено, але чотири значення OHLC не містять жодної інформації про те, що сталося першим. Ваш бектест усе одно повернув число. Десь у циклі якийсь рядок коду ухвалив рішення — імовірно, той, який ви не вважали припущенням моделі, коли його писали.

Це найбільше окреме джерело фальшивої дохідності, яке я бачу в короткострокових стратегіях, важливіше за комісії та прослизання, бо воно не схоже на припущення. Воно схоже на технічну деталь.

Хибний підхід перший: дозволити ланцюжку if вирішити за вас

Зазвичай це виглядає так:

if bar.high >= target:
    exit(target, "tp")
elif bar.low <= stop:
    exit(stop, "sl")

Ніхто не вирішував, що «тейк-профіт спрацьовує раніше за стоп». Перевірку тейк-профіту просто написали першою, бо це оптимістичний сценарій, про який ви думали. Поміняйте гілки місцями — і зміниться крива капіталу; це вже має підказати, що P&L стратегії частково залежить від вашого редактора коду.

Я запустив навмисно звичайну скальпінгову стратегію повернення до середнього на безстроковому контракті BTCUSDT: три місяці 1-хвилинних свічок, 4,812 угод, стоп 0.10% і тейк-профіт 0.18% від ціни входу. Ті самі сигнали, ті самі комісії — змінилася лише домовленість щодо розв’язання нічиєї.

12.6%угод зачепили обидва рівні в межах однієї свічки
2.31Sharpe, спершу тейк-профіт
0.18Sharpe, спершу стоп
0.94Sharpe, розв’язання за 1-секундними свічками

Восьма частина угод визначає весь результат. Така арифметика пари з близькими стопом і тейк-профітом: неоднозначні угоди — це ті, де ціна рухалася в обидва боки, а саме вони найцікавіші; результат кожної залежно від підкидання монети може відрізнятися на всю відстань між стопом і тейк-профітом. 12.6% угод × 0.28% діапазону — це 3.5% валового номінального обороту на одиницю вибірки, що набагато перевищує фактичну перевагу стратегії.

Частка залежить від розміру свічки відносно відстані між рівнями й швидко зростає, коли свічки стають довшими. Ті самі стоп і тейк-профіт, ті самі сигнали, інша частота свічок:

Інтервал свічкиУгоди, де обидва рівні всередині однієї свічкиSharpe (спершу тейк-профіт)
1s0.3%0.91
1m12.6%2.31
5m34%3.60
15m49%4.42
1h71%5.88

Подивіться, що насправді показує ця таблиця. На більших інтервалах бектест давав кращі результати. Дослідники зазвичай вважають годинні свічки консервативнішим вибором: менше шуму, менше підгонки під мікроструктуру. Але якщо внутрішньосвічкове правило розв’язує неоднозначність на вашу користь, більша свічка — це просто більша коробка, у якій можна припустити, що вам пощастило. На 1-годинних свічках сім угод із десяти залежать виключно від домовленості. Такий бектест перевіряє не стратегію, а порядок двох операторів `if`, повторений 3,400 разів.

Хибний підхід другий: вважати, що стоп виконався за ціною стопа

Припустімо, ви виправили порядок. Спершу спрацьовує стоп, ви фіксуєте збиток рівно 0.10% плюс комісію taker і вважаєте підхід строгим. Але ще дві речі лишаються неправильними.

Перша: стоп — це тригер, а не виконання ордера. На Binance USDⓈ-M ордер STOP_MARKET перетворюється на ринковий щойно виконується умова тригера, після чого він забирає доступну ліквідність зі стакана. У спокійну хвилину прослизання становить один-два тіки. У хвилину, коли ваш стоп справді спрацював, — зі свічкою, тіло якої має 40 пунктів, і каскадом ліквідацій нижче, — стакан тонкий саме з того боку, куди ви виходите. У моїй вибірці зіставлення тригерів стопа з потоком тіків дало медіанне виконання на 1.4 bps гірше за тригер, а 95-й перцентиль — 11 bps. За стопа в 10 bps хвостовий випадок додає ще десяту частину ризику, який, як ви думали, визначили.

Друга проблема тонша й специфічна для безстрокових контрактів: яка ціна запускає тригер. Binance за замовчуванням налаштовує стоп-ордери на ціну mark, яка обчислюється на основі індексу та згладженої базисної різниці, а не останньої угоди на цій біржі. У ваших рядах OHLC — остання ціна. Це різні ряди, і найбільше вони розходяться саме під час подій, що запускають стопи.

Остання ціна (ваші klines)Ціна mark (тригер за замовчуванням)
Джерелоугоди на цій біржііндекс кількох бірж + базисна різниця
Поведінка тінейповний розмах рухузначно згладжений
Типове відхилення1–3 bps у спокійний період, 20–35 bps у хвилину каскаду
Наслідок для бектестустопи спрацьовують, хоча не мали б, і навпаки

Отже, тінь у 25 bps на стрічці останніх цін вибиває вас у бектесті, хоча реальна ціна mark навіть не наблизилася до тригера на 10 bps. Або навпаки — у день, коли рухається індекс, а ваша біржа відстає. Якщо встановити workingType на CONTRACT_PRICE, ви принаймні узгодите реальну поведінку з даними, і для дослідника це зазвичай правильний вибір, адже чесна симуляція тригера за ціною mark означає, що доведеться передавати другий ціновий ряд через увесь рушій виконання ордерів.

Найкраще пам’ятаю такий випадок: хтось із нашої команди «покращив» стратегію, змінивши тейк-профіт із 0.18% на 0.21%. Sharpe зріс із 2.3 до 3.1. Жодної нової переваги не з’явилося. Тейк-профіт просто перемістився за межі щільної частини розподілу тіней на 1-хвилинних свічках, тож менше угод потрапляло до неоднозначної групи, де код непомітно зараховував їм виграш. Вони оптимізували правило розв’язання нічиєї.

Хибний підхід третій: завжди припускати найгірше й називати це консервативністю

Перша реакція — перейти до песимістичного сценарію. Якщо зачеплено обидва рівні, зарахувати стоп. Готово, жодного оптимізму, можна запускати.

Раніше я так і робив. Це краще за альтернативу, але все одно неправильно з двох причин.

Так ви відкидаєте цілком робочі стратегії. Песимістичне розв’язання для 12.6% угод коштувало цій стратегії 2.1 пункту Sharpe порівняно з 0.94 за розв’язанням на основі тіків. Якщо справжнє значення — 0.94, а ваше правило показує 0.18, ви відмовляєтеся від ідеї й беретеся за щось гірше. Консервативність із похибкою у два пункти Sharpe — це не консервативність, а шум із моральною позою.

Ще гірше, це спотворює оптимізацію. Якщо задати оптимізатору параметрів песимістичне правило розв’язання нічиєї, він навчиться уникати неоднозначності, бо тепер вона означає суцільний штраф. Він тяжітиме до широких стопів і близьких тейк-профітів або до довгих інтервалів свічок, де два рівні рідко опиняються в одній свічці, і запропонує параметри, підібрані під ваше правило виконання, а не під ринок. Та сама помилка, що й за оптимістичного підходу, але з протилежним знаком і так само непомітна у звіті про результати.

Практичне правило, яким ми користуємося насамперед: якщо stop_distance + target_distance менший за 75-й перцентиль діапазону свічки вашого інтервалу, внутрішньосвічкове припущення сильніше впливає на P&L, ніж сигнал. Обчисліть обидва значення. Це чотири рядки коду, і така перевірка зупиняла більше обговорень стратегій, ніж будь-яка інша окрема перевірка.

Що справді працює

Шлях ціни всередині свічки — це дані. Отримайте їх або оцініть межі невідомої частини.

  1. Розв’язуйте неоднозначність за найдетальнішим доступним рядом. Binance aggTrades за відповідні хвилини містить лише кілька сотень рядків і дає однозначну відповідь: який рівень зачепило першим і за якою ціною виконалося прослизання. Дані про кожен тік для всього бектесту не потрібні — лише для неоднозначних свічок. У моїй вибірці це були 606 хвилин із 129,600. Це невелике завантаження, а не інфраструктурний проєкт.
  2. Якщо даних про тіки немає, для розв’язання неоднозначності спустіться на один або два рівні інтервалів. Сигнали — на 15-хвилинних свічках, виходи визначаються за 1-секундними або 1-хвилинними свічками. Частка неоднозначності падає з 49% до часток відсотка, а решта настільки мала, що її можна чесно ігнорувати.
  3. Завжди показуйте діапазон результатів. Запускайте кожен бектест двічі — з оптимістичним і песимістичним розв’язанням — і виводьте обидва значення Sharpe поруч із результатом після розв’язання неоднозначностей. Різниця між ними — це ваша невизначеність усередині свічки; її слід показувати у звіті поруч із довірчим інтервалом для самого Sharpe. Якщо діапазон — 0.2–2.3, жоден висновок у його межах не є надійним.
  4. Відстежуйте частку неоднозначних угод як ключову метрику. Ми показуємо її вгорі кожної картки стратегії поруч із кількістю угод та оборотом. Якщо частка перевищує приблизно 5%, перевіряється логіка виходу, а не входу.
  5. Моделюйте тригер окремо від виконання. Використовуйте для тригера ціновий ряд, який фактично використовує біржа, а виконання моделюйте за ціною тригера плюс прослизання, оцінене на основі стрічки угод, а не за ціною самого тригера.

З акціями та сама проблема, лише проявляється інакше. Якщо акція закривається на рівні 62.00, а вночі відкривається на 58.40, стоп на 62.00 не виконається за цією ціною — його виконають десь нижче ціни відкриття. Бектест на денних свічках, який зараховує прослизання −$0.00 під час таких цінових розривів, охоче покаже, що стоп-лос зменшив просадку. Це не так. Просто його ніколи не перевіряли в дні, які мають значення. Те саме відбувається під час зупинок торгів: ваш стоп фактично виконується на аукціоні відновлення торгів за ціною, якої мінімум свічки не показує.

У цьому немає нічого екзотичного. Це просто визнання того, що свічка — лише зведення даних, а стратегія зі стопом і тейк-профітом робить ставку на порядок подій, який це зведення відкинуло. Коли рушій паперової торгівлі нарешті запускає стратегію на реальних даних, стрічка угод показує, у якому порядку все відбулося, і їй жодного разу не було важливо, яку гілку `if` ви написали першою.

внутрішньосвічкове виконаннястоп-ордерибектестуваннякриптоф’ючерсимікроструктура ринку
ПоділитисяXLinkedInFacebookRedditHacker NewsWhatsAppTelegramEmail
← Усі статті