Библиотеки Pine Script: импорт, экспорт и версии функций

Обложка статьи «Библиотеки Pine Script: импорт, экспорт и версии функций»: слева блок библиотеки с тремя экспортируемыми функциями, справа три скрипта; два подключены к первой версии, третий переведён на вторую

Когда библиотека оправдана, а когда хватит копипасты?

Честный порог простой. Пока расчёт нужен одному скрипту, он и должен лежать внутри этого скрипта. Как только вы ловите себя на том, что открываете старый индикатор, чтобы скопировать оттуда функцию, а потом ещё раз, и ещё, копипаста начинает стоить дороже, чем разовая возня с публикацией.

Цена у библиотеки есть, и о ней лучше знать заранее. Её нельзя просто сохранить в редакторе и подключить: она обязана быть опубликована, хотя бы приватно. Публикуется она всегда с открытым исходником, другого варианта TradingView не даёт. И править её задним числом уже не выйдет так же свободно, как черновик в редакторе.

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

Кому библиотека сэкономит время

Подойдёт, если

  • Один и тот же расчёт живёт в трёх и более ваших скриптах
  • Вы пишете набор индикаторов с общей логикой сигналов
  • Хотите отдать свои функции другим авторам
  • Собираете внутренний набор утилит: округление, форматирование, работа с сессиями

Не подойдёт, если

  • Функция нужна ровно одному индикатору
  • Код меняется каждый день и ещё не устоялся
  • Логику нельзя показывать: библиотека всегда открытая
  • Нужен закрытый продукт для продажи доступа

Если вы только присматриваетесь к языку и ещё не решили, стоит ли писать своё, начните с общей картины: что такое Pine Script и как он устроен. Библиотеки это уже вторая ступень, они нужны не всем.

Чем library() отличается от indicator() и strategy()?

Библиотеки Pine Script это третий тип скрипта наравне с индикатором и стратегией. Каждый скрипт начинается ровно с одного объявления, и оно стоит в глобальной области. Значит, выбор простой: либо indicator(), либо strategy(), либо library(). Третьего не дано и совмещения тоже.

Отличий у библиотеки от индикатора несколько, и почти все они логичны.

Слово export доступно только библиотекам. Индикатор при всём желании не может отдать свою функцию другому скрипту, это не запрет ради запрета, а разделение ролей.

Заголовок библиотеки работает как имя пространства имён, и правила к нему строже. Годятся только латинские буквы, цифры и подчёркивание, начинаться с цифры нельзя. Строка "Library for 14-day averages" вызовет ошибку компиляции просто из-за пробелов и дефиса, а "Library_for_14_day_averages" пройдёт.

Библиотеку не предполагается вешать на график. Справка TradingView говорит об этом прямо: библиотеки не для того, чтобы их накладывать, их импортируют, когда пишут другой скрипт. Технически код на графике всё же исполняется как обычный индикатор, и этим пользуются, чтобы подготовить чистый график перед публикацией.

И ещё одна мелочь, которая всплывает позже: сама библиотека не создаёт алерты. Она может экспортировать функцию с вызовом alert() внутри, а алерт уже создаст тот скрипт, который эту функцию вызвал.

Как собрать библиотеку: от функции до export

Каркас библиотеки в шестой версии языка выглядит так:

//@version=6
// @description Короткое описание, что тут лежит
library("My_Tools", overlay = true)

export myEma(int x) =>
    ta.ema(close, length = x)

У объявления library() ровно три параметра: title, overlay и dynamic_requests. Первый обязателен, он же служит идентификатором при импорте. Второй отвечает за то, попадёт ли рисунок на основную панель графика, по умолчанию нет. Третий разрешает динамические запросы, и в шестой версии он по умолчанию включён, тогда как в пятой был выключен.

Экспортировать можно не только функции. В список входят методы, пользовательские типы, enum и константные переменные. Константы приехали в язык позже остальных, в июне 2025 года, и у них своё условие: тип только int, float, bool, color или string, а в объявлении обязательно слово const.

С типами и enum есть неочевидное правило. Если экспортируемая функция принимает или возвращает объект вашего типа, то и сам тип обязан быть экспортирован. То же самое, если поле одного экспортируемого типа использует другой. А вот если тип нужен только для внутренней кухни библиотеки, наружу его выносить не надо.

Пустая библиотека не соберётся. Чтобы компиляция прошла, наружу должно уходить хотя бы одно имя: функция, метод, тип или enum.

  1. 1

    Выделить функцию

    Найти расчёт, который вы копируете из скрипта в скрипт

  2. 2

    Заменить объявление на library

    Заголовок только латиницей, цифрами и подчёркиванием

  3. 3

    Поставить export и типы

    Тип обязателен у каждого параметра, иначе не соберётся

  4. 4

    Убрать входы и глобальные ссылки

    Внутри нельзя вызывать input и брать глобальные массивы

  5. 5

    Проверить на графике

    Код библиотеки исполняется как обычный индикатор

  6. 6

    Опубликовать приватно или публично

    Без публикации импортировать библиотеку невозможно

Порядок действий, если у вас уже есть рабочая функция внутри обычного индикатора и вы хотите переиспользовать её в других скриптах.

Открыть редактор, сохранить черновик и добавить скрипт на график это отдельная механика, она подробно разобрана в статье про редактор Pine Script.

Почему у параметров экспортируемой функции обязательны типы?

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

Кроме типа у параметра бывает квалификатор, и тут выбор всего из двух слов: simple и series. Слово const в экспортируемой функции не разрешено, оно доступно только обычным, неэкспортируемым.

По умолчанию аргументы приезжают как series, если вы явно не написали simple. Компилятор при этом смотрит, как параметр используется внутри, и подбирает квалификатор сам. Отсюда самая частая ловушка, которую документация показывает буквально на пальцах. Возьмём функцию:

export myEma(int x) =>
    ta.ema(close, length = x)

Параметр x уходит в аргумент длины, а длина у скользящей средней обязана быть простым значением. Значит, x автоматически получает квалификатор simple. Вызов myEma(x = 20) соберётся спокойно, а вот myEma(x = bar_index) даст ошибку компиляции: bar_index меняется на каждом баре, это серия. Ничего мистического, просто контракт функции оказался жёстче, чем выглядел в коде.

Обратная ситуация тоже бывает. Если вам нужно, чтобы функция вернула простое значение, а не серию, придётся следить, чтобы внутри не оказалось ни одного серийного значения. Достаточно одного, и результат станет серией.

Что нельзя делать внутри экспортируемой функции?

Документация даёт закрытый список ограничений, и его стоит прочитать целиком один раз, чтобы потом не гадать.

Первое. Нельзя обращаться к переменным глобальной области библиотеки, кроме тех, что объявлены как константы. Значение, пришедшее из настроек, или массив, созданный глобально, внутри экспортируемой функции недоступны.

Второе. Нельзя вызывать функции семейства input. Настройки живут в скрипте-потребителе, а не в библиотеке, и это правильно: иначе одна библиотека диктовала бы интерфейс всем, кто её подключил.

Третье. Запросы request.*() внутри локальной области разрешены, пока параметр dynamic_requests не выключен вручную. Но у них своё условие: аргумент expression не может зависеть от параметров вашей функции. Тикер и таймфрейм зависеть могут, а само выражение нет.

Четвёртое. alert() внутри функции разрешён, но алерт создаст только тот, кто эту функцию вызвал. С самой библиотеки, висящей на графике, алерт не сделать.

Пятое. Библиотечная функция всегда возвращает simple или series, слабее не бывает. Поэтому её результат нельзя подставить туда, где ждут константу. Классический пример: параметр show_last у plot() требует значения типа input int или const int, и вернуть его из библиотеки не получится.

К этому добавляются два общих правила языка, про которые часто забывают. Вызов plot() живёт только в глобальной области, то есть внутри любой функции его не бывает вообще. А рекурсия у пользовательских функций запрещена, вызвать саму себя нельзя.

Как опубликовать библиотеку и кто её увидит?

Пока библиотека не опубликована, импортировать её нельзя. Никакого «сохранил в редакторе и подключил» тут не предусмотрено.

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

Правила сочетаемости стоит запомнить один раз. Публичный скрипт может использовать только публичные библиотеки и сам обязан быть открытым. Приватные скрипты и черновики в редакторе могут брать и публичные, и приватные библиотеки. А вот в публичном закрытом скрипте, protected или invite-only, приватную библиотеку использовать можно.

Право на правку у двух вариантов разное. Приватную публикацию автор может менять и удалять всегда. У публичной есть только пятнадцать минут, после этого она финализируется. Поэтому в описание публичной библиотеки не пишут ничего, что скоро устареет.

Про чужой код правила площадки говорят внятно: библиотеки отнесены к общественному достоянию. Вызывать их функции и переиспользовать код в открытом скрипте можно без разрешения автора. А вот перенести их код в закрытую публикацию уже нельзя без явного согласия. В обоих случаях автора указывают в описании. Открытый код по умолчанию идёт под лицензией Mozilla Public License 2.0, если автор не написал в исходнике другую.

И про тарифы, раз уж это упирается в деньги. На тарифе Basic публичных публикаций нет вовсе, приватные доступны. Для публичных действуют лимиты частоты: до пяти за скользящие сутки и до пятнадцати за тридцать дней. На приватные публикации и на обновления уже опубликованных скриптов эти лимиты не распространяются.

Как импортировать библиотеку и зачем нужен alias?

Синтаксис такой:

//@version=6
indicator("Using AllTimeHighLow library", "", true)
import PineCoders/AllTimeHighLow/1 as allTime

plot(allTime.hi())
plot(allTime.lo())

Путь из трёх частей однозначно определяет, что именно вы подключили. Здесь есть подвох, на который натыкаются регулярно: в импорте пишется title из объявления библиотеки, а не заголовок, который автор придумал для публикации. Это часто разные строки.

Псевдоним после as задаёт пространство имён. С as allTime функции зовут как allTime.hi(). Если псевдоним не писать, пространством имён станет само имя библиотеки. Через ту же точечную запись доступны и типы, и enum библиотеки.

Несколько правил про импорт, которые проще запомнить сразу:

  • Номер версии в пути пишется явно всегда, значения «последняя» в этой строке не существует.
  • Публичный скрипт может подключать только публичные библиотеки, и сам он обязан быть открытым.
  • Приватная библиотека годится для личного скрипта, черновика в редакторе и для публичного скрипта с закрытым кодом.

Число подключённых библиотек напрямую не ограничено, ограничен суммарный объём кода. Об этом ниже, в рисках.

  1. 1

    Открыть страницу библиотеки

    Публичные библиотеки лежат в ленте Community scripts

  2. 2

    Скопировать строку импорта

    На странице есть кнопка копирования готовой строки

  3. 3

    Вставить import под объявлением

    Номер версии в строке останется тем, что вы взяли

  4. 4

    Задать понятный псевдоним

    После as пишут короткое имя, через него зовут функции

  5. 5

    Указать автора в описании

    Требование правил площадки, а не вежливость

Короткий путь, если вы нашли в ленте сообщества готовую библиотеку Pine Script и хотите вызвать её функцию у себя.

Почему импорт не переезжает на новую версию сам?

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

Звучит как неудобство, а на деле это страховка. Представьте обратное: автор чужой библиотеки поправил формулу у себя, и наутро ваш индикатор рисует другие сигналы, хотя вы к нему не прикасались. Именно этого площадка и не допускает.

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

Где взять актуальный номер. На странице опубликованной библиотеки есть кнопка копирования готовой строки импорта в буфер обмена, это самый быстрый способ. Второй вариант, для тех, кто пишет вручную: в редакторе работает автодополнение по имени автора, оно покажет доступные библиотеки.

Как выпускать версии, чтобы не сломать чужие скрипты?

Библиотеки Pine Script нумеруются механически: площадка про смысл номера ничего не говорит и просто увеличивает его при каждой публикации обновления. Значит, дисциплину придётся держать самому, и тут проще одолжить готовую у соседних экосистем.

Спецификация Semantic Versioning формулирует правило, ради которого её и стоит открыть: выпущенная версия не меняется, любое изменение выходит новым выпуском. На TradingView это работает буквально, потому что публикация создаёт независимую копию исходника и она становится частью истории версий.

Что считать ломающим изменением, удобно смотреть по разбору из документации Cargo: удаление публичного элемента ломает совместимость, добавление обязательного параметра функции тоже, а вот добавление нового публичного элемента совместимо. Переводится на Pine Script один в один. Дописали в библиотеку новую экспортируемую функцию, старые потребители ничего не заметили. Добавили обязательный аргумент в существующую, и все, кто вызывал её по-старому, приехали.

Документация npm даёт совсем простую памятку по числам: первый выпуск 1.0.0, исправление 1.0.1, совместимая новинка 1.1.0, ломающее изменение 2.0.0. Правда, честнее сразу оговориться, как это делает руководство по упаковке Python: строгий semver на практике соблюдают не все, и номер в конце концов остаётся суждением автора.

Отдельно про описание. Раз публичную публикацию нельзя править после пятнадцати минут, историю изменений имеет смысл вести дисциплинированно с самого начала. Подход Keep a Changelog тут кстати: список изменений пишется для людей, у каждой версии своя запись с датой и понятными пунктами. Иначе через год вы сами не вспомните, чем третья версия отличалась от второй.

Ничего не ломается

Ломается у всех

  1. 1.Добавили новую функцию
  2. 2.Изменили расчёт внутри функции
  3. 3.Убрали или переименовали экспорт
Ориентир, когда достаточно тихо выпустить обновление, а когда стоит предупредить тех, кто вашу библиотеку уже импортировал.

Что изменилось в библиотеках с выходом Pine Script v6?

До шестой версии запрос данных внутри экспортируемой функции был закрыт, если автор не включал динамические запросы вручную. В ноябре 2024 года вместе с выходом v6 это изменилось: вызовы request.*() разрешили внутри циклов, условных конструкций и экспортируемых функций библиотек.

Заодно поменялось умолчание. В пятой версии dynamic_requests был выключен, в шестой включён. При переносе старого кода из-за этого попадаются в ловушку: если в объявлении остался явный dynamic_requests = false, то вызов запроса из локальной области будет запрещён, даже когда он спрятан внутри пользовательской функции. Руководство по миграции советует простое лечение: при конвертации просто убрать явный параметр и дать компилятору решить самому.

  1. Октябрь 2021

    Библиотеки появились в Pine Script v5

    Новый тип публикации для переиспользуемых функций

  2. Июнь 2024

    В язык добавили enum-типы

    Новый тип данных со списком допустимых значений

  3. Ноябрь 2024

    Pine Script v6 и динамические запросы

    request разрешён внутри экспортируемых функций

  4. Июнь 2025

    Экспорт константных переменных

    Только int, float, bool, color и string со словом const

Четыре момента, которые определяют, что именно ваша библиотека умеет сегодня и какой синтаксис вы встретите в чужом коде.

Если вы собираете из своих функций не просто набор утилит, а рабочий алгоритм, дальше пригодится общая рамка проверки: как устроен алготрейдинг и чем проверяют алгоритм до того, как доверить ему решения.

Риски и ограничения

Про объём. Скомпилированная форма одного скрипта ограничена примерно ста тысячами токенов, точное число 100 256. Библиотеки компилируются отдельно, и их код не раздувает размер вашего индикатора. Зато суммарный объём всех подключённых библиотек ограничен миллионом токенов, а сам запрос на компиляцию не может превышать пять мегабайт.

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

Ещё одна мелочь из той же оперы: запросы request.*() внутри импортированных функций считаются в ваш лимит запросов, даже если точно такой же запрос с теми же аргументами уже есть в основном коде скрипта.

Про зависимость. Подключая чужую библиотеку, вы принимаете чужие решения о расчётах. Автор выпустит новую версию, вы её не увидите, пока сами не переключитесь, и это хорошо. Но и обратное верно: если в старой версии есть ошибка, она останется у вас, пока вы не обновитесь вручную.

Про приватность. Повторим то, о чём говорили выше, потому что на этом обжигаются: приватная публикация скрыта от ленты и поиска, но открывается по прямой ссылке. Код, который нельзя показывать никому, в библиотеку не выносят вовсе.

Про авторство. Указать автора чужой библиотеки в описании своей публикации это не вежливость, а требование правил площадки. За плагиат правила площадки предусматривают вплоть до постоянной блокировки аккаунта, так что кредит автору это ещё и страховка.

И честная оговорка про эту статью. Точных текстов ошибок компиляции для случаев вроде пропущенного типа параметра или вызова input внутри экспортируемой функции в официальной документации нет: там описаны сами запреты. Так что если компилятор вам что-то выдал, сверяйтесь со списком ограничений, а не с чужими пересказами.

Источники

Частые вопросы

Обязательно ли публиковать библиотеку, чтобы ей пользоваться?

Да. Пока библиотека не опубликована, импортировать её нельзя. Приватной публикации при этом достаточно, в ленту сообщества она не попадёт.

Можно ли скрыть исходный код библиотеки?

Нет. Все библиотеки публикуются только с открытым исходником, выбрать другой тип видимости нельзя. Приватная публикация скрывает её от поиска, но не от того, у кого есть ссылка.

Почему в экспортируемой функции нельзя использовать input?

Настройки принадлежат скрипту-потребителю. Экспортируемая функция не может вызывать функции семейства input и не может брать глобальные переменные, инициализированные из настроек.

Обновится ли мой скрипт сам, когда автор выпустит новую версию библиотеки?

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

Можно ли подключить несколько библиотек в один скрипт?

Да, число импортов напрямую не ограничено. Ограничен суммарный объём: все подключённые библиотеки вместе не должны превышать миллион токенов. При этом каждый импорт пишется отдельной строкой со своим номером версии, а обращаться к функциям удобнее через понятный псевдоним после as.

Можно ли пользоваться чужой библиотекой из ленты TradingView?

Да, библиотеки отнесены правилами площадки к общественному достоянию: вызывать их функции и переиспользовать код в открытом скрипте разрешено без запроса автору. Для закрытой публикации разрешение нужно. Автора указывают в описании в любом случае.

Что вы узнаете
  • чем library отличается от indicator и когда библиотека вам вообще не нужна
  • как вынести функцию наружу через export и почему у параметров обязательны типы
  • как выглядит строка импорта с номером версии и что в ней делает alias
  • почему импорт не переезжает на новую версию сам и как выпускать версии аккуратно
Применить за 30 мин
Средний уровень
18просмотров
Материал был полезен?

Обсуждение

Алина

выше спрашивали про боковик - у меня та же беда была, помогло просто не лезть в такие периоды

Гость

почему myEma(x = 20) собирается, а myEma(x = bar_index) падает с ошибкой? функция же одна и та же

Макс

Параметр x уходит в аргумент длины скользящей средней, а длина обязана быть простым значением, поэтому x автоматически получает квалификатор simple. Число 20 под это подходит, а bar_index меняется на каждом баре и остаётся серией. Контракт экспортируемой функции просто оказался жёстче, чем выглядел в коде.

Павел

Наступил ровно на подвох с import: писал заголовок публикации, а нужен был title из объявления library

Макс

Проще всего брать готовую строку импорта кнопкой копирования на странице опубликованной библиотеки, тогда и номер версии в ней будет актуальный.

Оставить комментарий

Профильный эксперт
Макс Витковский
Аналитик рынка

Разбирает структуру рынка, уровни и торговые сценарии. Специализация — криптовалюты, технический анализ и ончейн-данные.

График TradingView с сигналами Buy и Sell индикатора Midas
Midas multi-индикатор for TradingViewОдин из самых продвинутых индикаторов для трейдинга
  • Сигналы без перерисовки
  • Интерактивный теханализ
  • 7 стратегий на выбор
Midas multi-индикатор for TradingViewСигнал, стоп и 3 цели — прямо на графике
  • Сигнал фиксируется по закрытию свечи
  • Стоп и 3 цели строятся автоматически
  • План сделки виден до входа