Вводный учебник по языку PINE FMZ Quant

Автор:Лидия., Создано: 2022-09-23 15:23:34, Обновлено: 2024-02-27 16:47:41

Итак, мы видим, что на трех выведенных линиях a, b и c линия b на один бар медленнее линии a, а линия c на один бар медленнее линии b.

Мы можем перетащить диаграмму в крайнее левое и наблюдать, что на первой K-линии оба значения b и c являются нулевыми (na). Это потому, что когда скрипт выполняется на первой K-линии BAR, он не существует при ссылке на историческое значение одного или двух периодов вперед, которое не существует. Поэтому нам нужно быть осторожными при написании стратегий, чтобы проверить, приведет ли ссылка на исторические данные к нулевым значениям. Если использовать нулевое значение небрежно, это вызовет ряд расчетных различий и может даже повлиять на BAR в режиме реального времени. Обычно мы будем использовать встроенные функцииna, nzчтобы судить в кодексе (на самом деле, мы также столкнулисьnz, ```na `` в наших предыдущих видеороликах, помните, какая глава это?) занимаются случаем нулевых значений, например:

close > nz(close[1], open)    // When referencing the historical value of the previous BAR of the close built-in variable, if it does not exist, the open built-in variable is used

Это способ обработки возможных ссылок на нулевые значения (na).

Приоритет оператора

Мы выучили много операторов в языке Пайн. Эти операторы формируют выражения через различные комбинации с операндами. Итак, каков приоритет этих операций при оценке в выражениях? Точно так же, как арифметика, которую мы изучали в школе, умножение и деление вычисляются сначала, а затем добавление и вычитание. То же самое относится к выражениям в языке Пайн.

Приоритетность Операторы
9 []
8 +-иnotв униарном операторе
7 */%
6 +-в бинарном операторе
5 ><>=<=
4 ==!=
3 and
2 or
1 ?:

Высокоприоритетные выражения вычисляются первыми, и если приоритеты одинаковы, он оценивается слева направо.()чтобы завершить выражение, чтобы заставить часть оцениваться первой.

Переменные

Переменная декларация

Мы уже изучали понятие marker раньше, которое используется в качестве названия переменной, то есть переменная - это маркер, который имеет значение.

  • Режим декларирования: Первое, что нужно написать при декларировании переменной, это режим декларирования.

    1. Используйте ключевое словоvar.
    2. Используйте ключевое словоvarip.
    3. Ничего не пишите.

    ВvarиvaripКлючевые слова были изучены в нашей предыдущей главеAssignment Operators, поэтому мы не будем вдаваться в детали здесь. Если ничего не написано для режима декларации переменной, например, утверждение:i = 1, как мы также упоминали ранее, такая переменная, объявленная и назначенная, выполняется на каждой K-линии BAR.

  • Тип Язык Pine на FMZ не является строгим в отношении типов, и его обычно можно опустить.

    int i = 0 
    float f = 1.1
    

    Требования к типу в Trading View достаточно строги, и будет сообщена ошибка, если в Trading View используется следующий код:

    baseLine0 = na          // compile time error!
    
  • Маркер Маркеры - это имена переменных.https://www.fmz.com/bbs-topic/9637#markers

Подводя итог, объявление переменной может быть написано так:

// [<declaration_mode>] [<type>] <marker> = value 
   declaration mode             type     marker      = value

Оператор назначения используется здесь:=При присвоении значения может быть строка, число, выражение, вызов функции,if, for, while, илиswitchи другие структуры (эти структурные ключевые слова и использование заявления будут подробно объяснены в последующих курсах.

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

функция ввода:

input function, parameters: defval、title、tooltip、inline、group

Функция ввода на FMZ несколько отличается от той, что на Trading View, но эта функция используется в качестве ввода назначения параметров стратегии.

param1 = input(10, title="name of param1", tooltip="description for param1", group="group name A")
param2 = input("close", title="name of param2", tooltip="description for param2", group="group name A")
param3 = input(color.red, title="name of param3", tooltip="description for param3", group="group name B")
param4 = input(close, title="name of param4", tooltip="description for param4", group="group name B")
param5 = input(true, title="name of param5", tooltip="description for param5", group="group name C")

ma = ta.ema(param4, param1)
plot(ma, title=param2, color=param3, overlay=param5)

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

img

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

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

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

[Variable A, Variable B, Variable C] = function or structure, such as ```if```, ```for```, ```while``` or ```switch```

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

[dif,dea,column] = ta.macd(close, 12, 26, 9)

plot(dif, title="dif")
plot(dea, title="dea")
plot(column, title="column", style=plot.style_histogram)

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

twoEMA(data, fastPeriod, slowPeriod) =>
    fast = ta.ema(data, fastPeriod)
    slow = ta.ema(data, slowPeriod)
    [fast, slow]

[ema10, ema20] = twoEMA(close, 10, 20)
plot(ema10, title="ema10", overlay=true)
plot(ema20, title="ema20", overlay=true)

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

[ema10, ema20] = if true
    fast = ta.ema(close, 10)
    slow = ta.ema(close, 20)
    [fast, slow]

plot(ema10, title="ema10", color=color.fuchsia, overlay=true)
plot(ema20, title="ema20", color=color.aqua, overlay=true)

Структура состояния

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

barcolor ((), fill ((), hline ((), indicator ((), plot ((), plotcandle ((), plotchar ((), plotshape (())

Trading View будет компилировать с ошибками, FMZ не так ограничивает, но рекомендуется следовать спецификациям Trading View.

strategy("test", overlay=true)
if close > open 
    plot(close, title="close")
else 
    plot(open, title="open")

если заявление

Пример объяснения:

var lineColor = na

n = if bar_index > 10 and bar_index <= 20
    lineColor := color.green
else if bar_index > 20 and bar_index <= 30
    lineColor := color.blue
else if bar_index > 30 and bar_index <= 40
    lineColor := color.orange
else if bar_index > 40
    lineColor := color.black
else 
    lineColor := color.red
    
plot(close, title="close", color=n, linewidth=5, overlay=true)
plotchar(true, title="bar_index", char=str.tostring(bar_index), location=location.abovebar, color=color.red, overlay=true)

Ключевой момент: выражения, используемые для суждений, которые возвращают булевые значения. Обратите внимание на отступление. Может быть максимум еще одна ветвь. Если все выражения ветвей не соответствуют действительности и нет другой ветви, верните na.

x = if close > open
    close
plot(x, title="x")

Заявление о переключении

Указание switch также является структурным указанием, которое используется для проектирования различных путей, которые должны быть выполнены в соответствии с определенными условиями.

  1. Заявление switch, как и if, также может возвращать значение.
  2. В отличие от инструкций switch в других языках, когда выполняется конструкция switch, выполняется только локальный блок кода, поэтому инструкция break является ненужной (т.е. нет необходимости писать ключевые слова, такие как break).
  3. Каждая ветвь коммутатора может записывать локальный кодный блок, последней строкой этого локального кодового блока является возвращаемое значение (это может быть тупл значений).
  4. Выражение в структуре переключателя определяет положение, может писать строку, переменную, выражение или вызов функции.
  5. switch позволяет указать возвращаемое значение, которое выступает в качестве значения по умолчанию, которое должно использоваться, когда в структуре нет другого случая для выполнения.

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

  1. А.switchс выражениями - пример объяснения:
// input.string: defval, title, options, tooltip
func = input.string("EMA", title="indicator name", tooltip="select the name of the indicator function to be used", options=["EMA", "SMA", "RMA", "WMA"])

// input.int: defval, title, options, tooltip
// param1 = input.int(10, title="period parameter")
fastPeriod = input.int(10, title="fastPeriod parameter", options=[5, 10, 20])
slowPeriod = input.int(20, title="slowPeriod parameter", options=[20, 25, 30])

data = input(close, title="data", tooltip="select the closing price, opening price, highest price...")
fastColor = color.red
slowColor = color.red

[fast, slow] = switch func
    "EMA" =>
        fastLine = ta.ema(data, fastPeriod)
        slowLine = ta.ema(data, slowPeriod)
        fastColor := color.red
        slowColor := color.red
        [fastLine, slowLine]
    "SMA" =>
        fastLine = ta.sma(data, fastPeriod)
        slowLine = ta.sma(data, slowPeriod)
        fastColor := color.green
        slowColor := color.green
        [fastLine, slowLine]
    "RMA" =>
        fastLine = ta.rma(data, fastPeriod)
        slowLine = ta.rma(data, slowPeriod)
        fastColor := color.blue
        slowColor := color.blue
        [fastLine, slowLine]
    =>
        runtime.error("error")
        
plot(fast, title="fast" + fastPeriod, color=fastColor, overlay=true)
plot(slow, title="slow" + slowPeriod, color=slowColor, overlay=true)

Мы изучали входную функцию раньше, здесь мы продолжаем изучать две функции, похожие на вход:input.string, input.int functions. input.stringиспользуется для возвращения строки, иinput.intВ примере, есть новое использованиеoptionsпараметр.optionsпараметр может быть передан массив необязательных значений, таких какoptions=["EMA", "SMA", "RMA", "WMA"]иoptions=[5, 10, 20]в примере (заметьте, что один из них - тип строки, другой - числовой тип). Таким образом, элементам управления на интерфейсе стратегии не нужно вводить конкретные значения, но элементы управления становятся выпадающими окнами для выбора этих опций, предоставленных в параметре опций.

Значение переменной func представляет собой строку, и переменная func используется в качестве выражения для переключения (которое может быть переменной, вызовом функции или выражением), чтобы определить, какая ветвь в переключателе выполняется.runtime.error("error")Функция будет выполнена, что заставляет стратегию бросить исключение и остановиться.

В нашем тестовом коде выше, после последней строки runtime.error в блоке кода раздела по умолчанию переключателя, мы не добавили код вроде [na, na] для совместимости с возвращаемым значением. Эта проблема должна быть рассмотрена в Trading View. Если тип несовместим, будет сообщена ошибка. Но на FMZ, поскольку тип не является строго необходимым, этот код совместимости может быть пропущен. Поэтому нет необходимости рассматривать совместимость типа возвращаемого значения if и switch branches на FMZ.

strategy("test", overlay=true)
x = if close > open
    close
else
    "open"
plotchar(true, title="x", char=str.tostring(x), location=location.abovebar, color=color.red)

Ошибки не будут сообщаться на FMZ, но ошибка будет сообщаться на торговом виде.

  1. switchбез выражений

Далее, давайте посмотрим на другой способ использованияswitch, то есть без выражений.

up = close > open     // up = close < open 
down = close < open 
var upOfCount = 0 
var downOfCount = 0 

msgColor = switch
    up  => 
        upOfCount += 1 
        color.green 
    down => 
        downOfCount += 1
        color.red

plotchar(up, title="up", char=str.tostring(upOfCount), location=location.abovebar, color=msgColor, overlay=true)
plotchar(down, title="down", char=str.tostring(downOfCount), location=location.belowbar, color=msgColor, overlay=true)

Как мы можем видеть из примера тестового кода, switch будет соответствовать выполнению локального кодового блока, который является истинным при условии ветви. В целом, условия ветви, следующие за инструкцией switch, должны быть взаимоисключающими. То есть, вверх и вниз в примере не могут быть истинными одновременно. Поскольку switch может выполнять только локальный кодовый блок одной ветви, если вы заинтересованы, вы можете заменить эту строку в коде:up = close > open // up = close < openВы обнаружите, что ветвь переключателя может выполнять только первую ветвь. Кроме того, необходимо обратить внимание на то, чтобы не писать вызовы функций в ветви переключателя, насколько это возможно, функция не может быть вызвана на каждом BAR, что может вызвать некоторые проблемы с расчетом данных (если только в примере "switchс выражениями", ветвь исполнения является детерминированной и не будет изменена во время операции стратегии).

Структура петли

для заявления

return value = for count = start count to final count by step length
    statement                                            // Note: There can be break and continue in the statement
    statement                                            // Note: The last statement is the return value

Заявление for очень простое в использовании, петля for может в конечном итоге вернуть значение (или несколько значений, в виде [a, b, c]). Как и переменная, присвоенная позиции return value в псевдокоде выше. За заявлением for следует переменная count, используемая для управления количеством петль, ссылки на другие значения и т. Д. Переменной count присваивается начальное число до начала петли, затем увеличивается в соответствии с установкой длины шага, и петля останавливается, когда переменная count больше окончательного числа.

ВbreakКлючевое слово, используемое в цикле for: цикл прекращается, когдаbreakЗаявление выполнено. ВcontinueКлючевое слово, используемое в цикле "for":continueкогда выполняется заявление, петля будет игнорировать код послеcontinueи выполнять следующий раунд петли напрямую. Заявление for возвращает значение от последнего выполнения петли. и возвращает null, если код не выполнен.

Затем мы покажем с помощью простого примера:

ret = for i = 0 to 10       // We can increase the keyword by to modify the step length, FMZ does not support reverse loops such as i = 10 to 0 for now
    // We can add condition settings, use continue to skip, use break to jump out
    runtime.log("i:", i)
    i                       // If this line is not written, it will return null because there is no variable to return
    
runtime.log("ret:", ret)
runtime.error("stop")

За... в заявлении

Вfor ... inЗаявление имеет две формы, мы покажем их в следующем псевдокоде.

return value = for array element in array 
    statement                        // Note: There can be break and continue in the statement
    statement                        // Note: The last statement is the return value
Return value = for [index variable, array element corresponding to index variable] in array
    statement                        // Note: There can be break and continue in the statement
    statement                        // Note: The last statement is the return value 

Мы можем видеть, что основное различие между двумя формами заключается в содержании, которое следует за ключевым словом for, одна из них - использование переменной в качестве переменной, которая относится к элементам массива, другая - использование структуры, содержащей индексные переменные, тупли переменных элементов массива в качестве ссылок. Для других правил возвращения значений, таких как использование перерыва, продолжения и т. Д., Соответствуют для петль.

testArray = array.from(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
for ele in testArray            // Modify it to the form of [i, ele]: for [i, ele] in testArray, runtime.log("ele:", ele, ", i:", i)
    runtime.log("ele:", ele)

runtime.error("stop")

Когда нужно использовать индекс, используйте грамматикуfor [i, ele] in testArray.

Применение для циклов

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

  1. Вычислить среднее значение

При проектировании с конструкцией петли:

length = 5
var a = array.new(length)
array.push(a, close)

if array.size(a) >= length
	array.remove(a, 0)
	
sum = 0 	
for ele in a
    sum += ele 

avg = sum / length
plot(avg, title="avg", overlay=true)

В примере используется петля for для расчета суммы, а затем вычислить среднее значение.

Расчет скользящей средней напрямую с помощью встроенной функции:

plot(ta.sma(close, length), title="ta.sma", overlay=true)

Использовать встроенную функциюta.smaДля расчета скользящей средней, очевидно, проще использовать встроенную функцию. Сравнивая на графике, можно увидеть, что рассчитанные результаты точно одинаковы.

  1. Подведение итогов

Мы все еще используем приведенный выше пример для иллюстрации.

При проектировании с конструкцией петли:

length = 5
var a = array.new(length)
array.push(a, close)

if array.size(a) >= length
	array.remove(a, 0)
	
sum = 0 	
for ele in a
    sum += ele 

avg = sum / length
plot(avg, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

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

length = 5
var a = array.new(length)
array.push(a, close)

if array.size(a) >= length
	array.remove(a, 0)
	
plot(array.sum(a) / length, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

Мы можем видеть, что рассчитанные данные точно такие же, как и те, которые отображаются на графике с использованием графиков.

Так зачем создавать петли, когда мы можем делать все это с встроенными функциями?

  1. Для некоторых операций и вычислений массивов.
  2. Чтобы просмотреть историю, например, выяснить, сколько прошлых максимумов выше, чем текущий максимум BAR. Поскольку максимум текущего BAR известен только в BAR, где работает скрипт, необходима петля для возвращения и анализа прошлых BAR во времени.
  3. Когда встроенные в язык Pine функции не могут завершить вычисления для прошлых BAR.

пока statemnet

ВwhileЗаявление сохраняет код в секции петли, выполняющейся до тех пор, пока условие суждения в структуре while не будет ложным.

return value = while judgment condition
    statement                    // Note: There can be break and continue in the statement
    statement                    // Note: The last statement is the return value

Другие правила while аналогичны правилам цикла for. Последней строкой локального кодового блока тела цикла является значение возврата, которое может возвращать несколько значений. Выполнить цикл, когда условие loop является истинным, и остановить цикл, когда условие является ложным. Указы break и continue также могут использоваться в теле цикла.

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

length = 10

sma(data, length) => 
    i = 0 
    sum = 0 
    while i < 10 
        sum += data[i]
        i += 1
        sum / length

plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

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

counter = 5
fact = 1

ret = while counter > 0
    fact := fact * counter
    counter := counter - 1
    fact

plot(ret, title="ret")  // ret = 5 * 4 * 3 * 2 * 1

Массивы

Определение массивов в языке Pine похоже на определение массивов в других языках программирования. Массивы Pines - это одномерные массивы. Обычно они используются для хранения непрерывной серии данных. Однообразные данные, хранящиеся в массиве, называются элементом массива, и типами этих элементов могут быть: целое число, плавающая запятая, строка, цветовое значение, булевое значение. Язык Pine на FMZ не очень строг в отношении типов, и он может даже хранить строки и числа в массиве одновременно. Поскольку основная структура массива также является структурой рядов, если исторический оператор используется для обозначения состояния массива на предыдущем BAR.[]чтобы ссылаться на элемент в массиве, мы должны использовать функцииarray.get()иarray.set()Индексовый порядок элементов в массиве заключается в том, что индекс первого элемента массива равен 0, а индекс следующего элемента увеличивается на 1.

Мы проиллюстрируем это простым кодом:

var a = array.from(0)
if bar_index == 0 
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1])
else if bar_index == 1 
    array.push(a, bar_index)
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1])
else if bar_index == 2
    array.push(a, bar_index)
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1], ", a on the last second BAR, i.e. the value of a[2]:", a[2])
else if bar_index == 3 
    array.push(a, bar_index)
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1], ", a on the last second BAR, i.e. the value of a[2]:", a[2], ", a on the last third BAR, i.e. the value of a[3]:", a[3])
else if bar_index == 4 
    // Obtain elements by index using array.get, modify elements by index using array.set
    runtime.log("Before array modification:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))
    array.set(a, 1, 999)
    runtime.log("After array modification:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))

Декларировать массив

Использованиеarray<int> a, float[] bчтобы объявить массив или просто объявить переменную, которая может быть присвоена массиву, например:

array<int> a = array.new(3, bar_index)
float[] b = array.new(3, close)
c = array.from("hello", "fmz", "!")
runtime.log("a:", a)
runtime.log("b:", b)
runtime.log("c:", c)
runtime.error("stop")

Переменные массива инициируются с помощьюarray.newиarray.fromСуществует также множество типовых функций, похожих наarray.newна языке соснового:array.new_int(), array.new_bool(), array.new_color(), array.new_string(), и т.д.

Ключевое слово var также работает с режимом декларирования массива. Массивы, декларируемые с ключевым словом var, инициируются только на первом BAR. Давайте посмотрим на пример:

var a = array.from(0)
b = array.from(0)

if bar_index == 1
    array.push(a, bar_index)
    array.push(b, bar_index)
else if bar_index == 2 
    array.push(a, bar_index)
    array.push(b, bar_index)
else if barstate.islast
    runtime.log("a:", a)
    runtime.log("b:", b)
    runtime.error("stop")

Можно увидеть, что изменения массива a были непрерывно определены и не были сброшены. массив b инициируется на каждом BAR. Наконец, когдаbarstate.islastЭто правда, есть только один элемент, напечатанный с значением 0.

Читать и записывать элементы в массиве

Используйте array.get, чтобы получить элемент в указанном индексе в массиве, и используйте array.set, чтобы изменить элемент в указанном индексе в массиве.

Первый параметр array.get - это массив, который должен быть обработан, а второй параметр - указанный индекс. Первый параметр array.set - это массив, который должен быть обработан, второй параметр - указанный индекс, а третий параметр - элемент, который должен быть записан.

Мы используем следующий простой пример для иллюстрации:

lookbackInput = input.int(100)
FILL_COLOR = color.green

var fillColors = array.new(5)
if barstate.isfirst
    array.set(fillColors, 0, color.new(FILL_COLOR, 70))
    array.set(fillColors, 1, color.new(FILL_COLOR, 75))
    array.set(fillColors, 2, color.new(FILL_COLOR, 80))
    array.set(fillColors, 3, color.new(FILL_COLOR, 85))
    array.set(fillColors, 4, color.new(FILL_COLOR, 90))

lastHiBar = - ta.highestbars(high, lookbackInput)
fillNo = math.min(lastHiBar / (lookbackInput / 5), 4)

bgcolor(array.get(fillColors, int(fillNo)), overlay=true)
plot(lastHiBar, title="lastHiBar")
plot(fillNo, title="fillNo")

Пример инициализирует базовый цвет зеленый, объявляет и инициализирует массив для хранения цветов, а затем присваивает различные значения прозрачности цветам (используяcolor.newУровень цвета рассчитывается путем расчета расстояния текущего BAR от максимального значения высокого в 100 периодов просмотра. Чем ближе расстояние к максимальному значению HIGH в последних 100 циклах просмотра, тем выше рейтинг и тем темнее (ниже прозрачность) соответствующее цветовое значение.

Итерация через элементы массива

Как итерации через массив, мы можем использовать для / для в / в то время как заявления, которые мы узнали раньше.

a = array.from(1, 2, 3, 4, 5, 6)

for i = 0 to (array.size(a) == 0 ? na : array.size(a) - 1)
    array.set(a, i, i)
    
runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)

i = 0
while i < array.size(a)
    array.set(a, i, i)
    i += 1

runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)

for [i, ele] in a 
    array.set(a, i, i)

runtime.log(a)
runtime.error("stop")

Эти три метода прохождения имеют одинаковые результаты выполнения.

Массивы могут быть объявлены в глобальном масштабе скрипта или в локальном масштабе функции или if-отдела.

Ссылки на исторические данные

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

a = array.new_float(1)
array.set(a, 0, close)
closeA1 = array.get(a, 0)[1]
closeB1 = close[1]
plot(closeA1, "closeA1", color.red, 6)
plot(closeB1, "closeB1", color.black, 2)

ma1 = ta.sma(array.get(a, 0), 20)
ma2 = ta.sma(close, 20)
plot(ma1, "ma1", color.aqua, 6)
plot(ma2, "ma2", color.black, 2)

Функции добавления и удаления массивов

  1. Функции, связанные с сложением массивов:

array.unshift(), array.insert(), array.push().

  1. Функции, связанные с удалением массивов:

array.remove(), array.shift(), array.pop(), array.clear().

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

a = array.from("A", "B", "C")
ret = array.unshift(a, "X")
runtime.log("array a:", a, ", ret:", ret)

ret := array.insert(a, 1, "Y")
runtime.log("array a:", a, ", ret:", ret)

ret := array.push(a, "D")
runtime.log("array a:", a, ", ret:", ret)

ret := array.remove(a, 2)
runtime.log("array a:", a, ", ret:", ret)

ret := array.shift(a)
runtime.log("array a:", a, ", ret:", ret)

ret := array.pop(a)
runtime.log("array a:", a, ", ret:", ret)

ret := array.clear(a)
runtime.log("array a:", a, ", ret:", ret)

runtime.error("stop")

Применение добавлений, удалений: массивы как очереди

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

Очередь - это структура, которая часто используется в области программирования, характеристиками очереди являются:

Элемент, который входит в очередь первым, выходит из очереди первым.

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

В следующем примере мы используем структуру очереди для записи цены каждого тика, вычисляем среднемобильную цену на уровне тика, а затем сравниваем ее с скользящей средней на уровне 1-минутной линии K.

strategy("test", overlay=true)

varip a = array.new_float(0)
var length = 10

if not barstate.ishistory
    array.push(a, close)

    if array.size(a) > length
        array.shift(a)

sum = 0.0
for [index, ele] in a 
    sum += ele

avgPrice = array.size(a) == length ? sum / length : na

plot(avgPrice, title="avgPrice")
plot(ta.sma(close, length), title="ta.sma")

Обратите внимание, что при декларировании массива a, мы указываем режим декларирования и используем ключевое словоvariantТаким образом, каждое изменение цены будет записываться в массиве a.

Обычно используемые функции расчета и работы массива

Расчет корреляционных функций:

array.avg()вычисляет среднее значение всех элементов массива,array.min()вычисляет самый маленький элемент в массиве,array.max()вычисляет самый маленький элемент в массиве,array.stdev()вычисляет стандартное отклонение всех элементов массива,array.sum()вычисляет стандартное отклонение всех элементов в массиве.

Функции, связанные с эксплуатацией:array.concat()чтобы объединить или соединить два массива.array.copy()чтобы скопировать массив.array.jointo соединяет все элементы массива в строку.array.sort()отсортировать по восходящему или нисходящему порядку.array.reverse()чтобы перевернуть массив.array.slice()чтобы разрезать массив.array.includes()чтобы судить элемент.array.indexof()Если значение не найдено, возвращается -1.array.lastindexof()чтобы найти последнее происхождение значения.

Примеры испытаний функций, связанных с расчетом массива:

a = array.from(3, 2, 1, 4, 5, 6, 7, 8, 9)

runtime.log("Arithmetic average of the array a:", array.avg(a))
runtime.log("The minimum element in the array a:", array.min(a))
runtime.log("The maximum element in the array a:", array.max(a))
runtime.log("Standard deviation in array a:", array.stdev(a))
runtime.log("Sum of all elements of the array a:", array.sum(a))
runtime.error("stop")

Это обычно используемые функции расчета массива.

Примеры операционных функций:

a = array.from(1, 2, 3, 4, 5, 6)
b = array.from(11, 2, 13, 4, 15, 6)

runtime.log("array a: ", a, ", array b: ", b)
runtime.log("array a, array b is concatenated with:", array.concat(a, b))
c = array.copy(b)

runtime.log("Copy an array b and assign it to the variable c, variable c:", c)

runtime.log("use array.join to process the array c, add the symbol + to the middle of each element, concatenating all elements results in a string:", array.join(c, "+"))
runtime.log("Sort the array b, in order from smallest to largest, using the parameter order.ascending:", array.sort(b, order.ascending))     // array.sort function modifies the original array
runtime.log("Sort the array b, in order from largest to smallest, using the parameter order.descending:", array.sort(b, order.descending))   // array.sort function modifies the original array

runtime.log("array a:", a, ", array b:", b)
array.reverse(a)   // This function modifies the original array
runtime.log("reverse the order of all elements in the array a, after reversing, the array a is:", a)    

runtime.log("Intercept array a, index 0~index 3, and follow the rule of left-closed and right-open interval:", array.slice(a, 0, 3))
runtime.log("Search for element 11 in array b:", array.includes(b, 11))
runtime.log("Search for element 100 in array a:", array.includes(a, 100))
runtime.log("Connect array a and array b, and search the index position of the first occurrence of element 2:", array.indexof(array.concat(a, b), 2), " , observe array.concat(a, b):", array.concat(a, b))
runtime.log("Connect array a and array b, and search the index position of the last occurrence of element 6:", array.lastindexof(array.concat(a, b), 6), " , observe array.concat(a, b):", array.concat(a, b))

runtime.error("stop")

Функции

Специальные функции

Язык Pine может быть разработан с помощью пользовательских функций.

  1. Все функции определяются в глобальном масштабе скрипта. Функция не может быть объявлена в рамках другой функции.
  2. Функции не могут называть себя в своем собственном коде (рекурсия).
  3. В принципе, все встроенные в язык PINE функции рисования (barcolor(), fill(), hline(), plot(), plotbar(), plotcandle()) не может быть вызвано в пользовательских функциях.
  4. Функции могут быть написаны как в одной строке, так и в нескольких строках.

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

barIsUp() => close > open

является ли текущая BAR положительной прямой, когда функция возвращается.

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

sma(data, length) => 
    i = 0 
    sum = 0 
    while i < 10 
        sum += data[i]
        i += 1
        sum / length

plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

Мы используем пользовательскую функцию, чтобы реализовать функцию среднего расчета.

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

twoEMA(data, fastPeriod, slowPeriod) =>
    fast = ta.ema(data, fastPeriod)
    slow = ta.ema(data, slowPeriod)
    [fast, slow]

[ema10, ema20] = twoEMA(close, 10, 20)
plot(ema10, title="ema10", overlay=true)
plot(ema20, title="ema20", overlay=true)

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

Встроенные функции

Встроенные функции можно легко найти вСкрипт FMZ PINE.

Классификация встроенных функций на языке Pine:

  1. Функция обработки строкиstr. series.
  2. Функция обработки цветовых значенийcolor. series.
  3. Входной параметрinput. series.
  4. Функция расчета показателяta. series.
  5. Функция чертежаplot. series.
  6. Функция обработки массиваarray. series.
  7. Торговые функцииstrategy. series.
  8. Функции, связанные с математическими операциямиmath. series.
  9. Другие функции (управление временем, функции чертежа неграфовых рядов,request.серийные функции, функции обработки типов и т.д.)

Торговые функции

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


  1. strategy.entry

strategy.entryфункция является более важной функцией, когда мы пишем стратегию размещения заказа, несколько важных параметров для функции:id, direction, qty, when, и т.д.

Параметры:

  • id: Это может быть понято как присвоение названия торговой позиции для ссылки.
  • direction: Если направление ордера длинное (купить), вставьте встроенную переменнуюstrategy.long, и если вы хотите пойти короткий (продать), пройти в переменнойstrategy.short.
  • qty: Укажите сумму заказов, которые должны быть размещены, если этот параметр не будет передан, будет использоваться сумма заказов по умолчанию.
  • when: Условие выполнения, вы можете указать этот параметр для контроля того, будет ли задействована текущая операция заказа или нет.
  • limit: Укажите предельную цену заказа.
  • stopСтоп-лосс.

Конкретные подробности исполненияstrategy.entryФункции контролируются параметром, когдаstrategyфункция называется, и она также может контролироваться с помощью [Pine Language Trade-Class Library Template Arguments]https://www.fmz.com/bbs-topic/9293#template-(arguments-of-pine-language-trade-class-library) установки управления, Pine language trade-class library template arguments контролируют более подробную информацию о транзакции, подробности можно узнать в ссылке.

Мы сосредоточены наpyramiding, default_qty_valueпараметры вstrategyМы используем следующий код для тестирования:

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)

ema10 = ta.ema(close, 10)

findOrderIdx(idx) =>
    if strategy.opentrades == 0 
        false 
    else 
        ret = false 
        for i = 0 to strategy.opentrades - 1 
            if strategy.opentrades.entry_id(i) == idx
                ret := true 
                break
        ret 
        

if not findOrderIdx("long1")
    strategy.entry("long1", strategy.long)

if not findOrderIdx("long2")
    strategy.entry("long2", strategy.long, 0.2, when = close > ema10)

if not findOrderIdx("long3")
    strategy.entry("long3", strategy.long, 0.2, limit = low[1])
    strategy.entry("long3", strategy.long, 0.3, limit = low[1])

if not findOrderIdx("long4")
    strategy.entry("long4", strategy.long, 0.2)

plot(ema10, title="ema10", color=color.red)

Часть в начале кода/* backtest... */является настройкой обратного теста, которая используется для записи времени настройки обратного теста и другой информации в это время для отладки, а не кода запуска.

В коде:strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true), когда мы указываемpyramidingПараметр 3, мы устанавливаем максимальное количество сделок в том же направлении 3.strategy.entryПоскольку мы также указалиdefault_qty_valueпараметр будет 0,1, этоstrategy.entryОперация с ИДlong1имеет размер заказа по умолчанию 0,1.strategy.entryФункция вызов, когда мы указываемdirectionкакstrategy.long, так что все тестовые заказы являются заказчиками.

Обратите внимание, что операция заказаstrategy.entry("long3", ...в коде называется дважды для одного и того же идентификатора:long3, первыйstrategy.entryОперация заказа не была заполнена, и второй вызовstrategy.entryВ другом случае, например, если первый порядок с ID long3 заполнен, продолжать использоватьstrategy.entryфункция размещения заказов в соответствии с ID long3, тогда позиции заказов будут накапливаться на ID long3.


  1. strategy.close

Вstrategy.closeФункция используется для закрытия позиции входа с указанным идентификационным идентификатором. Основными параметрами являются:id, when, qty, qty_percent.

Параметры:

  • id: Идентификатор входа, который необходимо закрыть, - это идентификатор, который мы указываем, когда открываем позицию с помощью функции входа, например:strategy.entry.
  • when: условия исполнения.
  • qty: Количество закрытых позиций.
  • qty_percent: Процент закрытых позиций.

Давайте ознакомимся с деталями использования этой функции на примере: В/*backtest ... */в коде указана информация о конфигурации дляFMZ.COMмеждународный веб-сайт backtest, вы можете удалить его и установить рынок, сорт, временной диапазон и другую информацию, которую вам нужно протестировать.

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy("close Demo", pyramiding=3)

var enableStop = false 
if enableStop
    runtime.error("stop")

strategy.entry("long1", strategy.long, 0.2)
if strategy.opentrades >= 3 
    strategy.close("long1")                   // Multiple entry orders, no qty parameters specified, close all
    // strategy.close()                          // Without specifying the id parameter, the current position will be closed
    // strategy.close("long2")                   // If a non-existent id is specified then nothing is done
    // strategy.close("long1", qty=0.15)         // Specify qty parameters to close a position
    // strategy.close("long1", qty_percent=50)   // qty_percent is set to 50 to close 50% of the positions marked by long1
    // strategy.close("long1", qty_percent=80, when=close<open)  // Specify the parameter when, change it to close>open and it won't trigger
    enableStop := true

Стратегия тестирования показывает три последовательных длинных записи с идентификатором записи long1, а затем использует различные параметрыstrategy.closeФункция для установки различных результатов обратного теста при закрытии позиции.strategy.closeфункция не имеет параметров для указания цены ордера на закрытие позиции, эта функция в основном используется для закрытия позиции сразу по текущей рыночной цене.


  1. strategy.close_all

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

Параметры:

  • when: условия исполнения.

Давайте посмотрим на следующий пример:

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy("closeAll Demo")

var enableStop = false 
if enableStop
    runtime.error("stop")

strategy.entry("long", strategy.long, 0.2, when=strategy.position_size==0 and close>open)
strategy.entry("short", strategy.short, 0.3, when=strategy.position_size>0 and close<open)

if strategy.position_size < 0 
    strategy.close_all()
    enableStop := true 

Код испытания начинается с номера позиции 0 (т.е.strategy.position_size==0является истинным), так что когда условия, установленные параметром когда выполнены, толькоstrategy.entryфункция входа с ID long выполняется.strategy.position_sizeЕсли длина позиции больше 0, то функция входа с ID short может быть выполнена, поскольку текущая длинная позиция удерживается, этот короткий обратный сигнал в это время приведет к закрытию длинной позиции, а затем к открытию короткой позиции в противоположном направлении.strategy.position_size < 0, т.е. при удержании короткой позиции все позиции в текущем направлении удержания будут закрыты.enableStop := true. Прекращает выполнение стратегии, чтобы можно было наблюдать за журналом.

Можно обнаружить, что функцияstrategy.close_allне имеет параметров для указания цены закрытия ордера, эта функция в основном используется для немедленного закрытия позиции по текущей рыночной цене.


  1. strategy.exit

Вstrategy.exitВ отличие от этой функции,strategy.closeиstrategy.close_allФункции закрывают позицию немедленно по текущей рыночной цене.strategy.exitФункция будет закрывать позицию в соответствии с параметрами.

Параметры:

  • id: Идентификатор ордера текущего ордера с условием закрытия.
  • from_entry: Используется для указания идентификатора входа позиции, которая должна быть закрыта.
  • qty: Количество закрытых позиций.
  • qty_percent: Процент закрытых позиций, диапазон: 0 ~ 100.
  • profit: Целевая прибыль, выраженная в пунктах.
  • loss: Цель стоп-лосса, выраженная в пунктах.
  • limit: Целевая прибыль, определяемая ценой.
  • stop: Цель стоп-лосса, определенная ценой.
  • when: условия исполнения.

Используйте стратегию тестирования для понимания использования параметров.

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
args: [["RunMode",1,358374],["ZPrecision",0,358374]]
*/

strategy("strategy.exit Demo", pyramiding=3)

varip isExit = false 

findOrderIdx(idx) =>
    ret = -1 
    if strategy.opentrades == 0 
        ret
    else 
        for i = 0 to strategy.opentrades - 1 
            if strategy.opentrades.entry_id(i) == idx
                ret := i 
                break
        ret

strategy.entry("long1", strategy.long, 0.1, limit=1, when=findOrderIdx("long1") < 0)
strategy.entry("long2", strategy.long, 0.2, when=findOrderIdx("long2") < 0)
strategy.entry("long3", strategy.long, 0.3, when=findOrderIdx("long3") < 0)

if not isExit and strategy.opentrades > 0
    // strategy.exit("exitAll")          // If only one id parameter is specified, the exit order is invalid, and the parameters profit, limit, loss, stop and other exit conditions also need to be set at least one, otherwise it is also invalid
    strategy.exit("exit1", "long1", profit=50)                    // Since the long1 entry order is not filled, the exit order with ID exit1 is also on hold until the corresponding entry order is filled before exit1 is placed
    strategy.exit("exit2", "long2", qty=0.1, profit=100)          // Specify the parameter qty to close 0.1 positions in the position with ID long2
    strategy.exit("exit3", "long3", qty_percent=50, limit=strategy.opentrades.entry_price(findOrderIdx("long3")) + 1000)   // Specify the parameter qty_percent to close 50% of the positions in the position with ID long3
    isExit := true 

if bar_index == 0 
    runtime.log("The price per point:", syminfo.mintick)    // The price per point is related to the "Pricing Currency Precision" parameter setting on the Pine language template parameters

Мы используем модель цены в реальном времени для обратного тестирования, стратегия тестирования начинается с 3 входных операций (strategy.entryфункцию), иlong1преднамеренно устанавливаетсяlimitпараметр с ожидаемой ценой ордера 1, так что он не может быть заполнен.strategy.exitМы использовали take profit by point и take profit by price, закрытие фиксированного количества позиций и закрытие позиций в процентах. Учитывая длину примера, демонстрируется только take profit. Операция стоп-лосса также такая же.strategy.exitФункция также имеет более сложные параметры остановки:trail_price, trail_points, trail_offsetтакже могут быть проверены в этом примере, чтобы узнать их использование.


  1. strategy.cancel

Вstrategy.cancelфункции используются для отмены/остановки всех предварительных ожидаемых заказов.strategy.order, strategy.entry , strategy.exitОсновными параметрами этой функции являются:id, when.

Параметры:

  • id: Идентификатор допуска отменяется.
  • when: условия исполнения.

Эта функция легко понятна, и она используется для отмены заказов, которые не выполнены.

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy("strategy.cancel Demo", pyramiding=3)

var isStop = false 
if isStop 
    runtime.error("stop")

strategy.entry("long1", strategy.long, 0.1, limit=1)
strategy.entry("long2", strategy.long, 0.2, limit=2)
strategy.entry("long3", strategy.long, 0

Больше