Entonces vemos que en las tres líneas dibujadas a, b y c, la línea b es un BAR más lenta que la línea a, y la línea c es un BAR más lenta que la línea b. La línea c es 2 BAR más lenta que la línea a.
Podemos tirar del gráfico a la izquierda y observar que en la primera línea K, ambos valores de b y c son nulos (na). Esto se debe a que cuando el script se ejecuta en la primera línea K BAR, no existe cuando se hace referencia al valor histórico de uno o dos períodos adelante, que no existe. Por lo tanto, debemos tener cuidado al escribir estrategias para verificar si hacer referencia a los datos históricos dará como resultado valores nulos. Si se usa el valor nulo con descuido, causará una serie de diferencias de cálculo, e incluso puede afectar el BAR en tiempo real. Por lo general, usaremos funciones integradasna
, nz
En la actualidad, la mayoría de los países de la Unión Europea han adoptado el código de conducta.nz
, ```na`` en nuestros videos anteriores, ¿recuerdas qué capítulo es?) tratar el caso de los valores nulos, por ejemplo:
close > nz(close[1], open) // When referencing the historical value of the previous BAR of the close built-in variable, if it does not exist, the open built-in variable is used
Esta es una forma de manejar posibles referencias a valores nulos (na).
Hemos aprendido muchos operadores en el lenguaje de Pine. Estos operadores forman expresiones a través de varias combinaciones con operandos. Entonces, ¿cuál es la prioridad de estas operaciones al evaluar en expresiones? Al igual que la aritmética que aprendimos en la escuela, la multiplicación y la división se calculan primero, seguida de suma y resta. Lo mismo ocurre con las expresiones en el lenguaje de Pine.
Prioridad | Operadores |
---|---|
9 | [] |
8 | + 、- ynot en el operador unario |
7 | * 、/ 、% |
6 | + 、- en el operador binario |
5 | > 、< 、>= 、<= |
4 | == 、!= |
3 | and |
2 | or |
1 | ?: |
Las expresiones de alta prioridad se calculan primero, y si las prioridades son las mismas, se evalúa de izquierda a derecha.()
para envolver la expresión para obligar a la parte a ser evaluada primero.
Ya hemos estudiado el concepto de
Modo de declaración:
La primera cosa que se escribe al declarar una variable es el
var
.varip
.Elvar
yvarip
Las palabras clave han sido estudiadas en nuestro capítulo anterior sobreAssignment Operators
Si no se escribe nada para el modo de declaración de variables, como la instrucción:i = 1
, como también hemos mencionado antes, tal variable declarada y asignada se ejecuta en cada K-línea BAR.
Tipo de producto El lenguaje Pine en FMZ no es estricto con respecto a los tipos, y generalmente se puede omitir. Sin embargo, para ser compatible con la estrategia de scripting en Trading View, las variables también se pueden declarar con tipos.
int i = 0
float f = 1.1
Los requisitos de tipo en Trading View son bastante estrictos y se notificará un error si se utiliza el siguiente código en Trading View:
baseLine0 = na // compile time error!
Marcador Los marcadores son nombres de variables. El nombre de los marcadores se ha mencionado en capítulos anteriores, por lo que puede revisarlo aquí:https://www.fmz.com/bbs-topic/9637#markers
En resumen, declarar una variable puede escribirse como:
// [<declaration_mode>] [<type>] <marker> = value
declaration mode type marker = value
El operador de asignación se utiliza aquí:=
asigna un valor a una variable cuando se declara. Cuando se asigna el valor puede ser una cadena, número, expresión, llamada de función,if
, for
, while
, oswitch
y otras estructuras (estas palabras clave estructurales y el uso de declaraciones se explicarán en detalle en cursos posteriores.
Aquí nos centramos en la función de entrada, que es una función que vamos a utilizar con frecuencia al diseñar y escribir estrategias.
Función de entrada:
input function, parameters: defval、title、tooltip、inline、group
La función de entrada en FMZ es algo diferente a la de Trading View, pero esta función se utiliza como la entrada de asignación de parámetros de estrategia.
param1 = input(10, title="name of param1", tooltip="description for param1", group="group name A")
param2 = input("close", title="name of param2", tooltip="description for param2", group="group name A")
param3 = input(color.red, title="name of param3", tooltip="description for param3", group="group name B")
param4 = input(close, title="name of param4", tooltip="description for param4", group="group name B")
param5 = input(true, title="name of param5", tooltip="description for param5", group="group name C")
ma = ta.ema(param4, param1)
plot(ma, title=param2, color=param3, overlay=param5)
La función de entrada se utiliza a menudo para asignar valores a las variables al declararlas. La función de entrada en FMZ dibuja controles para establecer parámetros de estrategia automáticamente en la interfaz de estrategia de FMZ. Los controles compatibles en FMZ actualmente incluyen cajas de entrada numéricas, cajas de entrada de texto, cajas desplegables y casillas de verificación booleanas. Y puede establecer la agrupación de parámetros de estrategia, establecer el mensaje de texto de solicitud de parámetros y otras funciones.
Introducimos varios parámetros principales de la función de entrada:
Además de la declaración y asignación de variables individuales, también hay una forma de declarar un grupo de variables y asignarlas en el lenguaje Pine:
[Variable A, Variable B, Variable C] = function or structure, such as ```if```, ```for```, ```while``` or ```switch```
El más común es cuando usamos elta.macd
la función para calcular el indicador MACD, ya que el indicador MACD es un indicador de varias líneas, se calculan tres conjuntos de datos.
[dif,dea,column] = ta.macd(close, 12, 26, 9)
plot(dif, title="dif")
plot(dea, title="dea")
plot(column, title="column", style=plot.style_histogram)
Podemos dibujar el gráfico MACD usando el código anterior fácilmente. No sólo las funciones integradas pueden regresar a múltiples variables, sino también las funciones personalizadas escritas pueden regresar a múltiples datos.
twoEMA(data, fastPeriod, slowPeriod) =>
fast = ta.ema(data, fastPeriod)
slow = ta.ema(data, slowPeriod)
[fast, slow]
[ema10, ema20] = twoEMA(close, 10, 20)
plot(ema10, title="ema10", overlay=true)
plot(ema20, title="ema20", overlay=true)
El método de escritura de usar si y otras estructuras como asignaciones de variables múltiples también es similar a la función personalizada anterior, y puede probarlo si está interesado.
[ema10, ema20] = if true
fast = ta.ema(close, 10)
slow = ta.ema(close, 20)
[fast, slow]
plot(ema10, title="ema10", color=color.fuchsia, overlay=true)
plot(ema20, title="ema20", color=color.aqua, overlay=true)
Algunas funciones no se pueden escribir en el bloque de código local de la rama condicional, incluidas principalmente las siguientes funciones:
barcolor (), llenar (), línea (), indicador (), gráfico (), vela de gráfico (), gráfico (), gráfico (), gráfico (), gráfico ())
Trading View compilará con errores, FMZ no es tan restrictivo, pero se recomienda seguir las especificaciones de Trading View.
strategy("test", overlay=true)
if close > open
plot(close, title="close")
else
plot(open, title="open")
Ejemplo de explicación:
var lineColor = na
n = if bar_index > 10 and bar_index <= 20
lineColor := color.green
else if bar_index > 20 and bar_index <= 30
lineColor := color.blue
else if bar_index > 30 and bar_index <= 40
lineColor := color.orange
else if bar_index > 40
lineColor := color.black
else
lineColor := color.red
plot(close, title="close", color=n, linewidth=5, overlay=true)
plotchar(true, title="bar_index", char=str.tostring(bar_index), location=location.abovebar, color=color.red, overlay=true)
Punto clave: Expresiones utilizadas para juicios que devuelven valores booleanos. Nótese la hendidura. Puede haber como máximo otra rama. Si todas las expresiones de rama no son ciertas y no hay otra rama, devuelva na.
x = if close > open
close
plot(x, title="x")
La instrucción de switch es también una instrucción estructurada por ramas, que se utiliza para diseñar diferentes rutas que se ejecutarán de acuerdo con ciertas condiciones.
Hay dos formas de cambio, vamos a ver los ejemplos uno por uno para entender su uso.
switch
con expresiones - ejemplo de explicación:// input.string: defval, title, options, tooltip
func = input.string("EMA", title="indicator name", tooltip="select the name of the indicator function to be used", options=["EMA", "SMA", "RMA", "WMA"])
// input.int: defval, title, options, tooltip
// param1 = input.int(10, title="period parameter")
fastPeriod = input.int(10, title="fastPeriod parameter", options=[5, 10, 20])
slowPeriod = input.int(20, title="slowPeriod parameter", options=[20, 25, 30])
data = input(close, title="data", tooltip="select the closing price, opening price, highest price...")
fastColor = color.red
slowColor = color.red
[fast, slow] = switch func
"EMA" =>
fastLine = ta.ema(data, fastPeriod)
slowLine = ta.ema(data, slowPeriod)
fastColor := color.red
slowColor := color.red
[fastLine, slowLine]
"SMA" =>
fastLine = ta.sma(data, fastPeriod)
slowLine = ta.sma(data, slowPeriod)
fastColor := color.green
slowColor := color.green
[fastLine, slowLine]
"RMA" =>
fastLine = ta.rma(data, fastPeriod)
slowLine = ta.rma(data, slowPeriod)
fastColor := color.blue
slowColor := color.blue
[fastLine, slowLine]
=>
runtime.error("error")
plot(fast, title="fast" + fastPeriod, color=fastColor, overlay=true)
plot(slow, title="slow" + slowPeriod, color=slowColor, overlay=true)
Aprendimos la función de entrada antes, aquí continuamos aprendiendo dos funciones similares a la entrada:input.string
, input.int
functions.
input.string
se utiliza para devolver una cadena, y elinput.int
En el ejemplo, hay un nuevo uso de laoptions
el parámetro.options
Parámetro puede ser pasado una serie de valores opcionales, tales comooptions=["EMA", "SMA", "RMA", "WMA"]
yoptions=[5, 10, 20]
De esta manera, los controles en la interfaz de estrategia no necesitan ingresar valores específicos, pero los controles se convierten en cuadros desplegables para seleccionar estas opciones proporcionadas en el parámetro de opciones.
El valor de la variable func es una cadena, y la variable func se utiliza como la expresión para el switch (que puede ser una variable, llamada de función o expresión) para determinar qué rama en el switch se ejecuta.runtime.error("error")
la función se ejecutará, haciendo que la estrategia lance una excepción y se detenga.
En nuestro código de prueba anterior, después de la última línea de runtime.error en el bloque de código de rama predeterminado de switch, no agregamos código como [na, na] para ser compatible con el valor de retorno. Este problema debe considerarse en Trading View. Si el tipo es inconsistente, se reportará un error. Pero en FMZ, ya que el tipo no es estrictamente requerido, este código de compatibilidad puede omitirse. Por lo tanto, no hay necesidad de considerar la compatibilidad de tipo del valor de retorno de if y switch branches en FMZ.
strategy("test", overlay=true)
x = if close > open
close
else
"open"
plotchar(true, title="x", char=str.tostring(x), location=location.abovebar, color=color.red)
No se reportará ningún error en FMZ, pero se reportará un error en la vista de negociación porque el tipo devuelto por la rama if es inconsistente.
switch
sin expresionesA continuación, vamos a ver otra manera de utilizarswitch
, es decir, sin expresiones.
up = close > open // up = close < open
down = close < open
var upOfCount = 0
var downOfCount = 0
msgColor = switch
up =>
upOfCount += 1
color.green
down =>
downOfCount += 1
color.red
plotchar(up, title="up", char=str.tostring(upOfCount), location=location.abovebar, color=msgColor, overlay=true)
plotchar(down, title="down", char=str.tostring(downOfCount), location=location.belowbar, color=msgColor, overlay=true)
Como podemos ver en el ejemplo de código de prueba, el switch coincidirá con la ejecución del bloque de código local que es verdad en la condición de rama. En general, las condiciones de rama después de una instrucción de switch deben ser mutuamente exclusivas. Es decir, arriba y abajo en el ejemplo no pueden ser verdaderos al mismo tiempo. Debido a que el switch solo puede ejecutar el bloque de código local de una rama, si está interesado, puede reemplazar esta línea en el código:up = close > open // up = close < open
En el caso de la aplicación de la función de comutación, la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación de la función de comutación.switch
con expresiones", la rama de ejecución es determinista y no se cambiará durante la operación de la estrategia).
return value = for count = start count to final count by step length
statement // Note: There can be break and continue in the statement
statement // Note: The last statement is the return value
La instrucción for es muy sencilla de usar, el bucle for puede finalmente devolver un valor (o múltiples valores, en la forma de [a, b, c]). Al igual que la variable asignada a la posición
Elbreak
Palabra clave utilizada en el bucle for: el bucle se detiene cuando elbreak
la declaración se ejecuta.
Elcontinue
Palabra clave utilizada en el bucle for: Cuando elcontinue
la instrucción se ejecuta, el bucle ignorará el código despuéscontinue
y ejecuta la siguiente ronda del bucle directamente. la instrucción for devuelve el valor de retorno de la última ejecución del bucle. y devuelve null si no se ejecuta código.
Luego demostramos con un ejemplo simple:
ret = for i = 0 to 10 // We can increase the keyword by to modify the step length, FMZ does not support reverse loops such as i = 10 to 0 for now
// We can add condition settings, use continue to skip, use break to jump out
runtime.log("i:", i)
i // If this line is not written, it will return null because there is no variable to return
runtime.log("ret:", ret)
runtime.error("stop")
Elfor ... in
La declaración tiene dos formas, las ilustraremos en el siguiente pseudocodo.
return value = for array element in array
statement // Note: There can be break and continue in the statement
statement // Note: The last statement is the return value
Return value = for [index variable, array element corresponding to index variable] in array
statement // Note: There can be break and continue in the statement
statement // Note: The last statement is the return value
Podemos ver que la principal diferencia entre las dos formas es el contenido que sigue a la palabra clave for, una es usar una variable como una variable que se refiere a los elementos de la matriz, la otra es usar una estructura que contiene variables de índice, tuplas de variables de elementos de matriz como referencias. Para otras reglas de valor de retorno, como el uso de break, continue, etc., son consistentes con para bucles. También ilustramos el uso con un ejemplo simple.
testArray = array.from(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
for ele in testArray // Modify it to the form of [i, ele]: for [i, ele] in testArray, runtime.log("ele:", ele, ", i:", i)
runtime.log("ele:", ele)
runtime.error("stop")
Cuando necesite usar el índice, use la gramáticafor [i, ele] in testArray
.
Aplicación de los bucles
Podemos utilizar las funciones incorporadas proporcionadas en el lenguaje Pine para completar algunos de los cálculos lógicos del bucle, ya sea escritos utilizando la estructura del bucle directamente o procesados utilizando las funciones incorporadas.
Cuando se diseñe con una estructura de bucle:
length = 5
var a = array.new(length)
array.push(a, close)
if array.size(a) >= length
array.remove(a, 0)
sum = 0
for ele in a
sum += ele
avg = sum / length
plot(avg, title="avg", overlay=true)
El ejemplo utiliza un bucle for para calcular la suma y luego calcular el valor medio.
Calcular la media móvil directamente utilizando la función integrada:
plot(ta.sma(close, length), title="ta.sma", overlay=true)
Utilice la función integradata.sma
El cálculo de la media móvil se realiza directamente mediante el cálculo del indicador de la media móvil. Obviamente, es más simple utilizar la función incorporada para calcular la media móvil.
Todavía usamos el ejemplo anterior para ilustrar.
Cuando se diseñe con una estructura de bucle:
length = 5
var a = array.new(length)
array.push(a, close)
if array.size(a) >= length
array.remove(a, 0)
sum = 0
for ele in a
sum += ele
avg = sum / length
plot(avg, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
Para calcular la suma de todos los elementos de una matriz, podemos usar un bucle para procesarlo, o usar la función incorporadaarray.sum
para calcular.
Calcular la suma directamente utilizando la función integrada:
length = 5
var a = array.new(length)
array.push(a, close)
if array.size(a) >= length
array.remove(a, 0)
plot(array.sum(a) / length, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
Podemos ver que los datos calculados es exactamente el mismo que se muestra en el gráfico utilizando gráfico.
Entonces, ¿por qué diseñar bucles cuando podemos hacer todo esto con funciones incorporadas?
Elwhile
la instrucción mantiene el código en la sección del bucle ejecutándose hasta que la condición de juicio en la estructura while sea falsa.
return value = while judgment condition
statement // Note: There can be break and continue in the statement
statement // Note: The last statement is the return value
Otras reglas de while son similares a las del bucle for. La última línea del bloque de código local del cuerpo del bucle es el valor de retorno, que puede devolver múltiples valores. Ejecuta el bucle cuando la condición
Seguiremos utilizando el ejemplo de cálculo de medias móviles para la demostración:
length = 10
sma(data, length) =>
i = 0
sum = 0
while i < 10
sum += data[i]
i += 1
sum / length
plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
Podemos ver que el bucle mientras también es muy simple de usar, y también es posible diseñar alguna lógica de cálculo que no puede ser reemplazado por las funciones incorporadas, tales como el cálculo factorial:
counter = 5
fact = 1
ret = while counter > 0
fact := fact * counter
counter := counter - 1
fact
plot(ret, title="ret") // ret = 5 * 4 * 3 * 2 * 1
La definición de matrices en el lenguaje Pine es similar a la de otros lenguajes de programación. Las matrices Pine[]
para referirse a un elemento en la matriz, necesitamos utilizar las funcionesarray.get()
yarray.set()
El orden de índice de los elementos en la matriz es que el índice del primer elemento de la matriz es 0, y el índice del siguiente elemento se incrementa en 1.
Lo ilustramos con un código simple:
var a = array.from(0)
if bar_index == 0
runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1])
else if bar_index == 1
array.push(a, bar_index)
runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1])
else if bar_index == 2
array.push(a, bar_index)
runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1], ", a on the last second BAR, i.e. the value of a[2]:", a[2])
else if bar_index == 3
array.push(a, bar_index)
runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1], ", a on the last second BAR, i.e. the value of a[2]:", a[2], ", a on the last third BAR, i.e. the value of a[3]:", a[3])
else if bar_index == 4
// Obtain elements by index using array.get, modify elements by index using array.set
runtime.log("Before array modification:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))
array.set(a, 1, 999)
runtime.log("After array modification:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))
Utilizaciónarray<int> a
, float[] b
para declarar una matriz o simplemente declarar una variable que se puede asignar a una matriz, por ejemplo:
array<int> a = array.new(3, bar_index)
float[] b = array.new(3, close)
c = array.from("hello", "fmz", "!")
runtime.log("a:", a)
runtime.log("b:", b)
runtime.log("c:", c)
runtime.error("stop")
Las variables de matriz se inicializan utilizando elarray.new
yarray.from
También hay muchas funciones relacionadas con tipos similares aarray.newen la lengua de los pinos:array.new_int()
, array.new_bool()
, array.new_color()
, array.new_string()
, etc.
La palabra clave var también funciona con el modo de declaración de matriz. Las matrices declaradas con la palabra clave var se inicializan solo en la primera BAR.
var a = array.from(0)
b = array.from(0)
if bar_index == 1
array.push(a, bar_index)
array.push(b, bar_index)
else if bar_index == 2
array.push(a, bar_index)
array.push(b, bar_index)
else if barstate.islast
runtime.log("a:", a)
runtime.log("b:", b)
runtime.error("stop")
Se puede ver que los cambios de la matriz a se han determinado continuamente y no se han restablecido. La matriz b se inicializa en cada BAR.barstate.islast
es cierto, todavía hay sólo un elemento impreso con un valor de 0.
Use array.get para obtener el elemento en la posición de índice especificada en la matriz, y use array.set para modificar el elemento en la posición de índice especificada en la matriz.
El primer parámetro de array.get es la matriz a procesar, y el segundo parámetro es el índice especificado. El primer parámetro de array.set es la matriz a procesar, el segundo parámetro es el índice especificado, y el tercer parámetro es el elemento a escribir.
Usamos el siguiente ejemplo simple para ilustrarlo:
lookbackInput = input.int(100)
FILL_COLOR = color.green
var fillColors = array.new(5)
if barstate.isfirst
array.set(fillColors, 0, color.new(FILL_COLOR, 70))
array.set(fillColors, 1, color.new(FILL_COLOR, 75))
array.set(fillColors, 2, color.new(FILL_COLOR, 80))
array.set(fillColors, 3, color.new(FILL_COLOR, 85))
array.set(fillColors, 4, color.new(FILL_COLOR, 90))
lastHiBar = - ta.highestbars(high, lookbackInput)
fillNo = math.min(lastHiBar / (lookbackInput / 5), 4)
bgcolor(array.get(fillColors, int(fillNo)), overlay=true)
plot(lastHiBar, title="lastHiBar")
plot(fillNo, title="fillNo")
El ejemplo inicializa el color base verde, declara e inicializa una matriz para almacenar colores, y luego asigna diferentes valores de transparencia a los colores (usando elcolor.newEl nivel de color se calcula calculando la distancia del BAR actual desde el valor máximo de alto en 100 períodos de vista. Cuanto más cerca esté la distancia del valor máximo de alto en los últimos 100 ciclos de vista, mayor será el rango y más oscuro (menor transparencia) el valor de color correspondiente.
Cómo iterar a través de una matriz, podemos usar las instrucciones for/for in/while que hemos aprendido antes.
a = array.from(1, 2, 3, 4, 5, 6)
for i = 0 to (array.size(a) == 0 ? na : array.size(a) - 1)
array.set(a, i, i)
runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)
i = 0
while i < array.size(a)
array.set(a, i, i)
i += 1
runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)
for [i, ele] in a
array.set(a, i, i)
runtime.log(a)
runtime.error("stop")
Estos tres métodos de travesía tienen los mismos resultados de ejecución.
Las matrices se pueden declarar en el ámbito global de un script, o en el ámbito local de una función o si rama.
Para el uso de elementos en matrices, las siguientes formas son equivalentes. Podemos ver por el siguiente ejemplo que dos conjuntos de líneas se dibujan en el gráfico, dos en cada conjunto, y las dos líneas en cada conjunto tienen exactamente el mismo valor.
a = array.new_float(1)
array.set(a, 0, close)
closeA1 = array.get(a, 0)[1]
closeB1 = close[1]
plot(closeA1, "closeA1", color.red, 6)
plot(closeB1, "closeB1", color.black, 2)
ma1 = ta.sma(array.get(a, 0), 20)
ma2 = ta.sma(close, 20)
plot(ma1, "ma1", color.aqua, 6)
plot(ma2, "ma2", color.black, 2)
array.unshift()
, array.insert()
, array.push()
.
array.remove()
, array.shift()
, array.pop()
, array.clear()
.
Usamos el siguiente ejemplo para probar estas funciones de operación de adición y eliminación de matriz.
a = array.from("A", "B", "C")
ret = array.unshift(a, "X")
runtime.log("array a:", a, ", ret:", ret)
ret := array.insert(a, 1, "Y")
runtime.log("array a:", a, ", ret:", ret)
ret := array.push(a, "D")
runtime.log("array a:", a, ", ret:", ret)
ret := array.remove(a, 2)
runtime.log("array a:", a, ", ret:", ret)
ret := array.shift(a)
runtime.log("array a:", a, ", ret:", ret)
ret := array.pop(a)
runtime.log("array a:", a, ", ret:", ret)
ret := array.clear(a)
runtime.log("array a:", a, ", ret:", ret)
runtime.error("stop")
Aplicación de adiciones, eliminaciones: matrices como colas
Podemos construir una estructura de datos
Una cola es una estructura que se utiliza a menudo en el campo de la programación, las características de una cola son:
El elemento que entra en la cola primero, sale de la cola primero.
De esta manera, se asegura de que los datos en la cola son los datos más recientes, y que la longitud de la cola no se expandirá indefinidamente.
En el siguiente ejemplo, utilizamos una estructura de cola para registrar el precio de cada tick, calcular el precio promedio móvil en el nivel de tick, y luego compararlo con el promedio móvil en el nivel de la línea K de 1 minuto.
strategy("test", overlay=true)
varip a = array.new_float(0)
var length = 10
if not barstate.ishistory
array.push(a, close)
if array.size(a) > length
array.shift(a)
sum = 0.0
for [index, ele] in a
sum += ele
avgPrice = array.size(a) == length ? sum / length : na
plot(avgPrice, title="avgPrice")
plot(ta.sma(close, length), title="ta.sma")
Tenga en cuenta que al declarar la matriz a, especificamos el modo de declaración y utilizar la palabra clavevariant
De esta manera, cada cambio de precio se registrará en la matriz a.
Calcular las funciones de correlación:
array.avg()
Calcula el valor medio de todos los elementos de una matriz,array.min()
Calcula el elemento más pequeño de una matriz,array.max()
Calcula el elemento más pequeño de una matriz,array.stdev()
Calcula la desviación estándar de todos los elementos de una matriz,array.sum()
Calcula la desviación estándar de todos los elementos de una matriz.
Funciones relacionadas con el funcionamiento:array.concat()
para fusionar o concatenar dos matrices.array.copy()
para copiar la matriz.array.join
para concatenar todos los elementos de una matriz en una cadena.array.sort()
para ordenar por orden ascendente o descendente.array.reverse()
para invertir la matriz.array.slice()
para cortar la matriz.array.includes()
para juzgar el elemento.array.indexof()
Si no se encuentra el valor, se devolverá -1.array.lastindexof()
para encontrar la última aparición del valor.
Ejemplos de pruebas de funciones relacionadas con el cálculo de matrices:
a = array.from(3, 2, 1, 4, 5, 6, 7, 8, 9)
runtime.log("Arithmetic average of the array a:", array.avg(a))
runtime.log("The minimum element in the array a:", array.min(a))
runtime.log("The maximum element in the array a:", array.max(a))
runtime.log("Standard deviation in array a:", array.stdev(a))
runtime.log("Sum of all elements of the array a:", array.sum(a))
runtime.error("stop")
Estas son funciones de cálculo de matriz comúnmente utilizadas.
Ejemplos de funciones relacionadas con el funcionamiento:
a = array.from(1, 2, 3, 4, 5, 6)
b = array.from(11, 2, 13, 4, 15, 6)
runtime.log("array a: ", a, ", array b: ", b)
runtime.log("array a, array b is concatenated with:", array.concat(a, b))
c = array.copy(b)
runtime.log("Copy an array b and assign it to the variable c, variable c:", c)
runtime.log("use array.join to process the array c, add the symbol + to the middle of each element, concatenating all elements results in a string:", array.join(c, "+"))
runtime.log("Sort the array b, in order from smallest to largest, using the parameter order.ascending:", array.sort(b, order.ascending)) // array.sort function modifies the original array
runtime.log("Sort the array b, in order from largest to smallest, using the parameter order.descending:", array.sort(b, order.descending)) // array.sort function modifies the original array
runtime.log("array a:", a, ", array b:", b)
array.reverse(a) // This function modifies the original array
runtime.log("reverse the order of all elements in the array a, after reversing, the array a is:", a)
runtime.log("Intercept array a, index 0~index 3, and follow the rule of left-closed and right-open interval:", array.slice(a, 0, 3))
runtime.log("Search for element 11 in array b:", array.includes(b, 11))
runtime.log("Search for element 100 in array a:", array.includes(a, 100))
runtime.log("Connect array a and array b, and search the index position of the first occurrence of element 2:", array.indexof(array.concat(a, b), 2), " , observe array.concat(a, b):", array.concat(a, b))
runtime.log("Connect array a and array b, and search the index position of the last occurrence of element 6:", array.lastindexof(array.concat(a, b), 6), " , observe array.concat(a, b):", array.concat(a, b))
runtime.error("stop")
En general, las siguientes reglas se aplican a las funciones personalizadas en el lenguaje Pine:
barcolor(), fill(), hline(), plot(), plotbar(), plotcandle()
) no se puede llamar en funciones personalizadas.También hemos utilizado las funciones personalizadas muchas veces en nuestros tutoriales anteriores, como las diseñadas como una sola línea:
barIsUp() => close > open
Si el BAR actual es una recta positiva cuando la función devuelve.
Funciones personalizadas diseñadas para ser múltiples líneas:
sma(data, length) =>
i = 0
sum = 0
while i < 10
sum += data[i]
i += 1
sum / length
plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
Usamos una función personalizada para realizar una función de cálculo medio sma.
Además, dos ejemplos de funciones personalizadas que podemos devolver:
twoEMA(data, fastPeriod, slowPeriod) =>
fast = ta.ema(data, fastPeriod)
slow = ta.ema(data, slowPeriod)
[fast, slow]
[ema10, ema20] = twoEMA(close, 10, 20)
plot(ema10, title="ema10", overlay=true)
plot(ema20, title="ema20", overlay=true)
Una función puede calcular la línea rápida, la línea lenta y dos promedios EMA.
Las funciones integradas se pueden encontrar fácilmente en elDocumento de guión FMZ PINE.
Clasificación de las funciones integradas en el lenguaje Pine:
str.
series.color.
series.input.
series.ta.
series.plot.
series.array.
series.strategy.
series.math.
series.request.
funciones de serie, funciones de manipulación de tipos, etc.)Elstrategy.
serie de funciones son funciones que a menudo utilizamos en el diseño de estrategias, y estas funciones están estrechamente relacionadas con la ejecución de operaciones comerciales cuando la estrategia se ejecuta específicamente.
strategy.entry
strategy.entry
función es una función más importante cuando escribimos una estrategia para colocar un pedido, varios parámetros importantes para la función son:id
, direction
, qty
, when
, etc.
Parámetros:
id
: se puede entender que se da un nombre a una posición de negociación para hacer referencia.direction
: Si la dirección de la orden es larga (comprar), pasar en la variable incorporadastrategy.long
, y si desea ir corto (vender), pasar en la variablestrategy.short
.qty
: Especificar el importe de las órdenes a realizar, si no se pasa este parámetro, se utilizará el importe predeterminado de las órdenes.when
: Condición de ejecución, puede especificar este parámetro para controlar si se activa o no esta operación de orden actual.limit
: Especificar el precio límite de la orden.stop
Precio de suspensión de pérdida.Los detalles específicos de la ejecución delstrategy.entry
La función se controla por los parámetros cuando elstrategy
función se llama, y también puede ser controlado por el [
Nos centramos en el elpyramiding
, default_qty_value
los parámetros en elstrategy
Utilizamos el siguiente código para la prueba:
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)
ema10 = ta.ema(close, 10)
findOrderIdx(idx) =>
if strategy.opentrades == 0
false
else
ret = false
for i = 0 to strategy.opentrades - 1
if strategy.opentrades.entry_id(i) == idx
ret := true
break
ret
if not findOrderIdx("long1")
strategy.entry("long1", strategy.long)
if not findOrderIdx("long2")
strategy.entry("long2", strategy.long, 0.2, when = close > ema10)
if not findOrderIdx("long3")
strategy.entry("long3", strategy.long, 0.2, limit = low[1])
strategy.entry("long3", strategy.long, 0.3, limit = low[1])
if not findOrderIdx("long4")
strategy.entry("long4", strategy.long, 0.2)
plot(ema10, title="ema10", color=color.red)
La parte al comienzo del código/* backtest... */
es un ajuste de backtest, que se utiliza para registrar el tiempo de ajuste de backtest y otra información en ese momento para la depuración, no el código de inicio.
En el código:strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)
, cuando especificamos elpyramiding
Parámetro como 3, fijamos el número máximo de operaciones en la misma dirección a 3.strategy.entry
Las operaciones de orden en el ejemplo no se ejecuta.default_qty_value
Parámetro para ser 0.1, estostrategy.entry
operación con IDlong1
tiene un tamaño de orden predeterminado de 0,1.strategy.entry
llamada de la función cuando especificamos eldirection
comostrategy.long
, por lo que las órdenes de prueba backtest son todos los pedidos de compra.
Tenga en cuenta que la operación de ordenstrategy.entry("long3", ...
en el código se llama dos veces, para el mismo ID:long3
, el primerostrategy.entry
la operación de orden no se llenó, y la segunda llamada a lastrategy.entry
En el caso de los ordenes de ordenes con el ID strategy.entry
la función para realizar órdenes de acuerdo con el ID
strategy.close
Elstrategy.close
La función se utiliza para cerrar la posición de entrada con el ID de identificación especificado. Los principales parámetros son:id
, when
, qty
, qty_percent
.
Parámetros:
id
: El ID de entrada que debe cerrarse es el ID que especificamos cuando abrimos una posición utilizando una función de orden de entrada, comostrategy.entry
.when
: Condiciones de ejecución.qty
: Número de posiciones cerradas.qty_percent
: Porcentaje de posiciones cerradas.Vamos a familiarizarnos con los detalles del uso de esta función a través de un ejemplo:
El/*backtest ... */
en el código está la información de configuración paraFMZ.COMbacktest, puede borrarlo y establecer el mercado, variedad, rango de tiempo y otra información que necesita para probar.
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy("close Demo", pyramiding=3)
var enableStop = false
if enableStop
runtime.error("stop")
strategy.entry("long1", strategy.long, 0.2)
if strategy.opentrades >= 3
strategy.close("long1") // Multiple entry orders, no qty parameters specified, close all
// strategy.close() // Without specifying the id parameter, the current position will be closed
// strategy.close("long2") // If a non-existent id is specified then nothing is done
// strategy.close("long1", qty=0.15) // Specify qty parameters to close a position
// strategy.close("long1", qty_percent=50) // qty_percent is set to 50 to close 50% of the positions marked by long1
// strategy.close("long1", qty_percent=80, when=close<open) // Specify the parameter when, change it to close>open and it won't trigger
enableStop := true
La estrategia de prueba muestra tres entradas largas consecutivas con el ID de entrada strategy.close
La función para establecer los diferentes resultados de la prueba de retroceso al cerrar una posición.strategy.close
la función no tiene parámetros para especificar el precio de la orden para cerrar la posición, esta función se utiliza principalmente para cerrar la posición inmediatamente al precio de mercado actual.
strategy.close_all
La funciónstrategy.close_all
se utiliza para cerrar todas las posiciones actuales, porque las posiciones de lenguaje de guión Pine sólo puede tener una dirección, es decir, si hay una señal activada en la dirección opuesta a la posición actual cerrará la posición actual y luego abrirlo de acuerdo con el disparador de la señal.strategy.close_all
cerrará todas las posiciones en la dirección actual cuando se llame.strategy.close_all
su función es:when
.
Parámetros:
when
: Condiciones de ejecución.Vamos a utilizar un ejemplo para observar:
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy("closeAll Demo")
var enableStop = false
if enableStop
runtime.error("stop")
strategy.entry("long", strategy.long, 0.2, when=strategy.position_size==0 and close>open)
strategy.entry("short", strategy.short, 0.3, when=strategy.position_size>0 and close<open)
if strategy.position_size < 0
strategy.close_all()
enableStop := true
El código de ensayo comienza con un número de posición de 0 (es decir,strategy.position_size==0
Es cierto), por lo que cuando las condiciones establecidas por el cuando parámetro se cumplen, sólo elstrategy.entry
se ejecuta la función de entrada con ID strategy.position_size
es mayor que 0, entonces la función de entrada con ID strategy.position_size < 0
, es decir, cuando se mantiene una posición corta, todas las posiciones en la dirección actual de mantenimiento se cerrarán.enableStop := true
. Detener la ejecución de la estrategia para que el registro pueda ser observado.
Se puede encontrar que la funciónstrategy.close_all
no tiene parámetros para especificar el precio de cierre de la orden, esta función se utiliza principalmente para cerrar inmediatamente la posición al precio de mercado actual.
strategy.exit
Elstrategy.exit
La función de cierre de una posición de entrada se utiliza para cerrar una posición de entrada.strategy.close
ystrategy.close_all
Las funciones de cierre de una posición inmediatamente al precio de mercado actual.strategy.exit
la función cerrará la posición de acuerdo con los parámetros.
Parámetros:
id
: El identificador de orden de la orden de la condición de cierre actual.from_entry
Se utiliza para especificar el ID de entrada de la posición que se va a cerrar.qty
: Número de posiciones cerradas.qty_percent
: Porcentaje de posiciones cerradas, rango: 0 ~ 100.profit
: Objetivo de utilidad, expresado en puntos.loss
Se trata de la suma de las pérdidas de los activos de la entidad.limit
: Objetivo de utilidad, especificado por precio.stop
: Objetivo de pérdida por parada, especificado por precio.when
: Condiciones de ejecución.Utilice una estrategia de prueba para comprender el uso de los parámetros.
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
args: [["RunMode",1,358374],["ZPrecision",0,358374]]
*/
strategy("strategy.exit Demo", pyramiding=3)
varip isExit = false
findOrderIdx(idx) =>
ret = -1
if strategy.opentrades == 0
ret
else
for i = 0 to strategy.opentrades - 1
if strategy.opentrades.entry_id(i) == idx
ret := i
break
ret
strategy.entry("long1", strategy.long, 0.1, limit=1, when=findOrderIdx("long1") < 0)
strategy.entry("long2", strategy.long, 0.2, when=findOrderIdx("long2") < 0)
strategy.entry("long3", strategy.long, 0.3, when=findOrderIdx("long3") < 0)
if not isExit and strategy.opentrades > 0
// strategy.exit("exitAll") // If only one id parameter is specified, the exit order is invalid, and the parameters profit, limit, loss, stop and other exit conditions also need to be set at least one, otherwise it is also invalid
strategy.exit("exit1", "long1", profit=50) // Since the long1 entry order is not filled, the exit order with ID exit1 is also on hold until the corresponding entry order is filled before exit1 is placed
strategy.exit("exit2", "long2", qty=0.1, profit=100) // Specify the parameter qty to close 0.1 positions in the position with ID long2
strategy.exit("exit3", "long3", qty_percent=50, limit=strategy.opentrades.entry_price(findOrderIdx("long3")) + 1000) // Specify the parameter qty_percent to close 50% of the positions in the position with ID long3
isExit := true
if bar_index == 0
runtime.log("The price per point:", syminfo.mintick) // The price per point is related to the "Pricing Currency Precision" parameter setting on the Pine language template parameters
Se utiliza el modelo de precios en tiempo real para backtest, la estrategia de prueba comienza con 3 operaciones de entrada (strategy.entry
función), ylong1
se establece intencionadamente conlimit
Parámetro con un precio de orden pendiente de 1, por lo que no se puede llenar.strategy.exit
En el caso de las posiciones de cierre, el precio de cierre es el mismo que el precio de cierre de las posiciones de cierre.strategy.exit
La función también tiene parámetros de parada de trail más complejos:trail_price
, trail_points
, trail_offset
También se puede probar en este ejemplo para aprender su uso.
strategy.cancel
Elstrategy.cancel
Estas funciones se utilizan para cancelar/detener todas las órdenes pendientes.strategy.order
, strategy.entry
, strategy.exit
Los parámetros principales de esta función son:id
, when
.
Parámetros:
id
: La identificación de admisión se cancela.when
: Condiciones de ejecución.Esta función es fácil de entender, y se utiliza para cancelar las órdenes de entrada que no se cumplen.
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy("strategy.cancel Demo", pyramiding=3)
var isStop = false
if isStop
runtime.error("stop")
strategy.entry("long1", strategy.long, 0.1, limit=1)
strategy.entry("long2", strategy.long, 0.2, limit=2)
strategy.entry("long3", strategy.long, 0