Открытая документация

TRA Script

Язык для собственных индикаторов и торговых стратегий в Trade Lab Pro. Скрипт выполняется последовательно на каждой свече и может обращаться к истории значений, рисовать на графике и моделировать сделки на истории.

Справочник описывает возможности текущего движка. Статус совместимости каждой функции доступен в каталоге API; наличие имени в каталоге само по себе не означает завершённую совместимость.

Правила написания TRA Script 2

Документация открыта без регистрации. Начните с небольшого индикатора ниже, затем используйте разделы и каталог функций. Примеры относятся к текущему движку TRA Script 2.

  1. Заголовок. Укажите //@tra=2 в первой строке. Объявите один индикатор через indicator("Название", overlay=true). Для отдельной панели используйте overlay=false.
  2. Имена и значения. Используйте понятные имена из латинских букв, цифр и подчёркивания, начиная с буквы или подчёркивания. Регистр важен: basis и Basis — разные имена. Дробные числа пишутся с точкой; строки — в кавычках. Логические значения: true и false. Комментарий начинается с //.
  3. Объявление и изменение. basis = ta.ema(close, 20) объявляет переменную. basis := close меняет уже объявленную. Тип можно указать явно: float, int, bool, string, color. var сохраняет состояние между свечами.
  4. Порядок расчёта. Код выполняется сверху вниз на каждой свече. close — текущее закрытие, close[1] — предыдущее. В начале истории значения могут отсутствовать: проверяйте na(value) или используйте nz(value, replacement). Отрицательный индекс истории не используйте.
  5. Выражения. Арифметика: + - * / %; сравнения: > >= < <= == !=; логика: and or not. Условный выбор: condition ? yes : no. Скобки задают порядок операций.
  6. Блоки и функции. Вложенный блок if/else, цикла или функции выделяйте одинаковым отступом, например четырьмя пробелами. Функция объявляется через => и возвращает последнее выражение. Примеры циклов и коллекций приведены ниже.
  7. Параметры и отрисовка. Используйте input.* для настроек пользователя. Значения выводятся через plot(), метки — через plotshape() и plotchar(), уровни — через hline(). Имена аргументов пишите точно, например title= и color=.
  8. Проверка. В редакторе нажмите «Проверить», исправьте указанные ошибки и сохраните скрипт. Сверьте значения и рисунки на нескольких свечах. При импорте Pine прочитайте отчёт: неподдержанная конструкция требует адаптации, а наличие имени в каталоге не гарантирует полного переноса.

Полный пример: EMA и сигнал пересечения

//@tra=2
indicator("EMA crossing", overlay=true)
length = input.int(20, "Period", minval=1)
basis = ta.ema(close, length)
crossed = ta.crossover(close, basis)
plot(basis, title="EMA", color=color.aqua, linewidth=2)
plotshape(crossed, title="Crossing", style=shape.triangleup, location=location.belowbar, color=color.lime)
alertcondition(crossed, title="EMA crossing", message="Close crossed above EMA")

Period появится в настройках. EMA рассчитывается по закрытию каждой свечи. Треугольник и уведомление возникают при пересечении цены снизу вверх. Измените название, период и цвета, затем проверьте результат на графике.

Первый индикатор

Откройте «Создать скрипт», введите название и код, затем нажмите «Проверить». После успешной проверки сохраните скрипт — он появится в библиотеке и на графике.

//@tra=2
indicator("My EMA", overlay=true)
length = input.int(20, "Length", minval=1)
basis = ta.ema(close, length)
plot(basis, title="EMA", color=color.aqua, linewidth=2)

indicator() создаёт индикатор. overlay=true размещает его поверх цены, overlay=false — в отдельной панели. strategy() создаёт стратегию с отчётом тестера.

Свечи и состояние

open, high, low, close, volume относятся к текущей свече. close[1] — закрытие предыдущей свечи. bar_index — номер свечи, начиная с нуля. Обычное объявление пересчитывается на каждой свече; var инициализируется один раз и сохраняет изменения.

var float total = 0.0
total += volume

float buffer = 0.0
buffer := nz(buffer[1]) * 0.9 + (close - open)
plot(buffer)

Знак = объявляет переменную, := изменяет уже объявленную. na означает отсутствующее значение; проверяйте его через na(value). nz(value, replacement) заменяет отсутствующее значение. История не должна обращаться к будущим свечам.

Параметры

input.int, input.float, input.bool, input.string, input.color, input.symbol и input.source создают настройки. Укажите понятное название, допустимые границы, шаг, группу и подсказку. Шестерёнка на графике открывает параметры; изменения применяются после сохранения.

period = input.int(34, "Period", minval=2, maxval=500)
memory = input.float(0.90, "Memory", minval=0.5, maxval=0.99, step=0.01)
show = input.bool(true, "Show signal")
mode = input.string("Fast", "Mode", options=["Fast", "Slow"])

Функции, условия и циклы

Отступы определяют блоки. Поддерживаются if/else, for, while, break и continue. Пользовательская функция объявляется через => и возвращает последнее выражение блока. Каждый отдельный вызов имеет собственную историю локальных переменных.

pressure(float source, float memory) =>
    float result = 0.0
    result := nz(result[1]) * memory + source
    result

up = pressure(math.max(close - open, 0), 0.9)
down = pressure(math.max(open - close, 0), 0.9)
plot(up - down)

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

Инструкции, циклы и серийные расчёты.

//@tra=2
indicator("Inline function")
normalise(source, length) => mean=ta.sma(source,length), deviation=ta.stdev(source,length), (source-mean)/deviation
plot(normalise(close,20), "Deviation")

Функции объявляются в глобальной области. Одно имя может иметь несколько вариантов с разным числом обязательных аргументов или их типами: int, float, bool, string, color, коллекции и собственные типы. Целое число может передаваться в float; обратное преобразование требует int(). Необязательные параметры и переименование аргументов не различают перегрузки. Именованные аргументы учитываются при выборе варианта. Метод требует тип первого аргумента и вызывается через объект; метод другого типа не подменяет встроенную операцию массива. Каждый вариант и отдельный вызов сохраняют свою историю. Рекурсивные вызовы запрещены.

//@tra=2
indicator("Function variants")
adjust(int value) => value + 10
adjust(float value) => value + 20.0
plot(adjust(1), "Integer")
plot(adjust(1.0), "Float")
plot(adjust(close), "Price")

Функция читает переменные глобальной области, а её аргументы и локальные переменные изолированы от вызывающей функции. Совпадение имени аргумента с глобальной переменной не меняет глобальное значение. Аргументы и глобальные скалярные переменные нельзя переназначать внутри функции. Содержимое переданного массива или поля переданного объекта можно изменять: они передаются по ссылке.

Явные квалификаторы const, simple и series проверяются в объявлениях и аргументах пользовательских функций. const принимает только значения, доступные при компиляции, и запрещает переназначение. simple принимает const, input и simple, а series допускает изменение по свечам. Неквалифицированный аргумент наследует свойство переданного значения; аргумент с указанным типом по умолчанию получает series. Квалификатор результата передаётся последующим вызовам. Для ссылочных типов квалификатор всегда series. Исходные аргументы функций должны быть const; при исходном na нужен тип. Полный статический анализ всех ветвей и автоматическое понижение квалификатора типизированных аргументов по требованиям встроенных функций ещё разрабатываются.

//@tra=2
indicator("Qualifiers")
const int base = 14
add(const int value) => value + 1
selected(simple int value) => value
length = input.int(20, "Length", minval=1)
plot(add(base), "Constant")
plot(selected(length), "Input")

Массивы, карты и матрицы

Массив хранит упорядоченные значения.

values = array.from(1.0, 2.0, 3.0)
window = values.slice(1, 3)
window.set(-1, 4.0)
plot(values.get(2)) // 4

var map<string, float> levels = map.new<string, float>()
levels.put("last", close)
plot(levels.get("last"))

m = matrix.new<float>(2, 2, 0.0)
m.set(0, 0, 1.0)
m.set(1, 1, 1.0)
plot(m.det()) // 1

Карта связывает уникальные ключи со значениями.

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

type задаёт поля объекта.

type Counter
    int count = 0

method increment(Counter this, int step = 1) =>
    this.count += step
    this.count

var Counter counter = Counter.new()
plot(counter.increment())

enum Direction
    longSide = "Long"
    shortSide = "Short"
direction = input.enum(Direction.longSide, "Direction")

Даты и периоды

time — время открытия свечи в миллисекундах Unix.

timestamp использует часовой пояс биржи.

//@tra=2
indicator("Trading time", overlay=true)
opening = timestamp("Europe/Moscow", 2026, 1, 1, 10, 0)
newDay = timeframe.change("1D")
plotshape(newDay and time >= opening, style=shape.flag, color=color.aqua)
plot(hour(time, "UTC"), "UTC hour", display=display.data_window)

Форматирование времени и часовых поясов.

//@tra=2
indicator("Time labels", overlay=true)
var panel=table.new(position.top_right,2,2,bgcolor=color.black)
if barstate.islast
    table.cell(panel,0,0,"Exchange",text_color=color.white)
    table.cell(panel,1,0,str.format_time(time,"dd.MM.yyyy HH:mm z"),text_color=color.aqua)
    table.cell(panel,0,1,"UTC",text_color=color.white)
    table.cell(panel,1,1,str.format_time(time,"yyyy-MM-dd HH:mm:ss.SSS Z","UTC"),text_color=color.white)

Объём и VWAP

Встроенные объёмные серии рассчитываются по OHLCV.

VWAP поддерживает условия сброса и границы отклонения.

//@tra=2
indicator("VWAP bands", overlay=true)
[average, upper, lower] = ta.vwap(hlc3, timeframe.change("1D"), 2)
center = plot(average, "VWAP", color=color.yellow)
top = plot(upper, "Upper", color=color.aqua)
bottom = plot(lower, "Lower", color=color.aqua)
fill(top, bottom, color.new(color.aqua, 90))

Строки

Строки заключаются в кавычки.

//@tra=2
indicator("Text example", overlay=true)
if barstate.islast
    message = str.repeat("[TRA]", 2, " · ")
    label.new(bar_index, high, message, textcolor=color.white)

log.info, log.warning и log.error записывают сообщения в журнал редактора. Аргументы после текста подставляются в {0}, {1} и следующие поля. Журнал показывает уровень, строку исходного кода и время свечи. Хранятся последние 1000 сообщений; текст одного сообщения ограничен 4096 символами.

str.tostring преобразует числа, логические значения, массивы и матрицы в текст. В числовой маске 0 требует цифру, # разрешает убрать лишние нули, запятая разделяет разряды, а % в конце умножает значение на 100. format.mintick округляет по шагу цены инструмента; format.percent добавляет знак процента без умножения. str.format поддерживает нумерованные заполнители, числовые маски, integer/currency/percent, а также даты и время в UTC. Апострофы выделяют буквальный текст; два апострофа дают один. Та же логика применяется к форматированным сообщениям log.*.

//@tra=2
indicator("Formatted values", overlay=true)
var panel=table.new(position.bottom_right,1,2)
if barstate.islast
    table.cell(panel,0,0,str.format("Price: {0,number,0.00}",close))
    table.cell(panel,0,1,str.format("Volume: {0}",str.tostring(volume,format.volume)))

Отрисовка и окно данных

plot() рисует серии, hline() — уровни, plotshape() — сигналы. bgcolor() задаёт цвет фона свечи, а box, line и label создают изменяемые объекты. color.new(color, transparency) использует прозрачность от 0 до 100. Служебные серии с display.data_window видны в окне данных и не меняют шкалу графика.

var box area = na
if bar_index == 10
    area := box.new(bar_index, high, bar_index + 10, low,
        bgcolor=color.new(color.blue, 90))
if not na(area)
    box.set_right(area, bar_index)
plot(volume, "Volume data", display=display.data_window)

plotshape рисует выбранную фигуру при выполнении условия.

Текст таблиц, подписей и прямоугольных областей может иметь числовой размер шрифта, стандартное или моноширинное семейство и полужирное либо курсивное начертание. text.format_bold + text.format_italic объединяет оба стиля, text.format_none сбрасывает их. Начертание меняется также через label.set_text_formatting, box.set_text_formatting и table.cell_set_text_formatting. Для box параметры text_halign и text_valign задают положение текста, text.wrap_auto переносит текст по ширине области, а text.wrap_none оставляет исходные строки.

//@tra=2
indicator("Styled text", overlay=true)
var panel=table.new(position.bottom_right,1,1)
if barstate.islast
    table.cell(panel,0,0,"TRA Script",text_size=18,
        text_font_family=font.family_monospace,
        text_formatting=text.format_bold+text.format_italic)

Другие инструменты и таймфреймы

request.security(symbol, timeframe, expression) вычисляет выражение в контексте другого источника. Источник должен быть доступен терминалу. Параметры gaps и lookahead управляют синхронизацией. В Replay внешние данные ограничены доступной свечой, а в обезличенном задании запросы внешних источников запрещены.

proxy = input.symbol("", "Turnover proxy")
symbol = proxy == "" ? syminfo.tickerid : proxy
turnover = request.security(symbol, timeframe.period, volume * hlc3,
    gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)

Стратегии и тестер

Стратегия описывает правила создания и отмены заявок. Тестер моделирует их на истории выбранного инструмента. Рыночная заявка, созданная после расчёта свечи, по умолчанию исполняется на открытии следующей. Комиссии и проскальзывание задаются в strategy().

//@tra=2
strategy("EMA crossover", overlay=true,
    initial_capital=100000,
    default_qty_type=strategy.fixed,
    default_qty_value=10,
    commission_type=strategy.commission.percent,
    commission_value=0.05,
    slippage=1)

fast = ta.ema(close, input.int(12, "Fast", minval=1))
slow = ta.ema(close, input.int(26, "Slow", minval=1))
if ta.crossover(fast, slow)
    strategy.entry("Long", strategy.long)
if ta.crossunder(fast, slow)
    strategy.close("Long")
plot(fast, color=color.aqua)
plot(slow, color=color.orange)
Результаты доступны во вкладке «Тестер стратегий»: кривая капитала, закрытые сделки, комиссии и максимальная просадка. Можно скачать сделки в CSV. Исторический тест ведёт собственный расчётный счёт и не изменяет баланс Demo или Replay.

Внутри исторической свечи тестер использует последовательный путь OHLC.

Чистый результат — сумма результатов закрытых сделок.

strategy.opentrades.* и strategy.closedtrades.* позволяют читать цену и время входа, объём со знаком направления, комиссии и прибыль отдельной сделки. Для закрытых сделок доступны также данные выхода. Нумерация начинается с 0; отсутствующий номер возвращает na. Прибыль учитывает комиссии, процент рассчитывается относительно стоимости входа. strategy.default_entry_qty(fill_price) рассчитывает объём по настройкам стратегии и стоимости одного пункта инструмента.

//@tra=2
strategy("Trade information", overlay=true)
if bar_index % 20 == 0
    strategy.entry("Long", strategy.long, comment="Entry")
if bar_index % 20 == 10
    strategy.close("Long", comment="Exit")
latest = strategy.closedtrades - 1
plot(strategy.closedtrades.profit(latest), "Trade result",
    display=display.data_window)
plot(strategy.closedtrades.commission(latest), "Fees",
    display=display.data_window)

Максимальное благоприятное и неблагоприятное движение сделки читается через max_runup и max_drawdown, а доля от стоимости входа — через версии с суффиксом _percent. Это положительные величины без комиссий. Учитываются только цены до выхода: максимум или минимум свечи после закрытия сделки не попадает в расчёт. Частичное закрытие распределяет результат пропорционально закрытому объёму. Эти показатели доступны в таблице сделок и CSV. Общая просадка стратегии учитывает движение цены внутри свечи и сравнивает капитал с предыдущим максимумом реализованного капитала.

//@tra=2
strategy("Trade excursions", overlay=true)
if bar_index % 20 == 0
    strategy.entry("Long", strategy.long)
if bar_index % 20 == 10
    strategy.close("Long")
latest = strategy.closedtrades - 1
plot(strategy.closedtrades.max_runup(latest), "Favorable",
    display=display.data_window)
plot(strategy.closedtrades.max_drawdown(latest), "Adverse",
    display=display.data_window)

Импорт Pine

Редактор распознаёт заголовок //@version=5 или //@version=6. Импорт переводит объявление языка в //@tra=2, сохраняя блоки, типы, функции и исходные строки. Неподдержанная функция выдаёт ошибку с номером строки; потерянная логика не заменяется фиктивными значениями.

Официальный справочник Pine v6 · OsEngine

Справочник API и совместимость

Найдите функцию, переменную или константу по имени или выберите раздел. Раскройте запись, чтобы увидеть сигнатуру и статус реализации. Статусы относятся к текущей версии TRA Script.

Статическая система типов ещё развивается: полная проверка квалификаторов const/simple/series, совместимость вложенных типов коллекций пока не завершены. При выборе перегрузок только по квалификаторам вызов может быть неоднозначным. Это не является полной совместимостью системы типов Pine.

Метки поддерживают круг, квадрат, ромб, треугольники, стрелки, кресты, флаг, текст с обводкой и указатели вверх, вниз, в стороны и по углам. style_none выводит только текст; style_label_center центрирует прямоугольную метку. Положение указателя определяет сторону текста относительно координаты. Для yloc.abovebar и yloc.belowbar используется high или low указанной свечи, в том числе при xloc.bar_time. Стили line.style_arrow_left, line.style_arrow_right и line.style_arrow_both рисуют наконечники на концах линии. Размеры текста таблиц/блоков и меток различаются: normal соответствует 14 для таблицы и 12 для метки; huge — 36 и 24. Числовой размер задаётся отдельно. Геометрия рисуется средствами браузера, поэтому пиксельное совпадение шрифтов с другим терминалом не гарантируется.

//@tra=2
indicator("Pointer and arrow", overlay=true)
if barstate.islast
    label.new(bar_index - 10, close, "Price", style=label.style_label_left, size=size.normal)
    line.new(bar_index - 20, low, bar_index, high, style=line.style_arrow_both)
plot(close, "Price", color=color.blue)

Ограничения выполнения

РесурсЛимит
Исходный код200 000
Свечи одного расчёта25 000
Параметры input100
Итерации одного цикла10 000
Глубина вызовов функций64
Элементы массива100 000
Уникальные внешние источники40
Данные серверного расчёта8 MB
Результат серверного расчёта16 MB
Время одного расчёта20 s
Пары карты50 000
Элементы матрицы100 000