← К документации
LootX

Синтаксис и типы

Собрано: 2026-10-01 docs.lootx.trade
Основы

1Синтаксис и типы

Как устроен LootScript: выполнение по барам, объявление скрипта, пять форм присваивания, типы и квалификаторы, na, история x[n], операторы, условия, switch, циклы, функции и свои типы.

Эта страница — краткое описание языка: как выполняется скрипт, как объявлять переменные, какие есть типы, что такое na, как читать прошлые бары, как писать условия, циклы и функции. Все примеры — целые рабочие скрипты: их можно вставить в редактор и добавить на график.

Как выполняется скрипт

Всё, что написано на верхнем уровне файла, выполняется один раз на каждый бар — от самого старого загруженного к текущему, строка за строкой. Поэтому переменная в LootScript хранит не одно число, а значение на каждом баре, и к прошлым значениям можно обратиться.

Loot
indicator("Тело свечи")

body = close - open          // считается заново на каждом баре
plot(body, "Тело", style = plot.style_columns)

На текущем, ещё не закрытом баре скрипт пересчитывается с каждым новым тиком, начиная с состояния, которое было на закрытии предыдущего бара. Закрытые бары при этом не пересчитываются.

Объявление

Первая инструкция скрипта — ровно одно объявление: indicator(...), strategy(...) или library(...). Выше него могут стоять только комментарии и необязательная строка версии #version 1. Без объявления скрипт не скомпилируется с ошибкой «Нет объявления скрипта — начните с indicator(“Название”)».

Loot
#version 1
// Комментарии и строка версии могут стоять выше объявления.
indicator("Размах бара", shortTitle = "Размах", overlay = false)

plot(high - low, "Размах", style = plot.style_histogram)

Главные параметры индикатора: title — название в списке скриптов, shortTitle — короткое имя для легенды, overlay — рисовать поверх цены (true) или в отдельной панели (false, по умолчанию). Параметры стратегии описаны на странице Стратегии и тестер.

Переменные и присваивание

Форма Что делает
x = выражение Объявляет переменную. На каждом баре она считается заново.
x := выражение Присваивает новое значение уже объявленной переменной. Тип должен совпадать.
var x = выражение Объявляет переменную один раз, на первом баре. Значение переходит на следующие бары.
varip x = выражение Как var, но значение сохраняется и между тиками текущего бара.
[a, b, c] = функция(...) Раскладывает несколько результатов функции по переменным.

Для изменения уже объявленной переменной есть и короткие формы: +=, -=, *=, /=, %=. Тип можно указать явно: float level = 0. Имена встроенных рядов и пространств (close, volume, ta и других) заняты — назвать так свою переменную нельзя.

Главное отличие от привычных языков — обычная переменная не накапливает значение между барами. Для счётчиков и сумм нужен var:

Loot
indicator("Счётчик зелёных свечей")

var greens = 0          // объявляется один раз и переживает переход к следующему бару
if close > open {
    greens += 1
}

today = 0               // а эта переменная равна 0 на каждом новом баре
today := today + 1      // поэтому здесь всегда 1

plot(greens, "Зелёных с начала истории")

varip нужен редко — например, чтобы считать тики внутри бара. На истории тиков нет, поэтому такое значение на прошлых барах будет другим, чем было вживую. Если значение varip попадает на график, редактор предупредит о перерисовке.

Типы

Тип Что хранит Пример
int целое число 14, 1_000_000, 0xFF
float дробное число 2.5, .5, 1e-9
bool истина или ложь true, false
string текст "BTC", 'BTC'
color цвет с прозрачностью #FF9800, #FF980080, color.teal

Есть и составные типы: массивы array<float>, словари map<string, float>, матрицы matrix<float>, а также ваши собственные типы и перечисления (см. ниже) и графические объекты line, box, label, table.

Правила, о которые спотыкаются чаще всего:

  • Тип фиксируется при объявлении. После x = 1 присвоить x := 2.5 нельзя: x — целое. Пишите x = 1.0 или float x = 1.
  • Целое превращается в дробное само, обратно — только явно: math.round, math.floor, math.ceil, math.trunc.
  • Деление целых — целое: 7 / 2 равно 3. Нужна дробь — сделайте одно из чисел дробным: 7.0 / 2.
  • Возведение в степень ^ всегда даёт дробное число: 2 ^ 3 равно 8.0.
  • Строку нельзя сложить с числом. "Цена: " + close не скомпилируется — пишите "Цена: " + str.tostring(close).
  • Условие — только bool. if volume { … } — ошибка; пишите if volume > 0 { … }.

Квалификаторы: насколько значение постоянно

Кроме типа у каждого значения есть квалификатор — насколько оно постоянно во время работы скрипта. От слабого к сильному:

Квалификатор Когда известно значение Пример
const уже при компиляции 14, "EMA"
input после чтения настроек, дальше не меняется input.int(14, "Период")
simple при запуске, дальше не меняется шаг цены инструмента
series может меняться на каждом баре close, bar_index

На практике это важно в одном месте: периоды функций ta.* должны быть постоянными — литералом или настройкой. Иначе функции пришлось бы пересобирать своё состояние на каждом баре.

Loot
indicator("Период из настроек")

len = input.int(14, "Период", minval = 2)
plot(ta.rsi(close, len), "RSI")

// ta.rsi(close, bar_index) не скомпилируется:
// «length» в «ta.rsi» требует значение не слабее input, а передано series

na — нет значения

na означает «значения нет»: не хватает истории для расчёта, биржа не отдаёт показатель, в данных дыра. na есть в любом типе.

  • Арифметика с na даёт na: na + 1 — это na.
  • Любое сравнение с na ложно, поэтому if x > 0 при x = na не выполнится.
  • Проверяйте отсутствие только через na(x). Запись x == na компилятор отклонит с ошибкой «Сравнение с na всегда ложно — используйте na(x)».
  • nz(x) подставляет 0 вместо na, nz(x, v) — значение v. То же короче: x ?? v.
  • na на графике — разрыв линии, а не ноль.
Loot
indicator("Средняя без пустого начала", overlay = true)

avg = ta.sma(close, 50)             // на первых 49 барах — na

line1 = na(avg) ? close : avg       // проверка через na(x)
line2 = avg ?? close                // то же короче

plot(line2, "Средняя или цена", color = color.orange)

История: x[n]

x[n] — значение x на баре, который был n баров назад; x[0] — текущее. Работает с любым рядом, в том числе с вашими переменными.

Loot
indicator("Наклон средней")

ma    = ta.ema(close, 20)
slope = ma - ma[1]                  // изменение за один бар

plot(slope, "Наклон", style = plot.style_histogram,
     color = slope >= 0 ? color.teal : color.red)
  • Если n больше числа баров до начала истории, результат — na, а не ошибка.
  • n может меняться от бара к бару: close[len] при переменном len допустимо.
  • Отрицательный индекс — ошибка: заглядывать в будущие бары нельзя.
  • Настраивать глубину истории не нужно: скрипту доступна вся история, загруженная на график.

Операторы

От высшего приоритета к низшему:

Приоритет Операторы
1 x[n], вызов f(…), доступ через точку a.b
2 ^ — степень, вычисляется справа налево
3 унарные -, +, not
4 *, /, %
5 +, -
6 <, >, <=, >=
7 ==, !=
8 and
9 or
10 ?? — замена na
11 ? : — условное выражение
  • Сравнения не сцепляются: вместо a < b < c пишите a < b and b < c.
  • and и or не вычисляют правую часть, если ответ уже ясен по левой.
  • Степень старше унарного минуса: -2 ^ 2 равно -4. Нужно 4 — пишите (-2) ^ 2.
  • Дробные числа сравнивайте с допуском: math.approx(a, b), потому что 0.1 + 0.2 == 0.3 ложно.

Условия

Блоки всегда в фигурных скобках. if бывает инструкцией и выражением; для простого выбора из двух есть ? :.

Loot
indicator("Направление бара")

dir = 0
if close > open {
    dir := 1
} else if close < open {
    dir := -1
}

// if как выражение: обе ветки обязательны
bodySize = if close >= open { close - open } else { open - close }

// условный оператор для простых случаев
barColor = dir > 0 ? color.teal : color.red

plot(bodySize, "Тело", style = plot.style_columns, color = barColor)

switch

switch выбирает первую подходящую ветку. Форма с объектом сравнивает его со значениями веток, форма без объекта проверяет условия по порядку. Ветка => без условия — ветка по умолчанию. Проваливания в следующую ветку нет. Когда switch возвращает значение, ветка по умолчанию обязательна.

Loot
indicator("Средняя на выбор", overlay = true)

kind = input.string("EMA", "Тип средней", options = ["EMA", "SMA", "WMA"])

ma = switch kind {
    "EMA" => ta.ema(close, 20)
    "SMA" => ta.sma(close, 20)
    =>       ta.wma(close, 20)
}

zone = switch {
    close > ma * 1.02 => 1
    close < ma * 0.98 => -1
    =>                   0
}

plot(ma, "Средняя", color = zone == 1 ? color.teal : zone == -1 ? color.red : color.gray)

Циклы

for i in a .. b перебирает числа от a включительно до b не включительно. Шаг задаётся словом by, в том числе отрицательный. Переменная цикла существует только внутри цикла и не меняется в теле.

Loot
indicator("Зелёные свечи за N баров")

n = input.int(10, "Сколько баров", minval = 1, maxval = 200)

greens = 0
for i in 0 .. n {               // i = 0, 1, …, n − 1
    if close[i] > open[i] {
        greens += 1
    }
}

plot(greens, "Зелёных", style = plot.style_columns)
Loot
indicator("Виды циклов")

total = 0
for i in 0 .. 10 by 2 {         // 0, 2, 4, 6, 8
    total += i
}
for i in 10 .. 0 by -1 {        // 10, 9, …, 1
    if i % 3 == 0 {
        continue                // сразу к следующему шагу
    }
    total += i
}

var closes = array.new<float>(0)
closes.push(close)
if closes.size() > 20 {
    closes.shift()
}
sum = 0.0
for v in closes {               // по элементам массива; for [i, v] in … — с индексом
    sum += v
}

k = 0
while k < 100 {
    k += 7
    if k > 50 {
        break                   // выход из цикла
    }
}

plot(sum / closes.size(), "Средняя из массива")

Бесконечный цикл терминал не повесит: у скрипта есть бюджет операций на бар и лимит времени. Превысив их, скрипт остановится с сообщением «Скрипт исчерпал бюджет операций на бар — упростите цикл» или «Скрипт превысил лимит времени на проход».

Функции

Функция объявляется словом func. Короткая форма — выражение после =>, длинная — тело в скобках с return. Типы параметров можно не указывать — они выводятся из вызовов, а у параметров могут быть значения по умолчанию. Аргументы передаются по порядку или по имени.

Loot
indicator("Z-оценка объёма")

func barRange(h, l) => h - l

func zscore(series float src, input int len) => float {
    m = ta.sma(src, len)
    s = ta.stdev(src, len)
    return s == 0 ? na : (src - m) / s
}

func scaled(x, k = 2.0) => x * k

z = zscore(volume, 50)
plot(z, "Z объёма", style = plot.style_columns, color = z > 2 ? color.orange : color.gray)
plot(scaled(barRange(high, low), k = 0.5), "Полразмаха")
  • Рекурсии нет. Функция не может вызывать саму себя, ни напрямую, ни через другую функцию. Повторение пишется циклом.
  • Состояние — у каждого вызова своё. Если внутри функции есть ta.* или var, два вызова одной функции ведут независимые расчёты:
Loot
indicator("Два вызова одной функции", overlay = true)

func smooth(src, len) => ta.ema(src, len)

fast = smooth(close, 10)        // своя средняя
slow = smooth(close, 30)        // другая, независимая средняя

plot(fast, "Быстрая", color = color.teal)
plot(slow, "Медленная", color = color.orange)

Функции, нужные в нескольких скриптах, выносят в библиотеку: скрипт с объявлением library("Имя", 1), перед функциями — слово export, в другом скрипте — import Имя/1 as имя. Библиотека берётся из вашей папки скриптов.

Несколько результатов

Некоторые функции возвращают сразу несколько рядов: ta.bb, ta.macd, ta.kc, ta.dmi, ta.supertrend и другие. Их результат раскладывается по переменным в квадратных скобках; число имён должно совпадать с числом результатов.

Loot
indicator("Полосы Боллинджера", overlay = true)

[basis, upper, lower] = ta.bb(close, 20, 2.0)

plot(basis, "Середина", color = color.gray)
plot(upper, "Верх", color = color.teal)
plot(lower, "Низ", color = color.teal)

Свои типы и перечисления

type собирает несколько полей в одну структуру. Объект создаётся через Имя.new(...), поля читаются и меняются через точку.

Loot
indicator("Последний локальный максимум", overlay = true)

type Swing {
    int   barIndex
    float price
}

var Swing last = Swing.new(barIndex = 0, price = high)

ph = ta.pivothigh(high, 5, 5)
if not na(ph) {
    last.barIndex := bar_index - 5
    last.price := ph
}

plot(last.price, "Максимум", style = plot.style_stepline, color = color.orange)

enum — закрытый список вариантов с подписями. Удобнее строк: опечатка в имени варианта — ошибка компиляции, а не тихо не сработавшее условие. Вместе с input.enum перечисление превращается в выпадающий список в настройках — см. Настройки и отрисовка.

Loot
indicator("Режим рынка")

enum Regime {
    trend = "Тренд"
    flat  = "Флэт"
}

regime = math.abs(close - close[20]) > ta.atr(14) * 3 ? Regime.trend : Regime.flat

plot(regime == Regime.trend ? 1 : 0, "Тренд", style = plot.style_stepline)

Функции ta.* внутри условий

Функции вроде ta.ema, ta.rsi или ta.atr помнят своё состояние с прошлого бара. Если бы такой вызов выполнялся только на части баров — внутри if или одной ветки ? :, — его расчёт пропускал бы бары и тихо давал неверные числа.

LootScript этого не допускает: компилятор сам выносит каждый вызов ta.* из условия и выполняет его на каждом баре, а условие лишь выбирает, использовать ли результат. Поэтому такой код считает RSI правильно:

Loot
indicator("RSI только на зелёных барах")

r = close > open ? ta.rsi(close, 14) : na

plot(r, "RSI", style = plot.style_circles)

Вынести вызов нельзя, только если его аргумент сам рождается внутри ветки. Тогда компилятор не угадывает, а сообщает: «Аргумент «len» нельзя вычислить вне ветки — вынесите вызов ta.* на верхний уровень». Решение — посчитать аргумент до условия:

Loot
indicator("Аргумент до условия")

// Так нельзя — len объявлена внутри ветки:
// if close > open {
//     len = 10
//     e = ta.ema(close, len)
// }

len = 10
e = ta.ema(close, len)

plot(close > open ? e : na, "EMA на зелёных барах", style = plot.style_circles)

Комментарии и перенос строк

  • // — комментарий до конца строки, /* … */ — блочный, блоки можно вкладывать друг в друга.
  • Инструкция заканчивается с концом строки, точка с запятой не нужна.
  • Длинное выражение продолжается на следующей строке, если строка оборвана внутри скобок, заканчивается оператором или следующая строка начинается с оператора, ? или :.
Loot
indicator("Перенос строк")

typical = high
        + low
        + close

signal = close > open
    ? typical / 3
    : na

plot(signal, "Типичная цена на зелёных барах", style = plot.style_circles)

Дальше

https://docs.lootx.trade/ru/loot/syntax