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

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

[TOC]

img

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

Поддерживающее видео обучение: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 по сравнению с оригинальной версией.

Краткий обзор некоторых очевидных различий:

    1. Стратегия 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)
    
    1. Некоторые настройки стратегии, связанные с торговлей (скрипт), устанавливаются параметрами Pine Language Trading Class Library на интерфейсе стратегии FMZ.
    • Модель ценообразования закрытия и модель ценообразования в режиме реального времени На торговый вид, мы можем использоватьcalc_on_every_tickпараметрstrategyФункция настройки сценария стратегии для выполнения логики стратегии в режиме реального времени, когда цена меняется каждый раз.calc_on_every_tickпараметр должен быть установлен наtrue.calc_on_every_tickпараметр по умолчанию:false, то есть логика стратегии выполняется только тогда, когда текущая K-линия BAR стратегии полностью завершена. На FMZ он устанавливается параметрами шаблона Pine Language Trading Class Library.

      img

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

    • Код фьючерсного контракта Если торговый продукт на FMZ является контрактом, он имеет два атрибута, они Trading Pair и Contract Code соответственно. В дополнение к установке торговой пары явно, также необходимо установить конкретный код контракта в параметре Variety Code шаблона Pine Language Trading Class Library во время реальной торговли и бэкстестинга.swap, и код контракта зависит от того, имеет ли действующая биржа такой контракт.quarterЭти коды контрактов согласуются с кодами фьючерсных контрактов, определенных в документе API языка Javascript/python/c++ FMZ.

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

    1. Функции для расширений 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)
      
    1. Вoverlayпараметр расширяется в некоторых из функций чертежа

    В языке Pine на FMZ, чертежные функцииplot, plotshape, plotchar, и т.д. добавилиoverlayподдержка параметров, позволяющая указать чертеж на основной или подграфике.overlayУстановлено наtrueрисовать на основной графике, иfalseнастраивается на использование подграфики, что позволяет стратегии Pine на FMZ одновременно использовать основную и подграфику.

    1. Стоимостьsyminfo.mintickвстроенная переменная

    Встроенная переменнаяsyminfo.mintickЭто значение может контролироваться с помощью шаблона параметра ценообразования валюты в Pine Language Trading Class Library на FMZбот/обратная проверкаУстановка точности ценообразования валюты 2 означает, что цена точна до второй запятой при торговле, а минимальная единица изменения цены равна 0,01.syminfo.mintickравен 0,01.

    1. Средняя цена в FMZ PINE Script включает комиссию

    Например: цена заказа 8000, направление продажи, количество 1 лота (дело, лист), средняя цена после сделки не 8000, но ниже 8000 (стоимость включает в себя комиссию за обработку).

Основы соснового языка

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

Исполнение модели

Когда начинаешь изучать язык Пайн, очень важно понимать связанные с ним понятия, такие как процесс выполнения программы сценария языка Пайн. Стратегия языка Пайн работает на основе диаграммы. Можно понять, что стратегия языка Пайн представляет собой серию вычислений и операций, которые выполняются на графике в порядке временных рядов из самых ранних данных, которые были загружены на графике. Количество данных, которые первоначально загружаются на графике, ограничено. В реальной торговле максимальное количество данных обычно определяется на основе максимального объема данных, возвращаемых обменным интерфейсом, и максимальное количество данных во время бэкстестинга определяется на основе данных, предоставляемых источником данных системы бэкстестинга. Самый левый K-линейный штрих на графике, то есть первый набор данных на графике, имеет значение индекса 0. Значение индекса данных K-линии в текущем сценарии Пайн может быть определено, когда переменная ссылается через встроенную переменную.bar_indexна языке сосны.

plot(bar_index, "bar_index")

img

В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:

  • Исторический бар

    Когда стратегия установлена на Tick Model и начинает выполняться, все K-линейные строки на графике, за исключением самого правогоHistorical Bars. Логика стратегии выполняется только один раз на каждомhistorical bar- Да, конечно. Когда стратегия установлена на Bar Model и начинает выполняться, все строки на графикеhistorical bars. Логика стратегии выполняется только один раз на каждомhistorical bar.

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

  • Бар в реальном времени

    Когда стратегия выполняется до последней K-линейной панели в правом углу, панель является панелью в реальном времени. После закрытия панели в реальном времени, панель становится прошедшей панелью в реальном времени (станет исторической панелью). Новая панель в реальном времени будет создана в правом углу диаграммы.

    Когда стратегия настроена на Tick Model и начинает выполняться, логика стратегии будет выполняться один раз для каждого изменения рынка на панели реального времени. Когда стратегия настроена на Bar Model и начинает выполняться, панель в режиме реального времени не будет отображаться на графике.

    Расчет на основе бар в реальном времени: Если стратегия установлена на Bar Model и на графике не отображаются панели в реальном времени, код стратегии будет выполняться только один раз при закрытии текущей панели. Если стратегия установлена на Tick Model, расчет на панели реального времени полностью отличается от исторической панели, и код стратегии будет выполняться один раз для каждого изменения рынка на прямых торговых панелях.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")
    

    img

Мы изучаем только сцену, выполненную в режиме реального времени, так что мы используемnot barstate.ishistoryвыражение для ограничения накопления переменной n только в режиме реального времени Bar, и использованиеruntime.logФункция вывода информации в журнале стратегии до и после операции накопления.plot, можно увидеть, что n всегда равен 0, когда стратегия выполняется в исторических Бар. Когда выполняется Бар в режиме реального времени, запускается операция добавления 1 к n, а операция добавления 1 к n выполняется, когда стратегия выполняется в каждом раунде Бар в режиме реального времени. Из сообщения журнала можно наблюдать, что n будет сброшен на значение, окончательно представленное предыдущей стратегией исполнения Бар, когда код стратегии повторно выполняется в каждом раунде. Обновление значения n будет представлено, когда код стратегии выполняется в Бар в режиме реального времени в последний раз, поэтому вы можете видеть, что значение кривой n увеличивается на 1 с каждым увеличением Бар, начиная с Бар в режиме реального времени на графике.

Резюме:

  1. Код стратегии выполняется один раз каждый раз, когда рынок обновляется, когда стратегия начинает выполняться в режиме реального времени.
  2. При выполнении в режиме реального времени переменные возвращаются каждый раз до выполнения кода стратегии.
  3. При выполнении в режиме реального времени переменные передаются один раз при обновлении закрытия.

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

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")

Скриншот времени Аimg

Скриншот времени Bimg

Мы только изменили предложение: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)
    

    Скриншот выполнения обратного теста

    img

    Код испытания относительно прост, в основном для изучения данных, ссылающихся на два метода, а именно: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")Нарисуйте символ A, цвет красный, он рисуется, когда oneBarInTwo является истинным, и выведенное положение (на оси Y): значение, возвращаемоеf(close).

  • plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")Нарисуйте символ B, цвет зелёный, он рисуется только тогда, когда oneBarInTwo является истинным, и выведенное положение (на оси Y) является: значение, возвращаемое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] для ссылки на исторические данные на серии данных, позиции маркера A и B на графике совершенно разные.plot(close[2], title = "close[2]", color = color.red, overlay = true), данные, использованные для начертания линии:close[2].

img

Причина заключается в том, чтобы рассчитать, следует ли нарисовать маркеры A и B через индекс K-линии Bar, то есть встроенную переменнуюbar_indexМаркеры A и B не рисуются на каждом K-линейном столбце (расчет функции вызван при рисовании).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-линии.

Как показано на графике:

img

Так что когда график рисуется, только данные с значением для переменной 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 - это все структуры временных рядов, и на каждом столбце есть соответствующие данные.

    img

    Поскольку переменная 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действительно перекрываются.

    img

    Точно так же я могу использовать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 в временных рядах отображаются выше и ниже соответствующих Бар. Мы можем сохранить этот чертежный код во время процесса обучения, потому что нам часто может потребоваться вывести информацию на графике для наблюдения во время обратного тестирования и экспериментов.

    img

Структура сценария

Общая структура

В начальной части учебника мы обобщили некоторые различия в использовании языка 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")

Маркеры и операторы

Маркеры

Перед тем, как признать переменные, мы должны сначала понять понятие маркеров.Функцияипеременная(используется для обозначения переменных и функций).ФункцииБудет видно в наших последующих уроках, давайте сначала узнаем о маркерах.

    1. Маркеры должны начинаться с большой буквы(A-Z)или малой буквы(a-z)письмо или подчерк(_)как первый символ маркера.
    1. Следующий символ после первого символа маркера может бытьписьмо, подчеркнуть, илиНомер.
    1. Название маркеров зависит от букв и букв.

Например, следующие маркеры:

fmzVar
_fmzVar
fmz666Var
funcName
MAX_LEN
max_len
maxLen
3barsDown  // Wrong naming! It used a numeric character as the leading character of the marker

Как и большинство языков программирования, язык Пайн также имеет предложения по написанию.

    1. Все большие буквы используются для обозначения констант.
    1. Используйтенижний корпус верблюдадля других маркерных названий.
// 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")
    

    Переменная a содержит цену закрытия первой строки в серии. Переменная b содержит цену закрытия первой ценовой строки green в серии. Переменная 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 Model и Tick Model:

    Модель строки: Помните ли вы, что стратегия исполнения мы объяснили ранее разделена на историческую стадию 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 изменяется каждый раз, предыдущее значение будет восстановлено, когда логика стратегии будет выполнена в следующем раунде (помните механизм обратного отвода, который мы объяснили в предыдущей главе Model Execution?), и значение i не будет обновляться, пока текущая K-линия BAR не будет завершена (то есть предыдущее значение не будет восстановлено, когда логика стратегии будет выполнена в следующем раунде). Таким образом, можно увидеть, что переменная i все еще увеличивается на 1 для каждого BAR. Но переменная ii накапливается несколько раз для каждого BAR.

    Модель отметки: Поскольку модель Tick выполняет логику стратегии только один раз на K-линию BAR. Так что в модели ценового закрытия переменные, заявленные var и varip, ведут себя точно так же в приведенном выше примере, увеличиваясь на 1 для каждой K-линии BAR во время исторической стадии K-линии и стадии K-линии в реальном времени.

Арифметические операторы
Операторы Описание
+ Добавление
- Вычитание
* Умножение
/ Разделение
% Модуль

В+и-Другие арифметические операторы могут использоваться только в качестве бинарных операторов, и он будет сообщать об ошибке, если он был использован в качестве унитарных операторов.

  1. Обе стороны арифметического оператора имеют числовой тип, результат является числовым типом, целым числом или плавающей запятой в зависимости от результата операции.
  2. Если один из операндов является строкой, а оператор -+, результатом вычисления является строка, значение будет преобразовано в форму строки, а затем строки будут сшиты вместе. Если это другой арифметический оператор, он попытается преобразовать строку в значение, а затем продолжить операцию.
  3. Если один из операндов является na, результатом расчета является нулевое значениеna, и он покажет NaN при печати на FMZ.
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. Если оба операнда арифметического оператора являются строками, система преобразует строки в числовые значения, а затем вычисляет их. Если нецифровая строка не может быть вычислена, результат системной операции является нулевым значениемna.

Операторы сравнения

Операторы сравнения - это двоичные операторы.

Операторы Описание
< <
> >
<= <=
>= >=
== ==
!= !=

Пример испытания:

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Если.conditionfalse, то значение выражения будет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 не так проста, как массив.


Больше