Tutorial introductorio del lenguaje PINE de FMZ Quant

El autor:- ¿ Por qué?, Creado: 2022-09-23 15:23:34, Actualizado: 2024-02-27 16:47:41

[TOC] ¿Qué quieres decir?

img

Tutorial introductorio del lenguaje PINE de FMZ Quant

Tutorial de video de apoyo:https://www.youtube.com/watch?v=CA3SwJQb_1g

FMZ Quant Trading Platform admite la escritura de estrategias de lenguaje Pine, backtesting y trading en vivo de estrategias de lenguaje Pine, y es compatible con versiones más bajas de lenguaje Pine.Plaza Estrategiaen la plataforma de negociación cuantitativa FMZ (FMZ.COM).

FMZ admite no solo el lenguaje Pine, sino también la poderosa función de dibujo del lenguaje Pine. Las diversas funciones, herramientas ricas y prácticas, gestión eficiente y conveniente en la plataforma FMZ mejoran aún más la viabilidad de la estrategia Pine (script). Basándose en la compatibilidad con el lenguaje Pine, FMZ también amplía, optimiza y recorta el lenguaje Pine hasta cierto punto. Antes de ingresar al tutorial oficialmente, echemos un vistazo a los cambios realizados en el lenguaje Pine en FMZ en comparación con la versión original.

Un breve resumen de algunas de las diferencias obvias:

    1. La estrategia Pine en FMZ, el identificador de versión al comienzo del código//@versiony elstrategy, indicatorlas declaraciones al comienzo del código no son obligatorias de escribir, FMZ no admiteimportpara importarlibraryFunciona por ahora.

    Se puede ver que algunas estrategias escritas así:

    //@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)
    

    O escribe así:

    //@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)
    

    En FMZ se puede simplificar a:

    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)
    

    O también:

    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. Algunos ajustes relacionados con el comercio de la estrategia (script) son establecidos por los parámetros Pine Language Trading Class Library en la interfaz de estrategia FMZ.
    • Modelo de precios de cierre y modelo de precios en tiempo real En la vista de comercio, podemos utilizar elcalc_on_every_tickParámetro delstrategyfunción para establecer el guión de la estrategia para ejecutar la lógica de la estrategia en tiempo real cuando el precio cambia cada vez.calc_on_every_tickel parámetro debe estar establecido entrueEl.calc_on_every_tickel parámetro predeterminado esfalse, es decir, la lógica de la estrategia se ejecuta solo cuando la BAR de la línea K actual de la estrategia se completa por completo. En FMZ, se establece por los parámetros de la plantilla Pine Language Trading Class Library.

      img

    • Control de precisión numérica, como el precio y el importe de la orden cuando se ejecuta la estrategia debe especificarse en FMZ En la vista comercial, no hay ningún problema de precisión al colocar órdenes comerciales reales, porque solo se puede probar en simulación. En FMZ, es posible ejecutar la estrategia Pine en el comercio real. Luego, la estrategia debe ser capaz de especificar la precisión del precio y la precisión del monto del pedido de la variedad comercial de manera flexible. La configuración de precisión controla el número de decimales en los datos relevantes para evitar que los datos no cumplan con los requisitos de orden del intercambio y, por lo tanto, no puedan realizar un pedido.

    • Código del contrato de futuros Si el producto de negociación en FMZ es un contrato, tiene dos atributos, son Trading Pair y Contract Code respectivamente. Además de establecer el par de negociación explícitamente, también es necesario establecer el código de contrato específico en el parámetro Variety Code de la plantilla Pine Language Trading Class Library durante la negociación real y la prueba posterior. Por ejemplo, para un contrato perpetuo, relleneswapPor ejemplo, algunas bolsas tienen contratos trimestrales, puede rellenar el código de contrato en el siguiente orden:quarterEstos códigos de contrato son consistentes con los códigos de contrato de futuros definidos en el documento de la API del lenguaje Javascript/python/c++ de FMZ.

    Para otros ajustes, como el importe mínimo de la orden, el importe de la orden por defecto, etc., consulte la introducción del parámetro enArgumentos modelo de la biblioteca de la clase de comercio de la lengua de pinoen la documentación del lenguaje Pine.

    1. Funciones para las extensiones FMZ:runtime.debug , runtime.log, runtime.errorse utiliza para depurar.

    Se han añadido 3 funciones a la plataforma FMZ para la depuración.

    • runtime.debug: Imprimir información de variables en la consola, que generalmente no se utiliza con esta función.

    • runtime.logFunciones específicas para el idioma PINE en FMZ.

      runtime.log(1, 2, 3, close, high, ...), Multiple parameters can be passed.
      
    • runtime.error: Resultará en un error de ejecución con el mensaje de error especificado en el parámetro de mensaje cuando se llame.

      runtime.error(message)
      
    1. EloverlayParámetro se extiende en algunas de las funciones de dibujo

    En el lenguaje Pine en FMZ, las funciones de dibujoplot, plotshape, plotchar, etc. han añadido eloverlayapoyo de parámetros, que permita especificar el dibujo del gráfico principal o del subgráfico.overlayestá configurado para:truepara dibujar en el gráfico principal, yfalsese establece para utilizar el subgráfico, lo que permite a la estrategia Pine en FMZ dibujar el gráfico principal y el subgráfico al mismo tiempo.

    1. Valor de lassyminfo.mintickVariable incorporada

    La variable incorporada desyminfo.mintickse define como el valor mínimo de tick para el símbolo actual. Este valor puede ser controlado por el parámetro modelo de precios de precisión de la moneda en la Pine Language Trading Class Library en el FMZbot (bot)/Prueba posteriorInterfaz. El ajuste de precisión de la moneda de precios 2 significa que el precio es preciso hasta el segundo decimal cuando se negocia, y la unidad mínima de cambio de precio es 0.01.syminfo.mintickes 0,01.

    1. El precio promedio en FMZ PINE Script incluye todas las comisiones

    Por ejemplo: el precio del pedido es de 8000, la dirección de venta, la cantidad es de 1 lote (pieza, hoja), el precio medio después de la transacción no es de 8000, sino inferior a 8000 (el coste incluye la tasa de manipulación).

Fundamentos del lenguaje del pino

Al comenzar a aprender los conceptos básicos del lenguaje Pine, puede haber algunos ejemplos de instrucciones y gramática de código con los que no estamos familiarizados. No importa si no lo entiendes, primero podemos familiarizarnos con los conceptos y entender el propósito de la prueba, o puedes consultar la documentación del lenguaje Pine en FMZ para obtener instrucciones. Luego sigue el tutorial paso a paso para familiarizarte con varias gramáticas, instrucciones, funciones y variables incorporadas.

Ejecución del modelo

Al comenzar a aprender el lenguaje Pine, es muy necesario entender los conceptos relacionados, como el proceso de ejecución del programa de script del lenguaje Pine. La estrategia del lenguaje Pine se ejecuta sobre la base del gráfico. Se puede entender que la estrategia del lenguaje Pine es una serie de cálculos y operaciones, que se ejecutan en el gráfico en el orden de las series de tiempo a partir de los primeros datos que se han cargado en el gráfico. La cantidad de datos que el gráfico carga inicialmente es limitada. En el comercio real, la cantidad máxima de datos generalmente se determina en función del volumen máximo de datos devueltos por la interfaz de intercambio, y la cantidad máxima de datos durante la prueba posterior se determina en función de los datos proporcionados por la fuente de datos del sistema de backtesting. La barra de K-line más a la izquierda en el gráfico, es decir, el primer conjunto de datos del gráfico, tiene un valor de índice de 0.bar_indexen el lenguaje Pine.

plot(bar_index, "bar_index")

img

Elplotla función es una de las funciones que vamos a utilizar más en el futuro. el uso es muy simple, es dibujar una línea en el gráfico de acuerdo con los parámetros de entrada, los datos de entrada esbar_index, y la línea se llamabar_index. Se puede ver que el valor de la línea llamada bar_index en la primera barra es 0, y aumenta en 1 a la derecha a medida que aumenta la barra.

Debido a que las configuraciones de la estrategia son diferentes, los métodos de ejecución del modelo de la estrategia son diferentes, se pueden dividir enclosing price modelyreal-time price modelTambién hemos introducido brevemente los conceptos de ellos antes.

  • Modelo de precios de cierre

    Cuando el código de estrategia se ejecuta, el período de la barra de la línea K actual se ejecuta completamente, y cuando la línea K se cierra, el período de la línea K se ha completado.

  • Modelo de precios en tiempo real

    Cuando se ejecuta el código de estrategia, independientemente de si la barra de la línea K actual está cerrada o no, la lógica de la estrategia Pine se ejecutará cuando el mercado cambie cada vez, y la señal de negociación activada se ejecutará inmediatamente.

Cuando la estrategia del lenguaje Pine se ejecuta de izquierda a derecha en el gráfico, las barras de la línea K en el gráfico se dividen enHistorical BarsyReal-time Bars:

  • Bar histórico

    Cuando la estrategia está configurada en Tick Model y comienza a ejecutarse, todas las barras de la línea K en el gráfico, excepto la más a la derecha sonHistorical Bars. La lógica de la estrategia se ejecuta sólo una vez en cadahistorical bar¿ Qué pasa? Cuando la estrategia se establece en Bar Model y comienza a ejecutarse, todas las barras en el gráfico sonhistorical bars. La lógica de la estrategia se ejecuta sólo una vez en cadahistorical bar.

    Cálculo basado en bares históricos: El código de estrategia se ejecuta una vez en el estado de cierre de la barra histórica, y luego el código de estrategia continúa ejecutándose en la siguiente barra histórica hasta que todas las barras históricas se ejecutan una vez.

  • Bar en tiempo real

    Cuando la estrategia se ejecuta hasta la última barra de la línea K en el extremo derecho, la barra es una barra en tiempo real.

    Cuando la estrategia se establece en Tick Model y comienza a ejecutarse, la lógica de la estrategia se ejecutará una vez por cada cambio de mercado en la barra en tiempo real. Cuando la estrategia está configurada en Bar Model y comienza a ejecutarse, la barra en tiempo real no se mostrará en el gráfico.

    Cálculo basado en Bar en tiempo real: Si la estrategia está configurada en Bar Model y el gráfico no muestra las barras en tiempo real, el código de estrategia solo se ejecutará una vez cuando se cierre la barra actual. Si la estrategia se establece en Tick Model, el cálculo en la barra de tiempo real es completamente diferente de la barra histórica, y el código de estrategia se ejecutará una vez por cada cambio de mercado en las barras de negociación en vivo.high, low, closeLos valores de los valores de mercado se determinan en barras históricas, y estos valores pueden cambiar cada vez que el mercado cambia en barras en tiempo real. Por lo tanto, los datos como los indicadores calculados en base a estos valores también cambiarán en tiempo real.closesiempre representa el último precio actual, yhighylowsiempre representan el punto más alto y el punto más bajo alcanzado desde el inicio de la barra actual en tiempo real. Estas variables integradas representan el valor final de la barra en tiempo real cuando se actualizó por última vez.

    Mecanismo de retroceso cuando se ejecutan estrategias en tiempo real Bar (modelo de precios en tiempo real): Durante la ejecución de Bar en tiempo real, el restablecimiento de variables definidas por el usuario antes de cada nueva iteración de la estrategia se llama rollback.

    Atención:

    /*backtest 
    ...
    ..
    .
    */
    

    El contenido del paquete es la información de configuración de backtest guardada en forma de código en la plataforma 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

Sólo examinamos la escena ejecutada durante el tiempo real bares, por lo que utilizamos elnot barstate.ishistoryexpresión para limitar la acumulación de la variable n sólo en el tiempo real Bar, y el usoruntime.logFunción para obtener la información en el registro de estrategia antes y después de la operación de acumulación.plot, se puede ver que n es siempre 0 cuando la estrategia se está ejecutando en Bares históricos. Cuando se ejecuta la Bar en tiempo real, se activa la operación de sumar 1 a n, y la operación de sumar 1 a n se ejecuta cuando la estrategia se ejecuta en cada ronda de la Bar en tiempo real. Se puede observar a partir del mensaje de registro que n se restablecerá al valor finalmente enviado por la estrategia de ejecución de Bar anterior cuando el código de estrategia se ejecute nuevamente en cada ronda. La actualización del valor de n se enviará cuando el código de estrategia se ejecute en la Bar en tiempo real por última vez, por lo que puede ver que el valor de la curva n aumenta en 1 con cada aumento de Bar a partir de la Bar en tiempo real en el gráfico.

Resumen:

  1. El código de estrategia se ejecuta una vez cada vez que el mercado se actualiza cuando la estrategia comienza a ejecutarse en una barra en tiempo real.
  2. Cuando se ejecuta en una barra en tiempo real, las variables se vuelven a rodar cada vez antes de que se ejecute el código de estrategia.
  3. Cuando se ejecuta en una barra en tiempo real, las variables se envían una vez cuando se actualiza el cierre.

Debido al retroceso de los datos, las operaciones de dibujo, como las curvas en el gráfico también pueden causar rediseño.

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

Captura de pantalla del tiempo Aimg

Captura de pantalla del tiempo Bimg

Sólo modificamos la frase:n := open > close ? n + 1 : n, sólo se añade 1 a n cuando la barra actual en tiempo real es una línea negativa (es decir, el precio de apertura es mayor que el precio de cierre). Se puede ver que en el primer gráfico (tiempo A), ya que el precio de apertura era mayor que el precio de cierre (línea negativa) en ese momento, n se acumuló en 1, y el valor de n mostrado en la curva del gráfico fue 5. Luego el mercado cambió y el precio se actualizó como se muestra en el segundo gráfico (tiempo B). En este momento, el precio de apertura es menor que el precio de cierre (línea positiva), y el valor de n retrocede sin incrementar en 1. La curva de n en el gráfico también se vuelve a dibujar inmediatamente, y el valor de n en la curva es 4. Por lo tanto, las señales, como el cruce y las mostradas en las barras en tiempo real, son inciertas y pueden cambiar.

  • Contexto variable en las funciones

    De acuerdo con algunas descripciones en tutoriales de Pine, las variables de la función tienen las siguientes diferencias con las variables fuera de la función:

    El historial de las variables de serie utilizadas en la función Pine se crea con cada llamada sucesiva a la función. Si la función no se llama en cada barra en la que se ejecuta el script, esto resultará en una discrepancia entre los valores históricos de la serie dentro y fuera del bloque local de la función. Por lo tanto, si la función no se llama en cada barra, la serie referenciada dentro y fuera de la función con el mismo valor de índice no se referirá al mismo punto histórico.

    No importa, lo resolveremos con un código de prueba que se ejecuta en 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)
    

    Captura de pantalla de la ejecución de la prueba de retroceso

    img

    El código de prueba es relativamente simple, principalmente para examinar los datos a los que se hace referencia mediante dos métodos, a saber:f(a) => a[1]yf2() => close[1].

    • f(a) => a[1]: Utilice el método de transmisión de parámetros, la función regresa aa[1] finally.

    • f2() => close[1]: Utilice la variable incorporadaclosedirectamente, y la función vuelve aclose[1] finally.

El[]El símbolo se utiliza para referirse al valor histórico de la variable de la serie de datos, y el cierre[1] se refiere a los datos del precio de cierre en la barra antes del precio de cierre actual.

  • plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")Dibuja un carácter A, el color es rojo, se dibuja cuando oneBarInTwo es verdadero, y la posición dibujada (en el eje Y) es: el valor devuelto porf(close).

  • plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")Dibuja un carácter B, el color es verde, se dibuja sólo cuando oneBarInTwo es verdadero, y la posición dibujada (en el eje Y) es: el valor devuelto porf2().

  • plot(close[2], title = "close[2]", color = color.red, overlay = true)Dibuje una línea, el color es rojo, y la posición dibujada (en el eje Y) es:close[2], que es el precio de cierre de la segunda barra antes de la barra actual (contando 2 barras a la izquierda).

  • plot(close[1], title = "close[1]", color = color.green, overlay = true)Dibuje una línea, el color es verde, y la posición dibujada (en el eje Y) es:close[1], que es el precio de cierre de la primera barra antes de la barra actual (contando 1 barra a la izquierda).

Se puede ver desde la captura de pantalla de la estrategia backtesting que aunque tanto la funciónf(a) => a[1]utilizado para dibujar el marcador A y la funciónf2() => close[1]La posición del marcador A siempre cae en la línea roja, que es la línea trazada por el código en la estrategia.plot(close[2], title = "close[2]", color = color.red, overlay = true), los datos utilizados para trazar la línea esclose[2].

img

La razón es calcular si dibujar los marcadores A y B a través del índice de la barra de línea K, es decir, la variable incorporadabar_indexLos marcadores A y B no se dibujan en cada barra de línea K (el cálculo de la función se llama al dibujar). El valor al que hace referencia la funciónf(a) => a[1]no será el mismo valor al que hace referencia la funciónf2() => close[1]si la función no se llama en cada Bar (incluso si ambos utilizan el mismo índice como [1]).

  • Algunas funciones integradas deben calcularse en cada barra para calcular sus resultados correctamente

    Para ilustrar esta situación con un ejemplo simple:

    res = close > close[1] ? ta.barssince(close < close[1]) : -1
    plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
    

    Escribimos el código de llamada de la funciónta.barssince(close < close[1])en un operador ternariocondition ? value1 : value2Esto hace que la función ta.barssince se llame sólo cuandoclose > close[1]Pero elta.barssinceLa función es calcular el número de K-líneas desde la última vezclose < close[1]Cuando se llama la función ta.barssince, siempre es close > close[1], es decir, el precio de cierre actual es mayor que el precio de cierre de la barra anterior. Cuando se llama la función ta.barssince, la condición close < close [1] no se establece, y no hay una posición reciente donde se mantenga.

    ta.barssince: Cuando se llama, la función devuelve na si la condición nunca se ha cumplido antes de la línea K actual.

Como se muestra en el cuadro:

img

Así que cuando el gráfico se dibuja, sólo los datos con un valor para la variable res (-1) se dibuja.

Para evitar este problema, simplemente tomamos elta.barssince(close < close[1])La función llama al operador ternario y lo escribe fuera de cualquier posible rama condicional, haciendo que realice cálculos en cada K-line 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)

Serie de tiempo

El concepto de serie de tiempo es muy importante en el lenguaje Pine, y es un concepto que debemos entender cuando aprendemos el lenguaje Pine. La serie de tiempo no es un tipo sino una estructura básica para almacenar valores continuos de variables a lo largo del tiempo. Sabemos que los scripts de Pine se basan en gráficos, y el contenido más básico que se muestra en el gráfico es el gráfico de líneas K. Serie de tiempo donde cada valor está asociado con una marca de tiempo de una barra de líneas K.openes una variable incorporada (integrada) del lenguaje Pine, y su estructura es almacenar la serie de tiempo del precio de apertura de cada K-line Bar.openrepresenta los precios de apertura de todas las barras de la línea K desde la primera barra al comienzo del gráfico de la línea K actual hasta la barra donde se ejecuta el script actual. Si el gráfico de la línea K actual es un período de 5 minutos, cuando cotizamos (o usamos)openEn el código de la estrategia de Pine, es el precio de apertura de la barra de la línea K cuando el código de la estrategia se ejecuta actualmente.[]Cuando la estrategia Pine se ejecuta en una determinada K-line Bar, utilizaropen[1]para referirse al precio de apertura de la barra anterior de la línea K (es decir, el precio de apertura del período anterior de la línea K) que hace referencia a laopenserie de tiempo en la que esta barra de línea K está siendo ejecutada actualmente por el script.

  • Las variables en series de tiempo son muy convenientes para la computación Tomemos la función incorporadata.cumPor ejemplo:

    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
    

    Código de ensayo:

    v1 = 1
    v2 = ta.cum(v1)
    plot(v1, title="v1")
    plot(v2, title="v2")
    plot(bar_index+1, title="bar_index")
    

    Hay muchas funciones incorporadas comota.cumque puede procesar datos sobre series temporales directamente.ta.cumes la acumulación de los valores correspondientes a las variables pasadas en cada K-línea Bar, y a continuación, usamos un gráfico para que sea más fácil de entender.

    Proceso de operación de la estrategia Variable incorporada bar_index V1 V2
    La estrategia se ejecuta en la primera barra de la línea K. 0 1 1
    La estrategia se ejecuta en la segunda barra de la línea K. 1 1 2
    La estrategia se ejecuta en la tercera barra de la línea K. 2 1 3
    La estrategia se ejecuta en la barra de la línea K N + 1 No 1 N + 1

    Se puede ver que v1, v2 e incluso bar_index son todas estructuras de series temporales, y hay datos correspondientes en cada barra.

    img

    Porque la variable v1 es 1 en cada Bar, cuando elta.cum(v1)La función se ejecuta en la primera K-línea Bar, sólo hay la primera Bar, por lo que el resultado del cálculo es 1 y asignado a la variable v2. ¿Cuándo?ta.cum(v1)Si el resultado de cálculo es 2, que se asigna a la variable v2, y así sucesivamente. De hecho, se puede observar que v2 es el número de barras de línea K en el gráfico, ya que el índice de la línea Kbar_indexse incrementa desde 0, entoncesbar_index + 1En el gráfico, también podemos ver que las líneasv2ybar_indexde hecho se superponen.

    img

    De la misma manera, también puedo utilizar elta.cumla función incorporada para calcular la suma de los precios de cierre para todas las barras en el gráfico actual. todo lo que tengo que hacer es escribirlo así:ta.cum(close), Cuando la estrategia se ejecuta a la barra en tiempo real en el extremo derecho, el resultado calculado porta.cum(close)es la suma de los precios de cierre de todas las barras en el gráfico (si no corre hacia la extrema derecha, solo se acumula hasta la barra actual).

    Las variables de las series temporales también se pueden calcular utilizando operadores, como el código:ta.sma(high - low, 14), restar la variable incorporadahigh(el precio más alto de la barra de la línea K) desdelow(el precio más bajo de K-line Bar), y finalmente utilizar elta.smafunción para calcular el valor medio.

  • El resultado de una llamada de función también dejará rastros de valores en la serie de tiempo.

    v1 = ta.highest(high, 10)[1]
    v2 = ta.highest(high[1], 10)
    plot(v1, title="v1", overlay=true)
    plot(v2, title="v2", overlay=true)
    

    El código de prueba se ejecuta durante el backtesting, y se puede observar que los valores dev1yv2El resultado calculado por la llamada de la función dejará rastros del valor en la serie de tiempo, como elta.highest(high, 10)en el códigota.highest(high, 10)[1]El resultado calculado por la llamada de la función también puede utilizar [1] para referirse a su valor histórico.ta.highest(high, 10)correspondiente a la barra anterior de la barra actual, el resultado del cálculo esta.highest(high[1], 10)Así que...ta.highest(high[1], 10)yta.highest(high, 10)[1]son exactamente equivalentes.

    Utilice otra función de dibujo para obtener la información de verificación:

    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)
    

    Podemos ver que los valores de la variable a y la variable b en la serie de tiempo se muestran por encima y por debajo de las barras correspondientes. Podemos mantener este código de dibujo durante el proceso de aprendizaje, porque a menudo es posible que necesitemos obtener información en la tabla para su observación durante las pruebas y la experimentación.

    img

Estructura del guión

Estructura general

En la parte inicial del tutorial, hemos resumido algunas diferencias en el uso del lenguaje Pine en FMZ y Trading View.indicator(), strategy(), ylibrary()Por supuesto, con el fin de ser compatible con versiones anteriores de guiones Pine, estrategias tales como://@version=5, indicator(), strategy()Algunos ajustes de estrategia también se pueden establecer pasando parámetros en elstrategy() function.

<version>
<declaration_statement>
<code>

El<version>La información de control de versiones puede omitirse.

Comentarios de la Comisión

El lenguaje del pino utiliza//como un símbolo de comentario de una sola línea, ya que el lenguaje Pine no tiene un símbolo de comentario de varias líneas. FMZ extiende el símbolo de comentario/**/para las observaciones de varias líneas.

Código

Las líneas en el script que no son comentarios o directivas del compilador son instrucciones, que implementan el algoritmo del script.

  • Declaración variable
  • Reasignación de las variables
  • Declaración de función
  • Llamadas de funciones integradas, llamadas de funciones definidas por el usuario
  • if, for, whileo bienswitchestructura

Las declaraciones pueden organizarse de varias maneras.

  • Algunas instrucciones pueden expresarse en una línea, como la mayoría de las declaraciones variables, las líneas que contienen solo una llamada de función o las declaraciones de función de una sola línea. Otras, como las estructuras, siempre requieren varias líneas, porque requieren un bloque local.
  • Las instrucciones en el alcance global de un script (es decir, partes que no son parte de un bloque local) no pueden comenzar con unspaceLas líneas que comienzan en la primera posición, por definición, se convierten en parte del alcance global del script.
  • Una declaración de estructura o de función de varias líneas siempre requiere unalocal blockUn bloque local debe estar recubierto por una pestaña o cuatro espacios (de lo contrario, se analizará como el código concatenado de la línea anterior, es decir, se juzgará que es el contenido continuo de la línea anterior de código), y cada bloque local define un alcance local diferente.
  • Múltiples declaraciones de una sola línea pueden ser concatenadas en una sola línea mediante el uso de comas (,) como separadores.
  • Una línea puede contener comentarios o sólo tener comentarios.
  • Las líneas también se pueden envolver (continuar en varias líneas).

Por ejemplo, incluye tres bloques locales, uno en la declaración de función personalizada y dos en la declaración de variable utilizando la estructura if, de la siguiente manera:

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)

Código de ruptura de línea

Las líneas largas se pueden dividir en varias líneas, o envueltas. Una línea envuelta debe ser recubierta por cualquier número de espacios, siempre que no sea un múltiplo de 4 (estos límites se utilizan para recubierto de bloques locales).

a = open + high + low + close

Se puede envuelven como (tenga en cuenta que el número de espacios recubiertos por línea no puede ser un múltiplo de 4):

a = open +
      high +
          low +
             close

Una llamada de trama larga puede ser envuelta como:

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)

Sin embargo, dado que un bloque local debe comenzar con una hendidura en la gramática (4 espacios o 1 pestaña), al dividirlo en la siguiente línea, la continuación de una declaración debe comenzar con más de una hendidura (no igual a 4 múltiplos de espacios). Por ejemplo:

test(c, o) =>
    ret = c > o ?
       (c > o+5000 ? 
          1 :
              0):
       (c < o-5000 ? 
          -1 : 
              0)
           
                   
a = test(close, open)
plot(a, title="a")

Marcadores y operadores

Marcadores

Antes de reconocer las variables, primero debemos entender el concepto de marcadores.FunciónyVariable(usado para nombrar variables y funciones).FuncionesSe verá en nuestros tutoriales posteriores, vamos a aprender acerca de los marcadores primero.

    1. Los marcadores deben comenzar con mayúsculas.(A-Z)o en minúsculas(a-z)letra o subrayado(_)como el primer carácter del marcador.
    1. El siguiente carácter después del primer carácter de un marcador puede ser uncarta, subrayado, o unaNúmero.
    1. El nombre de los marcadores es sensible a mayúsculas y minúsculas.

Por ejemplo, los siguientes marcadores denominados:

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

Al igual que la mayoría de los lenguajes de programación, el lenguaje Pine también tiene sugerencias de escritura.

    1. Todas las letras mayúsculas se usan para nombrar constantes.
    1. Utilice elEn el caso del camello inferiorpara otros nombres de marcadores.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7

// name functions
zeroOne(boolValue) => boolValue ? 1 : 0

Operadores

Los operadores son algunos símbolos de operación utilizados en lenguajes de programación para construir expresiones, y las expresiones son reglas computacionales diseñadas para ciertos propósitos computacionales cuando escribimos estrategias.

Operadores de asignación, operadores aritméticos, operadores de comparación, operadores lógicos,? :los operadores ternarios,[]los operadores de referencia históricos.

Tomando el operador aritmético*como ejemplo, es diferente del problema de tipo causado por el resultado de retorno del operador de lenguaje Pine en 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)

Cuando se ejecuta este script en Trading View, se producirá un error de compilación.adjustedLength = lenInput * factor, el resultado esseries inttipo (serie), pero el segundo parámetro de la funciónta.emaPero no hay restricciones tan estrictas en FMZ, el código anterior puede ejecutarse normalmente.

Echemos un vistazo al uso de varios operadores juntos.

Operadores de asignación

Hay 2 tipos de operadores de asignación:=, :=, que hemos visto en varios ejemplos en la parte inicial del tutorial.

El=El operador se utiliza para asignar un valor a una variable cuando se inicializa o se declara.=se iniciará con ese valor en cada barra siguiente. Estas son declaraciones de variables válidas:

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)

Tenga en cuenta que la declaración de asignacióna = close, la variable a de cada barra es el precio de cierre (cierre) actual de la barra. Otras variablesb, c, dse mantienen sin cambios y pueden probarse en el sistema de pruebas de retroceso en FMZ, y los resultados se pueden ver en el gráfico.

:=Se utiliza para reasignar valores a las variables existentes.:=El operador se utiliza para modificar los valores de las variables que han sido declaradas e inicializadas. Si usamos el:=operador para asignar un valor a una variable no inicializada o declarada, causará un error, por ejemplo:

a := 0

Por lo tanto, el:=el operador de asignación se utiliza generalmente para reasignar variables existentes, por ejemplo:

a = close > open 
b = 0 
if a
    b := b + 1

plot(b)

Juzgando siclose > open(es decir, el BAR actual es una recta positiva), la variable a es verdad.b := b + 1se ejecuta, y el operador de asignación:=Entonces usamos la función gráfica para dibujar el valor de la variable b en cada BAR de la serie de tiempo en el gráfico, y conectarlos en una línea.

¿Creemos que cuando aparece una línea positiva BAR, b continuará acumulándose por 1? por supuesto que no, aquí declaramos e iniciamos la variable b como 0 sin usar ninguna designación de palabra clave.b=0se ejecuta en cada BAR, por lo que podemos ver que el resultado de este código es restablecer la variable b a 0 cada vez, si la variable a es verdad, es decir, en línea conclose > open, entonces b se incrementará en 1 cuando el código se ejecuta en esta ronda, y b es 1 cuando la función de gráfico dibuja, pero b se reasigna a 0 cuando el código se ejecuta en la siguiente ronda.

Cuando se trata de operadores de asignación, debemos ampliar en dos palabras clave:var, varip

  • el var

    De hecho, hemos visto y utilizado esta palabra clave en tutoriales anteriores, pero no la discutimos en detalle en ese momento.

    var es una palabra clave utilizada para asignar y inicializar variables de una sola vez. En general, la gramática de asignación de variables que no contiene la palabra clave var hace que el valor de la variable se sobrescriba cada vez que se actualizan los datos.

    Todavía usamos este ejemplo, pero usamos elvarpalabra clave al asignar un valor a b aquí.

    a = close > open 
    var b = 0 
    if a
        b := b + 1
    
    plot(b)
    

    Elvarpalabra clave permite que la variable b para realizar la asignación inicial sólo, y luego no se restablecerá b a 0 cada vez que se ejecuta la lógica de la estrategia, por lo que se puede observar a partir de la línea dibujada en tiempo de ejecución que b es el número de barras de la línea positiva que han aparecido cuando la línea actual K BAR fue backtested.

    Las variables declaradas por var pueden escribirse no solo en el alcance global, sino también en bloques de código, como este ejemplo:

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

    La variable a contiene el precio de cierre de la primera barra de la serie. La variable b contiene el precio de cierre de la primera barra de precios verde de la serie. La variable c contiene el precio de cierre de la décima barra verde de la serie.

  • variedad

    Vemos la palabra clave.varipPor primera vez, podemos ver la descripción de esta palabra clave:

    varp (var intrabar persist) es una palabra clave para asignar e iniciar variables de una sola vez. Es similar a la palabra clave var, pero las variables declaradas con varp conservan sus valores cuando se actualizan en línea K en tiempo real.

    ¿Es difícil de entender? No importa, lo explicamos a través de un ejemplo, es fácil de entender.

    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
    

    Este código de prueba tiene diferentes prestaciones en el Modelo de barra y el Modelo de puntuación:

    Modelo de barra: ¿Recuerdan que la ejecución de la estrategia que explicamos anteriormente se divide en etapa histórica BAR y etapa BAR en tiempo real?i, iideclarado envar, varipLos números que se muestran en el K-line BAR del resultado de la prueba de retroceso se incrementan uno por uno. Cuando termina la etapa histórica de la línea K, comienza la etapa de la línea K en tiempo real. Las variables declaradas por var y varip comienzan a sufrir cambios diferentes.i := i + 1yii := ii + 1La diferencia es que ii se modifica cada vez. Aunque i se modifica cada vez, el valor anterior se restablecerá cuando se ejecute la lógica de estrategia en la siguiente ronda (recuerde el mecanismo de retroceso que explicamos en el capítulo anterior Execución del modelo?), y el valor de i no se actualizará hasta que se complete el BAR de la línea K actual (es decir, el valor anterior no se restablecerá cuando se ejecute la lógica de estrategia en la siguiente ronda). Por lo tanto, se puede ver que la variable i sigue aumentando en 1 para cada BAR. Pero la variable ii se acumula varias veces para cada BAR.

    Modelo de marca: Dado que el modelo Tick ejecuta la lógica de estrategia solo una vez por K-line BAR. Así que en el modelo de precio de cierre, las variables declaradas por var y varip se comportan exactamente igual en el ejemplo anterior incrementando en 1 para cada K-line BAR durante la etapa histórica de la línea K y la etapa de la línea K en tiempo real.

Operadores aritméticos
Operadores Descripción
+ Adición
- Sustracción
* Multiplicación
/ División
% Modulo

El+y-Otros operadores aritméticos sólo se pueden utilizar como operadores binarios y se informará de un error si se utilizó como operadores unarios.

  1. Ambos lados del operador aritmético son de tipo numérico, el resultado es de tipo numérico, entero o coma flotante dependiendo del resultado de la operación.
  2. Si uno de los operandos es una cadena y el operador es+, el resultado del cálculo es una cadena, el valor se convertirá a la forma de cadena, y luego las cadenas se unen. Si es otro operador aritmético, tratará de convertir la cadena a un valor y luego llevar a cabo la operación.
  3. Si uno de los operandos es na, el resultado del cálculo es el valor nulona, y mostrará NaN cuando se imprima en 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

El lenguaje Pine en FMZ es un poco diferente del lenguaje Pine en Trading View, el lenguaje Pine en FMZ no es muy estricto sobre los tipos de variables.

a = 1 * "1.1"
b = "1" / "1.1"
c = 5 % "A" 

plot(a)
plot(b)
plot(c)

Funciona en FMZ, pero reporta un error de tipo en la Vista de Comercio. Si ambos operandos del operador aritmético son cadenas, el sistema convierte las cadenas en valores numéricos y luego los calcula. Si una cadena no numérica no se puede calcular, el resultado de la operación del sistema es un valor nulona.

Operadores de comparación

Los operadores de comparación son todos operadores binarios.

Operadores Descripción
< <
> >
<= <=
>= >=
== ==
!= !=

Ejemplo de ensayo:

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

Como podemos ver, el operador de comparación es muy simple de usar, pero también es el operador que más usamos al escribir estrategias.close, open, etc. Al igual que con el operador, hay una diferencia sobre el lenguaje Pine entre FMZ y Trading View.d = "1" >= 2no reportará un error en FMZ, y se ejecutará convirtiendo la cadena a un valor primero y luego comparando la operación. En Trading View, reportará un error.

Operadores lógicos
Operadores Símbolos de código Descripción
No lo es No lo es Operador unario, no operaciones
y y Operadores binarios y operaciones
o bien o bien Operadores o operaciones binarias

Cuando se trata de operadores lógicos, entonces debemos hablar de tablas de valores verdaderos. Lo mismo que aprendimos en la escuela secundaria, aquí sólo probamos y aprendemos en nuestro sistema de backtesting:

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

Con el fin de no sobreimprimir mensajes, lanzamos un error con elruntime.error("stop")Después de eso, podemos observar la información de salida, y podemos encontrar que el contenido impreso es en realidad el mismo que la tabla de valores reales.

Operador ternario

Expresiones ternarias con el operador ternario? :combinado con operandoscondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalseTambién los hemos usado en las lecciones anteriores. la llamada expresión ternaria, operador ternario significa que hay tres operandos en él.

En elcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse, conditionSi es verdadero, el valor de la expresión es:valueWhenConditionIsTrue- Si es así.conditiones falso, entonces el valor de la expresión esvalueWhenConditionIsFalse.

Ejemplo de una demostración conveniente, aunque de poca utilidad práctica:

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)

¿Qué hacer si nos encontramos con un doji? ¡No importa! Expresiones ternales también se pueden anidar, como lo hicimos en el tutorial anterior.

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)

De hecho, es equivalente a reemplazar a lavalueWhenConditionIsTrueyvalueWhenConditionIsFalseEn elcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalsecon otra expresión ternaria.

Operador histórico

Utilice el operador histórico[]para referirse a valores históricos en una serie de tiempo. Estos valores históricos son los valores de la variable en la barra de línea K antes de la barra de línea K actual cuando el script se estaba ejecutando.[]El operador se utiliza después de las variables, expresiones y llamadas de funciones.[]Los paréntesis cuadrados es el desplazamiento de los datos históricos que queremos hacer referencia de la actual K-línea BAR. Por ejemplo, si quiero citar el precio de cierre de la última K-línea BAR, lo escribimos como:close[1].

Hemos visto algo así en las lecciones anteriores:

high[10]
ta.sma(close, 10)[1]
ta.highest(high, 10)[20]
close > nz(close[1], open)

El[]El operador sólo se puede utilizar una vez en el mismo valor, por lo que es incorrecto escribirlo así, y se reportará un error:

a = close[1][2]   // error

Aquí, alguien puede decir que el operador[]Se utiliza para la estructura de serie, parece que la estructura de serie (serie) es similar a la matriz! Usemos un ejemplo para ilustrar la diferencia entre series y matrices en el lenguaje 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]informará de un error, pero:

b = close[1]
c = b[1]

Pero si se escribe por separado, no reportará un error. Si lo entendemos según la matriz habitual, después de la asignación deb = close [1], b debería ser un valor, peroc = b[1]Se puede ver que el concepto de serie en el lenguaje Pine no es tan simple como una matriz. Se puede entender como el valor histórico en la última barra de cerrar (asignado a b), b también es una estructura de serie temporal (serie temporal), y su h


Más.