Когда библиотека оправдана, а когда хватит копипасты?
Главное
библиотека начинается там, где один и тот же кусок кода живёт в трёх скриптах и вы устали править его в трёх местах. Ради одной функции в одном индикаторе городить публикацию не стоит.
Честный порог простой. Пока расчёт нужен одному скрипту, он и должен лежать внутри этого скрипта. Как только вы ловите себя на том, что открываете старый индикатор, чтобы скопировать оттуда функцию, а потом ещё раз, и ещё, копипаста начинает стоить дороже, чем разовая возня с публикацией.
Цена у библиотеки есть, и о ней лучше знать заранее. Её нельзя просто сохранить в редакторе и подключить: она обязана быть опубликована, хотя бы приватно. Публикуется она всегда с открытым исходником, другого варианта TradingView не даёт. И править её задним числом уже не выйдет так же свободно, как черновик в редакторе.
Взамен вы получаете одно место правды. Поправили формулу в библиотеке, выпустили новую версию, и дальше решаете сами, какие скрипты на неё переводить. Никто не переведётся втихую, и это не недоработка, а сознательное решение площадки.
Подойдёт, если
- Один и тот же расчёт живёт в трёх и более ваших скриптах
- Вы пишете набор индикаторов с общей логикой сигналов
- Хотите отдать свои функции другим авторам
- Собираете внутренний набор утилит: округление, форматирование, работа с сессиями
Не подойдёт, если
- Функция нужна ровно одному индикатору
- Код меняется каждый день и ещё не устоялся
- Логику нельзя показывать: библиотека всегда открытая
- Нужен закрытый продукт для продажи доступа
Если вы только присматриваетесь к языку и ещё не решили, стоит ли писать своё, начните с общей картины: что такое Pine Script и как он устроен. Библиотеки это уже вторая ступень, они нужны не всем.
Чем library() отличается от indicator() и strategy()?
Главное
это третий тип скрипта. Объявление в скрипте ровно одно, совместить индикатор и библиотеку в одном файле нельзя, и только библиотеки умеют отдавать код наружу словом export.
Библиотеки Pine Script это третий тип скрипта наравне с индикатором и стратегией. Каждый скрипт начинается ровно с одного объявления, и оно стоит в глобальной области. Значит, выбор простой: либо indicator(), либо strategy(), либо library(). Третьего не дано и совмещения тоже.
Отличий у библиотеки от индикатора несколько, и почти все они логичны.
Слово export доступно только библиотекам. Индикатор при всём желании не может отдать свою функцию другому скрипту, это не запрет ради запрета, а разделение ролей.
Заголовок библиотеки работает как имя пространства имён, и правила к нему строже. Годятся только латинские буквы, цифры и подчёркивание, начинаться с цифры нельзя. Строка "Library for 14-day averages" вызовет ошибку компиляции просто из-за пробелов и дефиса, а "Library_for_14_day_averages" пройдёт.
Библиотеку не предполагается вешать на график. Справка TradingView говорит об этом прямо: библиотеки не для того, чтобы их накладывать, их импортируют, когда пишут другой скрипт. Технически код на графике всё же исполняется как обычный индикатор, и этим пользуются, чтобы подготовить чистый график перед публикацией.
И ещё одна мелочь, которая всплывает позже: сама библиотека не создаёт алерты. Она может экспортировать функцию с вызовом alert() внутри, а алерт уже создаст тот скрипт, который эту функцию вызвал.
Как собрать библиотеку: от функции до export
Главное
берёте рабочую функцию, меняете объявление скрипта на library, ставите перед функцией слово 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
Выделить функцию
Найти расчёт, который вы копируете из скрипта в скрипт
- 2
Заменить объявление на library
Заголовок только латиницей, цифрами и подчёркиванием
- 3
Поставить export и типы
Тип обязателен у каждого параметра, иначе не соберётся
- 4
Убрать входы и глобальные ссылки
Внутри нельзя вызывать input и брать глобальные массивы
- 5
Проверить на графике
Код библиотеки исполняется как обычный индикатор
- 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, не завязывать запросы на свои параметры. И рисовать через plot прямо из функции тоже нельзя, но это уже общее правило языка.
Документация даёт закрытый список ограничений, и его стоит прочитать целиком один раз, чтобы потом не гадать.
Первое. Нельзя обращаться к переменным глобальной области библиотеки, кроме тех, что объявлены как константы. Значение, пришедшее из настроек, или массив, созданный глобально, внутри экспортируемой функции недоступны.
Второе. Нельзя вызывать функции семейства 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?
Главное
строка импорта состоит из трёх частей через слэш, автор, имя и номер версии. Псевдоним после as превращается в пространство имён, через которое вы зовёте функции.
Синтаксис такой:
//@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
Открыть страницу библиотеки
Публичные библиотеки лежат в ленте Community scripts
- 2
Скопировать строку импорта
На странице есть кнопка копирования готовой строки
- 3
Вставить import под объявлением
Номер версии в строке останется тем, что вы взяли
- 4
Задать понятный псевдоним
После as пишут короткое имя, через него зовут функции
- 5
Указать автора в описании
Требование правил площадки, а не вежливость
Разберите инструмент на практике вместе с Midas
В экосистеме Midas есть пошаговые инструкции, обучение техническому анализу и поддержка сообщества — от первого запуска до разбора собственной торговой логики.
- пошаговые инструкции по продукту
- структурированное обучение теханализу
- чат с командой и участниками клуба
Почему импорт не переезжает на новую версию сам?
Главное
так задумано ради предсказуемости. Номер версии в строке импорта закреплён намертво, и переключение это ручная правка одной цифры.
Формулировка документации прямая: чтобы скрипты, использующие библиотеки, работали надёжно, автоматически подхватить последнюю версию невозможно. Каждый раз, когда автор публикует обновление, номер версии библиотеки растёт. Хотите новую версию, идите и меняйте число в строке 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.Добавили новую функцию
- 2.Изменили расчёт внутри функции
- 3.Убрали или переименовали экспорт
Что изменилось в библиотеках с выходом Pine Script v6?
Главное
главное изменение это динамические запросы. В шестой версии request.*() разрешили внутри экспортируемых функций, а параметр dynamic_requests стал включён по умолчанию.
До шестой версии запрос данных внутри экспортируемой функции был закрыт, если автор не включал динамические запросы вручную. В ноябре 2024 года вместе с выходом v6 это изменилось: вызовы request.*() разрешили внутри циклов, условных конструкций и экспортируемых функций библиотек.
Заодно поменялось умолчание. В пятой версии dynamic_requests был выключен, в шестой включён. При переносе старого кода из-за этого попадаются в ловушку: если в объявлении остался явный dynamic_requests = false, то вызов запроса из локальной области будет запрещён, даже когда он спрятан внутри пользовательской функции. Руководство по миграции советует простое лечение: при конвертации просто убрать явный параметр и дать компилятору решить самому.
Октябрь 2021
Библиотеки появились в Pine Script v5
Новый тип публикации для переиспользуемых функций
Июнь 2024
В язык добавили enum-типы
Новый тип данных со списком допустимых значений
Ноябрь 2024
Pine Script v6 и динамические запросы
request разрешён внутри экспортируемых функций
Июнь 2025
Экспорт константных переменных
Только int, float, bool, color и string со словом const
Если вы собираете из своих функций не просто набор утилит, а рабочий алгоритм, дальше пригодится общая рамка проверки: как устроен алготрейдинг и чем проверяют алгоритм до того, как доверить ему решения.
Риски и ограничения
Главное
библиотека уезжает на компиляцию целиком, лимиты на объём вполне достижимы, а чужая библиотека это зависимость со всеми вытекающими.
Про объём. Скомпилированная форма одного скрипта ограничена примерно ста тысячами токенов, точное число 100 256. Библиотеки компилируются отдельно, и их код не раздувает размер вашего индикатора. Зато суммарный объём всех подключённых библиотек ограничен миллионом токенов, а сам запрос на компиляцию не может превышать пять мегабайт.
Отсюда неприятная особенность, о которой лучше знать заранее: на компиляцию уходит библиотека целиком, независимо от того, сколько функций вы из неё вызвали. Одна маленькая функция из огромного набора всё равно тащит за собой весь набор. Вывод простой: библиотеки лучше держать компактными и по темам, а не сваливать всё в одну.
Ещё одна мелочь из той же оперы: запросы request.*() внутри импортированных функций считаются в ваш лимит запросов, даже если точно такой же запрос с теми же аргументами уже есть в основном коде скрипта.
Про зависимость. Подключая чужую библиотеку, вы принимаете чужие решения о расчётах. Автор выпустит новую версию, вы её не увидите, пока сами не переключитесь, и это хорошо. Но и обратное верно: если в старой версии есть ошибка, она останется у вас, пока вы не обновитесь вручную.
Про приватность. Повторим то, о чём говорили выше, потому что на этом обжигаются: приватная публикация скрыта от ленты и поиска, но открывается по прямой ссылке. Код, который нельзя показывать никому, в библиотеку не выносят вовсе.
Про авторство. Указать автора чужой библиотеки в описании своей публикации это не вежливость, а требование правил площадки. За плагиат правила площадки предусматривают вплоть до постоянной блокировки аккаунта, так что кредит автору это ещё и страховка.
И честная оговорка про эту статью. Точных текстов ошибок компиляции для случаев вроде пропущенного типа параметра или вызова input внутри экспортируемой функции в официальной документации нет: там описаны сами запреты. Так что если компилятор вам что-то выдал, сверяйтесь со списком ограничений, а не с чужими пересказами.
Источники
- Pine Script Libraries, официальная документация TradingView
- Declaration statements: library, indicator, strategy
- User-defined functions: типы и квалификаторы параметров
- Limitations: лимиты токенов и размера компиляции
- Publishing scripts: типы публикаций и лицензия
- Release notes Pine Script
- Migration guide to Pine Script v6
- Справка TradingView: что такое библиотека Pine и как её использовать
- Правила публикации скриптов TradingView
- Советы авторам скриптов: тарифы и лимиты публикаций
- Semantic Versioning 2.0.0
- The Cargo Book: SemVer compatibility
- npm: about semantic versioning
- Python Packaging User Guide: versioning
- Keep a Changelog 1.1.0
- PineCoders FAQ and Code
- PineCoders pine-utils на GitHub
- Awesome Pine Script: коллекция ресурсов
Частые вопросы
Обязательно ли публиковать библиотеку, чтобы ей пользоваться?
Да. Пока библиотека не опубликована, импортировать её нельзя. Приватной публикации при этом достаточно, в ленту сообщества она не попадёт.
Можно ли скрыть исходный код библиотеки?
Нет. Все библиотеки публикуются только с открытым исходником, выбрать другой тип видимости нельзя. Приватная публикация скрывает её от поиска, но не от того, у кого есть ссылка.
Почему в экспортируемой функции нельзя использовать input?
Настройки принадлежат скрипту-потребителю. Экспортируемая функция не может вызывать функции семейства input и не может брать глобальные переменные, инициализированные из настроек.
Обновится ли мой скрипт сам, когда автор выпустит новую версию библиотеки?
Нет. Номер версии в строке импорта закреплён, автоматического перехода на последнюю версию не существует. Переключение это ручная правка числа.
Можно ли подключить несколько библиотек в один скрипт?
Да, число импортов напрямую не ограничено. Ограничен суммарный объём: все подключённые библиотеки вместе не должны превышать миллион токенов. При этом каждый импорт пишется отдельной строкой со своим номером версии, а обращаться к функциям удобнее через понятный псевдоним после as.
Можно ли пользоваться чужой библиотекой из ленты TradingView?
Да, библиотеки отнесены правилами площадки к общественному достоянию: вызывать их функции и переиспользовать код в открытом скрипте разрешено без запроса автору. Для закрытой публикации разрешение нужно. Автора указывают в описании в любом случае.










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