[TOC]
Поддерживающее видео обучение:https://www.youtube.com/watch?v=CA3SwJQb_1g
FMZ Quant Trading Platform поддерживает написание стратегии языка Pine, бэкстестинг и прямую торговлю стратегиями языка Pine, и он совместим с более низкими версиями языка Pine.Площадь стратегиина платформе FMZ Quant Trading (FMZ.COM).
FMZ поддерживает не только язык Pine, но и мощную функцию рисования языка Pine. Различные функции, богатые и практичные инструменты, эффективное и удобное управление на платформе FMZ еще больше повышают практичность стратегии (скрипта) Pine. На основе совместимости с языком Pine FMZ также расширяет, оптимизирует и подстригает язык Pine в определенной степени. Прежде чем официально входить в учебник, давайте посмотрим, какие изменения были внесены в язык Pine на FMZ по сравнению с оригинальной версией.
Краткий обзор некоторых очевидных различий:
//@version
иstrategy
, indicator
заявления в начале кода не обязательно писать, FMZ не поддерживаетimport
импортироватьlibrary
Пока не работает.Можно заметить, что некоторые стратегии написаны так:
//@version=5
indicator("My Script", overlay = true)
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue)
plot(b, color = color.black)
plotshape(c, color = color.red)
Или напишите так:
//@version=5
strategy("My Strategy", overlay=true)
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
На FMZ это можно упростить:
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue, overlay=true)
plot(b, color = color.black, overlay=true)
plotshape(c, color = color.red, overlay=true)
Или:
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
Модель ценообразования закрытия и модель ценообразования в режиме реального времени
На торговый вид, мы можем использоватьcalc_on_every_tick
параметрstrategy
Функция настройки сценария стратегии для выполнения логики стратегии в режиме реального времени, когда цена меняется каждый раз.calc_on_every_tick
параметр должен быть установлен наtrue
.calc_on_every_tick
параметр по умолчанию:false
, то есть логика стратегии выполняется только тогда, когда текущая K-линия BAR стратегии полностью завершена.
На FMZ он устанавливается параметрами шаблона
Контроль численной точности, такой как цена и сумма заказа при выполнении стратегии, необходимо указать на FMZ. В торговом видении нет никаких проблем с точностью при размещении реальных торговых ордеров, поскольку это может быть протестировано только в симуляции. На FMZ можно запустить стратегию Pine в реальной торговле. Затем стратегия должна иметь возможность гибко указать точность цены и точность суммы ордера торгового сорта. Настройки точности контролируют количество десятичных мест в соответствующих данных, чтобы предотвратить не удовлетворение данных требованиям к заказу биржи и, следовательно, не удалось разместить заказ.
Код фьючерсного контракта
Если торговый продукт на FMZ является контрактом, он имеет два атрибута, они swap
, и код контракта зависит от того, имеет ли действующая биржа такой контракт.quarter
Эти коды контрактов согласуются с кодами фьючерсных контрактов, определенных в документе API языка Javascript/python/c++ FMZ
Для других параметров, таких как минимальная сумма заказа, сумма заказа по умолчанию и т.д., обратитесь к введению параметра на
runtime.debug
, runtime.log
, runtime.error
используется для отладки.В платформу FMZ были добавлены 3 функции для отладки.
runtime.debug
: Печать информации о переменной на консоли, которая обычно не используется с этой функцией.
runtime.log
Функции языка PINE на FMZ.
runtime.log(1, 2, 3, close, high, ...), Multiple parameters can be passed.
runtime.error
: Это приведет к ошибке во время выполнения с сообщением об ошибке, указанным в параметре сообщения при вызове.
runtime.error(message)
overlay
параметр расширяется в некоторых из функций чертежаВ языке Pine на FMZ, чертежные функцииplot
, plotshape
, plotchar
, и т.д. добавилиoverlay
поддержка параметров, позволяющая указать чертеж на основной или подграфике.overlay
Установлено наtrue
рисовать на основной графике, иfalse
настраивается на использование подграфики, что позволяет стратегии Pine на FMZ одновременно использовать основную и подграфику.
syminfo.mintick
встроенная переменнаяВстроенная переменнаяsyminfo.mintick
Это значение может контролироваться с помощью шаблона параметра ценообразования валюты в syminfo.mintick
равен 0,01.
Например: цена заказа 8000, направление продажи, количество 1 лота (дело, лист), средняя цена после сделки не 8000, но ниже 8000 (стоимость включает в себя комиссию за обработку).
Когда вы начинаете изучать основы языка Pine, могут быть некоторые примеры инструкций и грамматики кода, с которыми мы не знакомы. Не имеет значения, если вы не понимаете его, мы можем сначала ознакомиться с концепциями и понять цель теста, или вы можете проверить документацию на языке Pine на FMZ для получения инструкций. Затем следуйте пошаговому руководству, чтобы ознакомиться с различными грамматиками, инструкциями, функциями и встроенными переменными.
Когда начинаешь изучать язык Пайн, очень важно понимать связанные с ним понятия, такие как процесс выполнения программы сценария языка Пайн. Стратегия языка Пайн работает на основе диаграммы. Можно понять, что стратегия языка Пайн представляет собой серию вычислений и операций, которые выполняются на графике в порядке временных рядов из самых ранних данных, которые были загружены на графике. Количество данных, которые первоначально загружаются на графике, ограничено. В реальной торговле максимальное количество данных обычно определяется на основе максимального объема данных, возвращаемых обменным интерфейсом, и максимальное количество данных во время бэкстестинга определяется на основе данных, предоставляемых источником данных системы бэкстестинга. Самый левый K-линейный штрих на графике, то есть первый набор данных на графике, имеет значение индекса 0. Значение индекса данных K-линии в текущем сценарии Пайн может быть определено, когда переменная ссылается через встроенную переменную.bar_index
на языке сосны.
plot(bar_index, "bar_index")
Вplot
функция является одной из функций мы будем использовать больше в будущем. использование очень просто, это нарисовать линию на графике в соответствии с параметрами ввода, входные данныеbar_index
, и линия называетсяbar_index
. Можно увидеть, что значение строки bar_index на первом Bar равняется 0, и оно увеличивается на 1 направо по мере увеличения Bar.
Поскольку настройки стратегии различны, методы моделирования стратегии различны, их можно разделить на:closing price model
иreal-time price model
Мы также кратко представили их концепции раньше.
Модель цены закрытия
Когда код стратегии выполняется, период текущей K-линейной панели полностью выполняется, а когда K-линия закрывается, период K-линии завершается.
Модель цен в режиме реального времени
При выполнении кода стратегии, независимо от того, закрыт ли текущий K-линейный бар или нет, логика стратегии Pine будет выполняться каждый раз, когда рынок меняется, и задействованный торговый сигнал будет выполняться немедленно.
Когда стратегия языка Пайн выполняется слева направо на графике, K-линейные строки на графике разделены наHistorical Bars
иReal-time Bars
:
Исторический бар
Когда стратегия установлена на Historical Bars
. Логика стратегии выполняется только один раз на каждомhistorical bar
- Да, конечно.
Когда стратегия установлена на historical bars
. Логика стратегии выполняется только один раз на каждомhistorical bar
.
Расчет на основе исторических Бар: Код стратегии выполняется один раз в состоянии закрытия исторической строки, а затем код стратегии продолжает выполняться в следующей исторической строке до тех пор, пока все исторические строки не будут выполнены один раз.
Бар в реальном времени
Когда стратегия выполняется до последней K-линейной панели в правом углу, панель является панелью в реальном времени. После закрытия панели в реальном времени, панель становится прошедшей панелью в реальном времени (станет исторической панелью). Новая панель в реальном времени будет создана в правом углу диаграммы.
Когда стратегия настроена на
Расчет на основе бар в реальном времени:
Если стратегия установлена на high
, low
, close
Данные определяются на исторических Бар, и эти значения могут меняться каждый раз, когда рынок меняется на Бар в режиме реального времени.close
всегда представляет собой текущую последнюю цену, иhigh
иlow
Эти встроенные переменные представляют окончательное значение реального времени, когда он был в последний раз обновлен.
Механизм обратного движения при выполнении стратегий на баре в режиме реального времени (модель цены в режиме реального времени): Во время выполнения Bar в режиме реального времени, сброс пользовательских переменных перед каждой новой итерацией стратегии называется rollback.
Внимание:
/*backtest
...
..
.
*/
Содержание пакета - это информация о конфигурации бэкстеста, сохраненная в виде кода на платформе FMZ.
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := n + 1
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Мы изучаем только сцену, выполненную в режиме реального времени, так что мы используемnot barstate.ishistory
выражение для ограничения накопления переменной n только в режиме реального времени Bar, и использованиеruntime.log
Функция вывода информации в журнале стратегии до и после операции накопления.plot
, можно увидеть, что n всегда равен 0, когда стратегия выполняется в исторических Бар. Когда выполняется Бар в режиме реального времени, запускается операция добавления 1 к n, а операция добавления 1 к n выполняется, когда стратегия выполняется в каждом раунде Бар в режиме реального времени. Из сообщения журнала можно наблюдать, что n будет сброшен на значение, окончательно представленное предыдущей стратегией исполнения Бар, когда код стратегии повторно выполняется в каждом раунде. Обновление значения n будет представлено, когда код стратегии выполняется в Бар в режиме реального времени в последний раз, поэтому вы можете видеть, что значение кривой n увеличивается на 1 с каждым увеличением Бар, начиная с Бар в режиме реального времени на графике.
Резюме:
Из-за обратного отклонения данных операции по рисованию, такие как кривые на графике, также могут вызвать перерисование.
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := open > close ? n + 1 : n
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Скриншот времени А
Скриншот времени B
Мы только изменили предложение:n := open > close ? n + 1 : n
, только добавить 1 к n, когда текущий бар в реальном времени является отрицательной линией (то есть цена открытия выше цены закрытия). Можно видеть, что в первом графике (время А), поскольку цена открытия была выше цены закрытия (отрицательная линия) в то время, n было накоплено на 1, и значение n, отображаемое на кривой графика, было 5. Затем рынок изменился, и цена обновлялась, как показано на втором графике (время B). В это время цена открытия ниже цены закрытия (положительная линия), и значение n отскакивает назад без увеличения на 1. Кривая n в графике также перерисовывается немедленно, и значение n на кривой составляет 4. Поэтому сигналы, такие как перекресток и показаны на реального времени, являются неопределенными и могут меняться.
Переменный контекст в функциях
Давайте вместе изучим переменные в языковой функции Пайна. Согласно некоторым описаниям на учебниках Пайна, переменные в функции имеют следующие различия от переменных за пределами функции:
История переменных серии, используемых в функции Pine, создается при каждом последовательном вызове функции. Если функция не вызвана на каждой строке, на которой выполняется скрипт, это приведет к несоответствию между историческими значениями серии внутри и вне локального блока функции. Поэтому, если функция не вызвана на каждой строке, серии, ссылающиеся внутри и вне функции с тем же индексным значением, не будут относиться к той же исторической точке.
Неважно, мы выясним это с помощью тестового кода, работающего на FMZ:
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
f(a) => a[1]
f2() => close[1]
oneBarInTwo = bar_index % 2 == 0
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
plot(close[2], title = "close[2]", color = color.red, overlay = true)
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Скриншот выполнения обратного теста
Код испытания относительно прост, в основном для изучения данных, ссылающихся на два метода, а именно:f(a) => a[1]
иf2() => close[1]
.
f(a) => a[1]
: Используйте метод передачи параметров, функция возвращается кa[1]
finally.
f2() => close[1]
: Использовать встроенную переменнуюclose
напрямую, и функция возвращается кclose[1]
finally.
В[]
Этот символ используется для обозначения исторического значения переменной серии данных, а close[1] относится к данным о цене закрытия на Бар перед текущей ценой закрытия.
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
Нарисуйте символ f(close)
.
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
Нарисуйте символ f2()
.
plot(close[2], title = "close[2]", color = color.red, overlay = true)
Нарисуйте линию, цвет красный, а начертанное положение (на оси Y) будет:close[2]
, который является ценой закрытия второй строки перед текущей строкой (с учетом 2 строки слева).
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Нарисуйте линию, цвет зеленый, и начертанное положение (на оси Y) будет:close[1]
, который является ценой закрытия первого столбика перед текущим столбиком (с учетом 1 столбика слева).
Это можно увидеть на скриншоте стратегии обратного тестирования, что хотя и функцияf(a) => a[1]
используется для рисования маркера А и функцииf2() => close[1]
используется для рисования маркера B [1] для ссылки на исторические данные на серии данных, позиции маркера plot(close[2], title = "close[2]", color = color.red, overlay = true)
, данные, использованные для начертания линии:close[2]
.
Причина заключается в том, чтобы рассчитать, следует ли нарисовать маркеры bar_index
Маркеры f(a) => a[1]
не будет таким же, как значение, на которое ссылается функцияf2() => close[1]
если функция не вызывается на каждой строке (даже если они оба используют один и тот же индекс, например [1]).
Некоторые встроенные функции должны быть рассчитаны на каждом Бар, чтобы правильно рассчитать их результаты
Чтобы проиллюстрировать ситуацию простым примером:
res = close > close[1] ? ta.barssince(close < close[1]) : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
Мы пишем код вызова функцииta.barssince(close < close[1])
в тернарном оператореcondition ? value1 : value2
. Это приводит к тому, что функция ta.barssince будет вызвана только тогда, когдаclose > close[1]
Но...ta.barssince
Функция заключается в расчете количества K-линий с момента последнегоclose < close[1]
При вызове функции ta.barssince, она всегда находится на уровне close > close[1], то есть текущая цена закрытия больше, чем цена закрытия предыдущей Bar. При вызове функции ta.barssince условие close < close[1] не устанавливается, и не существует недавней позиции, на которой она находится.
ta.barssince: при вызове функция возвращает na, если условие никогда не выполнялось до текущей K-линии.
Как показано на графике:
Так что когда график рисуется, только данные с значением для переменной res (-1) рисуется.
Чтобы избежать этой проблемы, мы просто возьмемta.barssince(close < close[1])
Функция выходит из тернарного оператора и записывается за пределами любых возможных условных ветвей, заставляя ее выполнять вычисления на каждой строке K Bar.
a = ta.barssince(close < close[1])
res = close > close[1] ? a : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
Концепция временных рядов очень важна в языке Pine, и это концепция, которую мы должны понять, когда мы изучаем язык Pine. Временные ряды - это не тип, а основная структура для хранения непрерывных значений переменных с течением времени. Мы знаем, что скрипты Pine основаны на диаграммах, и самым основным содержанием, отображаемым в диаграмме, является K-линейный график. Временные ряды, где каждое значение связано с временной меткой K-линейной.open
является встроенной переменной (встроенной) языка Pine, и его структура состоит в том, чтобы хранить временные ряды цены открытия каждой K-линии Bar.open
представляет собой цены открытия всех K-линейных Бар от первого Бар в начале текущего K-линейного графика к Бар, где текущий скрипт выполняется. Если текущий K-линейный график является 5-минутным периодом, когда мы цитируем (или используем)open
Если вы хотите ссылаться на исторические значения в временных рядах, вам нужно использовать[]
Когда стратегия Pine выполняется на определенном K-линии Bar, использоватьopen[1]
для ссылки на цену открытия предыдущей K-линии Bar (т.е. цену открытия предыдущего периода K-линии), которая ссылается наopen
временные ряды, на которых этот K-линейный столбец в настоящее время выполняется скриптом.
Переменные на временных рядах очень удобны для вычислений
Давайте возьмем встроенную функциюta.cum
Например:
ta.cum
Cumulative (total) sum of `source`. In other words it's a sum of all elements of `source`.
ta.cum(source) → series float
RETURNS
Total sum series.
ARGUMENTS
source (series int/float)
SEE ALSO
math.sum
Код испытания:
v1 = 1
v2 = ta.cum(v1)
plot(v1, title="v1")
plot(v2, title="v2")
plot(bar_index+1, title="bar_index")
Есть много встроенных функций, таких какta.cum
которые могут обрабатывать данные о временных рядах напрямую.ta.cum
является накоплением значений, соответствующих переменным, переданным на каждой K-линии Bar, и затем мы используем график, чтобы облегчить его понимание.
Процесс реализации стратегии | Встроенная переменная bar_index | v1 | v2 |
---|---|---|---|
Стратегия работает на первой K-линии | 0 | 1 | 1 |
Стратегия работает на второй K-линии Бар | 1 | 1 | 2 |
Стратегия работает на третьем K-линии | 2 | 1 | 3 |
… | … | … | … |
Стратегия работает на N + 1th K-линейной панели | N | 1 | N+1 |
Можно увидеть, что v1, v2 и даже bar_index - это все структуры временных рядов, и на каждом столбце есть соответствующие данные.
Поскольку переменная v1 равна 1 на каждый бар, когдаta.cum(v1)
функция выполняется на первой K-линии Bar, есть только первый Bar, поэтому результат расчета 1 и присвоен переменной v2.
Когда?ta.cum(v1)
Если вычисление выполняется на второй K-линии Bar, то уже есть 2 K-линии Bars (встроенная переменная bar_index, соответствующая первой, равна 0, а вторая, соответствующая встроенной переменной bar_index, равна 1), поэтому результат расчета равен 2, который присваивается переменной v2, и так далее.bar_index
увеличивается от 0, тоbar_index + 1
На графике мы также видим, что линииv2
иbar_index
действительно перекрываются.
Точно так же я могу использоватьta.cum
Встроенная функция для расчета суммы цен закрытия для всех Бар на текущем графике.ta.cum(close)
, Когда стратегия работает в режиме реального времени Бар в крайнем правом углу, результат, рассчитанныйta.cum(close)
является суммой цен закрытия всех Бар на графике (если он не идет вправо, он только накапливается до текущего Бар).
Переменные по временным рядам также могут быть рассчитаны с помощью операторов, таких как код:ta.sma(high - low, 14)
, вычесть встроенную переменнуюhigh
(наивысшая цена K-линейной стойки) отlow
(наименьшая цена K-линии Bar), и, наконец, использоватьta.sma
функция для расчета среднего значения.
Результат вызова функции также оставит следы значений в временных рядах.
v1 = ta.highest(high, 10)[1]
v2 = ta.highest(high[1], 10)
plot(v1, title="v1", overlay=true)
plot(v2, title="v2", overlay=true)
Тест-код запускается во время обратного тестирования, и можно наблюдать, что значенияv1
иv2
Результат, вычисленный призывом функции, оставит следы значения в временных рядах, например,ta.highest(high, 10)
в кодеta.highest(high, 10)[1]
. Результат, рассчитанный вызовом функции, также может использоваться [1] для ссылки на его историческое значение.ta.highest(high, 10)
соответствует предыдущей строке текущей строки, результат расчета:ta.highest(high[1], 10)
Так что...ta.highest(high[1], 10)
иta.highest(high, 10)[1]
эквивалентны.
Использовать другую функцию рисунка для проверки информации:
a = ta.highest(close, 10)[1]
b = ta.highest(close[1], 10)
plotchar(true, title="a", char=str.tostring(a), location=location.abovebar, color=color.red, overlay=true)
plotchar(true, title="b", char=str.tostring(b), location=location.belowbar, color=color.green, overlay=true)
Мы можем видеть, что значения переменной a и переменной b в временных рядах отображаются выше и ниже соответствующих Бар. Мы можем сохранить этот чертежный код во время процесса обучения, потому что нам часто может потребоваться вывести информацию на графике для наблюдения во время обратного тестирования и экспериментов.
В начальной части учебника мы обобщили некоторые различия в использовании языка Pine на FMZ и Trading View.indicator()
, strategy()
, иlibrary()
Конечно, для совместимости с более ранними версиями скриптов Pine, такие стратегии, как://@version=5
, indicator()
, strategy()
Некоторые настройки стратегии также могут быть установлены путем передачи параметров вstrategy()
function.
<version>
<declaration_statement>
<code>
В<version>
информация о контроле версий может быть пропущена.
Язык сосны использует//
как символ комментария с одной строкой, поскольку язык Pine не имеет символа комментария с несколькими строками. FMZ расширяет символ комментария/**/
для комментариев в несколько строк.
Строки в скрипте, которые не являются комментариями или директивами компилятора, являются инструкциями, которые реализуют алгоритм скрипта.
if
, for
, while
илиswitch
структураЗаявления могут быть организованы различными способами
space
или ```tab` (клавиатура). Их первый символ также должен быть первым символом строки. Строки, начинающиеся с первой позиции, по определению становятся частью глобального объема сценария.local block
Локальный блок должен быть отрезан одним вкладышем или четырьмя пробелами (в противном случае он будет анализироваться как соединенный код предыдущей строки, то есть считается непрерывным содержанием предыдущей строки кода), и каждый локальный блок определяет другой локальный объем.Например, он включает три локальных блока, один в декларации пользовательской функции и два в декларации переменной с использованием структуры if, следующим образом:
indicator("", "", true) // declaration statement (global scope), can be omitted
barIsUp() => // function declaration (global scope)
close > open // local block (local scope)
plotColor = if barIsUp() // variable declaration (global scope)
color.green // local block (local scope)
else
color.red // local block (local scope)
runtime.log("color", color = plotColor) // Call a built-in function to output the log (global scope)
Длинные линии могут быть разделены на несколько линий, или "завязаны". Завязанная линия должна быть отвёрнута любым количеством пробелов, если она не является кратным 4 (эти границы используются для отвода локальных блоков).
a = open + high + low + close
Он может быть упакован так (обратите внимание, что количество пробелов, проложенных в отрезках на одну строку, не может быть кратным 4):
a = open +
high +
low +
close
Длинный сюжет ((() вызов может быть завершен как:
close1 = request.security(syminfo.tickerid, "D", close) // syminfo.tickerid daily level closing price data series for the current trading pair
close2 = request.security(syminfo.tickerid, "240", close) // syminfo.tickerid 240-minute level closing price data series for the current trading pair
plot(ta.correlation(close, open, 100), // line-long plot() calls can be wrapped
color = color.new(color.purple, 40),
style = plot.style_area,
trackprice = true)
Однако, поскольку локальный блок должен начинаться с отступления в грамматике (4 пробела или 1 вкладка), при разделе на следующую строку продолжение заявления должно начинаться с более чем одной отступления (не равняющейся 4 кратным пробелам).
test(c, o) =>
ret = c > o ?
(c > o+5000 ?
1 :
0):
(c < o-5000 ?
-1 :
0)
a = test(close, open)
plot(a, title="a")
Перед тем, как признать переменные, мы должны сначала понять понятие
(A-Z)
или малой буквы(a-z)
письмо или подчерк(_)
как первый символ маркера.Например, следующие маркеры:
fmzVar
_fmzVar
fmz666Var
funcName
MAX_LEN
max_len
maxLen
3barsDown // Wrong naming! It used a numeric character as the leading character of the marker
Как и большинство языков программирования, язык Пайн также имеет предложения по написанию.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7
// name functions
zeroOne(boolValue) => boolValue ? 1 : 0
Операторы - это некоторые символы операций, используемые в языках программирования для построения выражений, а выражения - это вычислительные правила, предназначенные для определенных вычислительных целей при написании стратегий.
Операторы назначения, арифметические операторы, операторы сравнения, логические операторы,? :
трехсторонние операторы,[]
исторические операторы ссылки.
Принимая арифметический оператор*
в качестве примера, это отличается от проблемы типа, вызванной результатом возврата оператора языка Pine в Trading View.
//@version=5
indicator("")
lenInput = input.int(14, "Length")
factor = year > 2020 ? 3 : 1
adjustedLength = lenInput * factor
ma = ta.ema(close, adjustedLength) // Compilation error!
plot(ma)
При выполнении этого сценария в Trading View произойдет компиляционная ошибка.adjustedLength = lenInput * factor
, результат:series int
тип (серия), но второй параметр функцииta.ema
Но нет таких строгих ограничений на FMZ, вышеуказанный код может работать нормально.
Давайте посмотрим на использование различных операторов вместе.
Существует два типа операторов назначения:=
, :=
, что мы видели в нескольких примерах в начале части учебника.
В=
Оператор используется для присвоения значения переменной при ее инициализации или декларировании.=
начнётся с этого значения на каждом последующем столбце. Это действительные декларации переменных:
a = close // Use built-in variables to assign values to a
b = 10000 // Use numerical assignment
c = "test" // Use string assignment
d = color.green // Use color value assignment
plot(a, title="a")
plot(b, title="b")
plotchar(true, title="c", char=str.tostring(c), color=d, overlay=true)
Обратите внимание, что заявление о назначенииa = close
, переменная a на каждой Бар - текущая цена закрытия (закрытия) Бар. Другие переменныеb
, c
, d
остаются неизменными и могут быть проверены в системе обратного тестирования на FMZ, и результаты можно увидеть на диаграмме.
:=
используется для переназначения значений существующим переменным.:=
Оператор используется для изменения значений переменных, которые были объявлены и инициированы.
Если мы используем:=
Оператор для присвоения значения неинтилированной или объявленной переменной, это вызовет ошибку, например:
a := 0
Поэтому:=
оператор назначения обычно используется для переназначения существующих переменных, например:
a = close > open
b = 0
if a
b := b + 1
plot(b)
Судя по тому,close > open
(то есть текущая BAR является положительной прямой), переменная a является истинной.b := b + 1
выполняется, и оператор назначения:=
Затем мы используем функцию графика, чтобы нарисовать значение переменной b на каждом BAR временного ряда на графике и соединить их в прямую.
Мы думаем, что когда появляется положительная прямая BAR, b будет продолжать накапливаться на 1? Конечно, нет, здесь мы объявляем и инициируем переменную b как 0 без использования ключевого слова обозначения.b=0
выполняется на каждом BAR, так что мы можем видеть, что результат этого кода, чтобы сбросить b переменная до 0 каждый раз, если переменная a является истинным, то есть в соответствии сclose > open
, то b будет увеличено на 1, когда код выполняется в этом раунде, и b равно 1, когда функция графика выводит, но b переназначается на 0, когда код выполняется в следующем раунде.
Когда дело доходит до операторов назначения, мы должны расширить на два ключевых слова:var
, varip
Вар
На самом деле, мы видели и использовали это ключевое слово в предыдущих учебниках, но мы не обсуждали его подробно в то время.
var - это ключевое слово, используемое для распределения и однократной инициализации переменных. В целом грамматика назначения переменных, которая не содержит ключевое слово var, приводит к тому, что значение переменной заменяется каждый раз, когда обновляются данные.
Мы все еще используем этот пример, но мы используемvar
Ключевое слово при присвоении значения b здесь.
a = close > open
var b = 0
if a
b := b + 1
plot(b)
Вvar
Ключевое слово позволяет переменной b выполнять только первоначальное назначение, и затем она не будет сбросить b до 0 каждый раз, когда логика стратегии выполняется, поэтому можно наблюдать из линии, нарисованной во время выполнения, что b - это количество положительных линий BAR, которые появились, когда текущая K-линия BAR была проверена.
Переменные, декларируемые var, могут быть записаны не только в глобальном масштабе, но и в блоках кода, например, в этом примере:
strategy(overlay=true)
var a = close
var b = 0.0
var c = 0.0
var green_bars_count = 0
if close > open
var x = close
b := x
green_bars_count := green_bars_count + 1
if green_bars_count >= 10
var y = close
c := y
plot(a, title = "a")
plot(b, title = "b")
plot(c, title = "c")
Переменная
разновидности
Мы видим ключевое словоvarip
Впервые мы можем посмотреть на описание этого ключевого слова:
varp (var intrabar persist) - ключевое слово для присвоения и однократной инициализации переменных.
Это трудно понять? Неважно, мы объясним это на примере, это легко понять.
strategy(overlay=true)
// test var varip
var i = 0
varip ii = 0
// Print the i and ii changed in each round of the strategy logic on the chart
plotchar(true, title="ii", char=str.tostring(ii), location=location.abovebar, color=color.red)
plotchar(true, title="i", char=str.tostring(i), location=location.belowbar, color=color.green)
// Increment i and ii by 1 for each round of logic execution
i := i + 1
ii := ii + 1
Этот тестовый код имеет различные характеристики на
Модель строки:
Помните ли вы, что стратегия исполнения мы объяснили ранее разделена на историческую стадию BAR и в режиме реального времени BAR стадии? в Бар модели, исторической K-линии стадии, переменныеi
, ii
заявленный вvar
, varip
выполнять инкрементальные операции при каждом раунде выполнения кода стратегии. Следовательно, можно увидеть, что числа, отображаемые на K-линии BAR результата обратного теста, увеличиваются на 1 один за другим. Когда историческая стадия K-линии заканчивается, начинается стадия K-линии в реальном времени. Переменные, заявленные var и varip, начинают претерпевать различные изменения. Поскольку это Bar Model, код стратегии будет выполняться один раз для каждого изменения цены в K-линии BAR,i := i + 1
иii := ii + 1
Различие заключается в том, что ii изменяется каждый раз. Хотя i изменяется каждый раз, предыдущее значение будет восстановлено, когда логика стратегии будет выполнена в следующем раунде (помните механизм обратного отвода, который мы объяснили в предыдущей главе
Модель отметки: Поскольку модель Tick выполняет логику стратегии только один раз на K-линию BAR. Так что в модели ценового закрытия переменные, заявленные var и varip, ведут себя точно так же в приведенном выше примере, увеличиваясь на 1 для каждой K-линии BAR во время исторической стадии K-линии и стадии K-линии в реальном времени.
Операторы | Описание |
---|---|
+ | Добавление |
- | Вычитание |
* | Умножение |
/ | Разделение |
% | Модуль |
В+
и-
Другие арифметические операторы могут использоваться только в качестве бинарных операторов, и он будет сообщать об ошибке, если он был использован в качестве унитарных операторов.
+
, результатом вычисления является строка, значение будет преобразовано в форму строки, а затем строки будут сшиты вместе. Если это другой арифметический оператор, он попытается преобразовать строку в значение, а затем продолжить операцию.a = 1 + 1
b = 1 + 1.1
c = 1 + "1.1"
d = "1" + "1.1"
e = 1 + na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e)
// a: 2 , b: 2.1 , c: 11.1 , d: 11.1 , e: NaN
Язык Pine на FMZ немного отличается от языка Pine на Trading View, язык Pine на FMZ не очень строг в отношении типов переменных.
a = 1 * "1.1"
b = "1" / "1.1"
c = 5 % "A"
plot(a)
plot(b)
plot(c)
Он работает на FMZ, но сообщает об ошибке типа в Trading View. Если оба операнда арифметического оператора являются строками, система преобразует строки в числовые значения, а затем вычисляет их. Если нецифровая строка не может быть вычислена, результат системной операции является нулевым значением
Операторы сравнения - это двоичные операторы.
Операторы | Описание |
---|---|
< | < |
> | > |
<= | <= |
>= | >= |
== | == |
!= | != |
Пример испытания:
a = 1 > 2
b = 1 < 2
c = "1" <= 2
d = "1" >= 2
e = 1 == 1
f = 2 != 1
g = open > close
h = na > 1
i = 1 > na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e, ", f:", f, ", g:", g, ", h:", h, ", i:", i)
// a: false , b: true , c: true , d: false , e: true , f: true , g: false , h: false , i: false
Как мы видим, оператор сравнения очень прост в использовании, но это также оператор, который мы используем больше всего при написании стратегий.close
, open
, и т.д.
Как и в случае с оператором, существует разница в отношении языка Pine между FMZ и Trading View. FMZ не имеет особенно строгих требований к типам, поэтому такие заявленияd = "1" >= 2
не будет сообщать об ошибке на FMZ, и будет выполняться путем преобразования строки в значение сначала, а затем сравнивая операцию.
Операторы | Символы кода | Описание |
---|---|---|
Нет, нет. | Нет, нет. | Унитарный оператор, не операции |
и | и | Бинарные операторы и операции |
или | или | Бинарные операторы или операции |
Когда дело доходит до логических операторов, то мы должны говорить о таблицах истинных значений.
a = 1 == 1 // An expression formed by using comparison operators, the result is a Boolean value
b = 1 != 1
c = not b // Logical not operators
d = not a // Logical not operators
runtime.log("test the logical operator:and", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a and c:", a and c)
runtime.log("a:", a, ", b:", b, ", a and b:", a and b)
runtime.log("b:", b, ", c:", c, ", b and c:", b and c)
runtime.log("d:", d, ", b:", b, ", d and b:", d and b)
runtime.log("test the logical operator:or", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a or c:", a or c)
runtime.log("a:", a, ", b:", b, ", a or b:", a or b)
runtime.log("b:", b, ", c:", c, ", b or c:", b or c)
runtime.log("d:", d, ", b:", b, ", d or b:", d or b)
runtime.error("stop")
Чтобы не перепечатать сообщения, мы бросаем ошибку сruntime.error("stop")
После этого мы можем наблюдать за выходной информацией, и мы можем обнаружить, что напечатанное содержимое на самом деле то же самое, что и таблица истинных значений.
Тернарные выражения с использованием тернарного оператора? :
в сочетании с операндамиcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
Мы также использовали их в предыдущих уроках. так называемое тернарное выражение, тернарный оператор означает, что в нем есть три операнда.
Вcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
, condition
является условием суждения. Если это верно, значение выражения:valueWhenConditionIsTrue
Если.condition
false, то значение выражения будетvalueWhenConditionIsFalse
.
Пример удобной демонстрации, хотя и мало практической:
a = close > open
b = a ? "positive line" : "negative line"
c = not a ? "negative line" : "positive line"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
Что делать, если мы столкнемся с додзи? Это не имеет значения! Тернарные выражения также могут быть вложены, как мы сделали в предыдущем уроке.
a = close > open
b = a ? math.abs(close-open) > 30 ? "positive line" : "doji" : math.abs(close-open) > 30 ? "negative line" : "doji"
c = not a ? math.abs(close-open) > 30 ? "negative line" : "doji" : math.abs(close-open) > 30 ? "positive line" : "doji"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
На самом деле, это равносильно заменеvalueWhenConditionIsTrue
иvalueWhenConditionIsFalse
вcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
с другим трехзначным выражением.
Использовать оператор истории[]
Эти исторические значения - это значения переменной на K-линейной строке до текущей K-линейной строки, когда выполнялся скрипт.[]
Оператор используется после переменных, выражений и вызовов функций.[]
Квадратные скобки - это смещение исторических данных, на которые мы хотим ссылаться из текущей K-линии BAR. Например, если я хочу процитировать цену закрытия последней K-линии BAR, мы запишем это так:close[1]
.
Мы видели что-то подобное в предыдущих уроках:
high[10]
ta.sma(close, 10)[1]
ta.highest(high, 10)[20]
close > nz(close[1], open)
В[]
Оператор может использоваться только один раз на одном и том же значении, поэтому неправильно писать его так, и будет сообщена ошибка:
a = close[1][2] // error
Здесь, кто-то может сказать, что оператор[]
используется для структуры ряда, кажется, что структура ряда (серия) похожа на массив!
Давайте воспользуемся примером, чтобы проиллюстрировать разницу между рядами и массивами на языке Pine.
strategy("test", overlay=true)
a = close
b = close[1]
c = b[1]
plot(a, title="a")
plot(b, title="b")
plot(c, title="c")
a = close[1][2]
будет сообщать об ошибке, но:
b = close[1]
c = b[1]
Но если написать отдельно, это не будет сообщать о ошибке.b = close [1]
, b должно быть значением, ноc = b[1]
, b все еще можно использовать для ссылки на историческое значение снова с помощью оператора истории. Можно видеть, что концепция рядов в языке Pine не так проста, как массив.