Примеры скриптов
Восемь готовых скриптов на LootScript: скользящие, RSI, VWAP с полосами, кумдельта с расхождениями, открытый интерес, фандинг, дисбалансы кластеров и стратегия для тестера.
Восемь готовых скриптов на LootScript: от скользящих и RSI до кумдельты, открытого интереса, фандинга и кластеров. Каждый — рабочий индикатор, который можно сразу положить на график и подстроить под себя. Все примеры собираются компилятором текущей версии без ошибок и предупреждений.
Как запустить пример
- Нажмите ƒ на панели графика, в окне Индикаторы — Редактор….
- В редакторе нажмите Новый (Ctrl+N), замените шаблон кодом примера и нажмите Сохранить.
- Нажмите Добавить на график (Ctrl+Enter). Настройки из
input.*появятся в окне настроек индикатора.
1. Три скользящие с выбором типа
Быстрая, средняя и медленная средние одного типа на выбор. Фон подсвечивается, когда все три выстроились по тренду.
indicator("Три скользящие", shortTitle = "MA×3", overlay = true)
maType = input.string("EMA", "Тип средней", options = ["EMA", "SMA", "WMA", "HMA"])
src = input.source(close, "Источник")
len1 = input.int(20, "Быстрая", minval = 1)
len2 = input.int(50, "Средняя", minval = 1)
len3 = input.int(200, "Медленная", minval = 1)
func ma(s, len) => switch maType {
"SMA" => ta.sma(s, len)
"WMA" => ta.wma(s, len)
"HMA" => ta.hma(s, len)
=> ta.ema(s, len)
}
m1 = ma(src, len1)
m2 = ma(src, len2)
m3 = ma(src, len3)
plot(m1, "Быстрая", color = m1 >= m1[1] ? color.teal : color.red, linewidth = 2)
plot(m2, "Средняя", color = color.orange)
plot(m3, "Медленная", color = color.gray, linewidth = 2)
bgcolor(m1 > m2 and m2 > m3 ? color.new(color.teal, 20) : m1 < m2 and m2 < m3 ? color.new(color.red, 20) : na)Как устроено
input.stringсoptionsпревращается в выпадающий список в настройках.- Функция
maвыбирает среднюю черезswitch. Состояниеta.*живёт у каждого места вызова, поэтому три вызоваma— три независимые средние. - Цвет быстрой линии — выражение: растёт по сравнению с прошлым баром (
m1[1]) — бирюзовая, падает — красная. - Панель рисует до трёх линий
plotна скрипт — поэтому средних три.
2. RSI с зонами и оповещениями
RSI с закрашенной полосой между уровнями. Линия и фон окрашиваются, когда RSI в зоне перекупленности или перепроданности.
indicator("RSI с зонами", shortTitle = "RSI", overlay = false)
len = input.int(14, "Период", minval = 2)
src = input.source(close, "Источник")
ob = input.float(70, "Перекупленность", minval = 50, maxval = 100)
os = input.float(30, "Перепроданность", minval = 0, maxval = 50)
r = ta.rsi(src, len)
hOb = hline(ob, "Перекупленность", color = color.red)
hOs = hline(os, "Перепроданность", color = color.teal)
fill(hOb, hOs, color = color.new(color.purple, 20))
plot(r, "RSI", color = r > ob ? color.red : r < os ? color.teal : color.purple, linewidth = 2)
bgcolor(r > ob ? color.new(color.red, 35) : r < os ? color.new(color.teal, 35) : na)
alertcondition(ta.crossunder(r, ob), "RSI вышел из перекупленности", "RSI опустился ниже верхнего уровня")
alertcondition(ta.crossover(r, os), "RSI вышел из перепроданности", "RSI поднялся выше нижнего уровня")Как устроено
hlineвозвращает ручку уровня, аfillзакрашивает полосу между двумя ручками.- Уровни — настройки
input.float, поэтому линии и заливка двигаются вместе с ними. alertconditionобъявляет два оповещения. Сами по себе они не звенят — как их включить, рассказано на странице Алерты из скриптов.- Альфа в
color.new— от 0 (прозрачно) до 255 (непрозрачно).
3. VWAP дня с полосами отклонения
Средневзвешенная по объёму цена с начала суток по UTC и полоса ±σ вокруг неё.
indicator("VWAP дня с полосами", shortTitle = "VWAP", overlay = true)
src = input.source(hlc3, "Цена")
mult = input.float(1.0, "Ширина полосы, σ", minval = 0.1, step = 0.1)
anchor = session.isfirstbar
vw = ta.vwap(src, anchor)
// Сумма объёма и сумма объёма на квадрат цены с начала дня — для разброса вокруг VWAP.
var float sumV = 0.0
var float sumV2 = 0.0
if anchor {
sumV := 0.0
sumV2 := 0.0
}
if not na(volume) and not na(src) {
sumV := sumV + volume
sumV2 := sumV2 + volume * src * src
}
sigma = sumV > 0 and not na(vw) ? math.sqrt(math.max(sumV2 / sumV - vw * vw, 0)) : na
pUp = plot(vw + mult * sigma, "Верх", color = color.new(color.blue, 150))
plot(vw, "VWAP", color = color.blue, linewidth = 2)
pDn = plot(vw - mult * sigma, "Низ", color = color.new(color.blue, 150))
fill(pUp, pDn, color = color.new(color.blue, 25))Как устроено
ta.vwapначинает счёт заново на баре, где якорь истинен. У криптовалютsession.isfirstbar— первый бар после 00:00 UTC.- Переменные
varпереживают переход между барами. Их обнуляет тот же якорь, поэтому полоса считается за тот же период, что и VWAP. - Разброс — корень из средневзвешенного квадрата цены минус квадрат VWAP.
math.max(…, 0)страхует от крошечного минуса из-за округления. - Смысл есть на внутридневных таймфреймах. Хотите VWAP от последней выплаты фандинга — поставьте
anchor = funding.settled.
4. Дневная кумдельта с расхождениями
Кумулятивная дельта, которая обнуляется каждые сутки, и отметки на барах, где цена обновила экстремум, а дельта — нет.
indicator("Дневная кумдельта с расхождениями", shortTitle = "CVD", overlay = false)
len = input.int(20, "Окно поиска максимумов", minval = 3)
var float cvd = 0.0
if session.isfirstbar {
cvd := 0.0
}
if delta.available {
cvd := cvd + delta.value
}
cvdLine = delta.available ? cvd : na
// Цена обновила максимум окна, а кумдельта — нет: покупатели не подтверждают рост.
bearish = high >= ta.highest(high, len) and cvdLine < ta.highest(cvdLine, len)
bullish = low <= ta.lowest(low, len) and cvdLine > ta.lowest(cvdLine, len)
plot(cvdLine, "Кумдельта", color = cvdLine >= 0 ? color.teal : color.red, linewidth = 2)
hline(0, "Ноль", color = color.gray)
plotshape(bearish, "Медвежье расхождение", style = shape.triangledown, location = location.top, color = color.red, size = size.tiny)
plotshape(bullish, "Бычье расхождение", style = shape.triangleup, location = location.bottom, color = color.teal, size = size.tiny)Как устроено
delta.value— агрессивные покупки минус агрессивные продажи за бар. Где покрытия нет,delta.availableложно, и линия прерывается, а не рисует ноль.- Накопитель
varобнуляется в начале суток. Нужна кумдельта по всей загруженной истории без сброса — возьмите готовый рядdelta.cum. - Расхождение: цена на максимуме окна, а кумдельта ниже своего максимума за то же окно. Для минимумов — зеркально.
location.topиlocation.bottomставят отметки у краёв подокна, а не у цены.
5. Всплески открытого интереса
Изменение открытого интереса от бара к бару в процентах и отметки там, где оно выбивается из обычного разброса.
indicator("Изменение открытого интереса", shortTitle = "ΔOI %", overlay = false)
len = input.int(50, "Окно для всплесков", minval = 5)
k = input.float(3.0, "Порог всплеска, σ", minval = 0.5, step = 0.5)
pct = oi.available and oi.close[1] > 0 ? 100 * (oi.close - oi.close[1]) / oi.close[1] : na
sd = ta.stdev(pct, len)
spike = not na(sd) and math.abs(pct) > k * sd
plot(pct, "ΔOI, %", style = plot.style_columns, color = pct >= 0 ? color.teal : color.red)
hline(0, "Ноль", color = color.gray)
plotshape(spike, "Всплеск", style = shape.circle, location = location.top, color = color.orange, size = size.tiny)Как устроено
oi.close— открытый интерес на закрытии бара,oi.close[1]— на прошлом баре. Проверкаoi.availableне даёт посчитать изменение из пустоты.- Всплеск — бар, где изменение по модулю больше
kстандартных отклонений за окноlen. - Открытый интерес приходит поминутно. На таймфреймах меньше минуты соседние бары повторяют одно значение, и изменение там нулевое — это данные, а не ошибка.
6. Ставка финансирования
Фандинг бессрочного фьючерса в процентах за период или в пересчёте на год, с отметками выплат и подсветкой высокой ставки.
indicator("Ставка финансирования", shortTitle = "Funding", overlay = false)
mode = input.string("За период", "Показывать", options = ["За период", "Годовых"])
thr = input.float(0.05, "Порог, %", minval = 0.0, step = 0.01)
perPeriod = funding.rate * 100
periodsPerYear = funding.interval_hours > 0 ? 24.0 / funding.interval_hours * 365 : na
value = mode == "Годовых" ? perPeriod * periodsPerYear : perPeriod
level = mode == "Годовых" ? thr * periodsPerYear : thr
plot(value, "Ставка, %", style = plot.style_columns, color = value >= 0 ? color.teal : color.red)
hline(0, "Ноль", color = color.gray)
plotshape(funding.settled, "Выплата", style = shape.circle, location = location.bottom, color = color.gray, size = size.tiny)
bgcolor(math.abs(value) > level ? color.new(color.orange, 30) : na)Как устроено
funding.rate— доля, а не проценты: 0.0001 — это 0,01 %. Поэтому ставка умножается на 100.- Для годовых ставка умножается на число выплат в год.
24.0записано дробью намеренно: деление двух целых в LootScript отбрасывает остаток. funding.settledистинно на баре, где прошла выплата, — там внизу ставится точка.- Порог задаётся за период и для годовых пересчитывается тем же множителем.
7. Дисбалансы в кластерах
Отметки на графике цены там, где в кластерах бара стоит «лесенка» дисбалансов или их просто много, плюс уровень максимального объёма (POC) каждого бара.
indicator("Дисбалансы кластеров", shortTitle = "Imbalance", overlay = true)
minSteps = input.int(3, "Уровней подряд", minval = 2)
minCount = input.int(5, "Дисбалансов в баре", minval = 1)
levels = cluster.available ? cluster.levels : 0
buys = 0
sells = 0
for i in 0 .. levels {
side = cluster.imbalance(i)
if side == 1 {
buys := buys + 1
} else if side == -1 {
sells := sells + 1
}
}
plot(cluster.poc, "POC", style = plot.style_circles, color = color.orange)
plotshape(cluster.stacked_buy_steps >= minSteps, "Лесенка покупок", style = shape.triangleup, location = location.belowbar, color = color.teal, size = size.small)
plotshape(cluster.stacked_sell_steps >= minSteps, "Лесенка продаж", style = shape.triangledown, location = location.abovebar, color = color.red, size = size.small)
plotshape(buys >= minCount, "Много дисбалансов покупок", style = shape.circle, location = location.belowbar, color = color.new(color.teal, 120), size = size.tiny)
plotshape(sells >= minCount, "Много дисбалансов продаж", style = shape.circle, location = location.abovebar, color = color.new(color.red, 120), size = size.tiny)Как устроено
cluster.imbalance(i)возвращает +1, −1 или 0 для ценового уровняi. Порог дисбаланса берётся из настроек кластеров графика, поэтому скрипт видит то же, что вы.cluster.stacked_buy_steps— сколько уровней подряд идёт дисбаланс покупок. Циклfor i in 0 .. levelsсчитает все дисбалансы бара, не только подряд.- Скрипт сам подписывается на кластеры и работает на обычном свечном графике. На бирже без покрытия и на таймфреймах меньше 30 секунд или больше суток кластеров нет — тогда
levelsравно 0 и отметок не будет.
8. Стратегия: пересечение EMA со стопом по ATR
Вход в лонг на пересечении средних, стоп и цель — в долях ATR, выход и по обратному пересечению.
strategy("EMA-кросс со стопом по ATR", overlay = true, initialCapital = 10000,
defaultQtyType = strategy.qty.percent_of_equity, defaultQtyValue = 10,
slippageTicks = 2)
fastLen = input.int(12, "Быстрая EMA", minval = 1)
slowLen = input.int(26, "Медленная EMA", minval = 2)
atrLen = input.int(14, "Период ATR", minval = 1)
stopK = input.float(1.5, "Стоп, в ATR", minval = 0.1, step = 0.1)
takeK = input.float(3.0, "Цель, в ATR", minval = 0.1, step = 0.1)
fast = ta.ema(close, fastLen)
slow = ta.ema(close, slowLen)
atr = ta.atr(atrLen)
// ATR запоминается в момент сигнала, чтобы стоп и цель не ползли вслед за волатильностью.
var float riskAtr = na
if ta.crossover(fast, slow) and strategy.position_size == 0 {
riskAtr := atr
strategy.entry("Long", strategy.long)
}
if ta.crossunder(fast, slow) {
strategy.close("Long", comment = "Обратный кросс")
}
if strategy.position_size > 0 {
entry = strategy.position_avg_price
strategy.exit("Стоп/цель", from_entry = "Long", stop = entry - stopK * riskAtr, limit = entry + takeK * riskAtr)
}
plot(fast, "Быстрая", color = color.teal)
plot(slow, "Медленная", color = color.orange)Как устроено
- Параметры
strategy(…)— литералы: капитал 10 000, размер позиции 10 % капитала, проскальзывание 2 тика. Комиссия не задана, поэтому тестер берёт тариф по умолчанию: 0,02 % за лимитное исполнение и 0,05 % за рыночное и стоп. Свой тариф в процентах задаётcommissionValue. - Рыночный вход исполняется по открытию следующего бара, а не по закрытию сигнального: так тестер не заглядывает в будущее.
strategy.exitставит стоп и цель от средней цены входа. Если за один бар задеты оба, тестер по умолчанию считает, что сработал стоп.- Результаты — сделки, кривая капитала, просадки — в окне Тестер стратегии… редактора. Подробнее — на странице Стратегии и тестер.