LootX

Example scripts

new Updated September 30, 2026 This page as PDF

Eight ready LootScript scripts: moving averages, RSI, VWAP with bands, cumulative delta with divergences, open interest, funding rate, cluster imbalances, and a strategy for the Strategy Tester.

Eight ready LootScript scripts — from moving averages and RSI to cumulative delta, open interest, funding rate and clusters. Each is a working indicator you can add to a chart and tweak. All examples compile with the current compiler without errors or warnings.

How to run an example

  1. Click ƒ on the chart panel; in the Indicators window, click Editor….
  2. In the Editor, click New (Ctrl+N), replace the template with the example code, and click Save.
  3. Click Add to chart (Ctrl+Enter). Settings from input.* will appear in the indicator settings window.

1. Three MAs with type selector

Fast, medium and slow moving averages of the selected type. Background highlights when all three align by trend.

Loot
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)

How it works

  • input.string with options becomes a dropdown in the settings.
  • The ma function selects the MA via switch. The ta.* state is local to each call site, so three calls to ma produce three independent averages.
  • The fast line’s color is an expression: if it’s rising vs the prior bar (m1[1]) it’s teal, otherwise red.
  • The panel can draw up to three plot lines per script — hence three MAs.

2. RSI with zones and alerts

RSI with a filled band between levels. Line and background color when RSI sits in overbought or oversold zones.

Loot
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 поднялся выше нижнего уровня")

How it works

  • hline returns a level handle, and fill paints the zone between two handles.
  • Levels are input.float settings, so lines and fill move with them.
  • alertcondition declares two alerts. They don’t ring by themselves — how to enable them is explained on the Script alerts page.
  • Alpha in color.new is 0 (transparent) to 255 (opaque).

3. Day VWAP with deviation bands

Volume-weighted average price from UTC midnight and a ±σ band around it.

Loot
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))

How it works

  • ta.vwap restarts on the bar where the anchor is true. For crypto session.isfirstbar is the first bar after 00:00 UTC.
  • var variables persist across bars. They are zeroed by the same anchor, so the band is calculated for the same period as VWAP.
  • Deviation is sqrt(weighted mean of price^2 minus VWAP^2). math.max(…, 0) protects against tiny negatives from rounding.
  • This is meaningful on intraday timeframes. If you want VWAP since the last funding payment, set anchor = funding.settled.

4. Daily cumulative delta with divergences

Cumulative delta that resets daily, with marks on bars where price updates an extreme but delta does not.

Loot
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)

How it works

  • delta.value is aggressive buying minus aggressive selling for the bar. Where the feed is missing delta.available is false and the line gaps rather than plotting zero.
  • The accumulator var is reset at the start of the day. If you need a cumulative delta over the whole loaded history without resets, use the ready series delta.cum.
  • Divergence: price at the window high, but cumulative delta below its high for the same window. Minimums are mirrored.
  • location.top and location.bottom place marks at the pane edges, not at price.

5. Open interest spikes

Open interest change from bar to bar in percent, with marks where it deviates from normal volatility.

Loot
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)

How it works

  • oi.close is open interest at the bar close, oi.close[1] at the previous bar. oi.available prevents computing changes from nothing.
  • A spike is a bar whose absolute change exceeds k standard deviations over len bars.
  • Open interest arrives per minute. On timeframes under a minute adjacent bars repeat the same value and the change is zero — that’s the data, not a bug.

6. Funding rate

Perpetual funding rate in percent per period or annualized, with marks on settlements and highlighting for high rates.

Loot
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)

How it works

  • funding.rate is a fraction, not percent: 0.0001 means 0.01%. So the rate is multiplied by 100.
  • For annualized values the rate is multiplied by the number of settlements per year. 24.0 is written as a float deliberately: integer division in LootScript truncates.
  • funding.settled is true on the bar where a payment occurred — a dot is placed there.
  • The threshold is specified per period and is converted the same way for annualized mode.

7. Cluster imbalances

Marks on the price chart where a bar’s clusters contain a vertical ladder of imbalances or simply many imbalances, plus the POC (maximum volume level) of each bar.

Loot
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)

How it works

  • cluster.imbalance(i) returns +1, −1 or 0 for price level i. The imbalance threshold comes from the chart cluster settings, so the script sees the same definition as the chart.
  • cluster.stacked_buy_steps is how many consecutive levels contain buy imbalances. The for i in 0 .. levels loop counts all imbalances in the bar, not only consecutive ones.
  • The script subscribes to cluster data and runs on a normal candlestick chart. On exchanges without coverage, and on timeframes under 30 seconds or over a day, there are no clusters — then levels is 0 and no marks appear.

8. Strategy: EMA cross with ATR stop

Long entry on MA cross, stop and take-profit in ATR multiples, exit on the opposite cross.

Loot
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)

How it works

  • strategy(…) parameters are literals: capital 10,000, position size 10% of equity, slippage 2 ticks. Commission is not set, so the tester uses the default fee: 0.02% for limit fills and 0.05% for market and stop. Set your own percentage via commissionValue.
  • Market entries execute on the open of the next bar, not on the signal bar’s close: the tester does not look into the future.
  • strategy.exit sets stop and target from the average entry price. If both are touched in one bar, the tester by default assumes the stop fired.
  • Results — trades, equity curve, drawdowns — appear in the Strategy Tester… window of the Editor. See Strategies and tester for details.

Next

FAQ

How do I add an example to the chart?
Open the Editor (ƒ on the chart panel → 'Editor…'), click 'New', paste the code, save and click 'Add to chart'.
Why doesn't the example with footprint clusters or the funding rate draw anything?
That data isn't available for the current instrument: footprint clusters aren't available on every exchange or timeframe, and the funding rate exists only for perpetual futures. When the data is missing, the value is na and is omitted from the chart.
Can I trade a strategy from an example?
Only in the Strategy Tester, on a simulated account. LootScript scripts do not place real orders.