Então vemos que nas três linhas desenhadas a, b e c, a linha b é um BAR mais lenta que a linha a, e a linha c é um BAR mais lenta que a linha b. A linha c é 2 BAR mais lenta que a linha a.
Podemos puxar o gráfico para a extrema esquerda e observar que na primeira linha K, ambos os valores de b e c são nulos (na). Isso ocorre porque, quando o script é executado na primeira linha K BAR, ele não existe quando se refere ao valor histórico de um ou dois períodos para frente, o que não existe. Portanto, precisamos ter cuidado ao escrever estratégias para verificar se a referência a dados históricos resultará em valores nulos. Se o valor nulo for usado descuidadamente, isso causará uma série de diferenças de cálculo e pode até afetar o BAR em tempo real.na
, nz
A partir de agora, o sistema de gestão dos recursos humanos será mais eficaz.nz
, ```na`` nos nossos vídeos anteriores, você se lembra de que capítulo é?) tratar do caso de valores nulos, por exemplo:
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 é uma forma de lidar com possíveis referências a valores nulos (na).
Aprendemos muitos operadores na linguagem de Pine. Estes operadores formam expressões através de várias combinações com operandos. Então qual é a prioridade dessas operações ao avaliar em expressões? Assim como a aritmética que aprendemos na escola, a multiplicação e a divisão são calculadas primeiro, seguidas de adição e subtração. O mesmo vale para expressões na linguagem de Pine.
Prioridade | Operadores |
---|---|
9 | [] |
8 | + 、- enot no operador unário |
7 | * 、/ 、% |
6 | + 、- no operador binário |
5 | > 、< 、>= 、<= |
4 | == 、!= |
3 | and |
2 | or |
1 | ?: |
Expresões de alta prioridade são calculadas primeiro, e se as prioridades são as mesmas, é avaliado da esquerda para a direita.()
para enrolar a expressão para forçar a parte a ser avaliada primeiro.
Nós já estudamos o conceito de
Modo de declaração:
A primeira coisa a escrever ao declarar uma variável é o
var
.varip
.Ovar
evarip
palavras-chave foram realmente estudados em nosso capítulo anterior sobreAssignment Operators
Se nada for escrito para o modo de declaração de variável, como a instrução:i = 1
, como também mencionamos anteriormente, tal variável declarada e atribuída é executada em cada K-line BAR.
Tipo A linguagem Pine no FMZ não é rigorosa sobre tipos, e geralmente pode ser omitida. No entanto, para ser compatível com a estratégia de script no Trading View, as variáveis também podem ser declaradas com tipos. Por exemplo:
int i = 0
float f = 1.1
Os requisitos de tipo no Trading View são bastante rigorosos e um erro será notificado se o seguinte código for utilizado no Trading View:
baseLine0 = na // compile time error!
Marcador Os marcadores são nomes de variáveis. O nome de marcadores foi mencionado em capítulos anteriores, então você pode revisá-lo aqui:https://www.fmz.com/bbs-topic/9637#markers
Em resumo, declarar uma variável pode ser escrito como:
// [<declaration_mode>] [<type>] <marker> = value
declaration mode type marker = value
O operador de atribuição é usado aqui:=
atribui um valor a uma variável quando ela é declarada.if
, for
, while
, ouswitch
e outras estruturas (estas palavras-chave estruturais e uso de declarações serão explicados em detalhes nos cursos subsequentes.
Aqui nos concentramos na função de entrada, que é uma função que vamos usar com frequência ao projetar e escrever estratégias.
Função de entrada:
input function, parameters: defval、title、tooltip、inline、group
A função de entrada no FMZ é um pouco diferente da do Trading View, mas esta função é usada como a entrada de atribuição de parâmetros de estratégia.
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)
A função de entrada é frequentemente usada para atribuir valores a variáveis ao declará-las. A função de entrada no FMZ desenha controles para definir parâmetros de estratégia automaticamente na interface de estratégia do FMZ. Os controles suportados no FMZ atualmente incluem caixas de entrada numérica, caixas de entrada de texto, caixas suspensivas e caixas de seleção booleanas. E você pode definir o agrupamento de parâmetros de estratégia, definir a mensagem de texto de prompt de parâmetro e outras funções.
Introduzimos vários parâmetros principais da função de entrada:
Além da declaração e atribuição de variáveis individuais, há também uma maneira de declarar um grupo de variáveis e atribuí-los na linguagem Pine:
[Variable A, Variable B, Variable C] = function or structure, such as ```if```, ```for```, ```while``` or ```switch```
O mais comum é quando usamos ota.macd
A função para calcular o indicador MACD, uma vez que o indicador MACD é um indicador de várias linhas, três conjuntos de dados são calculados.
[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 desenhar o gráfico MACD usando o código acima facilmente. Não só as funções embutidas podem retornar a várias variáveis, mas também as funções personalizadas escritas podem retornar a vários dados.
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)
O método de escrita de usar se e outras estruturas como atribuições de múltiplas variáveis também é semelhante à função personalizada acima, e você pode experimentá-lo se estiver interessado.
[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)
Algumas funções não podem ser escritas no bloco de código local do ramo condicional, incluindo principalmente as seguintes funções:
barcolor ((), fill ((), hline ((), indicator ((), plot ((), plotcandle ((), plotchar ((), plotshape (()
O Trading View irá compilar com erros, FMZ não é tão restritivo, mas é recomendado seguir as especificações do Trading View.
strategy("test", overlay=true)
if close > open
plot(close, title="close")
else
plot(open, title="open")
Exemplo de explicação:
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)
Ponto-chave: Expressões usadas para julgamentos que retornam valores booleanos. Observe a indentada. Pode haver no máximo mais um ramo. Se todas as expressões de ramo não forem verdadeiras e não houver outro ramo, retorne na.
x = if close > open
close
plot(x, title="x")
A instrução switch é também uma instrução estruturada por ramificação, que é usada para projetar diferentes caminhos a serem executados de acordo com certas condições.
Existem duas formas de comutação, vamos olhar para os exemplos um por um para entender o seu uso.
switch
com expressões - exemplo de explicação:// 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)
Aprendemos a função de entrada antes, aqui continuamos a aprender duas funções semelhantes à entrada:input.string
, input.int
functions.
input.string
é usado para retornar uma cadeia, e oinput.int
função é usada para retornar um valor inteiro. No exemplo, há um novo uso dooptions
Parâmetro.options
Parâmetro pode ser passado uma matriz de valores opcionais, tais comooptions=["EMA", "SMA", "RMA", "WMA"]
eoptions=[5, 10, 20]
Neste exemplo (observe que um é tipo de string, o outro é tipo numérico). Desta forma, os controles na interface de estratégia não precisam inserir valores específicos, mas os controles se tornam caixas suspensivas para selecionar essas opções fornecidas no parâmetro de opções.
O valor da variável func é uma cadeia de caracteres, e a variável func é usada como expressão para switch (que pode ser uma variável, chamada de função ou expressão) para determinar qual ramo no switch é executado.runtime.error("error")
função será executada, fazendo com que a estratégia lance uma exceção e pare.
Em nosso código de teste acima, após a última linha de runtime.error no bloco de código de ramificação padrão do switch, não adicionamos código como [na, na] para ser compatível com o valor de retorno. Este problema precisa ser considerado na Visualização de Negociação. Se o tipo for inconsistente, um erro será relatado. Mas no FMZ, como o tipo não é estritamente necessário, esse código de compatibilidade pode ser omitido. Portanto, não há necessidade de considerar a compatibilidade de tipo do valor de retorno de if e switch branches no 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)
Nenhum erro será relatado no FMZ, mas um erro será relatado na vista de negociação.
switch
sem expressõesEm seguida, vamos olhar para outra maneira de usarswitch
, isto é, sem expressões.
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 no exemplo de código de teste, o switch corresponderá à execução do bloco de código local que é verdadeiro na condição de ramificação. Em geral, as condições de ramificação após uma instrução de switch devem ser mutuamente exclusivas. Ou seja, para cima e para baixo no exemplo não podem ser verdadeiras ao mesmo tempo. Como o switch só pode executar o bloco de código local de um ramo, se você estiver interessado, você pode substituir esta linha no código:up = close > open // up = close < open
Você verá que o ramo do switch só pode executar o primeiro ramo. Além disso, é necessário prestar atenção para não escrever chamadas de função no ramo do switch tanto quanto possível, a função não pode ser chamada em cada BAR pode causar alguns problemas de cálculo de dados (a menos que no exemplo de "switch
com expressões", o ramo de execução é determinista e não será alterado durante a operação da estratégia).
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
A instrução for é muito simples de usar, o loop for pode finalmente retornar um valor (ou múltiplos valores, na forma de [a, b, c]). Como a variável atribuída à posição
Obreak
Palavra-chave utilizada no loop for: o loop pára quando obreak
A declaração é executada.
Ocontinue
Palavra-chave utilizada no loop for:continue
se a instrução for executada, o loop ignorará o código apóscontinue
A instrução for retorna o valor de retorno da última execução do loop e retorna null se nenhum código for executado.
Então vamos demonstrar com um exemplo simples:
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")
Ofor ... in
A declaração tem duas formas, vamos ilustrá-los no seguinte pseudocódigo.
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 a principal diferença entre as duas formas é o conteúdo que segue a palavra-chave for, uma é usar uma variável como uma variável que se refere aos elementos da matriz, a outra é usar uma estrutura contendo variáveis de índice, tuplas de variáveis de elementos de matriz como referências.
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")
Quando ele precisa usar o índice, use a gramáticafor [i, ele] in testArray
.
Aplicação de circuitos
Podemos usar as funções embutidas fornecidas na linguagem Pine para completar alguns dos cálculos lógicos do loop, ou escritos usando a estrutura do loop diretamente ou processados usando as funções embutidas.
No caso de projetos com estrutura de circuito:
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)
O exemplo usa um loop for para calcular a soma e, em seguida, calcular o valor médio.
Calcular a média móvel directamente utilizando a função integrada:
plot(ta.sma(close, length), title="ta.sma", overlay=true)
Utilize a função integradata.sma
O cálculo da média móvel pode ser feito diretamente para calcular o indicador da média móvel. Obviamente, é mais simples usar a função incorporada para calcular a média móvel. Ao comparar no gráfico, pode-se ver que os resultados calculados são exatamente os mesmos.
Ainda usamos o exemplo acima para ilustrar.
No caso de projetos com estrutura de circuito:
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 a soma de todos os elementos em uma matriz, podemos usar um loop para processá-lo, ou usar a função embutidaarray.sum
Para calcular.
Calcular a soma directamente utilizando a função embutida:
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 os dados calculados é exatamente o mesmo que é exibido no gráfico usando gráfico.
Então, por que projetar loops quando podemos fazer tudo isso com funções embutidas?
Owhile
A instrução mantém o código na seção do loop executando até que a condição de julgamento na estrutura while seja 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
Outras regras do while são semelhantes às do loop for. A última linha do bloco de código local do corpo do loop é o valor de retorno, que pode retornar múltiplos valores. Execute o loop quando a condição
Continuaremos a usar o exemplo de cálculo de médias móveis para demonstração:
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 o while loop também é muito simples de usar, e também é possível projetar alguma lógica de cálculo que não pode ser substituída pelas funções embutidas, como calcular fatorial:
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
A definição de matrizes na linguagem Pine é semelhante à de outras linguagens de programação. As matrizes Pine[]
para se referir a um elemento na matriz, precisamos usar as funçõesarray.get()
earray.set()
A ordem de índice dos elementos da matriz é que o índice do primeiro elemento da matriz é 0, e o índice do próximo elemento é incrementado por 1.
Ilustramo-lo com um código simples:
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))
Utilizaçãoarray<int> a
, float[] b
para declarar uma matriz ou apenas declarar uma variável que pode ser atribuída a uma matriz, por exemplo:
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")
As variáveis de matriz são inicializadas usando oarray.new
earray.from
Há também muitas funções relacionadas com tipos semelhantes aarray.newna língua do pinheiro:array.new_int()
, array.new_bool()
, array.new_color()
, array.new_string()
, etc.
A palavra-chave var também funciona com o modo de declaração de matriz.
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")
Pode-se ver que as alterações da matriz a foram continuamente determinadas e não foram reiniciadas.barstate.islast
é verdade, ainda há apenas um elemento impresso com um valor de 0.
Use array.get para obter o elemento na posição de índice especificada na matriz e use array.set para modificar o elemento na posição de índice especificada na matriz.
O primeiro parâmetro de array.get é a matriz a ser processada, e o segundo parâmetro é o índice especificado. O primeiro parâmetro para array.set é a matriz a ser processada, o segundo parâmetro é o índice especificado e o terceiro parâmetro é o elemento a ser escrito.
Usamos o seguinte exemplo simples para ilustrar:
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")
O exemplo inicializa a cor de base verde, declara e inicializa uma matriz para armazenar cores, e então atribui diferentes valores de transparência para as cores (usando ocolor.newO nível de cor é calculado pela distância do BAR atual do valor máximo de alto em 100 períodos de lookback. Quanto mais próxima a distância do valor máximo de HIGH nos últimos 100 ciclos de lookback, maior a classificação e mais escuro (menor transparência) o valor de cor correspondente.
Como iterar através de uma matriz, podemos usar as instruções for/for in/while que aprendemos 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")
Estes três métodos de travessia têm os mesmos resultados de execução.
As matrizes podem ser declaradas no escopo global de um script, ou no escopo local de uma função ou se for ramificada.
Para o uso de elementos em matrizes, as seguintes maneiras são equivalentes. Podemos ver pelo exemplo a seguir que dois conjuntos de linhas são desenhados no gráfico, dois em cada conjunto, e as duas linhas em cada conjunto têm exatamente o mesmo 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 o seguinte exemplo para testar essas funções de adição e exclusão de operações 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")
Aplicação de adições, exclusões: matrizes como filas
Podemos construir uma estrutura de dados
Uma fila é uma estrutura que é frequentemente usada no campo da programação, as características de uma fila são:
O elemento que entra na fila primeiro, sai da fila primeiro.
Desta forma, ele garante que os dados na fila são os dados mais recentes, e que o comprimento da fila não se expandirá indefinidamente.
No exemplo a seguir, usamos uma estrutura de fila para registrar o preço de cada tick, calcular o preço médio móvel no nível do tick e, em seguida, compará-lo com a média móvel no nível da linha 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")
Observe que ao declarar matriz a, especificamos o modo de declaração e usar a palavra-chavevariant
Desta forma, cada mudança de preço será registrada na matriz a.
Calcular funções de correlação:
array.avg()
Calcula o valor médio de todos os elementos de uma matriz,array.min()
Calcula o elemento mais pequeno de uma matriz,array.max()
Calcula o elemento mais pequeno de uma matriz,array.stdev()
Calcula o desvio-padrão de todos os elementos de uma matriz,array.sum()
Calcula o desvio padrão de todos os elementos de uma matriz.
Funções relacionadas com a operação:array.concat()
para fundir ou concatenar duas matrizes.array.copy()
para copiar a matriz.array.join
Para concatenates todos os elementos de uma matriz em uma cadeia.array.sort()
para ordenar por ordem ascendente ou descendente.array.reverse()
para inverter a matriz.array.slice()
para cortar a matriz.array.includes()
para julgar o elemento.array.indexof()
Para retornar ao índice da primeira ocorrência do valor passado como parâmetro.array.lastindexof()
para encontrar a última ocorrência do valor.
Exemplos de teste de funções relacionadas com o cálculo de matriz:
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 são funções de cálculo de matriz comumente usadas.
Exemplos de funções relacionadas com a operação:
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")
A linguagem Pine pode ser projetada com funções personalizadas. Em geral, as seguintes regras são aplicadas às funções personalizadas na linguagem Pine:
barcolor(), fill(), hline(), plot(), plotbar(), plotcandle()
) não pode ser chamado em funções personalizadas.Nós também usamos as funções personalizadas por muitas vezes em nossos tutoriais anteriores, tais como aqueles projetados como uma única linha:
barIsUp() => close > open
Se o BAR atual é uma reta positiva quando a função retorna.
Funções personalizadas concebidas para ser múltiplas linhas:
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)
Nós usamos uma função personalizada para realizar uma função de cálculo média sma.
Além disso, dois exemplos de funções personalizadas que podemos retornar:
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)
Uma função pode calcular a linha rápida, linha lenta e duas médias EMA.
As funções integradas podem ser facilmente encontradas noDocumento de script do FMZ PINE.
Classificação de funções embutidas na língua Pine:
str.
series.color.
series.input.
series.ta.
series.plot.
series.array.
series.strategy.
series.math.
series.request.
Funções de série, funções de manipulação de tipos, etc.)Ostrategy.
série de funções são funções que usamos frequentemente no design de estratégias, e essas funções estão intimamente relacionadas com a execução de operações de negociação quando a estratégia está sendo executada especificamente.
strategy.entry
strategy.entry
função é uma função mais importante quando escrevemos uma estratégia para colocar um pedido, vários parâmetros importantes para a função são:id
, direction
, qty
, when
, etc.
Parâmetros:
id
O identificador pode ser utilizado para cancelar, modificar ordens e fechar posições.direction
: Se a direcção da ordem for longa (comprar), passar na variável incorporadastrategy.long
, e se você quiser ficar curto (vender), passe na variávelstrategy.short
.qty
: Especificar o montante das ordens a realizar, se este parâmetro não for transmitido, será utilizado o montante por defeito das ordens.when
: Condição de execução, pode especificar este parâmetro para controlar se esta operação de ordem actual é desencadeada ou não.limit
: Especificar o preço limite da ordem.stop
Preço stop loss.Os pormenores específicos da execução dostrategy.entry
A função é controlada pelas definições dos parâmetros quando ostrategy
função é chamada, e também pode ser controlada pelo [
Concentramos-nos nopyramiding
, default_qty_value
Parâmetros nostrategy
Utilizamos o seguinte código para testar:
/*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)
A parte no início do código/* backtest... */
é uma configuração de backtest, que é usada para registrar o tempo de configuração de backtest e outras informações nesse momento para depuração, não o código de inicialização.
No código:strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)
, quando especificamos opyramiding
O parâmetro como 3, definimos o número máximo de negócios na mesma direção para 3.strategy.entry
Como nós também especificamos odefault_qty_value
Parâmetro para ser 0,1, estestrategy.entry
operação com IDlong1
tem um tamanho de ordem por defeito de 0,1.strategy.entry
chamada função quando especificamos odirection
comostrategy.long
, então as ordens de teste backtest são todas ordens de compra.
Observe que a operação de ordemstrategy.entry("long3", ...
no código é chamado duas vezes, para o mesmo ID:long3
, o primeirostrategy.entry
A operação de encomenda não foi preenchida, e a segunda chamada para ostrategy.entry
A função principal da função foi modificar a ordem para este ID (os dados mostrados no teste de backtest também mostram que a quantidade de ordem para este limite de ordem foi modificada para 0,3).strategy.entry
Função para colocar ordens de acordo com o ID
strategy.close
Ostrategy.close
A função é utilizada para fechar a posição de entrada com o ID de identificação especificado. Os principais parâmetros são:id
, when
, qty
, qty_percent
.
Parâmetros:
id
: O ID de entrada que precisa ser fechado é o ID que especificamos quando abrimos uma posição usando uma função de ordem de entrada, comostrategy.entry
.when
: Condições de execução.qty
: Número de posições fechadas.qty_percent
: Percentagem de posições fechadas.Vamos familiarizar-nos com os pormenores da utilização desta função através de um exemplo:
O/*backtest ... */
no código está a informação de configuração paraFMZ.COMbacktest, você pode excluí-lo e definir o mercado, variedade, faixa de tempo e outras informações que você precisa para testar.
/*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
A estratégia de ensaio mostra três entradas longas consecutivas com o ID de entrada strategy.close
A função para definir os diferentes resultados do backtest ao fechar uma posição.strategy.close
A função não tem parâmetros para especificar o preço da ordem para fechar a posição, esta função é utilizada principalmente para fechar a posição imediatamente ao preço de mercado corrente.
strategy.close_all
A funçãostrategy.close_all
é usado para fechar todas as posições atuais, porque as posições da linguagem de script Pine só pode ter uma direção, ou seja, se houver um sinal desencadeado na direção oposta da posição atual vai fechar a posição atual e, em seguida, abri-lo de acordo com o gatilho de sinal.strategy.close_all
será fechado todas as posições na direção atual quando é chamado.strategy.close_all
função é:when
.
Parâmetros:
when
: Condições de execução.Vamos usar um exemplo 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
O código de ensaio começa com um número de posição de 0 (ou seja,strategy.position_size==0
é verdade), então quando as condições definidas pelo quando parâmetro são atendidos, apenas ostrategy.entry
A função de entrada com ID strategy.position_size
é maior que 0, então a função de entrada com ID strategy.position_size < 0
, ou seja, quando se mantém uma posição curta, todas as posições na direcção de detenção corrente serão fechadas.enableStop := true
. Pára a execução da estratégia para que o log possa ser observado.
Pode-se encontrar que a funçãostrategy.close_all
Não tem parâmetros para especificar o preço de encerramento da ordem, esta função é utilizada principalmente para fechar imediatamente a posição ao preço de mercado corrente.
strategy.exit
Ostrategy.exit
A função é utilizada para fechar uma posição de entrada.strategy.close
estrategy.close_all
O preço de mercado é o preço de venda de uma posição.strategy.exit
A função fechará a posição de acordo com as definições dos parâmetros.
Parâmetros:
id
O número de ordem de encerramento da ordem.from_entry
O valor da posição deve ser calculado em conformidade com o modelo de referência.qty
: Número de posições fechadas.qty_percent
: Percentagem de posições fechadas, intervalo: 0 ~ 100.profit
: Objetivo de lucro, expresso em pontos.loss
O valor da posição em risco deve ser calculado em função da posição em risco.limit
: Objetivo de lucro, especificado por preço.stop
O valor da posição em risco deve ser calculado em função da posição em risco.when
: Condições de execução.Usar uma estratégia de teste para compreender o uso dos 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
Utilizamos o modelo de preços em tempo real para backtest, a estratégia de teste começa com 3 operações de entrada (strategy.entry
função), elong1
é definido intencionalmente comlimit
Parâmetro com um preço de ordem pendente de 1, para que ele não possa ser preenchido.strategy.exit
O valor de uma operação de stop-loss é o valor de uma operação de stop-loss, que é a operação de uma operação de stop-loss, que é a operação de uma operação de stop-loss.strategy.exit
A função também tem parâmetros mais complexos de trailing stop:trail_price
, trail_points
, trail_offset
também pode ser testado neste exemplo para aprender o seu uso.
strategy.cancel
Ostrategy.cancel
Estas funções são utilizadas para cancelar/parar todas as ordens pendentes.strategy.order
, strategy.entry
, strategy.exit
Os principais parâmetros desta função são:id
, when
.
Parâmetros:
id
A identificação de admissão deve ser cancelada.when
: Condições de execução.Esta função é fácil de entender e é usada para cancelar ordens de entrada que não são preenchidas.
/*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