Рисунки – Robo.trading

Рисунки

  • Создание рисунков
  • Расчет рисунков по обновлению баров
  • Координаты
  • Изменение рисунков
  • Стили меток
  • Стили линий
  • Удаление рисунков
  • Примеры классических индикаторов
  • Стандартные точки разворота
  • Точки разворота: максимум / минимум
  • Линейная регрессия
  • Зигзаг
  • Лимиты
  • Общее количество рисунков
  • Дополнительные ценные бумаги
  • max_bars_back of time

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

Примечание

На графиках TradingView полный набор инструментов рисования позволяет пользователям создавать и изменять рисунки с помощью мыши. Хотя они могут выглядеть похожими на объекты рисования, созданные с помощью кода Pine, по сути, это разные вещи. Объекты рисования, созданные с помощью кода Pine, нельзя изменить с помощью мыши.

Новые рисунки линий и меток в Pine v4 позволяют создавать индикаторы с более сложными визуальными компонентами, например, точки разворота, уровни поддержки / сопротивления, зигзагообразные линии, метки, содержащие динамический текст и т. д.

В отличие от индикаторных участков (участки создаются с функциями plotplotshapeplotchar), графические объекты могут быть созданы на исторических барах, а также в будущем, где еще не существует баров.

Создание рисунков

Объекты рисунков Pine создаются с помощью функций label.new и line.new. Хотя каждая функция имеет много параметров, обязательными являются только координаты. Это пример кода, используемого для создания метки на каждом баре:

//@version=4
study("My Script", overlay=true)
label.new(bar_index, high)

Метка создается с параметрами x=bar_index (индекс текущего бара, bar_index) и y=high (максимальная цена текущего бара). Когда открывается новый бар, на нем создается новая метка. Объекты меток, созданные на предыдущих барах, остаются на графике до тех пор, пока индикатор не удалит их явным вызовом функции label.delete или пока процесс автоматической сборки мусора не удалит их.

Вот модифицированная версия того же скрипта, который показывает значения x и y координат, используемых для создания меток:

//@version=4
study("My Script", overlay=true)
label.new(bar_index, high, style=label.style_none,
          text="x=" + tostring(bar_index) + "\ny=" + tostring(high))

В этом примере метки показаны без окраски фона (из-за параметра style=label.style_none), но с динамически созданным текстом (text=»x=» + tostring(bar_index) + «\ny=» + tostring(high)), который печатает координаты метки.

Это пример кода, который создает линейные объекты на графике:

//@version=4
study("My Script", overlay=true)
line.new(x1=bar_index[1], y1=low[1], x2=bar_index, y2=high)

Расчет рисунков по обновлению баров

Объекты рисования подвержены действиям как фиксации, так и отката, которые влияют на поведение сценария при его выполнении в баре реального времени, модели выполнения.

Этот скрипт демонстрирует эффект отката при запуске на баре реального времени:

//@version=4
study("My Script", overlay=true)
label.new(bar_index, high)

Хотя label.new создает новую метку на каждой итерации скрипта, когда цена изменяется в баре реального времени, самая последняя метка, созданная в предыдущей итерации скрипта, также автоматически удаляется из-за отката перед следующей итерацией. Будет зафиксирована только последняя метка, созданная перед закрытием бара реального времени, и таким образом останется.

Координаты

Объекты рисования располагаются на графике по координатам x и y, используя комбинацию из 4 параметров: x, y, xloc и yloc. Значение xloc определяет, будет ли хранить индекс бара или значение времени. Когда yloc=yloc.price, y фиксирует цену. y игнорируется, когда yloc установлен в yloc.abovebar или yloc.belowbar.

Если рисуемый объект использует xloc.bar_index, то координата x рассматривается как абсолютный индекс бара. Индекс текущего бара можно получить из встроенной переменной bar_index. Индексом предыдущего бара является bar_index[1], bar_index[2] и т.д. xloc.bar_index является значением по умолчанию для параметров x-локации как на рисунках меток, так и на линейных рисунках.

Если объект рисования использует xloc.bar_time, то координата x в миллисекундах рассматривается как UNIX-время. Время начала текущего бара можно получить из встроенной переменной time. Время бара предыдущих баров — это time[1], time[2] и так далее. Время также может быть установлено в абсолютную точку времени с помощью функции timestamp.

Режим xloc.bar_time позволяет поместить рисунок объекта в будущем, справа от текущего бара.

Например:

//@version=4
study("My Script", overlay=true)
dt = time - time[1]
if barstate.islast
    label.new(time + 3*dt, close, xloc=xloc.bar_time)

Этот код помещает объект метки в будущее. Логика X-location одинаково работает как для меток, так и для рисунков.

В отличие от этого, логика Y-location отличается для меток и рисунков. В линейных рисунках Pine всегда используется yloc.price, поэтому их координата Y всегда рассматривается как абсолютное значение цены.

Рисунки меток имеют дополнительные значения y-координаты : yloc.abovebar и yloc.belowbar. Когда они используются, значение параметра  игнорируется и объект рисования помещается выше или ниже бара.

Изменение рисунков

Объект рисования может быть изменен после его создания. Функции label.new и line.new возвращают ссылку на созданный объект рисунка (типа label серии и линии серии соответственно). Эта ссылка затем может быть использована в качестве первого аргумента к функциям label.set_* и line.set_*, используемым для модификации рисунков.

Например:

//@version=4
study("My Script", overlay=true)
l = label.new(bar_index, na)
if close >= open
    label.set_text(l, "green")
    label.set_color(l, color.green)
    label.set_yloc(l, yloc.belowbar)
    label.set_style(l, label.style_labelup)
else
    label.set_text(l, "red")
    label.set_color(l, color.red)
    label.set_yloc(l, yloc.abovebar)
    label.set_style(l, label.style_labeldown)

Этот простой скрипт сначала создает метку на текущем баре, а затем записывает ссылку на него в переменной l. Затем, в зависимости от того, поднимается или опускается текущий бар (условие close >= open), изменяется ряд свойств рисования меток: текст, цвет, положение координаты y (yloc) и стиль метки.

Можно заметить, что na передается в качестве аргумента y при вызове функции label.new. Причина этого заключается в том, что в примере label использует либо yloc.belowbar, либо yloc.overbar y-локации, которые не требуют значения y. Конечное значение y необходимо только в том случае, если метка использует yloc.price.

Доступные функции установок для рисования меток:

  • label.set_color — меняет цвет метки
  • label.set_size — изменяет размер метки
  • label.set_style — изменяет стиль метки
  • label.set_text — изменяет текст метки
  • label.set_textcolor — меняет цвет текста
  • label.set_x — изменяет x-координату метки
  • label.set_y — изменяет координату y метки
  • label.set_xy — изменяет координаты x и y метки
  • label.set_xloc — изменяет x-расположение метки
  • label.set_yloc — изменяет y-расположение метки
  • label.set_tooltip — изменяет всплывающую подсказку метки

Доступные функции установок для линейных рисунков:

  • line.set_color — меняет цвет линии
  • line.set_extend — изменяет атрибут, который создает:
  • extend.none — отрезок линии
  • extend.leftextend.right— луч
  • extend.both — бесконечную линию
  • line.set_style — меняет стиль линии
  • line.set_width — изменяет ширину линии
  • line.set_xloc — изменяет x-положение строки (как x1, так и x2)
  • line.set_x1 — изменяет x1-координату линии
  • line.set_y1 — изменяет y1-координату линии
  • line.set_xy1 — изменяет координаты x1 и y1 линии
  • line.set_x2 — изменяет x2-координату линии
  • line.set_y2 — изменяет y2-координату линии
  • line.set_xy2 — одновременно меняет координаты x2 и y2 линии

Стили меток

К меткам можно применять различные стили с помощью функции label.new или label.set_style :

Название стиля ярлыка Метка Метка с текстом
label.style_none
label.style_xcross
label.style_cross
label.style_triangleup
label.style_triangledown
label.style_flag
label.style_circle
label.style_arrowup
label.style_arrowdown
label.style_labelup
label.style_labeldown
label.style_square
label.style_diamond

Стили линий

К линиям можно применять различные стили с помощью функции line.new или line.set_style :

Название стиля линии Линия
line.style_solid
line.style_dotted
line.style_dashed
line.style_arrow_left
line.style_arrow_right
line.style_arrow_both

Удаление рисунков

Функции label.delete и line.delete удаляют объекты label и line drawing objects с графика.

Вот пример кода Pine, который хранит только один объект рисования меток на текущем баре, удаляя старые:

//@version=4
study("Last Bar Close 1", overlay=true)
c = close >= open ? color.lime : color.red
l = label.new(bar_index, na,
  text=tostring(close), color=c,
  style=label.style_labeldown, yloc=yloc.abovebar)
label.delete(l[1])

При каждом обновлении нового бара в исследовании «Last Bar Close 1» создается новый объект метки и записывается в переменную l. Переменная l имеет тип метки серии, поэтому для получения объекта метки предыдущего бара используется оператор []. Затем эта предыдущая метка передается в функцию label.delete для ее удаления.

Функции label.delete и line.delete ничего не делают, если в качестве id используется значение na, что делает избыточным следующий код:

if not na(l[1])
    label.delete(l[1])

Поведение предыдущего скрипта можно воспроизвести с помощью другого подхода:

//@version=4
study("Last Bar Close 2", overlay=true)
var label l = na
label.delete(l)
c = close >= open ? color.lime : color.red
l := label.new(bar_index, na,
  text=tostring(close), color=c,
  style=label.style_labeldown, yloc=yloc.abovebar)

Когда индикатор «Last Bar Close 2» получает новое обновление бара, переменная l все еще ссылается на старый объект метки, созданный на предыдущем баре. Эта метка удаляется вызовом label.delete(l). Затем создается новая метка, а ее идентификатор сохраняется в l. При таком подходе нет необходимости использовать оператор [].

Обратите внимание на использование нового ключевого слова Pine v4 var. Оно создает переменную и инициализирует ее значением na только один раз. label.delete(l) не имел бы объекта для удаления, если бы не тот факт, что l инициализируется только один раз.

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

//@version=4
study("Last Bar Close 3", overlay=true)
var label l = label.new(bar_index, na,
  style=label.style_labeldown, yloc=yloc.abovebar)
c = close >= open ? color.lime : color.red
label.set_color(l, c)
label.set_text(l, tostring(close))
label.set_x(l, bar_index)

Опять же, использование нового ключевого слова var необходимо. Это то, что позволяет выполнить вызов label.new только один раз, на самом первом историческом баре.

Примеры классических индикаторов

Стандартные точки разворота

 

//@version=4
study("Pivot Points Standard", overlay=true)
higherTF = input("D", type=input.resolution)
prevCloseHTF = security(syminfo.tickerid, higherTF, close[1], lookahead=true)
prevOpenHTF = security(syminfo.tickerid, higherTF, open[1], lookahead=true)
prevHighHTF = security(syminfo.tickerid, higherTF, high[1], lookahead=true)
prevLowHTF = security(syminfo.tickerid, higherTF, low[1], lookahead=true)
pLevel = (prevHighHTF + prevLowHTF + prevCloseHTF) / 3
r1Level = pLevel * 2 - prevLowHTF
s1Level = pLevel * 2 - prevHighHTF
var line r1Line = na
var line pLine = na
var line s1Line = na
if pLevel[1] != pLevel
    line.set_x2(r1Line, bar_index)
    line.set_x2(pLine, bar_index)
    line.set_x2(s1Line, bar_index)
    line.set_extend(r1Line, extend.none)
    line.set_extend(pLine, extend.none)
    line.set_extend(s1Line, extend.none)
    r1Line := line.new(bar_index, r1Level, bar_index, r1Level, extend=extend.right)
    pLine := line.new(bar_index, pLevel, bar_index, pLevel, width=3, extend=extend.right)
    s1Line := line.new(bar_index, s1Level, bar_index, s1Level, extend=extend.right)
    label.new(bar_index, r1Level, "R1", style=label.style_none)
    label.new(bar_index, pLevel, "P", style=label.style_none)
    label.new(bar_index, s1Level, "S1", style=label.style_none)
if not na(pLine) and line.get_x2(pLine) != bar_index
    line.set_x2(r1Line, bar_index)
    line.set_x2(pLine, bar_index)
    line.set_x2(s1Line, bar_index)

Точки разворота максимум/минимум

//@version=4
study("Pivot Points High Low", shorttitle="Pivots HL", overlay=true)
lenH = input(title="Length High", type=input.integer, defval=10, minval=1)
lenL = input(title="Length Low", type=input.integer, defval=10, minval=1)
fun(src, len, isHigh, _style, _yloc, _color) =>
    p = nz(src[len])
    isFound = true
    for i = 0 to len - 1
        if isHigh and src[i] > p
            isFound := false
        if not isHigh and src[i] < p
            isFound := false
    for i = len + 1 to 2 * len
        if isHigh and src[i] >= p
            isFound := false
        if not isHigh and src[i] <= p
            isFound := false
    if isFound
        label.new(bar_index[len], p, tostring(p), style=_style, yloc=_yloc, color=_color)
fun(high, lenH, true, label.style_labeldown, yloc.abovebar, color.lime)
fun(low, lenL, false, label.style_labelup, yloc.belowbar, color.red)

Линейная регрессия

//@version=4
study("Linear Regression", shorttitle="LinReg", overlay=true)
upperMult = input(title="Upper Deviation", defval=2)
lowerMult = input(title="Lower Deviation", defval=-2)
useUpperDev = input(title="Use Upper Deviation", defval=true)
useLowerDev = input(title="Use Lower Deviation", defval=true)
showPearson = input(title="Show Pearson's R", defval=true)
extendLines = input(title="Extend Lines", defval=false)
len = input(title="Count", defval=100)
src = input(title="Source", defval=close)
extend = extendLines ? extend.right : extend.none
calcSlope(src, len) =>
    if not barstate.islast or len <= 1
        [float(na), float(na), float(na)]
    else
        sumX = 0.0
        sumY = 0.0
        sumXSqr = 0.0
        sumXY = 0.0
        for i = 0 to len - 1
            val = src[i]
            per = i + 1.0
            sumX := sumX + per
            sumY := sumY + val
            sumXSqr := sumXSqr + per * per
            sumXY := sumXY + val * per
        slope = (len * sumXY - sumX * sumY) / (len * sumXSqr - sumX * sumX)
        average = sumY / len
        intercept = average - slope * sumX / len + slope
        [slope, average, intercept]
[s, a, i] = calcSlope(src, len)
startPrice = i + s * (len - 1)
endPrice = i
var line baseLine = na
if na(baseLine) and not na(startPrice)
    baseLine := line.new(bar_index - len + 1, startPrice, bar_index, endPrice, width=1, extend=extend, color=color.red)
else
    line.set_xy1(baseLine, bar_index - len + 1, startPrice)
    line.set_xy2(baseLine, bar_index, endPrice)
    na
calcDev(src, len, slope, average, intercept) =>
    upDev = 0.0
    dnDev = 0.0
    stdDevAcc = 0.0
    dsxx = 0.0
    dsyy = 0.0
    dsxy = 0.0
    periods = len - 1
    daY = intercept + (slope * periods) / 2
    val = intercept
    for i = 0 to periods
        price = high[i] - val
        if (price > upDev)
            upDev := price
        price := val - low[i]
        if (price > dnDev)
            dnDev := price
        price := src[i]
        dxt = price - average
        dyt = val - daY
        price := price - val
        stdDevAcc := stdDevAcc + price * price
        dsxx := dsxx + dxt * dxt
        dsyy := dsyy + dyt * dyt
        dsxy := dsxy + dxt * dyt
        val := val + slope
    stdDev = sqrt(stdDevAcc / (periods == 0 ? 1 : periods))
    pearsonR = dsxx == 0 or dsyy == 0 ? 0 : dsxy / sqrt(dsxx * dsyy)
    [stdDev, pearsonR, upDev, dnDev]
[stdDev, pearsonR, upDev, dnDev] = calcDev(src, len, s, a, i)
upperStartPrice = startPrice + (useUpperDev ? upperMult * stdDev : upDev)
upperEndPrice = endPrice + (useUpperDev ? upperMult * stdDev : upDev)
var line upper = na
lowerStartPrice = startPrice + (useLowerDev ? lowerMult * stdDev : -dnDev)
lowerEndPrice = endPrice + (useLowerDev ? lowerMult * stdDev : -dnDev)
var line lower = na
if na(upper) and not na(upperStartPrice)
    upper := line.new(bar_index - len + 1, upperStartPrice, bar_index, upperEndPrice, width=1, extend=extend, color=#0000ff)
else
    line.set_xy1(upper, bar_index - len + 1, upperStartPrice)
    line.set_xy2(upper, bar_index, upperEndPrice)
    na
if na(lower) and not na(lowerStartPrice)
    lower := line.new(bar_index - len + 1, lowerStartPrice, bar_index, lowerEndPrice, width=1, extend=extend, color=#0000ff)
else
    line.set_xy1(lower, bar_index - len + 1, lowerStartPrice)
    line.set_xy2(lower, bar_index, lowerEndPrice)
    na
// Pearson's R
var label r = na
transparent = color.new(color.white, 100)
label.delete(r[1])
if showPearson and not na(pearsonR)
    r := label.new(bar_index - len + 1, lowerStartPrice, tostring(pearsonR, "#.################"), color=transparent, textcolor=#0000ff, size=size.normal, style=label.style_labelup)

Зигзаг

//@version=4
study("Zig Zag", overlay=true)
dev_threshold = input(title="Deviation (%)", type=input.float, defval=5, minval=1, maxval=100)
depth = input(title="Depth", type=input.integer, defval=10, minval=1)
pivots(src, length, isHigh) =>
    p = nz(src[length])
    if length == 0
        [bar_index, p]
    else
        isFound = true
        for i = 0 to length - 1
            if isHigh and src[i] > p
                isFound := false
            if not isHigh and src[i] < p
                isFound := false
        for i = length + 1 to 2 * length
            if isHigh and src[i] >= p
                isFound := false
            if not isHigh and src[i] <= p
                isFound := false
        if isFound and length * 2 <= bar_index
            [bar_index[length], p]
        else
            [int(na), float(na)]
[iH, pH] = pivots(high, floor(depth / 2), true)
[iL, pL] = pivots(low, floor(depth / 2), false)
calc_dev(base_price, price) =>
    100 * (price - base_price) / base_price
var line lineLast = na
var int iLast = 0
var float pLast = 0
var bool isHighLast = true // otherwise the last pivot is a low pivot
var int linesCount = 0
pivotFound(dev, isHigh, index, price) =>
    if isHighLast == isHigh and not na(lineLast)
        // same direction
        if isHighLast ? price > pLast : price < pLast
            if linesCount <= 1
                line.set_xy1(lineLast, index, price)
            line.set_xy2(lineLast, index, price)
            [lineLast, isHighLast, false]
        else
            [line(na), bool(na), false]
    else // reverse the direction (or create the very first line)
        if na(lineLast)
            id = line.new(index, price, index, price, color=color.red, width=2)
            [id, isHigh, true]
        else
            // price move is significant
            if abs(dev) >= dev_threshold
                id = line.new(iLast, pLast, index, price, color=color.red, width=2)
                [id, isHigh, true]
            else
                [line(na), bool(na), false]
if not na(iH) and not na(iL) and iH == iL
    dev1 = calc_dev(pLast, pH)
    [id2, isHigh2, isNew2] = pivotFound(dev1, true, iH, pH)
    if isNew2
        linesCount := linesCount + 1
    if not na(id2)
        lineLast := id2
        isHighLast := isHigh2
        iLast := iH
        pLast := pH
    dev2 = calc_dev(pLast, pL)
    [id1, isHigh1, isNew1] = pivotFound(dev2, false, iL, pL)
    if isNew1
        linesCount := linesCount + 1
    if not na(id1)
        lineLast := id1
        isHighLast := isHigh1
        iLast := iL
        pLast := pL
else
    if not na(iH)
        dev1 = calc_dev(pLast, pH)
        [id, isHigh, isNew] = pivotFound(dev1, true, iH, pH)
        if isNew
            linesCount := linesCount + 1
        if not na(id)
            lineLast := id
            isHighLast := isHigh
            iLast := iH
            pLast := pH
    else
        if not na(iL)
            dev2 = calc_dev(pLast, pL)
            [id, isHigh, isNew] = pivotFound(dev2, false, iL, pL)
            if isNew
                linesCount := linesCount + 1
            if not na(id)
                lineLast := id
                isHighLast := isHigh
                iLast := iL
                pLast := pL

Лимиты

Общее количество рисунков

Объекты рисунков потребляют ресурсы сервера, поэтому существует ограничение на общее количество рисунков для каждого инидикатора или стратегии. Когда создается слишком много рисунков, старые автоматически удаляются в процессе выполнения средой Pine, называемом сборкой мусора.

Этот код создает рисунок на каждом баре:

//@version=4
study("My Script", overlay=true)
label.new(bar_index, high)

Прокручивая график влево, примерно через 50 столбцов можно увидеть, что рисунков нет:

Дополнительные ценные бумаги

Pine code иногда использует дополнительные тикеры и/или таймфреймы с функцией securityФункции рисования можно использовать только в контексте основного инструмента.

max_bars_back of time

Использование barstate.isrealtime в сочетании с рисунками может иногда давать неожиданные результаты. В этом коде, например, предполагается игнорировать все исторические бары и создавать рисунок метки на баре в реальном времени:

//@version=4
study("My Script", overlay=true)
if barstate.isrealtime
    label.new(bar_index[10], na, text="Label", yloc=yloc.abovebar)

Однако во время выполнения произойдет сбой. Причина ошибки заключается в том, что Pine не может определить размер буфера для исторических значений time графика, даже если встроенная переменная  time не упоминается в коде. Это связано с тем, что встроенная переменная bar_index использует серию time во внутренней работе. Чтобы получить доступ к значению индекса бара на 10 баров назад, необходимо, чтобы размер буфера истории серии time был не менее 10.

В Pine есть механизм, который автоматически определяет необходимый размер исторического буфера для большинства случаев. Автоопределение работает, позволяя коду Pine получать доступ к историческим значениям на любое количество баров в течение ограниченного времени. В случае этого сценария условие if barstate.isrealtime предотвращает любой такой доступ, поэтому невозможно вывести требуемый размер исторического буфера, и код не работает.

Решением этой головоломки является использование функции max_bars_back для явной установки размера исторического буфера для time серии:

//@version=4
study("My Script", overlay=true)
max_bars_back(time, 10)
if barstate.isrealtime
    label.new(bar_index[10], na, text="Label", yloc=yloc.abovebar)

Такие случаи сбивают с толку, но редки. Со временем команда Pine надеется их устранить.

/

Robo.Trading

eVe Developer
Мы используем cookie-файлы для наилучшего представления нашего сайта. Продолжая использовать этот сайт, вы соглашаетесь с использованием cookie-файлов.
Принять