Tutorial de introdução à linguagem PINE quantitativa do Inventor

Criado em: 2022-05-30 16:23:43, atualizado em: 2022-09-28 17:10:21
comments   0
hits   8860

omo o valor histórico na barra anterior de close, b também é uma estrutura de sequência de tempo, que pode continuar a referir-se ao seu valor histórico.

Podemos puxar o gráfico para o lado esquerdo e observar que na primeira linha K, os valores de b e c são nulos ((na)). Isso ocorre porque, quando o script é executado na primeira linha K BAR, quando o histórico de referência de um ou dois períodos para a frente é nulo, ele não existe. Por isso, quando escrevemos uma estratégia, precisamos ter cuidado para não citar o valor nulo quando citamos dados históricos. Se não formos cuidadosos, o uso do valor nulo pode causar uma série de diferenças de cálculo e até mesmo afetar o tempo real.nanzA função embutida é a função que julga a função embutida, e a função embutida julga a função embutida.nznaFunções, lembra-se em que capítulo?) que tratam especificamente de valores vazios, como:

close > nz(close[1], open)    // 当引用close内置变量前一个BAR的历史值时,如果不存在,则使用open内置变量

Isso é um tratamento para o que pode ser citado como um valor nulo ((na)).


Prioridade do operador

Nós já aprendemos muitos operadores da linguagem Pine, que formam expressões através de várias combinações de números e operações. Então, como é a prioridade dessas operações quando se calcula em expressões? Como as operações de quatro regras que aprendemos quando fomos para a escola, há multiplicação e subtração com prioridade e adição e subtração.

Prioridade Operador
9 []
8 O operador da base de dados +- e `not
7 */%
6 O operador binário +, -
5 ><>=<=
4 ==!=
3 and
2 or
1 ?:

A parte da expressão com alta prioridade é operada primeiro, e se a mesma prioridade é operada da esquerda para a direita. Se você quiser forçar uma parte a ser operada primeiro, pode usar()Envolva a parte da expressão que é obrigatória para a operação.

Variações

Declaração de variáveis

Nós já aprendemos o conceito de um identificador de variável, o identificador de variável é o nome dado a uma variável como um nome de variável. Então, também podemos dizer que uma variável é um identificador de um valor preservado. Então, como declarar uma variável?

  • Modo de declaração: A primeira coisa que se escreve na declaração de uma variável é o “modelo de declaração”. Há três tipos de modelos de declaração de variáveis: 1 - Usar palavras-chavevar。 2o, usar palavras-chave.varip
    1. Não escreva nada.

varvaripA palavra-chave já foi aprendida no capítulo anterior “Operadores de atribuição” e não é mais mencionada aqui. Se o padrão de declaração da variável não escrever nada, por exemplo, a frase:i = 1Na verdade, como dissemos anteriormente, as variáveis declaradas e atribuídas são executadas em cada linha BAR de K.

  • tipo A linguagem Pine no FMZ não é rigorosa quanto aos requisitos de tipo e geralmente pode ser omitida. No entanto, para ser compatível com a política de scripts no Trading View, as variáveis podem ser declaradas com tipos. Por exemplo:
  int i = 0 
  float f = 1.1

Os tipos no Trading View são mais exigentes, e o uso do seguinte código no Trading View pode gerar erros:

  baseLine0 = na          // compile time error!

Em resumo, a declaração de uma variável pode ser escrita como:

// [<declaration_mode>] [<type>] <identifier> = value 
   声明模式             类型     标识符       = 值

O operador de atribuição é usado aqui:=A atribuição de valores às variáveis pode ser uma string, um valor, uma expressão, uma chamada de função, ou um número.ifforwhileouswitchEstrutura etc. ((Essas palavras-chave estruturais, o uso de frases serão explicados em detalhes em nossos cursos posteriores, na verdade, nós já aprendemos em um curso anterior a atribuição de uma simples frase if, pode rever)

A função de entrada é uma função que usamos com muita frequência na elaboração de estratégias.

Funções de entrada:

input函数,参数defval、title、tooltip、inline、group

A função de entrada no FMZ é um pouco diferente da função de entrada no Trading View, mas é usada como uma entrada de atribuição de um parâmetro de estratégia. A seguir, vamos detalhar o uso da função de entrada no FMZ com um exemplo:

param1 = input(10, title="参数1名称", tooltip="参数1的描述信息", group="分组名称A")
param2 = input("close", title="参数2名称", tooltip="参数2的描述信息", group="分组名称A")
param3 = input(color.red, title="参数3名称", tooltip="参数3的描述信息", group="分组名称B")
param4 = input(close, title="参数4名称", tooltip="参数4的描述信息", group="分组名称B")
param5 = input(true, title="参数5名称", tooltip="参数5的描述信息", group="分组名称C")

ma = ta.ema(param4, param1)
plot(ma, title=param2, color=param3, overlay=param5)

A atribuição de valores às variáveis durante a declaração de variáveis é frequentemente usada como uma função de entrada, na qual a função de entrada do FMZ desenha automaticamente no interface da política do FMZ um controle para a configuração de parâmetros de política. Os controles suportados no FMZ atualmente possuem caixa de entrada de valores, caixa de entrada de texto, caixa de deslize e seleção de valores de burro. Também é possível configurar o grupo de parâmetros de política e informações de texto de aviso para a configuração de parâmetros.

Tutorial de introdução à linguagem PINE quantitativa do Inventor

Aqui estão alguns dos principais parâmetros da função de entrada:

  • defval: o valor padrão da opção de parâmetro de política definido como uma função de entrada, com suporte a variáveis, valores e strings embutidos na linguagem Pine
  • title: O nome do parâmetro que a política mostra na interface da política no disco rígido/retrospectiva.
  • tooltip: Informação de sugestão para o parâmetro da política, que é exibida quando o mouse é suspenso sobre o parâmetro da política.
  • group: nome de grupo de parâmetros de política, que pode ser dado a um grupo de parâmetros ∂.

Além de uma declaração de variável individual, a linguagem Pine também declara um conjunto de variáveis e atribui um valor:

[变量A,变量B,变量C] = 函数 或者 ```if```、 ```for```、```while```或```switch```等结构

O mais comum é o que usamosta.macdQuando a função calcula o MACD, como o MACD é um indicador de várias linhas, calcula três conjuntos de dados. Portanto, pode ser escrito como:

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

É muito fácil de desenhar um gráfico MACD usando o código acima. Não só as funções embutidas podem retornar várias variáveis, mas também as funções personalizadas podem retornar 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 uso de estruturas como if como atribuição de valores a várias variáveis também é semelhante ao método de função personalizada acima, e os interessados também podem tentar.

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

Estrutura condicional

Algumas funções não podem ser escritas em blocos de código local de ramificações condicionais, principalmente as seguintes:

barcolor(), fill(), hline(), indicator(), plot(), plotcandle(), plotchar(), plotshape()

O Trading View compila relatórios de erros. As restrições do FMZ não são tão rigorosas, mas também é recomendado seguir as normas do Trading View. Por exemplo, embora não haja relatórios de erros no FMZ, não é recomendado escrever assim.

strategy("test", overlay=true)
if close > open 
    plot(close, title="close")
else 
    plot(open, title="open")

Declaração if

Exemplo:

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)

Nota: A expressão usada para julgar retorna um valor de burr. Note que não há mais do que uma subalterna else. Todas as subalternas expressões são falsas, e sem nenhuma subalterna else, retorna na.

x = if close > open
    close
plot(x, title="x")

Como o bloco local de if não é executado quando a linha K BAR é negativa, ou seja, quando close < open, a expressão após a frase if é falsa, não há nenhuma ramificação else, a frase if retorna na. x é atribuído a na. Este ponto não pode ser desenhado no diagrama, mas também pode ser observado pelo diagrama de ressonância.

Declaração de switch

A sentença de switch também é uma sentença de estrutura ramificada, usada para projetar diferentes caminhos de execução de acordo com certas condições. A sentença de switch geralmente tem os seguintes pontos-chave de conhecimento:

1 , a sentença switch, tal como a sentença if, também pode retornar um valor. 2. Ao contrário das declarações de switch em outras linguagens, quando a estrutura de switch é executada, apenas um bloco local de seu código é executado, então a declaração de quebra não é necessária (ou seja, não é necessário escrever palavras-chave como break). 3. cada uma das sucursais do switch pode escrever um bloco de código local, cuja última linha é o valor de retorno (que pode ser um subconjunto de um valor). Se nenhum dos blocos de código local dos quais as sucursais são executadas, o retorno é na. 4 , a posição de julgamento de expressões na estrutura do switch, pode escrever strings, variáveis, expressões ou chamadas de funções. 5. switch permite especificar um valor de retorno, que é o valor padrão usado quando não há outra situação na estrutura.

O switch é dividido em duas formas, vamos ver um exemplo e ver como ele é usado.

1 com expressãoswitchA história é contada por exemplo:

// input.string: defval, title, options, tooltip
func = input.string("EMA", title="指标名称", tooltip="选择要使用的指标函数名称", options=["EMA", "SMA", "RMA", "WMA"])

// input.int: defval, title, options, tooltip
// param1 = input.int(10, title="周期参数")
fastPeriod = input.int(10, title="快线周期参数", options=[5, 10, 20])
slowPeriod = input.int(20, title="慢线周期参数", options=[20, 25, 30])

data = input(close, title="数据", tooltip="选择使用收盘价、开盘价、最高价...")
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)

Antes de começarmos a estudar as funções de input, vamos continuar a estudar duas funções semelhantes a input:input.stringinput.intfunção. input.stringO que é o código de barras?input.intFunção usada para retornar um valor inteiro.optionsO uso de parâmetros,optionsOs parâmetros podem ser passados para um conjunto de valores selecionáveis.options=["EMA", "SMA", "RMA", "WMA"]eoptions=[5, 10, 20](Observe que um deles é de tipo string e o outro é de tipo numérico). Assim, os controles da interface de estratégia não precisam inserir valores específicos, mas os controles se transformam em caixas de seleção para selecionar as opções fornecidas no parâmetro options.

O valor da variável func é uma sequência de caracteres, a variável func como uma expressão do switch, que pode ser uma variável, uma chamada de função, uma expressão, para determinar qual ramo do switch será executado. Se a variável func não for capaz de corresponder a uma expressão em qualquer ramo do switch, a execução do bloco de código do ramo padrão será executada.runtime.error("error")A função faz com que a política de lançamento de exceções pare.

No nosso código de teste acima, depois da última linha de runtime.error do bloco de código de suporte padrão do switch, não adicionamos[Na, na] como um código para compatibilizar o valor de retorno, no trading view é necessário considerar o problema, se o tipo não é consistente será compensado. Mas na FMZ, como não há tipo de exigência rigorosa, pode ser omitido este código de compatibilidade. Portanto, na FMZ não precisa considerar o tipo de compatibilidade do valor de retorno do ramo if, switch.

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 FMZ, não há erros, mas no trading view há erros. Porque o tipo de retorno de if branches não é consistente.

  1. Sem expressãoswitch

Vamos ver.switchOutra forma de escrever o termo é sem expressão.

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)

O exemplo de código de teste mostra que o switch irá corresponder aos blocos de código local verdadeiros em termos de execução de ramificação. Em geral, as condições de ramificação após a sentença de switch devem ser mutuamente recusadas. Isto significa que os blocos de código local de up e down não podem ser verdadeiros ao mesmo tempo.up = close > open // up = close < openAlém disso, é preciso ter cuidado para não escrever a chamada da função na filial do switch. A função não pode ser chamada em cada BAR, o que pode causar alguns problemas com a computação de dados.switch“No exemplo, o braço de execução é definido e não é alterado durante a execução da estratégia”.)

Estrutura circular

Para sentenças

返回值 = for 计数 = 起始计数 to 最终计数 by 步长
    语句                                            // 注释:语句里可以有break,continue
    语句                                            // 注释:最后一条语句为返回值

A função for é muito simples de usar, pois o loop for pode retornar um valor final (for) ou retornar vários valores, para que o loop for retorne um valor final (for).[a, b, c] como forma de a, b, c)

Usado no ciclo forbreakPalavras-chave: quando executadobreakO ciclo termina com a frase. Usado no ciclo forcontinuePalavras-chave: quando executadocontinueDepois da frase, o ciclo ignora.continueO código seguinte executa diretamente a próxima rodada de ciclo. A declaração for retorna o valor de retorno da última execução de ciclo. Se não houver nenhuma execução de código, retorna um valor nulo.

Aqui está um exemplo simples:

ret = for i = 0 to 10       // 可以增加by关键字修改步长,暂时FMZ不支持 i = 10 to 0 这样的反向循环
    // 可以增加条件设置,使用continue跳过,break跳出
    runtime.log("i:", i)
    i                       // 如果这行不写,就返回空值,因为没有可返回的变量
    
runtime.log("ret:", ret)
runtime.error("stop")

Para … em sentenças

for ... inHá duas formas de sentenças, com o seguinte código de pseudo para ilustrar:

返回值 = for 数组元素 in 数组 
    语句                        // 注释:语句里可以有break,continue
    语句                        // 注释:最后一条语句为返回值
返回值 = for [索引变量, 索引变量对应的数组元素] in 数组
    语句                        // 注释:语句里可以有break,continue
    语句                        // 注释:最后一条语句为返回值 

A principal diferença entre as duas formas é o conteúdo que segue após a palavra-chave for, uma é usar uma variável como referência para um elemento da matriz. A outra é usar uma estrutura que contém uma variável de indexação, um subconjunto de uma variável do elemento da matriz para fazer referência.

testArray = array.from(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
for ele in testArray            // 修改成 [i, ele]的形式:for [i, ele] in testArray , runtime.log("ele:", ele, ", i:", i)
    runtime.log("ele:", ele)

runtime.error("stop")

Usar índices sempre que necessáriofor [i, ele] in testArrayComo se escreve?

Aplicações circulares

Quando você pode usar funções embutidas fornecidas pela linguagem Pine para fazer alguns cálculos de lógica circular, você pode usar a estrutura circular para escrever diretamente, ou você pode usar funções embutidas para processar. Vamos dar dois exemplos.

1 - Calcular a média

Usando um design de estrutura circular:

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 a soma circular for, e então calcula a média.

Calcule a linha média diretamente com a função embutida:

plot(ta.sma(close, length), title="ta.sma", overlay=true)

Funções embutidas diretamenteta.smaPara calcular o indicador da linha média, é obviamente mais fácil usar a função embutida para calcular a linha média. A comparação no gráfico mostra que os resultados dos cálculos são totalmente consistentes.

2o, adição

Ou use o exemplo acima para ilustrar.

Usando um design de estrutura circular:

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 de uma matriz, pode-se usar um loop ou uma função embutida.array.sumPara calcular. A soma é calculada diretamente com 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)

Os dados obtidos são mostrados na gráfica de acordo com o gráfico usado.

Então, se você pode fazer isso com funções embutidas, por que criar um loop? 1 . Para algumas operações do array, computação . 2 - Revisar o histórico, por exemplo, para descobrir quantos pontos altos do passado foram mais altos que os pontos altos do BAR atual. Como os pontos altos do BAR atual são conhecidos apenas no BAR em execução do script, é necessário um loop para retornar e analisar o BAR do passado. 3 A função embutida na linguagem Pine não consegue completar o cálculo do BAR passado.

Declaração while

whileA frase permite que o código da parte do loop continue a ser executado até que a condição de julgamento na estrutura while seja falsa.

返回值 = while 判断条件
    语句                    // 注释:语句里可以有break,continue
    语句                    // 注释:最后一条语句为返回值

As outras regras do while são semelhantes às do ciclo for, onde a última linha do bloco de código local do corpo do ciclo é um valor de retorno, que pode retornar vários valores. O ciclo é executado quando a “condição do ciclo” é verdadeira e o ciclo é interrompido quando a condição é falsa.

Eu ainda vou usar o exemplo da linha de equilíbrio:

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)

Pode-se ver que o uso do while loop também é muito simples, e também pode-se projetar algumas lógicas de computação que não podem ser substituídas por funções embutidas, como o cálculo da multiplicação por grau:

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

Grupo

A definição de array na linguagem Pine é semelhante à definição de arrays em outras linguagens de programação. Uma array é uma matriz de dimensões. Normalmente, ela é usada para armazenar uma série contínua de dados. Arrays em que os dados individuais são armazenados são chamados de elementos de um array.[]É preciso usararray.get()earray.set()A função 。 a ordem de indexação dos elementos na matriz é a seguinte: o primeiro elemento da matriz tem um índice de 0 e o seguinte tem um índice de 1 。

O que podemos dizer com um código simples:

var a = array.from(0)
if bar_index == 0 
    runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1])
else if bar_index == 1 
    array.push(a, bar_index)
    runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1])
else if bar_index == 2
    array.push(a, bar_index)
    runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1], ", 向前数2根BAR上的a,即a[2]值:", a[2])
else if bar_index == 3 
    array.push(a, bar_index)
    runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1], ", 向前数2根BAR上的a,即a[2]值:", a[2], ", 向前数3根BAR上的a,即a[3]值:", a[3])
else if bar_index == 4 
    // 使用array.get 按索引获取元素,使用array.set按索引修改元素
    runtime.log("数组修改前:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))
    array.set(a, 1, 999)
    runtime.log("数组修改后:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))

Array de declaração

usararray<int> afloat[] bUma variável pode ser atribuída a uma matriz, declarando uma matriz ou declarando apenas uma variável, 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")

Geralmente usado para a inicialização de variáveis de arrayarray.newearray.fromFunções 。 Há muitas outras funções no idioma Pine que são semelhantes às do array.new em relação ao tipo:array.new_int()array.new_bool()array.new_color()array.new_string()espere.

A palavra-chave var também pode funcionar com o modo de declaração de uma matriz, usando a palavra-chave var, uma matriz declarada é iniciada apenas na primeira linha BAR. Observamos por meio de um exemplo:

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 mudanças na matriz a são fixadas de forma contínua, sem serem realocadas. A matriz b é initializada em cada BAR.barstate.islastPara impressão em tempo real, ainda há apenas um elemento, o valor 0。

Leia e escreva elementos de uma matriz

Use array.get para obter os elementos que indicam a posição de indexação na matriz e use array.set para modificar os elementos que indicam a posição de indexação na matriz.

O primeiro parâmetro de array.get é o array a ser processado, e o segundo parâmetro é o índice especificado. O primeiro parâmetro de array.set é o conjunto a ser processado, o segundo é o índice especificado, e o terceiro é o elemento a ser escrito.

Para ilustrar, use o seguinte exemplo simples:

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

Este exemplo inicializa a cor de base verde, declara e inicializa um array para preservar a cor e, em seguida, confere uma transparência diferente ao valor da cor (usando a função color.new). Computação da classificação da cor, calculando a distância entre o valor máximo do BAR atual e o máximo de 100 ciclos de revisão. Quanto mais próximo o valor máximo do BAR atual dos 100 ciclos de revisão mais recentes, maior é a classificação e mais profunda é a correspondente quantidade de cores (baixa transparência).

Passar por elementos de uma matriz

Como percorrer uma matriz, podemos usar as expressões for/for in/while que aprendemos anteriormente.

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

Os resultados são os mesmos em todas as três viagens.

Arrays podem ser declarados no âmbito global do script, ou no âmbito local da função ou da filial if

Dados históricos

Para o uso de elementos em uma matriz, a seguinte maneira é equivalente, podemos ver através do exemplo a seguir que dois grupos de linhas são desenhados na tabela, dois em cada grupo, e os valores dos dois grupos de linhas são exatamente os mesmos.

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)

Funções de adição e remoção de arquivos

1, função relacionada à operação de adição de um array:

array.unshift()array.insert()array.push()

  1. Funções relacionadas com a operação de eliminação do array:

array.remove()array.shift()array.pop()array.clear()

Nós usamos o exemplo a seguir para testar as funções de adição e subtração dessas matrizes.

a = array.from("A", "B", "C")
ret = array.unshift(a, "X")
runtime.log("数组a:", a, ", ret:", ret)

ret := array.insert(a, 1, "Y")
runtime.log("数组a:", a, ", ret:", ret)

ret := array.push(a, "D")
runtime.log("数组a:", a, ", ret:", ret)

ret := array.remove(a, 2)
runtime.log("数组a:", a, ", ret:", ret)

ret := array.shift(a)
runtime.log("数组a:", a, ", ret:", ret)

ret := array.pop(a)
runtime.log("数组a:", a, ", ret:", ret)

ret := array.clear(a)
runtime.log("数组a:", a, ", ret:", ret)

runtime.error("stop")

Aplicações de adição e remoção: Arrays as queues

Usando uma matriz, e algumas funções de adição e remoção da matriz, podemos construir uma estrutura de dados chamada “coordenada”. As coordenadas podem ser usadas para calcular a média móvel do preço do tick. Alguns colegas podem perguntar: “Por que construir uma estrutura de coordenadas?

Uma coordenada é uma estrutura frequentemente usada na programação, que possui as seguintes características:

Os elementos que entram primeiro na fila saem primeiro.

Isso garante que os dados existentes na fila sejam os mais recentes e que o comprimento da fila não se expanda indefinidamente (o código de expansão ilimitada só pode ser escrito ao meio-dia, pois isso pode causar problemas de madrugada e de noite).

O exemplo a seguir usa uma estrutura de coordenadas para registrar o preço de cada tick, calcular a média móvel em nível de tick e compará-la com a média móvel em nível de linha K em 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 quando declaramos um array, nós especificamos o modo de declaração e usamos a palavra-chavevaripAssim, cada mudança de preço é registrada na matriz a.

Funções de computação e operação de matrizes comuns

Funções de cálculo:

array.avg()Então vamos ter uma média de todos os elementos do conjunto.array.min()O que é o menor elemento do conjunto?array.max()Então, o que é o maior elemento de uma matriz?array.stdev()Busque a diferença padrão de todos os elementos da matriz.array.sum()Obter a soma de todos os elementos da matriz.

Funções relacionadas com a operação: array.concat()Combinação ou conexão de dois conjuntos. array.copy()Copiar uma matriz. array.joinConecte todos os elementos da matriz em uma string. array.sort()Classificação por ascensão ou descensão. array.reverse()Matriz invertida. array.slice()Tape a matriz. array.includes()Elemento de julgamento. array.indexof()Retorna o índice em que o valor do parâmetro foi introduzido pela primeira vez. Se este valor não for encontrado, retorna -1 array.lastindexof()Encontre o valor da última vez que apareceu.

Exemplos de testes de funções relacionadas com o cálculo de matrizes:

a = array.from(3, 2, 1, 4, 5, 6, 7, 8, 9)

runtime.log("数组a的算数平均:", array.avg(a))
runtime.log("数组a中的最小元素:", array.min(a))
runtime.log("数组a中的最大元素:", array.max(a))
runtime.log("数组a中的标准差:", array.stdev(a))
runtime.log("数组a的所有元素总和:", array.sum(a))
runtime.error("stop")

Estas são as funções de cálculo de matrizes mais usadas.

Exemplos de funções operacionais:

a = array.from(1, 2, 3, 4, 5, 6)
b = array.from(11, 2, 13, 4, 15, 6)

runtime.log("数组a:", a, ", 数组b:", b)
runtime.log("数组a,数组b连接在一起:", array.concat(a, b))
c = array.copy(b)

runtime.log("复制一个数组b,赋值给变量c,变量c:", c)

runtime.log("使用array.join处理数组c,给每个元素中间增加符号+,连接所有元素结果为字符串:", array.join(c, "+"))
runtime.log("排序数组b,按从小到大顺序,使用参数order.ascending:", array.sort(b, order.ascending))     // array.sort函数修改原数组
runtime.log("排序数组b,按从大到小顺序,使用参数order.descending:", array.sort(b, order.descending))   // array.sort函数修改原数组

runtime.log("数组a:", a, ", 数组b:", b)
array.reverse(a)   // 此函数修改原数组
runtime.log("反转数组a中的所有元素顺序,反转之后数组a为:", a)    

runtime.log("截取数组a,索引0 ~ 索引3,遵循左闭右开区间规则:", array.slice(a, 0, 3))
runtime.log("在数组b中搜索元素11:", array.includes(b, 11))
runtime.log("在数组a中搜索元素100:", array.includes(a, 100))
runtime.log("将数组a和数组b连接,搜索其中第一次出现元素2的索引位置:", array.indexof(array.concat(a, b), 2), " , 参考观察 array.concat(a, b):", array.concat(a, b))
runtime.log("将数组a和数组b连接,搜索其中最后一次出现元素6的索引位置:", array.lastindexof(array.concat(a, b), 6), " , 参考观察 array.concat(a, b):", array.concat(a, b))

runtime.error("stop")

função

Funções de configuração

A linguagem Pine pode projetar funções personalizadas, geralmente com as seguintes regras:

  1. Todas as funções são definidas no âmbito global do script. Uma função não pode ser declarada em outra função. 2 , não permite que a função se chame a si mesma ((recursivo) }} no seu próprio código.
  2. Em princípio, todas as linguagens PINE têm funções de desenho integradas.barcolor()、 fill()、 hline()、plot()、 plotbar()、 plotcandle()) não pode ser chamado dentro de uma função personalizada. 4 , funções podem ser escritas em uma única linha, multilínea. O valor de retorno da última frase é o valor de retorno da função atual, o valor de retorno pode ser devolvido na forma de um módulo.

Também usamos funções personalizadas em tutoriais anteriores, por exemplo, funções personalizadas projetadas em linhas únicas:

barIsUp() => close > open

Esta função retorna se o BAR atual é o diagrama.

Concebido como uma função personalizada de várias 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)

A função que nós mesmos realizamos com uma função personalizada chamada sma mediana linear.

Além disso, pode-se retornar um exemplo de função-custom para duas variáveis:

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 uma linha rápida, uma linha lenta e dois indicadores de linha média EMA.

Funções embutidas

A função embutida pode ser facilmenteFMZ PINE Script DocumentárioConsulta no site.

As funções embutidas na linguagem Pine são classificadas:

1 , função de processamento de stringstr.Série 2 Funções de processamento de valores de corcolor.Série Funções de entrada de parâmetrosinput.Série 4. Funções de cálculo de indicadoresta.Série Funções de desenhoplot.Série 6 Funções de processamento de arraysarray.Série 7 Funções relacionadas a transaçõesstrategy.Série Funções matemáticasmath.Série 9 , outras funções ((processamento de tempo, não-plot função de desenho de série),request.Funções de série, funções de processamento de tipos, etc.)

Função de transação

strategy.As funções de série são funções que usamos frequentemente na concepção de estratégias, e que estão relacionadas com a execução de operações de transação quando a estratégia é executada.


1、strategy.entry

strategy.entryA função é uma função de encomenda que é muito importante quando escrevemos estratégias. Alguns dos seus principais parâmetros são:id, direction, qty, whenespere.

parâmetro:

  • id: pode ser entendido como um nome dado a uma posição de negociação para ser usado como referência. Pode ser citado como um id para cancelar, alterar ordens, liquidar posições.
  • directionSe a direção do pedido é fazer mais (comprar), o parâmetro é transmitido.strategy.longEssa variável interna é transmitida se você for à falência.strategy.shortEsta variável é:
  • qty: especificar o volume de encomenda, se este parâmetro não for transmitido, será usado o volume de encomenda padrão.
  • when: condição de execução, você pode especificar este parâmetro para controlar se a operação de encomenda atual é acionada ou não.
  • limitEspecifique o preço de limite da encomenda
  • stopPreço de parada:

strategy.entryDetalhes da execução específica da funçãostrategyO controle de configuração de parâmetros de uma chamada de função também pode ser feito por“Parâmetros de modelagem da biblioteca de classes de transação da linguagem Pine”Para mais detalhes sobre o controle de configuração e o controle de parâmetros de modelagem da biblioteca de classes de transação em linguagem Pine, consulte o documento em link.

Aqui está o foco.strategyA funçãopyramidingdefault_qty_valueParâmetros. Teste com o seguinte código:

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

Início do código/*backtest ... */A parte do pacote é a configuração de retroalimentação, para registrar informações como o tempo de configuração de retroalimentação no momento, para facilitar a delimitação, e não o código de estratégia.

No código:strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)Quando nós designamospyramidingQuando o parâmetro é 3, nós definimos o máximo de 3 vezes para o mesmo negócio. Então, no exemplo, quatro vezes.strategy.entryA seguinte operação não foi executada uma vez.default_qty_valueO parâmetro é 0,1, então o ID é o de longitude1strategy.entryO número de comandos de cada operação é o 0.1.strategy.entryA função que nós especificamos quando chamamosdirectionSão:strategy.longA resposta é que os testes são pagos.

Atenção no códigostrategy.entry("long3", ...A operação seguinte foi chamada duas vezes, para o mesmo ID: long3.strategy.entryA operação de envio não deu resultado, segunda chamadastrategy.entryA função é para modificar o pedido deste ID ((os dados exibidos no teste de retrospectiva também podem mostrar que o pedido abaixo do limite de preço foi modificado para 0.3)). Outra situação, por exemplo, se o primeiro pedido do ID for de 3 tons de comprimento, continue a usar o ID de 3 tons de comprimento.strategy.entrySe a função for encomendada, então o valor da encomenda será acumulado no IDlong3.


2、strategy.close

strategy.closeA função é usada para definir a posição de entrada para a posição de entrada que identifica o ID. Os principais parâmetros são:idwhenqtyqty_percent

parâmetro:

  • idA identificação de entrada necessária para a liquidação é a que usamos.strategy.entryO ID definido no momento da abertura da posição.
  • whenCondições de execução
  • qtyNúmero de liquidações.
  • qty_percentPorcentagem de equilíbrio

Para se familiarizar com os detalhes do uso desta função, veja um exemplo: No código/*backtest ... */É a informação de configuração do FMZ.COM International Station Retest, que pode ser removida, configurando o mercado, a variedade e o período de tempo que você precisa 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")                   // 多个入场订单,不指定qty参数,全部平仓
    // strategy.close()                          // 不指定id参数,会平掉当前的持仓
    // strategy.close("long2")                   // 如果指定一个不存在的id则什么都不操作
    // strategy.close("long1", qty=0.15)         // 指定qty参数平仓
    // strategy.close("long1", qty_percent=50)   // qty_percent设置50即为平掉long1标识仓位的50%持仓
    // strategy.close("long1", qty_percent=80, when=close<open)  // 指定when参数,修改为close>open就不触发了
    enableStop := true

A estratégia de testes mostrou que a entrada foi feita três vezes seguidas, com IDs de entrada de 1 long e 1 long, e que a entrada foi feita usando o ID de entrada de 1 long e 1 long.strategy.closeOs diferentes resultados de uma função quando os diferentes parâmetros são ajustados para a posição de equilíbrio podem ser encontrados.strategy.closeEsta função não possui parâmetros para especificar o preço de pedido de liquidação. Esta função é usada principalmente para liquidação imediata a preços de mercado atuais.


3、strategy.close_all

strategy.close_allA função é usada para nivelar todas as posições atuais, uma vez que os scripts de linguagem Pine possuem apenas uma direção, ou seja, se houver um sinal de ação oposto à direção da posição atual, a posição atual será nivelada e a posição será ativada de acordo com o sinal.strategy.close_allQuando chamado, ele elimina todas as posições na direção atual.strategy.close_allOs principais parâmetros da função são:when

parâmetro:

  • whenCondições de execução

O que podemos observar usando um exemplo:

/*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 teste começa com uma posição de 0…strategy.position_size==0É verdade), então executa o ID como um longínquo longínquo quando ele atende às condições do parâmetro when.strategy.entryFunção de entrada: após ter várias posiçõesstrategy.position_sizeMaior do que 0, então a função de entrada do ID para o short quebra-cabeça só pode ser executada, devido a posse atual de posições de muitos cabeças, este sinal de inverso de defesa que aparece neste momento pode levar a quebrar as posições de muitos cabeças e voltar a abrir. Em seguida, nós escrevemos em se condição quandostrategy.position_size < 0Quando, ou seja, quando a posição de cabeça vazia é levada, a posição total da direção atual é eliminada. E a marcaenableStop := true◦ Fazer com que a estratégia pare de ser executada para que o log possa ser observado.

Descobrirstrategy.close_allEsta função não possui parâmetros para especificar o preço de pedido de liquidação. Esta função é usada principalmente para liquidação imediata a preços de mercado atuais.


4、strategy.exit

strategy.exitA função é usada para a operação de liquidação de entrada para a posição, diferentemente da funçãostrategy.closeestrategy.close_allA função é a liquidação imediata a preços de mercado atuais.strategy.exitA função planeja a liquidação de acordo com a configuração dos parâmetros.

parâmetro:

  • id: ID de pedido para esta lista de condições de liquidação.
  • from_entry: ID de entrada para especificar a operação de liquidação.
  • qtyNúmero de liquidações.
  • qty_percentPercentagem de equilíbrio, faixa: 0 ~ 100 ◦
  • profitO objetivo de lucro, expresso em pontos.
  • lossO objetivo é parar os danos, em pontos.
  • limitA meta de lucro é definida pelo preço.
  • stopA meta de stop loss é definida pelo preço.
  • whenCondições de execução

Use uma estratégia de teste para entender o uso de cada parâmetro.

/*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")          // 如果仅仅指定一个id参数,则该退场订单无效,参数profit, limit, loss, stop等出场条件也至少需要设置一个,否则也无效
    strategy.exit("exit1", "long1", profit=50)                    // 由于long1入场订单没有成交,因此ID为exit1的出场订单也处于暂待状态,直到对应的入场订单成交才会放置exit1
    strategy.exit("exit2", "long2", qty=0.1, profit=100)          // 指定参数qty,平掉ID为long2的持仓中0.1个持仓
    strategy.exit("exit3", "long3", qty_percent=50, limit=strategy.opentrades.entry_price(findOrderIdx("long3")) + 1000)   // 指定参数qty_percent,平掉ID为long3的持仓中50%的持仓
    isExit := true 

if bar_index == 0 
    runtime.log("每点价格为:", syminfo.mintick)    // 每点价格和Pine语言模板参数上「定价货币精度」参数设置有关

Usando o teste de retorno do modelo de preço em tempo real, a estratégia de teste começa a executar três operações de entrada:strategy.entryFunção), long1 deliberadamente definidolimitParâmetros, o preço do pendrive é de 1 para que ele não possa ser transacionado. Então teste a função de saída da condiçãostrategy.exit。 Usou paradas por pontos, paradas por preços, paradas por posições de quantidade fixa, paradas por percentagem. 。 Considerando que o exemplo de extensão apenas demonstra paradas. 。 A operação de parada de perda também é equivalente.strategy.exitA função também possui parâmetros de tracking stop loss mais complexos:trail_pricetrail_pointstrail_offsetPode-se também testar o seu uso neste exemplo.


5、strategy.cancel

strategy.cancelFunções usadas para cancelar / desativar todos os comandos de lista de pré-enrolamento. Estas funçõesstrategy.order, strategy.entry , strategy.exitPode gerar entrada ID. O principal parâmetro da função é:idwhen

parâmetro:

  • idA identificação de entrada a ser cancelada:
  • whenCondições de execução

Esta função é muito bem compreendida e é usada para cancelar ordens de entrada que não foram feitas.

/*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.3, limit=3)

if not barstate.ishistory and close < open 
    strategy.cancel("long1")
    strategy.cancel("long2")
    strategy.cancel("long3")
    isStop := true 

6、strategy.cancel_all

strategy.cancel_allFunções estrategy.cancelA função é semelhante a: cancelar / desativar todas as ordens de lista pré-encomendada: pode ser especificadawhenParâmetros

parâmetro:

  • whenCondições de execução

”`pine /*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,