request.security в Pine Script: как взять данные другого символа и таймфрейма

Обложка статьи «request.security в Pine Script: как взять данные другого символа и таймфрейма»: три бара старшего таймфрейма, перенос их значения вниз и ступенчатая линия на младшем графике; третий бар незакрыт

Что делает request.security и когда он нужен?

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

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

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

Когда запрос чужих данных уместен, а когда лишний

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

  • Фильтр по тренду старшего таймфрейма: торгуем на 15m, разрешение берём с 1D
  • Сравнение своего инструмента с индексом, индексом доллара или другой парой
  • Сбор одного индикатора сразу по списку символов для собственного индекса
  • Подсчёт внутрибарового объёма или числа интрабаров внутри свечи графика

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

  • Любая формула по свечам текущего графика: считайте напрямую, без запроса
  • Попытка поймать закрытие старшего бара через состояние бара внутри вызова
  • Замена управления риском: чужие данные не делают правило безопаснее
  • Ускорение скрипта: каждый уникальный запрос стоит времени и места в лимите

Как устроены три аргумента: symbol, timeframe и expression?

Полная сигнатура в официальной документации записана так:

pine
request.security(symbol, timeframe, expression, gaps, lookahead, ignore_invalid_symbol, currency, calc_bars_count)

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

  1. 1

    symbol - откуда берём

    Тикер с префиксом биржи или syminfo.tickerid своего графика

  2. 2

    timeframe - в каком масштабе

    Строка вида 1D, 60, 15. Пустая означает таймфрейм графика

  3. 3

    expression - что посчитать

    Считается в чужом контексте и не переносится готовым

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

Со строкой таймфрейма связана ошибка, на которой обжигаются почти все. Формат строится из множителя и буквы единицы, причём у минут буквы нет вовсе: "15" - это пятнадцать минут, "1D" - день, "1W" - неделя. Часа в этом наборе просто нет. Документация формулирует без вариантов: "1H" невалиден, правильная запись часа - "60".

Третья часть - выражение - самая недооценённая. Оно не переносится посчитанным, оно вычисляется заново там, у запрошенного символа. Если написать ta.sma(close, 20), средняя посчитается по чужим закрытиям, а не по вашим. Это ровно то, что нужно, и ровно то, что удивляет человека, который ждал переноса готового значения.

Как запросить данные другого символа?

Минимальный рабочий пример выглядит буквально в одну строку. Дальше его можно вывести на график или сравнить со своей ценой:

pine
//@version=6
indicator("Чужой символ на своём таймфрейме")
float otherClose = request.security("NASDAQ:AAPL", timeframe.period, close)
plot(otherClose)

Куда вставлять этот код и как его сохранить, разобрано в отдельной инструкции про то, как открыть Pine Editor и сохранить скрипт. Здесь важнее поведение, а не кнопки.

Официальный FAQ снимает главный страх новичка: запрос данных другого символа на таймфрейме графика перекраской не грозит. Смещать значение и трогать необязательные аргументы в этом случае не нужно.

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

Как получить данные старшего таймфрейма через request.security?

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

Собирается такой скрипт по шагам:

  1. Заводите вход для таймфрейма, чтобы масштаб не был зашит в код намертво.
  2. Ставите проверку: если выбранный масштаб меньше масштаба графика, вызываете runtime.error() с текстом на человеческом языке.
  3. Пишете сам запрос со строкой старшего таймфрейма.
  4. Выводите результат на график, чтобы глазами увидеть ступеньки.
pine
//@version=6
indicator("Дневная средняя на младшем графике", overlay = true)

string htf = input.timeframe("1D", "Старший таймфрейм")

if timeframe.in_seconds() > timeframe.in_seconds(htf)
    runtime.error("Выбранный таймфрейм ниже таймфрейма графика. Возьмите старший.")

float htfMa = request.security(syminfo.tickerid, htf, ta.sma(close, 20))
plot(htfMa)

Линия получится ступенчатой, и это нормально: значение обновляется раз в дневной бар, а не на каждой пятнадцатиминутке. Вопрос «как сгладить ступеньки» на форумах повторяется годами, но сглаживать тут нечего - на младшем масштабе дневного значения между обновлениями попросту не существует.

Почему значение старшего таймфрейма меняется на незакрытом баре?

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

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

  1. 00:00

    Дневной бар открылся

    Запрос вернёт текущее закрытие дня, оно равно текущей цене

  2. В течение дня

    Значение живёт и меняется

    Каждая новая цена меняет закрытие незакрытого дневного бара

  3. Сигнал на графике

    Метка появилась и стоит

    Условие выполнено по текущему, ещё не подтверждённому значению

  4. 23:59

    Дневной бар закрылся

    Значение зафиксировано и больше не изменится

  5. После перезагрузки

    История переписана

    Скрипт видит только подтверждённое, часть меток исчезает

Схема учебная. На ней отмечен момент, когда запрошенное значение перестаёт меняться.

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

Как взять подтверждённое значение старшего таймфрейма без заглядывания в будущее?

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

pine
//@version=6
indicator("Подтверждённое дневное закрытие", overlay = true)
float dailyClose = request.security(syminfo.tickerid, "1D", close[1], lookahead = barmerge.lookahead_on)
plot(dailyClose)

Логика тут не такая хитрая, как кажется. Аргумент lookahead в положении on разрешает функции брать значение из старшего бара, который на момент вашего бара ещё не закончился. Смещение на один бар отодвигает запрос ровно настолько, чтобы он попал в уже закрытый старший бар. Будущего не остаётся, остаётся подтверждённое прошлое.

Тот же самый lookahead без смещения превращается в оружие против самого себя. Документация приводит анти-пример с комментарием «FUTURE LEAK, не используйте» и отдельно предупреждает: скрипты, которые через lookahead протаскивают будущее в прошлое, к публикации не допускаются. На бэктесте такой код показывает фантастическую статистику. На живом счёте не показывает ничего.

Только подтверждённый старший бар

Данные, которых на баре ещё не было

  1. 1.Смещение плюс lookahead включён
  2. 2.Настройки по умолчанию, живой бар
  3. 3.lookahead включён без смещения
Слева скрипт опирается только на закрытые бары и после перезагрузки показывает то же самое. Справа он берёт данные, которых на том баре ещё не существовало.

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

Отдельная ловушка ждёт тех, кто пробует поймать закрытие старшего бара напрямую. Состояние бара внутри запроса не работает: документация фиксирует это одной строкой - barstate.isconfirmed в вызове request.security() не работает. Часть сообщества обходит это своими обёртками, переключая смещение по состоянию бара снаружи вызова. Оба подхода дают подтверждённое значение, но момент его появления на графике у них разный: официальный отдаёт закрывшийся старший бар сразу в начале нового старшего периода, поведение обёрток сообщества документацией не описано и его надо проверять глазами на своём графике. Разбор того, как отличать честный сигнал от переехавшего, лежит в материале про индикаторы без перерисовки.

Чем barmerge.gaps_on отличается от barmerge.gaps_off?

Разница видна на одной таблице:

Что сравниваемbarmerge.gaps_offbarmerge.gaps_on
Стоит по умолчаниюданет
Бар без нового значенияповторяет последнее подтверждённое, на живом баре - текущее развивающеесяотдаёт пустое значение
Как выглядит на графикесплошная ступенчатая линияточки только в моменты обновления
Когда удобнофильтры, сравнения, любая логика «выше или ниже»подсветка самого факта обновления старшего бара
Что ломаетскрывает момент обновленияарифметика с пустыми значениями требует проверки
pine
//@version=6
indicator("gaps на глаз", overlay = true)
float noGaps = request.security(syminfo.tickerid, "60", close, gaps = barmerge.gaps_off)
float withGaps = request.security(syminfo.tickerid, "60", close, gaps = barmerge.gaps_on)
plot(noGaps, "Без разрывов", color.blue, 2, plot.style_linebr)
plot(withGaps, "С разрывами", color.purple, 6, plot.style_linebr)

Выбор простой. Для условий вида «цена выше дневной средней» берите положение по умолчанию: вам нужно значение на каждом баре. Пустые значения понадобятся, когда важен сам момент прихода новых данных, например для метки на старте нового старшего периода.

Почему request.security неудобен для младшего таймфрейма?

Поведение описано в документации без прикрас. При обращении к младшему масштабу функция считает выражение в младшем контексте, но отдаёт результат только одного интрабара на каждый бар графика. С lookahead в положении on это первый доступный интрабар, в положении off - последний. На барах реального времени возвращается последнее доступное значение независимо от аргумента.

Для нормальной работы с внутрибаровыми данными существует request.security_lower_tf(). Её сигнатура другая: нет аргументов gaps и lookahead, зато есть ignore_invalid_timeframe, и по умолчанию он выключен. Возвращает функция массив значений, отсортированный по времени интрабаров.

pine
//@version=6
indicator("Сколько минуток в одном баре")
array<float> closes = request.security_lower_tf(syminfo.tickerid, "1", close)
int qty = array.size(closes)
plot(qty, style = plot.style_histogram)

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

Как проверить код после перезагрузки на истории и в реальном времени?

Проверка нужна ровно потому, что глазами разницу не поймать: живой график и он же после перезагрузки выглядят одинаково убедительно. Такой же ритуал read-back описан в разборе проверки сигнала после перезагрузки, здесь он адаптирован под чужие данные.

  1. 1

    Включить лог значений

    log.info() выводит запрошенное значение и время бара

  2. 2

    Снять снимок живого графика

    Записать время последних трёх меток и значения из лога

  3. 3

    Перезагрузить страницу

    Реальные бары становятся историческими, скрипт считает заново

  4. 4

    Сравнить метки и значения

    Сверить те же три метки и те же значения из лога

  5. 5

    Повторить на выходных данных

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

Ритуал повторяется после каждой правки запроса. Шаги 2 и 4 нельзя менять местами: снимок делается до перезагрузки.

Что считать провалом, стоит договориться заранее. Сдвиг метки на один бар - это перекраска, а не мелочь. Исчезнувшая метка - перекраска. Совпавшие метки при разошедшихся значениях в логе - тоже сигнал: значит, условие пока держится случайно.

Отдельно проверьте поведение на стыке торговых сессий и на выходных. Именно там расходятся оси времени двух инструментов, и именно там ломается логика, которая на ровной истории работала. Как встроить такую проверку в правила целиком, разобрано в материале про правила входа и выхода в стратегии.

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

Дисклеймер. Материал носит образовательный характер и не является индивидуальной инвестиционной рекомендацией. Решения о сделках вы принимаете самостоятельно, торговля на финансовых рынках связана с риском потери вложенных средств.

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

Данные приходят от поставщика, а не рождаются в скрипте. TradingView прямо оговаривает, что не генерирует данные и опирается на своих поставщиков. Дневные и внутридневные ленты могут отличаться по объёму, а расширенная сессия в дневную ленту не попадает. На акциях это заметно, на круглосуточных инструментах почти нет.

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

Аргумент lookahead в положении on без смещения выражения - прямой обман самого себя. Публикация такого скрипта в библиотеке TradingView запрещена, а результаты его проверки на истории не значат ничего.

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

Источники

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

Почему запрос дневного значения меняется в течение дня?

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

Что писать в строке таймфрейма для часа?

Строку "60". Часовой единицы в формате Pine Script нет вообще, и запись "1H" невалидна. Формат собирается из множителя и буквы единицы, причём у минут буквы нет: "15" означает пятнадцать минут, "1D" - день, "1W" - неделю, "1M" - месяц. Если множитель не написан, подразумевается единица: "D" равно "1D". Одинокая "1" читается как одна минута.

Можно ли поймать момент закрытия старшего бара?

Напрямую внутри вызова - нет, документация фиксирует это отдельной строкой: состояние бара barstate.isconfirmed в вызове request.security() не работает. Обходные пути есть, и они разные. Официальный - брать смещённое выражение с lookahead в положении on, тогда подтверждённое значение приходит в начале нового старшего периода. Обёртки сообщества переключают смещение по состоянию бара снаружи вызова; момент, в который у них приходит значение, документацией не описан, его надо проверять на своём графике.

Чем request.security_lower_tf лучше обычного запроса младшего масштаба?

Она возвращает все интрабары, а обычный запрос отдаёт только один на каждый бар графика. Сигнатура у неё другая: аргументов gaps и lookahead нет, зато есть ignore_invalid_timeframe, выключенный по умолчанию. Результат приходит массивом, отсортированным по времени интрабаров, и размер этого массива надо проверять до обращения к элементам, иначе скрипт упадёт на пустом массиве.

Перекрашивается ли запрос данных другого символа?

На таймфрейме графика - нет. Официальный FAQ TradingView говорит прямо: запрос данных другого символа на масштабе графика перекраской не грозит, смещать значение и менять аргумент lookahead в этом случае не нужно. Перекраска приходит вместе со старшим масштабом, а не вместе с чужим тикером. Единственное, что стоит держать в голове при чужом символе, - несовпадение торговых сессий.

Что изменилось в запросах данных в шестой версии Pine Script?

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

Что вы узнаете
  • из каких трёх обязательных аргументов собирается вызов и в каком порядке они идут
  • почему значение старшего таймфрейма меняется на незакрытом баре и как получить подтверждённое
  • как проверить свой код после перезагрузки графика и в реальном времени
Применить за 30 мин
Средний уровень
11просмотров
Материал был полезен?

Обсуждение

Айгуль

А новичку с чего начать, чтобы не утонуть?

Сергей

Вывел BTCUSD на график акции, и в субботу линия стоит на месте. Это баг в скрипте?

Макс

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

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

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

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

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