Стратегии и тестер
1Стратегии и тестер
Как написать стратегию на LootScript и проверить её на истории в тестере: объявление strategy(), заявки, модель исполнения, издержки, вкладки отчёта и ограничения бэктеста.
Стратегия — это скрипт, который кроме линий на графике подаёт заявки. Заявки исполняет не биржа, а симулятор внутри терминала: он прогоняет стратегию по истории графика с комиссиями, проскальзыванием и фандингом и показывает результат в окне Тестер стратегии.
Объявление strategy()
Стратегию отличает от индикатора первая строка: strategy(...) вместо indicator(...). Всё остальное — настройки, отрисовка, данные рынка — работает так же. Функции strategy.* в индикаторе не компилируются: «strategy.* доступно только в strategy(…)».
| Параметр | По умолчанию | Что задаёт |
|---|---|---|
initialCapital |
10000 |
Стартовый капитал |
defaultQtyType, defaultQtyValue |
strategy.qty.fixed, 1 |
Размер заявки, если в ней не указан qty |
commissionType, commissionValue |
процент, не задана | Комиссия: strategy.commission.percent — процент от оборота (0.05 = 0,05 %), strategy.commission.cash_per_contract — сумма за единицу количества, strategy.commission.cash_per_order — сумма за исполнение |
slippageTicks |
0 |
Проскальзывание в тиках |
pyramiding |
0 |
Сколько входов в одну сторону может быть открыто; 0 — один |
positionModel |
strategy.model.netting |
Одна общая позиция или раздельные длинные и короткие (strategy.model.hedging) |
processOrdersOnClose |
false |
Исполнять рыночные заявки по закрытию текущего бара |
calcOnEveryTick |
false |
Разрешить заявки на формирующемся баре |
marginLong, marginShort, leverage |
0, 0, 1 |
Маржа в процентах или плечо; при 0 ликвидация не моделируется |
applyFunding |
true |
Учитывать фандинг |
fillModel |
strategy.fill.conservative |
Предположение о пути цены внутри бара |
Значения параметров — только числа и константы прямо в тексте. Выражение или input.* в объявлении — ошибка компиляции. Параметр calcOnOrderFills принимается, но в этой версии не действует: на вкладке «Свойства» тестера он помечен «не действует в этой сборке».
Размер заявки при qty = na задаёт defaultQtyType:
| Тип | defaultQtyValue означает |
|---|---|
strategy.qty.fixed |
Количество в монетах (базовой валюте) |
strategy.qty.cash |
Сумму в валюте котировки |
strategy.qty.percent_of_equity |
Процент текущего капитала |
strategy.qty.risk_percent |
Процент капитала, которым рискуете до защитного стопа |
Для strategy.qty.risk_percent нужен strategy.exit со стопом (loss или stop), иначе компилятор остановит: «требует стоп, от которого считать размер». Количество округляется вниз до шага количества инструмента. Если после округления остаётся ноль, заявка отклоняется и попадает в счётчик тестера «Отвергнуто заявок (размер)».
Заявки
| Функция | Что делает |
|---|---|
strategy.entry(id, direction, qty, limit, stop, …) |
Открывает позицию или разворачивает её. Учитывает модель позиции и pyramiding |
strategy.order(id, direction, qty, limit, stop, …) |
То же без учёта модели позиции и pyramiding |
strategy.exit(id, from_entry, qty, qty_percent, profit, limit, loss, stop, trail_price, trail_points, trail_offset, …) |
Ставит выход — тейк, стоп или трейлинг — на открытый вход |
strategy.close(id, comment, qty, qty_percent) |
Закрывает вход рыночной заявкой |
strategy.close_all(comment) |
Закрывает всё |
strategy.cancel(id), strategy.cancel_all() |
Снимает ещё не исполненные заявки |
Направление — strategy.long или strategy.short. У всех функций есть необязательный when: when = false пропускает вызов. У входов, выходов и закрытий есть ещё comment — текстовая подпись к заявке.
- Тип входа. Без
limitиstop— рыночная заявка, сlimit— лимитная, соstop— стоп-заявка, с обоими — стоп-лимит. Повторныйstrategy.entryс тем жеidзаменяет ещё не исполненную заявку. - Разворот. В модели
nettingвход в противоположную сторону закрывает текущую позицию и открывает новую одним действием. - Единицы выхода.
profitиloss— в тиках от цены входа,limitиstop— абсолютные цены. Трейлинг задаётся парой:trail_offsetплюсtrail_priceилиtrail_points. - Выход без уровней ничего не делает. Компилятор предупредит: «strategy.exit без единого уровня (profit/loss/stop/limit/trail) — пустая операция».
- Перенос стопа. Повторный
strategy.exitс тем жеidзаменяет выход — так двигают стоп.
Состояние стратегии читается рядами: strategy.position_size (со знаком, короткая позиция — отрицательная), strategy.position_avg_price, strategy.equity, strategy.netprofit, strategy.openprofit, strategy.opentrades, strategy.closedtrades, strategy.funding_paid и другими — полный список в справочнике.
Как тестер исполняет заявки
Заявка, поданная на баре, исполняется не раньше следующего бара. Решение принимается по закрытию бара, и исполнять его по той же цене значило бы заглянуть вперёд.
| Заявка | Исполнение |
|---|---|
| Рыночная | По открытию следующего бара плюс проскальзывание. С processOrdersOnClose = true — по закрытию текущего |
| Лимитная | Когда цена проходит лимит, по цене лимита. Если бар открылся за лимитом — по открытию |
| Стоп | Когда цена проходит стоп, по цене стопа плюс проскальзывание. При гэпе — по открытию плюс проскальзывание |
Путь цены внутри бара. Настоящего пути внутри бара тестер не знает и предполагает его. Модель strategy.fill.conservative (по умолчанию) считает, что цена сначала сходила к худшему экстремуму: на растущем баре — открытие → минимум → максимум → закрытие, на падающем — наоборот. Если в одном баре оказались и стоп, и тейк, срабатывает стоп. Модель strategy.fill.optimistic предполагает обратное — годится только для сравнения. Проигрывания по младшему таймфрейму в этой версии нет: в колонке «Путь» каждая сделка отмечена как предположенная моделью.
Издержки.
- Комиссия. Если
commissionValueне задан, тестер берёт тариф по умолчанию: 0,02 % за лимитные исполнения и 0,05 % за рыночные и стоп. Тариф вашего счёта и рибейты терминалу неизвестны. Заданное число действует на все исполнения. - Проскальзывание —
slippageTicksпротив вас на рыночных и стоп-исполнениях. Лимитные не проскальзывают. - Фандинг списывается на каждом реальном расчёте биржи — по фактической ставке и цене закрытия бара расчёта. Для этого нужны данные фандинга: фьючерсы на биржах из таблицы покрытия.
- Маржа. С
marginLong/marginShortили плечом больше 1 тестер моделирует упрощённую изолированную маржу. Длинная позиция ликвидируется при цене вход × (1 − m/2), короткая — при вход × (1 + m/2), где m — доля начальной маржи. Ликвидированная сделка теряет всю начальную маржу.
Сделки, не закрытые к концу истории, закрываются по закрытию последнего бара с причиной «конец данных».
Тестер стратегии
Добавьте стратегию на график
В редакторе скриптов нажмите Добавить на график (Ctrl+Enter). Скрипт с ошибками на график не попадёт.
Откройте тестер
В панели инструментов редактора нажмите Тестер стратегии… — кнопка активна только для скрипта с strategy(...). Второй путь — настройки стратегии из легенды графика (шестерёнка), кнопка Тестер стратегии….
Дождитесь прогона
Тестер считает по свечам, загруженным на графике. Пока их нет, окно пишет «Стратегия ещё не считалась: дождитесь загрузки свечей». Когда приходят новые свечи, результат пересчитывается сам; кнопка Пересчитать запускает полный прогон вручную.
Если на графиках несколько стратегий, нужную можно выбрать прямо в окне тестера.
| Вкладка | Что на ней |
|---|---|
| Сводка | Плитки «Чистая прибыль», «Профит-фактор», «Макс. просадка (по закрытиям)», «Доля прибыльных», «SQN»; полоса «Чего этот бэктест не знает»; кривые капитала, «купил-и-держи», капитала без фандинга и двух просадок |
| Показатели | Результат, риск (просадка по закрытиям и внутри бара отдельно, Шарп, Сортино, Калмар), сделки (доля прибыльных, средние и крупнейшие, серии), издержки (комиссия и фандинг) |
| Сделки | Таблица сделок: вход, выход, причина, объём, P&L, ран-ап, просадка, комиссия, фандинг. Клик по строке прокручивает график к бару входа, Ctrl+C копирует выделенные строки |
| Свойства | Параметры объявления, диапазон баров, шаг цены, шаг количества, минимальный лот, комиссия, счётчики прогона: отклонённые заявки, пропущенные по pyramiding, ликвидации, расчёты фандинга |
В заголовке окна — метки режима: conservative или optimistic по модели исполнения и close-fill при processOrdersOnClose = true. Метки optimistic и close-fill значат, что вживую результат, скорее всего, будет хуже.
Чего стратегия читать не может
Живой стакан и лента. Стратегия, которая читает book.* или tape.*, не компилируется: «Стратегия читает … у стакана и ленты нет истории, бэктест был бы вымыслом». Для истории стакана есть depth.*, для сделок — delta.* и cluster.*.
Скринер. screener.* тоже есть только на последнем баре, но компилятор такую стратегию пропускает. На истории значения скринера — na, поэтому условия на них там не выполняются, и сделок по ним в отчёте не будет.
Заглядывание вперёд. request.security(..., lookahead = true) в стратегии даёт в редакторе предупреждение «Стратегия заглядывает вперёд … отчёт помечен как недостижимый в реальном времени». Относитесь к такому отчёту как к завышенному: на истории стратегия видит значения, которых вживую ещё не было. Подробнее — в разделе Данные рынка.
Пример
Пробой канала с подтверждением дельтой. Вход — на закрытии за границей канала за последние len баров, если дельта бара в ту же сторону. Стоп — в ATR, тейк — вдвое дальше.
strategy("Пробой канала с дельтой", overlay = true,
initialCapital = 10000,
defaultQtyType = strategy.qty.percent_of_equity, defaultQtyValue = 20,
commissionValue = 0.05, slippageTicks = 2)
len = input.int(20, "Канал, баров", minval = 5, maxval = 200)
stopAtr = input.float(1.5, "Стоп, ATR", minval = 0.5, maxval = 10, step = 0.1)
needFlow = input.bool(true, "Подтверждать дельтой")
upper = ta.highest(high, len)[1]
lower = ta.lowest(low, len)[1]
atr = ta.atr(14)
flowLong = not needFlow or (delta.available and delta.value > 0)
flowShort = not needFlow or (delta.available and delta.value < 0)
if close > upper and flowLong and strategy.position_size <= 0 {
strategy.entry("L", strategy.long, comment = "пробой вверх")
}
if close < lower and flowShort and strategy.position_size >= 0 {
strategy.entry("S", strategy.short, comment = "пробой вниз")
}
stopTicks = math.round(atr * stopAtr / sym.tick_size)
strategy.exit("L-x", from_entry = "L", loss = stopTicks, profit = stopTicks * 2)
strategy.exit("S-x", from_entry = "S", loss = stopTicks, profit = stopTicks * 2)
plot(upper, "Верх канала", color = color.teal)
plot(lower, "Низ канала", color = color.red)На бирже без дельты (например, Binance Alpha) при включённой настройке «Подтверждать дельтой» стратегия не сделает ни одной сделки: delta.available там ложно. Это правило na в действии, а не ошибка.
Ограничения
- Только симуляция: реальных заявок нет.
- Лимитные заявки исполняются так, будто вы стояли первым в очереди. Для мейкерских стратегий это главный источник завышенного результата.
- Ваши сделки не двигают цену, частичных исполнений нет: заявка исполняется целиком или никак.
- Путь цены внутри бара предполагается моделью, а не берётся из младшего таймфрейма.
- Ликвидация упрощена: движок ликвидаций биржи, ADL и страховой фонд не моделируются.
- Комиссия — тариф по умолчанию, а не тариф вашего счёта.
- Отсутствие данных — не отсутствие движения: бар без кластеров всё равно двигался.
- Результат зависит от того, сколько истории загружено на графике.
Дальше
https://docs.lootx.trade/ru/loot/strategies