Что делает request.security и когда он нужен?
Главное
функция запускает ваше выражение в контексте другого символа или другого таймфрейма и возвращает результат на текущий бар. Она нужна, когда скрипт должен смотреть шире своего графика: фильтр по дневному тренду, сравнение с индексом, объём с младшего масштаба. Для расчётов внутри своего же графика она лишняя.
Скрипт по умолчанию видит ровно один набор свечей - тот, что открыт на экране. Как он их обходит и почему считает значения бар за баром, разобрано в материале про то, как Pine Script считает значения по барам. Там же был честно оговорён пропуск: минимальный пример намеренно не запрашивал ничего со стороны. Вот этот пропуск мы и закрываем.
Запрос через request.security решает вполне конкретные задачи. Отфильтровать вход по направлению дневного тренда, пока вы работаете на пятнадцатиминутке. Наложить на график альткоина поведение биткоина. Посчитать индикатор сразу на четырёх инструментах и усреднить. Во всех этих случаях расчёт должен произойти именно там, в чужом контексте, а к вам приехать уже готовое число.
А вот когда данные брать не надо, стоит запомнить отдельно. Если выражение считается по свечам вашего же графика, никакой запрос не нужен: пишите обычную формулу. Официальный FAQ TradingView прямо говорит, что запрашивать собственный символ и собственный таймфрейм обычно незачем: проще сослаться на значения своего же графика.
Подойдёт, если
- Фильтр по тренду старшего таймфрейма: торгуем на 15m, разрешение берём с 1D
- Сравнение своего инструмента с индексом, индексом доллара или другой парой
- Сбор одного индикатора сразу по списку символов для собственного индекса
- Подсчёт внутрибарового объёма или числа интрабаров внутри свечи графика
Не подойдёт, если
- Любая формула по свечам текущего графика: считайте напрямую, без запроса
- Попытка поймать закрытие старшего бара через состояние бара внутри вызова
- Замена управления риском: чужие данные не делают правило безопаснее
- Ускорение скрипта: каждый уникальный запрос стоит времени и места в лимите
Как устроены три аргумента: symbol, timeframe и expression?
Главное
вызов собирается из трёх обязательных частей в жёстком порядке. Сначала символ, потом строка таймфрейма, потом выражение. Остальные аргументы необязательные и передаются по имени. Порядок первых трёх при позиционной записи менять нельзя, и именно на нём спотыкаются, когда копируют чужой код из форума без разбора. Сверить его стоит до первого запуска скрипта.
Полная сигнатура в официальной документации записана так:
request.security(symbol, timeframe, expression, gaps, lookahead, ignore_invalid_symbol, currency, calc_bars_count)Первые три части и есть рабочий минимум. Символ - строка с тикером, желательно вместе с площадкой: документация рекомендует указывать префикс биржи, иначе она подберёт её сама и результат может отличаться от ожидаемого. Для текущего инструмента есть готовая переменная syminfo.tickerid, её и советуют брать, чтобы не было двусмысленности.
- 1
symbol - откуда берём
Тикер с префиксом биржи или syminfo.tickerid своего графика
- 2
timeframe - в каком масштабе
Строка вида 1D, 60, 15. Пустая означает таймфрейм графика
- 3
expression - что посчитать
Считается в чужом контексте и не переносится готовым
Со строкой таймфрейма связана ошибка, на которой обжигаются почти все. Формат строится из множителя и буквы единицы, причём у минут буквы нет вовсе: "15" - это пятнадцать минут, "1D" - день, "1W" - неделя. Часа в этом наборе просто нет. Документация формулирует без вариантов: "1H" невалиден, правильная запись часа - "60".
Третья часть - выражение - самая недооценённая. Оно не переносится посчитанным, оно вычисляется заново там, у запрошенного символа. Если написать ta.sma(close, 20), средняя посчитается по чужим закрытиям, а не по вашим. Это ровно то, что нужно, и ровно то, что удивляет человека, который ждал переноса готового значения.
Как запросить данные другого символа?
Главное
подставляете тикер вместо своего символа, оставляете таймфрейм графика и получаете значение чужого инструмента на каждом своём баре. Запрос другого символа на своём же таймфрейме перекраской не грозит. Единственный подвох тут в оси времени: чужие данные обновятся только тогда, когда обновится ваш график.
Минимальный рабочий пример выглядит буквально в одну строку. Дальше его можно вывести на график или сравнить со своей ценой:
//@version=6
indicator("Чужой символ на своём таймфрейме")
float otherClose = request.security("NASDAQ:AAPL", timeframe.period, close)
plot(otherClose)Куда вставлять этот код и как его сохранить, разобрано в отдельной инструкции про то, как открыть Pine Editor и сохранить скрипт. Здесь важнее поведение, а не кнопки.
Официальный FAQ снимает главный страх новичка: запрос данных другого символа на таймфрейме графика перекраской не грозит. Смещать значение и трогать необязательные аргументы в этом случае не нужно.
Но ось времени остаётся вашей. Документация приводит показательный пример: данные BTCUSD на графике SPY покажут новое значение только тогда, когда новый бар появится у самого SPY. Круглосуточный инструмент на графике с выходными будет стоять на месте всю субботу, и это не сбой, а свойство сцепки двух рядов данных.
Как получить данные старшего таймфрейма через request.security?
Главное
вместо таймфрейма графика подставляете строку старшего масштаба, например дневную. Обязательно добавьте защиту от обратного случая: если пользователь выберет масштаб ниже графика, скрипт должен остановиться с понятной ошибкой. Такую проверку официальные примеры Pine Script включают прямо в шаблон. Без неё скрипт молча посчитает совсем не то, чего вы от него ждали.
Классическая задача звучит так: торгую на пятнадцатиминутке, вход разрешаю только по направлению дневного тренда. Сам фильтр может быть любым, выбрать индикатор тренда без подгонки - отдельная работа. Механика запроса при этом одна и та же.
Собирается такой скрипт по шагам:
- Заводите вход для таймфрейма, чтобы масштаб не был зашит в код намертво.
- Ставите проверку: если выбранный масштаб меньше масштаба графика, вызываете
runtime.error()с текстом на человеческом языке. - Пишете сам запрос со строкой старшего таймфрейма.
- Выводите результат на график, чтобы глазами увидеть ступеньки.
//@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)Линия получится ступенчатой, и это нормально: значение обновляется раз в дневной бар, а не на каждой пятнадцатиминутке. Вопрос «как сгладить ступеньки» на форумах повторяется годами, но сглаживать тут нечего - на младшем масштабе дневного значения между обновлениями попросту не существует.
Почему значение старшего таймфрейма меняется на незакрытом баре?
Главное
потому что дневной бар ещё не закрыт, и его закрытие меняется вместе с текущей ценой. На истории функция вернёт только подтверждённые значения, в реальном времени - живые. После перезагрузки страницы вчерашние живые значения превратятся в подтверждённые, и картинка поедет. Это штатное поведение request.security. Ошибки в вашем коде тут может и не быть вовсе.
У такого поведения есть имя - перерисовка. TradingView определяет её широко: поведение скрипта, при котором расчёты на истории и в реальном времени работают по-разному. И тут же снимает панику оценкой: по их прикидке больше девяноста пяти процентов существующих индикаторов в той или иной форме перекрашиваются, включая MACD и RSI на незакрытой свече.
Механику документация описывает буквально. На исторических барах функция возвращает только подтверждённые значения из запрошенного контекста, а на барах реального времени может вернуть неподтверждённые. Когда скрипт перезапускается, вчерашние реальные бары становятся историческими - и значения на них меняются на подтверждённые.
00:00
Дневной бар открылся
Запрос вернёт текущее закрытие дня, оно равно текущей цене
В течение дня
Значение живёт и меняется
Каждая новая цена меняет закрытие незакрытого дневного бара
Сигнал на графике
Метка появилась и стоит
Условие выполнено по текущему, ещё не подтверждённому значению
23:59
Дневной бар закрылся
Значение зафиксировано и больше не изменится
После перезагрузки
История переписана
Скрипт видит только подтверждённое, часть меток исчезает
Практический вывод неприятный, но честный. Пока вы смотрите на живой график, вы видите не то, что увидит скрипт завтра на этой же истории. Именно отсюда растёт разрыв между красивой картинкой и результатом проверки торгового алгоритма.
Разберите инструмент на практике вместе с Midas
В экосистеме Midas есть пошаговые инструкции, обучение техническому анализу и поддержка сообщества — от первого запуска до разбора собственной торговой логики.
- пошаговые инструкции по продукту
- структурированное обучение теханализу
- чат с командой и участниками клуба
Как взять подтверждённое значение старшего таймфрейма без заглядывания в будущее?
Главное
официальный рецепт состоит из двух частей сразу. Выражение смещается на один бар назад, и одновременно включается аргумент lookahead в положении on. Порознь эти две части не работают: смещение без lookahead запаздывает, lookahead без смещения тянет данные из будущего. Документация Pine Script называет эти две части взаимозависимыми.
Формулировка документации звучит так: самый надёжный способ получить неперекрашивающийся результат - передавать выражение, ссылающееся только на прошлые бары, и при этом ставить lookahead = barmerge.lookahead_on. Про связку там же сказано жёстко: смещение и lookahead взаимозависимы, убрать одно без вреда для другого нельзя.
//@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.Смещение плюс lookahead включён
- 2.Настройки по умолчанию, живой бар
- 3.lookahead включён без смещения
Средняя отметка шкалы - это положение по умолчанию, и оно не обман: функция честно отдаёт то, что есть в старшем контексте прямо сейчас. Правая отметка - уже подлог, потому что на историческом баре в скрипт приезжает будущее.
Отдельная ловушка ждёт тех, кто пробует поймать закрытие старшего бара напрямую. Состояние бара внутри запроса не работает: документация фиксирует это одной строкой - barstate.isconfirmed в вызове request.security() не работает. Часть сообщества обходит это своими обёртками, переключая смещение по состоянию бара снаружи вызова. Оба подхода дают подтверждённое значение, но момент его появления на графике у них разный: официальный отдаёт закрывшийся старший бар сразу в начале нового старшего периода, поведение обёрток сообщества документацией не описано и его надо проверять глазами на своём графике. Разбор того, как отличать честный сигнал от переехавшего, лежит в материале про индикаторы без перерисовки.
Чем barmerge.gaps_on отличается от barmerge.gaps_off?
Главное
аргумент gaps решает, что показывать на барах, где у старшего контекста ещё нет нового подтверждённого значения. В положении off функция повторяет последнее известное, в положении on возвращает пустое значение. По умолчанию стоит off, поэтому линия старшего таймфрейма и выглядит ступеньками. Менять этот аргумент приходится редко и всегда осознанно.
Разница видна на одной таблице:
| Что сравниваем | barmerge.gaps_off | barmerge.gaps_on |
|---|---|---|
| Стоит по умолчанию | да | нет |
| Бар без нового значения | повторяет последнее подтверждённое, на живом баре - текущее развивающееся | отдаёт пустое значение |
| Как выглядит на графике | сплошная ступенчатая линия | точки только в моменты обновления |
| Когда удобно | фильтры, сравнения, любая логика «выше или ниже» | подсветка самого факта обновления старшего бара |
| Что ломает | скрывает момент обновления | арифметика с пустыми значениями требует проверки |
//@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. Первый или последний. Для работы со всеми интрабарами в Pine Script есть отдельная функция, которая возвращает массив значений. Её имя отличается от request.security одним хвостом, и перепутать их легко.
Поведение описано в документации без прикрас. При обращении к младшему масштабу функция считает выражение в младшем контексте, но отдаёт результат только одного интрабара на каждый бар графика. С lookahead в положении on это первый доступный интрабар, в положении off - последний. На барах реального времени возвращается последнее доступное значение независимо от аргумента.
Для нормальной работы с внутрибаровыми данными существует request.security_lower_tf(). Её сигнатура другая: нет аргументов gaps и lookahead, зато есть ignore_invalid_timeframe, и по умолчанию он выключен. Возвращает функция массив значений, отсортированный по времени интрабаров.
//@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
Включить лог значений
log.info() выводит запрошенное значение и время бара
- 2
Снять снимок живого графика
Записать время последних трёх меток и значения из лога
- 3
Перезагрузить страницу
Реальные бары становятся историческими, скрипт считает заново
- 4
Сравнить метки и значения
Сверить те же три метки и те же значения из лога
- 5
Повторить на выходных данных
Отдельно проверить участок, где у чужого символа были пропуски
Что считать провалом, стоит договориться заранее. Сдвиг метки на один бар - это перекраска, а не мелочь. Исчезнувшая метка - перекраска. Совпавшие метки при разошедшихся значениях в логе - тоже сигнал: значит, условие пока держится случайно.
Отдельно проверьте поведение на стыке торговых сессий и на выходных. Именно там расходятся оси времени двух инструментов, и именно там ломается логика, которая на ровной истории работала. Как встроить такую проверку в правила целиком, разобрано в материале про правила входа и выхода в стратегии.
Риски и ограничения
Дисклеймер. Материал носит образовательный характер и не является индивидуальной инвестиционной рекомендацией. Решения о сделках вы принимаете самостоятельно, торговля на финансовых рынках связана с риском потери вложенных средств.
Правильно написанный запрос перестаёт врать задним числом, но точнее сигнал от этого не становится. Скрипт, который после перезагрузки показывает то же самое, честен - но честность и прибыльность это разные свойства, и первое второго не гарантирует.
Данные приходят от поставщика, а не рождаются в скрипте. TradingView прямо оговаривает, что не генерирует данные и опирается на своих поставщиков. Дневные и внутридневные ленты могут отличаться по объёму, а расширенная сессия в дневную ленту не попадает. На акциях это заметно, на круглосуточных инструментах почти нет.
Число запросов ограничено, и лимит зависит от тарифа. Одинаковые вызовы обычно считаются за один, но вызовы внутри импортированных библиотек считаются отдельно. Скрипт, который просит данные у десятка символов, упрётся в потолок быстрее, чем кажется на этапе идеи.
Аргумент lookahead в положении on без смещения выражения - прямой обман самого себя. Публикация такого скрипта в библиотеке TradingView запрещена, а результаты его проверки на истории не значат ничего.
Проверка на истории не переносится на будущее. Ритуал из этой статьи ловит только один класс дефектов - расхождение между историей и реальным временем. Он ничего не говорит о том, будет ли правило работать дальше, и не заменяет ни стоп-приказ, ни размер позиции, заданный до входа.
Источники
- Other timeframes and data, Pine Script v6 User Manual, tradingview.com
- Repainting, Pine Script v6 User Manual, tradingview.com
- Timeframes, Pine Script v6 User Manual, tradingview.com
- Limitations, Pine Script v6 User Manual, tradingview.com
- Bar states, Pine Script v6 User Manual, tradingview.com
- To Pine Script version 6, migration guide, tradingview.com
- PineCoders FAQ and Code, pinecoders.com
- Исходник FAQ PineCoders, github.com
- Barmerge lookahead on or off, обсуждение, stackoverflow.com
- How to get bar confirmation of higher time frame, обсуждение, stackoverflow.com
- Optimizing performance in Pine Script when using request.security, обсуждение, stackoverflow.com
- Pine Script repainting, CrossTrade, crosstrade.io
- Pine Script v6 dynamic requests, Tom Hartman, blog.traderspost.io
- Palomar D. P. The Seven Sins of Quantitative Investing, Portfolio Optimization, portfoliooptimizationbook.com
- Problems in Backtesting, конспект CFA Level 2, analystprep.com
Частые вопросы
Почему запрос дневного значения меняется в течение дня?
Потому что дневной бар ещё не закрыт. Пока он живой, его закрытие равно текущей цене и меняется вместе с ней. Функция честно отдаёт то, что есть в запрошенном контексте прямо сейчас. Как только бар закроется, значение зафиксируется. Если вам нужно только подтверждённое число, смещайте выражение на один бар назад и одновременно включайте аргумент 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, запрос всё равно выполняется на каждом баре, даже если вызов стоит внутри условия.










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