Tutorial de Introdução à Língua PINE do FMZ Quant

Autora:Lydia., Criado: 2022-09-23 15:23:34, Atualizado: 2024-02-27 16:47:41

[TOC]

img

Tutorial de Introdução à Língua PINE do FMZ Quant

Video tutorial de suporte:https://www.youtube.com/watch?v=CA3SwJQb_1g

FMZ Quant Trading Platform suporta a escrita de estratégias de linguagem Pine, backtesting e negociação ao vivo de estratégias de linguagem Pine, e é compatível com versões mais baixas da linguagem Pine.Praça da Estratégiana plataforma de negociação quantitativa FMZ (FMZ.COM).

O FMZ suporta não apenas a linguagem Pine, mas também a poderosa função de desenho da linguagem Pine. As várias funções, ferramentas ricas e práticas, gerenciamento eficiente e conveniente na plataforma FMZ aumentam ainda mais a praticidade da estratégia Pine (script). Com base na compatibilidade com a linguagem Pine, o FMZ também expande, otimiza e corrige a linguagem Pine até certo ponto. Antes de entrar no tutorial oficialmente, vamos dar uma olhada em quais mudanças foram feitas na linguagem Pine no FMZ em comparação com a versão original.

Um breve resumo de algumas das diferenças óbvias:

    1. A estratégia Pine no FMZ, o identificador de versão no início do código//@versione ostrategy, indicatordeclarações no início do código não são obrigatórias para escrever, FMZ não suportaimportimportarlibraryFunciona por enquanto.

    Pode-se ver que algumas estratégias escritas assim:

    //@version=5
    indicator("My Script", overlay = true)
    src = close
    a = ta.sma(src, 5)
    b = ta.sma(src, 50)
    c = ta.cross(a, b)
    plot(a, color = color.blue)
    plot(b, color = color.black)
    plotshape(c, color = color.red)
    

    Ou escreva assim:

    //@version=5
    strategy("My Strategy", overlay=true)
    
    longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
    if (longCondition)
        strategy.entry("My Long Entry Id", strategy.long)
    
    shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
    if (shortCondition)
        strategy.entry("My Short Entry Id", strategy.short)
    

    Na FMZ, pode ser simplificado para:

    src = close
    a = ta.sma(src, 5)
    b = ta.sma(src, 50)
    c = ta.cross(a, b)
    plot(a, color = color.blue, overlay=true)
    plot(b, color = color.black, overlay=true)
    plotshape(c, color = color.red, overlay=true)
    

    Ou:

    longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
    if (longCondition)
        strategy.entry("My Long Entry Id", strategy.long)
    
    shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
    if (shortCondition)
        strategy.entry("My Short Entry Id", strategy.short)
    
    1. Algumas configurações relacionadas à negociação da estratégia (script) são definidas pelos parâmetros Pine Language Trading Class Library na interface de estratégia FMZ.
    • Modelo de preço de fechamento e modelo de preço em tempo real Na visão de negociação, podemos usar ocalc_on_every_tickParâmetro dostrategyfunção para definir o script estratégia para executar a lógica estratégia em tempo real quando o preço muda sempre.calc_on_every_ticko parâmetro deve ser definido emtrue. Ocalc_on_every_tickO parâmetro padrão éfalse, ou seja, a lógica da estratégia é executada somente quando o atual BAR da linha K da estratégia está completamente concluído. No FMZ, é definido pelos parâmetros do modelo Pine Language Trading Class Library.

      img

    • O controlo de precisão numérica, como o preço e o montante da ordem quando a estratégia é executada, deve ser especificado na FMZ Na visão de negociação, não há problema de precisão ao colocar ordens de negociação reais, porque só pode ser testado em simulação. No FMZ, é possível executar a estratégia Pine em negociação real. Em seguida, a estratégia precisa ser capaz de especificar a precisão do preço e a precisão do valor da ordem da variedade de negociação de forma flexível. As configurações de precisão controlam o número de casas decimais nos dados relevantes para evitar que os dados não atendam aos requisitos de ordem da bolsa e, portanto, não possam fazer uma ordem.

    • Código do contrato de futuros Se o produto de negociação no FMZ for um contrato, ele tem dois atributos, eles são Trading Pair e Contract Code, respectivamente. Além de definir o par de negociação explicitamente, também é necessário definir o código de contrato específico no parâmetro Variety Code do modelo Pine Language Trading Class Library durante a negociação real e o backtesting.swapPor exemplo, algumas bolsas têm contratos trimestrais, você pode preencherquarterEstes códigos de contrato são consistentes com os códigos de contrato de futuros definidos no documento da API da linguagem Javascript/python/c++ da FMZ.

    Para outras definições, tais como o montante mínimo da ordem, o montante da ordem por defeito, etc., consulte a introdução do parâmetro noArgumentos de modelo da biblioteca de classe comercial de linguagem de pinhona documentação da língua Pine.

    1. Funções para extensões FMZ:runtime.debug , runtime.log, runtime.errorutilizadas para depuração.

    Foram adicionadas 3 funções à plataforma FMZ para depuração.

    • runtime.debug: Imprimir informação sobre variáveis no console, que geralmente não é utilizada com esta função.

    • runtime.logFunções específicas da língua PINE na FMZ.

      runtime.log(1, 2, 3, close, high, ...), Multiple parameters can be passed.
      
    • runtime.error: Resultará num erro de execução com a mensagem de erro especificada no parâmetro de mensagem quando chamada.

      runtime.error(message)
      
    1. OoverlayParâmetro é estendido em algumas das funções de desenho

    Na linguagem Pine no FMZ, as funções de desenhoplot, plotshape, plotchar, etc. acrescentaram ooverlaysuporte de parâmetros, permitindo especificar o desenho no gráfico principal ou no sub-gráfico.overlayestá definido emtruepara desenhar no gráfico principal, efalseé definido para utilizar o sub-gráfico, o que permite à estratégia Pine na FMZ desenhar simultaneamente o gráfico principal e o sub-gráfico.

    1. Valor dosyminfo.mintickvariável incorporada

    A variável integrada desyminfo.mintickEste valor pode ser controlado pelo parâmetro modelo de precificação de preços da moeda na Pine Language Trading Class Library no FMZbot/backtestA configuração de precisão de moeda de preço 2 significa que o preço é preciso até ao segundo decimal quando se negocia, e a unidade mínima de mudança de preço é 0,01.syminfo.mintické 0,01.

    1. O preço médio em FMZ PINE Script são todos incluindo comissão

    Por exemplo: o preço da encomenda é 8000, a direcção de venda, a quantidade é 1 lote (peça, folha), o preço médio após a transacção não é 8000, mas inferior a 8000 (o custo inclui a taxa de manipulação).

Fundamentos da Língua dos Pinheiros

Ao começar a aprender os fundamentos da linguagem Pine, pode haver alguns exemplos de instruções e gramática de código com os quais não estamos familiarizados. Não importa se você não entende, podemos familiarizar-nos com os conceitos primeiro e entender o propósito do teste, ou você pode verificar a documentação da linguagem Pine no FMZ para instruções. Em seguida, siga o tutorial passo a passo para se familiarizar com várias gramáticas, instruções, funções e variáveis incorporadas.

Execução do modelo

Quando se começa a aprender a linguagem Pine, é muito necessário entender os conceitos relacionados, como o processo de execução do programa de script da linguagem Pine. A estratégia da linguagem Pine é executada com base no gráfico. Pode-se entender que a estratégia da linguagem Pine é uma série de cálculos e operações, que são executadas no gráfico na ordem de séries temporais a partir dos dados mais antigos que foram carregados no gráfico. A quantidade de dados que o gráfico inicialmente carrega é limitada. Na negociação real, a quantidade máxima de dados é geralmente determinada com base no volume máximo de dados retornado pela interface de troca, e a quantidade máxima de dados durante o backtesting é determinada com base nos dados fornecidos pela fonte de dados do sistema de backtesting. A barra de linha K mais à esquerda do gráfico, ou seja, o primeiro conjunto de dados do gráfico, tem um valor de índice de 0.bar_indexna língua Pine.

plot(bar_index, "bar_index")

img

OplotA função é uma das funções que vamos usar mais no futuro. o uso é muito simples, é desenhar uma linha no gráfico de acordo com os parâmetros de entrada, os dados de entrada ébar_index, e a linha é denominada comobar_index. Pode-se ver que o valor da linha chamada bar_index no primeiro Bar é 0, e aumenta em 1 para a direita à medida que o Bar aumenta.

Como as configurações da estratégia são diferentes, os métodos de execução do modelo da estratégia são diferentes, eles podem ser divididos emclosing price modelereal-time price modelTambém apresentamos brevemente os conceitos deles antes.

  • Modelo de preço de encerramento

    Quando o código de estratégia é executado, o período da barra de linha K atual é completamente executado, e quando a linha K é fechada, o período de linha K foi concluído.

  • Modelo de preços em tempo real

    Quando o código de estratégia é executado, independentemente de a barra de linha K atual estar fechada ou não, a lógica da estratégia Pine será executada quando o mercado mudar toda vez, e o sinal de negociação desencadeado será executado imediatamente.

Quando a estratégia da linguagem Pine é executada da esquerda para a direita no gráfico, as barras da linha K no gráfico são divididas emHistorical BarseReal-time Bars:

  • Bar Histórico

    Quando a estratégia é definida como Tick Model e começa a ser executada, todas as barras de linha K no gráfico, exceto a mais à direita, sãoHistorical BarsA lógica da estratégia é executada apenas uma vez em cadahistorical bar- Não. Quando a estratégia é definida como Bar Model e começa a ser executada, todas as barras no gráfico sãohistorical barsA lógica da estratégia é executada apenas uma vez em cadahistorical bar.

    Cálculo baseado em Bars históricos: O código de estratégia é executado uma vez no estado de fechamento da barra histórica e, em seguida, o código de estratégia continua a ser executado na próxima barra histórica até que todas as barras históricas sejam executadas uma vez.

  • Bar em tempo real

    Quando a estratégia é executada para a última barra de linha K no extremo direito, a barra é uma barra em tempo real.

    Quando a estratégia for definida em Tick Model e começar a ser executada, a lógica da estratégia será executada uma vez para cada mudança de mercado na barra em tempo real. Quando a estratégia for definida em Bar Model e começar a ser executada, a barra em tempo real não será exibida no gráfico.

    Cálculo baseado em Bar em tempo real: Se a estratégia estiver definida em Bar Model e o gráfico não exibir as barras em tempo real, o código da estratégia só será executado uma vez quando a barra atual se fechar. Se a estratégia for definida em Tick Model, o cálculo na barra em tempo real é completamente diferente da barra histórica, e o código da estratégia será executado uma vez para cada mudança de mercado em barras de negociação ao vivo.high, low, closeOs valores de mercado são determinados em barras históricas, e estes valores podem mudar cada vez que o mercado muda em barras em tempo real.closeRepresenta sempre o último preço corrente, ehighelowrepresentam sempre o ponto mais alto e o ponto mais baixo alcançado desde o início da barra em tempo real atual. Estas variáveis embutidas representam o valor final da barra em tempo real quando foi atualizada pela última vez.

    Mecanismo de reversão ao executar estratégias em Bar em tempo real (modelo de preços em tempo real): Durante a execução de Bar em tempo real, a reinicialização de variáveis definidas pelo usuário antes de cada nova iteração da estratégia é chamada de rollback.

    Atenção:

    /*backtest 
    ...
    ..
    .
    */
    

    O conteúdo do pacote é a informação de configuração do backtest guardada sob a forma de código na plataforma FMZ.

    /*backtest
    start: 2022-06-03 09:00:00
    end: 2022-06-08 15:00:00
    period: 1m
    basePeriod: 1m
    exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
    */
    
    var n = 0
    if not barstate.ishistory
        runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
        n := n + 1
        runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
      
    plot(n, title="n")
    

    img

Nós só examinar a cena executada durante o tempo real Bars, por isso usamos onot barstate.ishistoryexpressão para limitar o acúmulo da variável n apenas no Bar em tempo real, e usarruntime.logFunção para extrair as informações no log da estratégia antes e depois da operação de acumulação.plot, pode-se ver que n é sempre 0 quando a estratégia está sendo executada em Bars históricos. Quando a Barra em tempo real é executada, a operação de adição de 1 para n é acionada, e a operação de adição de 1 para n é executada quando a estratégia é executada em cada rodada da Barra em tempo real. Pode-se observar a partir da mensagem de log que n será redefinido para o valor finalmente enviado pela estratégia de execução da Barra anterior quando o código de estratégia é re-executado em cada rodada. A atualização do valor n será enviada quando o código de estratégia é executado na Barra em tempo real pela última vez, então você pode ver que o valor da curva n aumenta em 1 com cada aumento de Bar começando da Barra em tempo real no gráfico.

Resumo:

  1. O código de estratégia é executado uma vez cada vez que o mercado é atualizado quando a estratégia começa a ser executada em um Bar em tempo real.
  2. Quando executadas em um Bar em tempo real, as variáveis são revertidas cada vez que o código de estratégia é executado.
  3. Quando executadas em uma barra em tempo real, as variáveis são enviadas uma vez quando o fechamento é atualizado.

Devido ao rollback de dados, as operações de desenho, como curvas no gráfico, também podem causar redesenho.

var n = 0
if not barstate.ishistory
    runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
    n := open > close ? n + 1 : n
    runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
  
plot(n, title="n")

Captura de tela do tempo Aimg

Captura de tela do tempo Bimg

Só modificamos a frase:n := open > close ? n + 1 : n, somente adicionar 1 a n quando a barra em tempo real atual é uma linha negativa (ou seja, o preço de abertura é maior que o preço de fechamento). Pode-se ver que no primeiro gráfico (tempo A), uma vez que o preço de abertura era maior que o preço de fechamento (linha negativa) naquele momento, n foi acumulado por 1, e o valor de n exibido na curva do gráfico foi 5. Então o mercado mudou e o preço foi atualizado como mostrado no segundo gráfico (tempo B). Neste momento, o preço de abertura é menor que o preço de fechamento (linha positiva), e o valor de n retrocede sem aumentar 1. A curva de n no gráfico também é redesenhada imediatamente, e o valor de n na curva é 4. Portanto, os sinais, como cruzamento e descida exibidos nas barras em tempo real, são incertos e podem mudar.

  • Contexto variável em funções

    De acordo com algumas descrições em tutoriais de Pine, as variáveis na função têm as seguintes diferenças das variáveis fora da função:

    O histórico das variáveis de série usadas na função Pine é criado com cada chamada sucessiva à função. Se a função não for chamada em cada barra em que o script é executado, isso resultará em uma discrepância entre os valores históricos da série dentro e fora do bloco local da função. Portanto, se a função não for chamada em cada barra, a série referenciada dentro e fora da função com o mesmo valor de índice não se referirá ao mesmo ponto histórico.

    Não importa, vamos descobrir com um código de teste a correr no FMZ:

    /*backtest
    start: 2022-06-03 09:00:00
    end: 2022-06-08 15:00:00
    period: 1m
    basePeriod: 1m
    exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
    */
      
    f(a) => a[1]
    f2() => close[1]  
    
    oneBarInTwo = bar_index % 2 == 0
    plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")   
    plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")   
    plot(close[2], title = "close[2]", color = color.red, overlay = true)
    plot(close[1], title = "close[1]", color = color.green, overlay = true)
    

    Captura de tela do backtest em execução

    img

    O código de ensaio é relativamente simples, principalmente para examinar os dados referenciados por dois métodos, a saber:f(a) => a[1]ef2() => close[1].

    • f(a) => a[1]: Utilize o método de passagem de parâmetros, a função retorna paraa[1] finally.

    • f2() => close[1]: Utilize a variável embutidaclosediretamente, e a função retorna paraclose[1] finally.

O[]O símbolo é usado para se referir ao valor histórico da variável da série de dados, e fechar[1] refere-se aos dados do preço de fechamento na barra antes do preço de fechamento atual.

  • plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")Desenhe um caractere A, a cor é vermelha, ele é desenhado quando oneBarInTwo é verdade, e a posição desenhada (no eixo Y é: o valor devolvido porf(close).

  • plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")Desenhe um caractere B, a cor é verde, ele é desenhado apenas quando oneBarInTwo é verdade, e a posição desenhada (no eixo Y é: o valor devolvido porf2().

  • plot(close[2], title = "close[2]", color = color.red, overlay = true)Desenhe uma linha, a cor é vermelha, e a posição desenhada (no eixo Y) é:close[2], que é o preço de encerramento da segunda barra antes da barra corrente (contando 2 barras à esquerda).

  • plot(close[1], title = "close[1]", color = color.green, overlay = true)Desenhe uma linha, a cor é verde, e a posição desenhada (no eixo Y) é:close[1], que é o preço de encerramento da primeira barra antes da barra corrente (contando 1 barra para a esquerda).

Pode ser visto a partir da captura de tela da estratégia backtesting que, embora tanto a funçãof(a) => a[1]usado para desenhar o marcador A e a funçãof2() => close[1]Usado para desenhar o marcador B [1] para se referir aos dados históricos da série de dados, as posições do marcador A e B no gráfico são completamente diferentes.plot(close[2], title = "close[2]", color = color.red, overlay = true), os dados utilizados para desenhar a linha são:close[2].

img

A razão é calcular se deve desenhar os marcadores A e B através do índice da barra de linha K, ou seja, a variável embutidabar_indexOs marcadores A e B não são desenhados em cada barra de linha K (o cálculo da função é chamado ao desenhar).f(a) => a[1]não será o mesmo que o valor referenciado pela funçãof2() => close[1]se a função não for chamada em cada Bar (mesmo que ambos usem o mesmo índice como [1]).

  • Algumas funções integradas precisam ser calculadas em cada barra para calcular os seus resultados corretamente

    Para ilustrar esta situação com um exemplo simples:

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

    Nós escrevemos o código de chamada de funçãota.barssince(close < close[1])num operador ternáriocondition ? value1 : value2. Isso faz com que a função ta.barssince seja chamada somente quandoclose > close[1]Mas o...ta.barssinceFunção é calcular o número de K-linhas desde a última vezclose < close[1]Quando a função ta.barssince é chamada, ela é sempre close > close[1], ou seja, o preço de fechamento atual é maior que o preço de fechamento do Bar anterior. Quando a função ta.barssince é chamada, a condição close < close[1] não é estabelecida e não há posição recente onde ela se mantenha.

    ta.barssince: Quando chamada, a função retorna na se a condição nunca tiver sido atendida antes da linha K atual.

Como mostra o gráfico:

img

Então, quando o gráfico é desenhado, apenas os dados com um valor para a variável res (-1) é desenhado.

Para evitar este problema, basta tomar ota.barssince(close < close[1])função chamar para fora do operador ternário e escrevê-lo fora de quaisquer possíveis ramos condicionais, fazendo-o realizar cálculos em cada K-line Bar.

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

Séries temporais

O conceito de série de tempo é muito importante na linguagem Pine, e é um conceito que devemos entender quando aprendemos a linguagem Pine. A série de tempo não é um tipo, mas uma estrutura básica para armazenar valores contínuos de variáveis ao longo do tempo. Sabemos que os scripts Pine são baseados em gráficos e o conteúdo mais básico exibido no gráfico é o gráfico de linha K. Série de tempo onde cada valor está associado a um carimbo de tempo de uma barra de linha K.opené uma variável embutida (embutida) da linguagem Pine, e sua estrutura é armazenar a série de tempo do preço de abertura de cada K-line Bar.openrepresenta os preços de abertura de todos os K-line Bars a partir da primeira barra no início do gráfico de linha K atual para a barra onde o script atual é executado.openno código de estratégia Pine, é o preço de abertura da barra da linha K quando o código de estratégia é executado atualmente.[]Quando a estratégia Pine é executada em uma certa K-line Bar, useopen[1]referir-se ao preço de abertura da barra da linha K anterior (ou seja, o preço de abertura do período da linha K anterior) que se refere aoopensérie de tempo em que esta barra de linha K está sendo executada atualmente pelo script.

  • Variaveis em séries de tempo são muito convenientes para computação Vamos tomar a função embutidata.cumPor exemplo:

    ta.cum
    
    Cumulative (total) sum of `source`. In other words it's a sum of all elements of `source`.
    ta.cum(source) → series float
    RETURNS
    Total sum series.
    ARGUMENTS
    source (series int/float)
    SEE ALSO
    math.sum
    

    Código de ensaio:

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

    Há muitas funções embutidas comota.cumque pode processar dados sobre séries temporais diretamente.ta.cumé a acumulação dos valores correspondentes às variáveis passadas em cada barra de linha K, e depois usamos um gráfico para facilitar a compreensão.

    Processo de operação da estratégia Variavel incorporada bar_index V1 V2
    A estratégia é executada na primeira barra da linha K. 0 1 1
    A estratégia é executada na segunda barra da linha K. 1 1 2
    A estratégia funciona na terceira barra da linha K. 2 1 3
    A estratégia é executada na N + 1a K-line Bar N 1 N+1

    Pode-se ver que v1, v2 e até mesmo bar_index são todas estruturas de séries temporais, e há dados correspondentes em cada barra. Se o código de teste usa o modelo Bar ou o modelo Tick, a única diferença é se a barra em tempo real é exibida no gráfico. Para backtest rápido, usamos o modelo Tick para testar.

    img

    Porque a variável v1 é 1 em cada Bar, quando ota.cum(v1)A função é executada na primeira linha K Bar, há apenas a primeira Bar, então o resultado do cálculo é 1 e atribuído à variável v2. Quando?ta.cum(v1)O resultado do cálculo é 2, que é atribuído à variável v2, e assim por diante. De fato, pode-se observar que v2 é o número de barras de linha K no gráfico, já que o índice da linha K é o número de barras de linha K no gráfico.bar_indexé incrementado a partir de 0, entãobar_index + 1No gráfico, também podemos ver que as linhasv2ebar_indexde facto se sobrepõem.

    img

    Da mesma forma, também posso usar ota.cumTudo o que eu preciso fazer é escrever assim:ta.cum(close), Quando a estratégia é executada para a barra em tempo real no extremo direito, o resultado calculado porta.cum(close)é a soma dos preços de fechamento de todas as Barras no gráfico (se não correr para a extrema direita, só se acumula até a Bar atual).

    As variáveis da série temporal podem também ser calculadas utilizando operadores, como o código:ta.sma(high - low, 14), subtrair a variável incorporadahigh(o preço mais elevado da barra da linha K)low(o preço mais baixo de K-line Bar), e finalmente usar ota.smaFunção para calcular o valor médio.

  • O resultado de uma chamada de função também deixará vestígios de valores na série temporal.

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

    O código de teste é executado durante o backtesting, e pode-se observar que os valores dev1ev2O resultado calculado pela chamada da função deixará vestígios do valor na série temporal, tais como ota.highest(high, 10)no códigota.highest(high, 10)[1]O resultado calculado pela chamada da função também pode usar [1] para se referir ao seu valor histórico.ta.highest(high, 10)correspondente à barra anterior da barra atual, o resultado do cálculo éta.highest(high[1], 10)Então...ta.highest(high[1], 10)eta.highest(high, 10)[1]são exatamente equivalentes.

    Utilize outra função de desenho para a verificação de informações de saída:

    a = ta.highest(close, 10)[1]
    b = ta.highest(close[1], 10)
    plotchar(true, title="a", char=str.tostring(a), location=location.abovebar, color=color.red, overlay=true)
    plotchar(true, title="b", char=str.tostring(b), location=location.belowbar, color=color.green, overlay=true)
    

    Podemos ver que os valores da variável a e da variável b na série de tempo são exibidos acima e abaixo das Bars correspondentes. Podemos manter este código de desenho durante o processo de aprendizagem, porque muitas vezes podemos precisar de extrair informações no gráfico para observação durante backtesting e experimentação.

    img

Estrutura do script

Estrutura geral

Na parte inicial do tutorial, resumimos algumas diferenças no uso da linguagem Pine no FMZ e no Trading View.indicator(), strategy(), elibrary()É claro que, a fim de ser compatível com versões anteriores de scripts Pine, estratégias como://@version=5, indicator(), strategy()Algumas configurações de estratégia também podem ser definidas passando parâmetros nostrategy() function.

<version>
<declaration_statement>
<code>

O<version>As informações de controlo de versão podem ser omitidas.

Observações

A língua do pinheiro usa//como um símbolo de comentário de linha única, uma vez que a língua Pine não tem um símbolo de comentário de várias linhas. FMZ estende o símbolo de comentário/**/para comentários de várias linhas.

Código

As linhas no script que não são comentários ou diretrizes do compilador são instruções, que implementam o algoritmo do script.

  • Declaração variável
  • Reatribuição de variáveis
  • Declaração de função
  • Chamadas de funções integradas, chamadas de funções definidas pelo utilizador
  • if, for, whileouswitchEstrutura

As declarações podem ser organizadas de várias maneiras.

  • Algumas instruções podem ser expressas em uma linha, como a maioria das declarações de variáveis, linhas contendo apenas uma chamada de função ou declarações de função de linha única.
  • As instruções no escopo global de um script (ou seja, partes que não fazem parte de um bloco local) não podem começar com umspaceAs linhas que começam na primeira posição, por definição, tornam-se parte do escopo global do script.
  • Uma declaração de estrutura ou de função de várias linhas requer sempre umalocal blockUm bloco local deve ser recuado por uma aba ou quatro espaços (caso contrário, será analisado como o código concatenado da linha anterior, ou seja, julgado como o conteúdo contínuo da linha anterior de código), e cada bloco local define um escopo local diferente.
  • Várias instruções de linha única podem ser concatenadas em uma única linha usando vírgulas (,) como separadores.
  • Uma linha pode conter comentários ou ter apenas comentários.
  • As linhas também podem ser enroladas (continuadas em várias linhas).

Por exemplo, inclui três blocos locais, um na declaração da função personalizada e dois na declaração da variável usando a estrutura if, da seguinte forma:

indicator("", "", true)             // declaration statement (global scope), can be omitted

barIsUp() =>                        // function declaration (global scope)
    close > open                    // local block (local scope)

plotColor = if barIsUp()            // variable declaration (global scope)
    color.green                     // local block (local scope)
else
    color.red                       // local block (local scope)

runtime.log("color", color = plotColor)  // Call a built-in function to output the log (global scope)

Código de quebra de linha

As linhas longas podem ser divididas em várias linhas, ou enroladas. Uma linha enrolada deve ser recuada por qualquer número de espaços, desde que não seja um múltiplo de 4 (esses limites são usados para recuar blocos locais).

a = open + high + low + close

Pode ser embalado como (observe-se que o número de espaços recuados por linha não pode ser múltiplo de 4):

a = open +
      high +
          low +
             close

Uma chamada longa pode ser enrolada como:

close1 = request.security(syminfo.tickerid, "D", close)      // syminfo.tickerid daily level closing price data series for the current trading pair
close2 = request.security(syminfo.tickerid, "240", close)    // syminfo.tickerid 240-minute level closing price data series for the current trading pair
plot(ta.correlation(close, open, 100),                       // line-long plot() calls can be wrapped
   color = color.new(color.purple, 40),
   style = plot.style_area,
   trackprice = true)

As declarações em declarações de funções definidas pelo usuário também podem ser enroladas. No entanto, uma vez que um bloco local deve começar com uma hendidura na gramática (4 espaços ou 1 guia), ao dividi-lo na próxima linha, a continuação de uma declaração deve começar com mais de uma hendidura (não igual a 4 múltiplos de espaços).

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

Marcadores e operadores

Marcadores

Antes de reconhecer as variáveis, devemos entender o conceito de marcadores primeiro.funçãoevariável(usado para nomear variáveis e funções).Funçõesvamos ver em nossos tutoriais posteriores, vamos aprender sobre markers primeiro.

    1. Os marcadores devem começar com uma maiúscula.(A-Z)ou em minúsculas(a-z)letra ou sublinhação(_)como o primeiro carácter do marcador.
    1. O próximo caracter após o primeiro caracter de um marcador pode ser umcarta, sublinhação, ou umNúmero.
    1. A designação dos marcadores é sensível às minúsculas e pequenas.

Tal como os seguintes marcadores denominados:

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

Como a maioria das linguagens de programação, a linguagem Pine também tem sugestões de escrita.

    1. Todas as letras maiúsculas são usadas para nomear constantes.
    1. Utilize oCasca inferior de camelopara outros nomes de marcadores.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7

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

Operadores

Os operadores são alguns símbolos de operação usados em linguagens de programação para construir expressões, e expressões são regras computacionais projetadas para certos propósitos computacionais quando escrevemos estratégias.

Operadores de atribuição, operadores aritméticos, operadores de comparação, operadores lógicos,? :operadores ternários,[]operadores de referência históricos.

Tomando o operador aritmético*como exemplo, é diferente do problema de tipo causado pelo resultado de retorno do operador da linguagem Pine no Trading View.

//@version=5
indicator("")
lenInput = input.int(14, "Length")
factor = year > 2020 ? 3 : 1
adjustedLength = lenInput * factor
ma = ta.ema(close, adjustedLength)  // Compilation error!
plot(ma)

Ao executar este script no Trading View, um erro de compilação ocorrerá.adjustedLength = lenInput * factor, o resultado é:series inttipo (série), mas o segundo parâmetro da funçãota.emaMas não há tais restrições rigorosas no FMZ, o código acima pode ser executado normalmente.

Vamos dar uma olhada na utilização de vários operadores juntos.

Operadores de atribuição

Existem 2 tipos de operadores de atribuição:=, :=, que vimos em vários exemplos no início do tutorial.

O=O operador é usado para atribuir um valor a uma variável quando ela é inicializada ou declarada.=Estas são declarações válidas de variáveis:

a = close           // Use built-in variables to assign values to a
b = 10000           // Use numerical assignment
c = "test"          // Use string assignment
d = color.green     // Use color value assignment
plot(a, title="a")
plot(b, title="b")
plotchar(true, title="c", char=str.tostring(c), color=d, overlay=true)

Observe que a declaração de atribuiçãoa = close, a variável a de cada barra é o preço de fechamento (fechamento) corrente da barra. Outras variáveisb, c, dsão inalteradas e podem ser testadas no sistema de backtest na FMZ, e os resultados podem ser vistos no gráfico.

:=A definição de valor de uma variável pode ser simplesmente entendida como a:=O operador é usado para modificar os valores das variáveis que foram declaradas e inicializadas. Se usarmos o:=Operador para atribuir um valor a uma variável não inicializada ou declarada, ele causará um erro, por exemplo:

a := 0

Por conseguinte, o:=O operador de atribuição é geralmente usado para reatribuir variáveis existentes, por exemplo:

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

plot(b)

Julgando seclose > open(ou seja, o BAR atual é uma linha positiva), a variável a é verdadeira.b := b + 1é executado, e o operador de atribuição:=Em seguida, usamos a função de gráfico para desenhar o valor da variável b em cada BAR da série de tempo no gráfico, e conectá-los em uma linha.

Pensamos que quando uma linha positiva BAR aparece, b continuará a acumular por 1? Claro que não, aqui declaramos e inicializamos a variável b como 0 sem usar qualquer designação de palavra-chave.b=0é executado em cada BAR, então podemos ver que o resultado deste código é redefinir a variável b para 0 toda vez, se a variável a for verdadeira, ou seja, em linha comclose > open, então b será incrementado em 1 quando o código é executado nesta rodada, e b é 1 quando a função de gráfico desenha, mas b é reatribuído a 0 quando o código é executado na próxima rodada.

Quando se trata de operadores de atribuição, devemos expandir em duas palavras-chave:var, varip

  • Var

    Na verdade, vimos e usamos esta palavra-chave em tutoriais anteriores, mas não discutimos em detalhes naquela época.

    var é uma palavra-chave usada para atribuir e inicialização de uma única vez de variáveis. em geral, gramática de atribuição de variáveis que não contém a palavra-chave var faz com que o valor da variável s seja substituído toda vez que os dados são atualizados.

    Nós ainda usamos este exemplo, mas usamos ovarpalavra-chave ao atribuir um valor a b aqui.

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

    Ovarpalavra-chave permite que a variável b para executar a atribuição inicial apenas, e então ele não irá redefinir b para 0 toda vez que a lógica de estratégia é executada, por isso pode ser observado a partir da linha desenhada no tempo de execução que b é o número de linhas positivas BARs que apareceram quando o atual K linha BAR foi backtested.

    As variáveis declaradas por var podem ser escritas não apenas no escopo global, mas também em blocos de código, como este exemplo:

    strategy(overlay=true)
    var a = close
    var b = 0.0
    var c = 0.0
    var green_bars_count = 0
    if close > open
        var x = close
        b := x
        green_bars_count := green_bars_count + 1
        if green_bars_count >= 10
            var y = close
            c := y
    plot(a, title = "a")
    plot(b, title = "b")
    plot(c, title = "c")
    

    A variável a contém o preço de fechamento da primeira barra da série. A variável b contém o preço de encerramento da primeira barra de preços verde da série. A variável c detém o preço de fechamento da décima barra verde da série.

  • variável

    Vemos a palavra chavevarippela primeira vez, podemos olhar para a descrição desta palavra-chave:

    varip (var intrabar persist) é uma palavra-chave para atribuição e inicialização única de variáveis.

    É difícil de entender? Não importa, explicamos através de um exemplo, é fácil de entender.

    strategy(overlay=true)
    
    // test var varip
    var i = 0
    varip ii = 0  
    
    // Print the i and ii changed in each round of the strategy logic on the chart
    plotchar(true, title="ii", char=str.tostring(ii), location=location.abovebar, color=color.red)
    plotchar(true, title="i", char=str.tostring(i), location=location.belowbar, color=color.green)  
    
    // Increment i and ii by 1 for each round of logic execution
    i := i + 1
    ii := ii + 1
    

    Este código de teste tem diferentes desempenhos no Modelo de barra e no Modelo de tique:

    Modelo de barra: Você se lembra que a execução da estratégia que explicamos anteriormente é dividida em estágio histórico BAR e estágio BAR em tempo real? no Modelo de Bar, o estágio histórico da linha K, as variáveisi, iideclarado em:var, varipexecutar operações incrementais em cada rodada de execução do código de estratégia. Portanto, pode-se ver que os números exibidos no K-line BAR do resultado do backtest são incrementados por 1 um por um. Quando o estágio histórico da linha K termina, o estágio da linha K em tempo real começa. As variáveis declaradas por var e varip começam a sofrer mudanças diferentes. Como é o Modelo de Bar, o código de estratégia será executado uma vez para cada mudança de preço em um BAR de linha K,i := i + 1eii := ii + 1A diferença é que ii é modificado toda vez. Embora i seja modificado toda vez, o valor anterior será restaurado quando a lógica de estratégia é executada na próxima rodada (lembre-se do mecanismo de rollback que explicamos no capítulo anterior Execução do Modelo?), e o valor de i não será atualizado até que o atual BAR da linha K seja concluído (isto é, o valor anterior não será restaurado quando a lógica de estratégia é executada na próxima rodada).

    Modelo da marca: Uma vez que o modelo Tick executa a lógica da estratégia apenas uma vez por K-line BAR. Assim, no modelo de preço de fechamento, as variáveis declaradas por var e varip se comportam exatamente da mesma forma no exemplo acima incrementando em 1 para cada K-line BAR durante o estágio histórico de K-line e o estágio de K-line em tempo real.

Operadores aritméticos
Operadores Descrição
+ Adição
- Subtração
* Multiplicação
/ Divisão
% Modulo

O+e-Outros operadores aritméticos só podem ser usados como operadores binários e ele irá relatar um erro se ele foi usado como operadores unários.

  1. Ambos os lados do operador aritmético são de tipo numérico, o resultado é um tipo numérico, inteiro ou ponto flutuante dependendo do resultado da operação.
  2. Se um dos operandos é uma cadeia e o operador é+, o resultado do cálculo é uma cadeia, o valor será convertido para a forma de cadeia, e então as cadeias são costurados juntos.
  3. Se um dos operandos for na, o resultado do cálculo é o valor nulona, e ele mostrará NaN quando impresso no FMZ.
a = 1 + 1 
b = 1 + 1.1
c = 1 + "1.1"
d = "1" + "1.1"
e = 1 + na 

runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e)   
// a: 2 , b: 2.1 , c: 11.1 , d: 11.1 , e: NaN

A linguagem Pine no FMZ é um pouco diferente da linguagem Pine no Trading View, a linguagem Pine no FMZ não é muito rigorosa sobre tipos de variáveis.

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

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

Ele funciona no FMZ, mas relata um erro de tipo na Visualização de Negociação. Se ambos os operandos do operador aritmético forem cadeias, o sistema converte as cadeias em valores numéricos e depois os calcula. Se uma cadeia não numérica não puder ser calculada, o resultado da operação do sistema é um valor nulona.

Operadores de comparação

Os operadores de comparação são todos operadores binários.

Operadores Descrição
< <
> >
<= <=
>= >=
== ==
!= !=

Exemplo de ensaio:

a = 1 > 2 
b = 1 < 2 
c = "1" <= 2 
d = "1" >= 2 
e = 1 == 1 
f = 2 != 1 
g = open > close 
h = na > 1 
i = 1 > na

runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e, ", f:", f, ", g:", g, ", h:", h, ", i:", i)   
// a: false , b: true , c: true , d: false , e: true , f: true , g: false , h: false , i: false

Como podemos ver, o operador de comparação é muito simples de usar, mas este também é o operador que mais usamos quando escrevemos estratégias.close, open, etc. Como com o operador, há uma diferença sobre a linguagem Pine entre FMZ e Trading View.d = "1" >= 2não irá relatar um erro no FMZ, e será executado convertendo a cadeia para um valor primeiro e depois comparando a operação. no Trading View, irá relatar um erro.

Operadores lógicos
Operadores Símbolos de código Descrição
Não, não. Não, não. Operador unário, não operações
e e Operadores binários e operações
ou ou Operadores binários ou operações

Quando se trata de operadores lógicos, então devemos falar sobre tabelas de valores verdadeiros.

a = 1 == 1  // An expression formed by using comparison operators, the result is a Boolean value
b = 1 != 1
c = not b   // Logical not operators
d = not a   // Logical not operators

runtime.log("test the logical operator:and", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a and c:", a and c)
runtime.log("a:", a, ", b:", b, ", a and b:", a and b)
runtime.log("b:", b, ", c:", c, ", b and c:", b and c)
runtime.log("d:", d, ", b:", b, ", d and b:", d and b)

runtime.log("test the logical operator:or", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a or c:", a or c)
runtime.log("a:", a, ", b:", b, ", a or b:", a or b)
runtime.log("b:", b, ", c:", c, ", b or c:", b or c)
runtime.log("d:", d, ", b:", b, ", d or b:", d or b)

runtime.error("stop")

A fim de não sobreimprimir mensagens, lançamos um erro com oruntime.error("stop")Depois disso, podemos observar as informações de saída, e podemos descobrir que o conteúdo impresso é realmente o mesmo que a tabela de valores reais.

Operador ternário

Expressões ternárias usando o operador ternário? :combinado com operandoscondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalseA chamada expressão ternária, operador ternário significa que há três operandos nela.

Emcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse, conditioné a condição de julgamento. Se for verdadeira, o valor da expressão é:valueWhenConditionIsTrueSe...conditioné falso, então o valor da expressão évalueWhenConditionIsFalse.

Exemplo de uma demonstração conveniente, embora de pouca utilidade prática:

a = close > open
b = a ? "positive line" : "negative line"
c = not a ? "negative line" : "positive line"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)

O que fazer se encontrarmos um doji? Não importa! Expressões ternais também podem ser aninhadas, como fizemos no tutorial anterior.

a = close > open
b = a ? math.abs(close-open) > 30 ? "positive line" : "doji" : math.abs(close-open) > 30 ? "negative line" : "doji"
c = not a ? math.abs(close-open) > 30 ? "negative line" : "doji" : math.abs(close-open) > 30 ? "positive line" : "doji"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)

De facto, é equivalente a substituir avalueWhenConditionIsTrueevalueWhenConditionIsFalseemcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalsecom outra expressão ternária.

Operador histórico

Use o operador histórico[]Para se referir a valores históricos em uma série de tempo. Estes valores históricos são os valores da variável na barra de linha K antes da barra de linha K atual quando o script estava sendo executado.[]O operador é usado após variáveis, expressões e chamadas de funções.[]Parênteses quadrados é o deslocamento dos dados históricos que queremos referenciar do atual K-line BAR. Por exemplo, se eu quiser citar o preço de fechamento do último K-line BAR, nós escrevemos como:close[1].

Já vimos algo assim nas lições anteriores:

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

O[]O operador só pode ser usado uma vez no mesmo valor, por isso é errado escrevê-lo assim, e um erro será relatado:

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

Aqui, alguém pode dizer que o operador[]é usado para a estrutura de série, parece que a estrutura de série (série) é semelhante à matriz! Vamos usar um exemplo para ilustrar a diferença entre séries e matrizes na linguagem Pine.

strategy("test", overlay=true)

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

plot(a, title="a")
plot(b, title="b")
plot(c, title="c")

a = close[1][2]Informará de um erro, mas:

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

Mas se for escrito separadamente, não irá relatar um erro.b = close [1], b deve ser um valor, masc = b[1]Pode-se ver que o conceito de série na linguagem Pine não é tão simples quanto uma matriz. Pode ser entendido como o valor histórico na última barra de fechamento (atribuída a b), b também é uma estrutura de série temporal (série temporal), e seu h


Mais.