Tutorial de introdução à linguagem PINE quantitativa do Inventor
Acompanhado de um vídeo tutorial:
É muito difícil entrar em um negócio quantificado?
A plataforma de negociação quantitativa do inventor suporta a estratégia de programação da linguagem Pine, suporta a retrospectiva, a estratégia de linguagem Pine em tempo real e é compatível com versões mais baixas da linguagem Pine.Praça da EstratégiaO blog é uma coleção de várias estratégias e scripts de Pine.
O FMZ não apenas suporta a linguagem Pine, mas também suporta o poderoso recurso de gráficos da linguagem Pine. Os recursos da plataforma FMZ, as ferramentas práticas e eficientes e fáceis de gerenciar, também aumentam a praticidade da estratégia e do script do Pine. O FMZ é baseado na compatibilidade com a linguagem Pine, e também faz um certo grau de extensão, otimização e corte na linguagem Pine.
A seguir, um breve resumo de algumas das diferenças mais evidentes:
-
1 Política Pine no FMZ, versão identificada no início do código
//@versionE o código começastrategy、indicatorA frase não é obrigatória e o FMZ não o suporta por enquantoimportImportaçãolibraryfunção.O blogueiro também escreveu sobre a estratégia do Facebook:
pine//@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 então:
pine//@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)Em FMZ, pode ser simplificado como:
pinesrc = 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:
pinelongCondition = 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) -
Algumas configurações relacionadas a transações são definidas pelo parâmetro "Pine language transaction class library" na interface de estratégia do FMZ.
-
Modelos de preços de fechamento e modelos de preços em tempo real
Na visão de trading, podemos passar porstrategyFunção decalc_on_every_tickParâmetros para definir o script de estratégia para executar a lógica da estratégia em tempo real a cada mudança de preçocalc_on_every_tickOs parâmetros devem ser definidos comotruePor padrãocalc_on_every_tickO parâmetro éfalseA lógica da estratégia só é executada quando a linha K BAR atual da estratégia está completa.
No FMZ, é configurado através dos parâmetros do modelo "Pine Language Transaction Classroom". -
Precisa-se de um controle de precisão numérica na FMZ, como o preço e a quantidade de encomendas no momento da execução da estratégia
Na visão de negociação, como só é possível fazer testes em simulação, não há problemas de precisão de encomendas em tempo real. Na FMZ, é possível executar a estratégia Pine em tempo real. Então, a estratégia precisa ser flexível para especificar a precisão do preço da variedade de negociação e a precisão da quantidade de encomendas. Essas configurações de precisão controlam o número pequeno de dados relevantes, evitando que os dados não atendam aos requisitos de notificação da bolsa e, portanto, não possam ser encomendados. -
Código de contrato a prazo
O tipo de transação no FMZ, se for um contrato, tem duas propriedades: "contract pair" e "contract code". Além da necessidade de definir um par de transação explicitamente no disco e na retrospectiva, também é necessário definir um código de contrato específico no parâmetro "variety code" do modelo "Pine language trading library". Por exemplo, o contrato de permanência é preenchidoswap, Código de contrato para ver se a troca de operação específica tem esse contrato. Por exemplo, todos os contratos trimestrais de algumas transações podem ser preenchidos aquiquarterEstes códigos de contratos coincidem com os códigos de contratos futuros definidos na documentação da API de linguagem Javascript/python/c++ da FMZ.
Outras configurações, como a quantidade mínima, a quantidade padrão, etc., podem ser encontradas na documentação do idioma Pine sobre"Pine Language Exchange" (em inglês)Introdução aos parâmetros.
-
-
3、
runtime.debug、runtime.log、runtime.errorFunção de extensão FMZ, usada para a delimitação <unk>.Três funções foram adicionadas à plataforma FMZ para a delimitação.
-
runtime.debug: Esta função não é geralmente usada para imprimir informações de variáveis no console. -
runtime.logOutput content in the log. Funções específicas da linguagem FMZ PINE.pineruntime.log(1, 2, 3, close, high, ...),可以传多个参数。 -
runtime.error: quando chamado, pode causar erros de execução e trazer uma mensagem de erro especificada no parâmetro de mensagem.pineruntime.error(message)
-
-
4 - Expansão em parte da função gráfica
overlayparâmetroFunção de desenho em linguagem Pine no FMZ
plot、plotshape、plotcharAumentou.overlaySuporte de parâmetros, permitindo especificar imagens no mapa principal ou no mapa secundário.overlayconfigurartrueDesenho no mapa principal, configurado parafalseDesenhar em subdivisões. Permite que a estratégia Pine em FMZ seja executada ao mesmo tempo que o mapa principal e o subdivisão. -
5、
syminfo.mintickValor de variável embutidasyminfo.mintickA variável embutida é definida como o valor de medida mínimo da variedade atual.Oferta Firme/Testes retrospectivosO valor pode ser controlado pela precisão de precificação monetária, um parâmetro do modelo na interface "Pine Language Transaction Classroom". A definição de precisão monetária 2 significa que o preço é exato para o segundo lugar de um número menor quando o preço é negociado, com uma mínima variação de 0,01syminfo.mintickO valor de um valor de 0,01 <unk> é 0.01 <unk>. -
6o Os preços médios do FMZ PINE Script incluem taxas
Por exemplo: o preço de encomenda é de 8000, a direção de venda, a quantidade é de 1 mão, o preço médio após a transação não é de 8000, é inferior a 8000, o custo inclui a taxa de processamento.
Fundamentos da linguagem Pine
Quando começamos a aprender o básico da linguagem Pine, talvez não estejamos familiarizados com as instruções e a gramática do código de alguns exemplos. Não importa se não entendemos ou não, podemos primeiro familiarizar-nos com os conceitos, entender o objetivo do teste, ou consultar a documentação da linguagem Pine da FMZ para ver as instruções.
Execução do modelo
Quando se inicia a aprendizagem da linguagem Pine, é muito necessário entender os conceitos relacionados, como o processo de execução de scripts da linguagem Pine. A estratégia da linguagem Pine é baseada em gráficos e pode ser entendida como uma série de cálculos e operações, executadas na tabela em ordem cronológica, a partir dos primeiros dados já carregados.bar_indexReferência ao valor de índice da linha K Bar atual na execução do script Pine.
pine
plot(bar_index, "bar_index")
plotA função é uma das funções que mais usaremos no futuro. É muito simples de usar, é apenas desenhar uma linha no gráfico com base nos parâmetros de entrada, e os dados são:bar_indexA linha é denominadabar_index。 A linha chamada bar_index na primeira barra tem um valor de 0 e aumenta 1 。 à medida que a barra aumenta para a direita.
Dependendo da configuração da estratégia, o modelo de execução da estratégia é diferente.收盘价模型e实时价模型◦ O conceito de modelo de preço de fechamento, modelo de preço em tempo real, já foi apresentado anteriormente.
-
Modelo de preço de fechamento
Quando o código de estratégia é executado, o ciclo do atual Bar de linha K é executado completamente. Quando o K é fechado, o ciclo de linha K está terminado.
-
Modelo de preço em tempo real
Quando o código da estratégia é executado, a barra de linha K atual, seja ela fechada ou não, executa a lógica da estratégia de pin em cada mudança de movimento, e o sinal de transação acionado é executado imediatamente.
Quando a estratégia da linguagem Pine é executada de esquerda para direita no gráfico, a linha K do gráfico é dividida em历史Bare实时Bar- Não.
-
História Bar
Quando a estratégia é configurada como um "modelo de preço real" e começa a ser executada, todas as barras K do gráfico, exceto a barra K na parte direita, são
历史BarA lógica da estratégia em cada raiz历史BarExecutar apenas uma vez.
Quando a estratégia é definida como um "modelo de preço de fechamento" e começa a ser executada, todas as barras do gráfico são历史BarA lógica da estratégia em cada raiz历史BarExecutar apenas uma vez.Baseado no cálculo da barra de histórico:
O código da estratégia é executado uma vez no estado de fechamento da barra de histórico, e o código da estratégia continua a ser executado na próxima barra de histórico até que todas as barras de histórico sejam executadas uma vez. -
Bar em tempo real
Quando a estratégia é executada na última linha K Bar à direita, a barra é a barra do tempo real. Quando a barra do tempo real é fechada, a barra torna-se uma barra do tempo real passada (transformando-se em uma barra do tempo histórico). A direita do gráfico gera uma nova barra do tempo real.
Quando a estratégia é configurada como um "modelo de preço em tempo real" e começa a ser executada, uma lógica de estratégia é executada em cada mudança de comportamento na barra em tempo real.
Quando a estratégia é definida como "modelo de preço de fechamento" e começa a ser executada, o gráfico não exibe a barra real.Baseado em cálculos em tempo real do Bar:
Se a estratégia for configurada como um gráfico de "modelo de preço de fechamento" e não for exibida a barra em tempo real, o código da estratégia será executado apenas uma vez no fechamento da barra atual.
Se a estratégia é definida como "modelo de preço de parada real", o cálculo na barra de tempo real e o histórico na barra de tempo real são completamente diferentes. Cada mudança de comportamento na barra de tempo real é executada por um código de estratégia.high、low、closeNo Bar histórico é certo, no Bar em tempo real, esses valores podem mudar a cada vez que a situação muda. Portanto, os indicadores e outros dados calculados com base nesses valores também podem mudar em tempo real.closeO preço é sempre o mais atualizado e o preço é sempre o mais baixo.highelowSempre representa o máximo máximo e o mínimo mínimo atingidos desde o início da barra atual. Estas variáveis embutidas representam o valor final da última atualização da barra.Mecanismos de reversão da execução da estratégia no Bar em tempo real (modelo de preço em tempo real):
Quando executado em Bar em tempo real, cada nova geração da estratégia executa uma variável redefinida pelo usuário anterior, chamada de reversão. Para entender o mecanismo de reversão com um exemplo, vamos testar o código.Perceber:
/*backtest ... .. . */O pacote contém informações de configuração de retrospectiva armazenadas em código na plataforma FMZ.
pine/*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("n + 1之前, n:", n, " 当前bar_index:", bar_index) n := n + 1 runtime.log("n + 1之后, n:", n, " 当前bar_index:", bar_index) plot(n, title="n")Nós só examinamos cenas executadas em tempo real no Bar, então usamos
not barstate.ishistoryA restrição de expressão é somente aplicada à variável n em tempo real no Bar e é usada antes e depois de executar a operação de agregaçãoruntime.logA função de saída de informações no log da estratégia. A partir do uso da função de gráficoplotA curva n desenhada pode ser vista como sempre 0 quando a estratégia está na barra de histórico. A operação n + 1 é acionada quando executada na barra de tempo real, e a operação n + 1 é executada em cada rodada de execução da estratégia na barra de tempo real. Pode ser observado a partir da informação do log que a cada rodada de execução do código da estratégia n é reinstalado como o valor que a estratégia de execução da barra de tempo real anterior finalmente enviou.Para resumir:
1 Quando a estratégia começa a ser executada na barra de tempo real, um código de estratégia é executado para cada atualização de situação.
2. Quando executado na barra de tempo real, a variável é revertida antes de cada código de política executado.
3 Quando executado na barra de tempo real, a variável é submetida uma vez na atualização do fechamento.Uma vez que os dados são rebocados, as operações de desenho, como as curvas no gráfico, também podem causar um redesenho, por exemplo, se modificarmos o código de teste que acabamos de fazer, o teste em disco rígido:
pinevar n = 0 if not barstate.ishistory runtime.log("n + 1之前, n:", n, " 当前bar_index:", bar_index) n := open > close ? n + 1 : n runtime.log("n + 1之后, n:", n, " 当前bar_index:", bar_index) plot(n, title="n")A única coisa que mudamos foi a seguinte frase:
n := open > close ? n + 1 : n, o Bar real atual é negativo ((ou seja, o preço de abertura é superior ao preço de fechamento) só quando a n acumula 1 ≠. Pode ser visto no primeiro gráfico ((momento A) porque o preço de abertura era superior ao preço de fechamento ((o sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de um sinal de -
Contexto das variáveis na função
A seguir, vamos examinar as variáveis das funções da linguagem Pine. De acordo com a descrição de alguns dos tutoriais do Pine, as variáveis da função diferem das variáveis fora da função da seguinte maneira:
O histórico das variáveis de série usadas na função Pine é criado por cada chamada contínua da função. Se a função não for chamada em cada coluna do script executado, isso levará a uma diferença entre os valores de história do bloco interno e externo da função. Portanto, se a função não for chamada em cada coluna, a série que utiliza o mesmo valor de índice dentro e fora da função não irá citar o mesmo ponto de história.
Não importa, vamos entender isso com um código de teste que está sendo executado no FMZ:
pine/*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 imagem da detecção em funcionamento
O código de teste é simples, e visa examinar os dados citados de duas maneiras:
f(a) => a[1]ef2() => close[1]。-
f(a) => a[1]A forma como a função retorna no final usando os parâmetros:a[1]。 -
f2() => close[1]Utilização direta de variáveis embutidascloseA função é a última que retorna.close[1]。
[]O símbolo é usado para referenciar o valor histórico de uma variável de uma série de dados, close[1] é uma referência ao preço de fechamento de uma barra anterior ao preço de fechamento atual. Nosso código de teste desenhou quatro tipos de dados no gráfico:-
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<unk>, colorido em vermelho, quando oneBarInTwo for verdadeiro, e a posição desenhada (no eixo Y) é:f(close)Retornará o valor: -
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
Desenhe um caractere em B<unk>, colorido em verde, quando oneBarInTwo é verdadeiro, e a posição desenhada (no eixo Y) é:f2()Retornará o valor: -
plot(close[2], title = "close[2]", color = color.red, overlay = true)
A linha, colorida em vermelho, é desenhada na posição ((no eixo Y) como:close[2]Ou seja, o preço de fechamento no Bar atual na 2a linha à frente (a 2a linha à esquerda). -
plot(close[1], title = "close[1]", color = color.green, overlay = true)
A linha, colorida em verde, é desenhada na posição (no eixo Y):close[1]Ou seja, o preço de fechamento do Bar atual na 1a raiz à esquerda.
A retrospectiva de funcionamento através da estratégia pode ser visto no screenshot, embora a função usada para marcar o desenho A
f(a) => a[1]Funções usadas para marcar e desenhar Bf2() => close[1]Todos são usados.[1] para referenciar dados históricos da série de dados, mas a posição dos marcadores "A" e "B" no gráfico é completamente diferente. A posição do marcador "A" sempre cai na linha vermelha, ou seja, o código da estratégiaplot(close[2], title = "close[2]", color = color.red, overlay = true)A linha que ele desenhou e os dados que ele usou foram:close[2]。A razão é que o índice através da barra de linha K, ou seja, a variável embutida
bar_indexCalcular se a marca "A" e "B" deve ser desenhada. A marca "A" e "B" não devem ser desenhadas em cada barra de linha K.f(a) => a[1]Se a função não for chamada em cada Bar, o valor que será citado dessa maneira será relacionado à funçãof2() => close[1]Este método cita valores diferentes, mesmo que ambos usem[1] com o mesmo índice). -
-
Algumas funções embutidas precisam ser calculadas em cada barra para calcular seus resultados corretamente.
Para ilustrar, um exemplo simples:
pineres = close > close[1] ? ta.barssince(close < close[1]) : -1 plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)Vamos chamar a função de código.
ta.barssince(close < close[1])Escrever em um operador triangularcondition ? value1 : value2Isso levou a que apenasclose > close[1]A função ta.barssince.ta.barssinceA função é calculada a partir da última vezclose < close[1]Número de linhas K criadas. Quando a função ta.barssince é chamada, é 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 com a condição close < close[1] não foi criado, e não foi criado na última vez.ta.barssince: Quando chamada, a função retorna na。 se esta condição nunca foi satisfeita antes da linha K atual.
Conforme mostrado na figura:
Então, quando desenhamos um gráfico, apenas desenhamos dados quando a variável res tem um valor ((-1) }}.
Para evitar esse problema, nós apenas usamos
ta.barssince(close < close[1])A chamada de função é extraída do operador triangular e escrita no exterior de qualquer possível sub-branche condicional. Ela executa o cálculo em cada linha K 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)
Sequência de tempo
O conceito de sequência de tempo é muito importante na linguagem Pine, e é um conceito que temos que entender quando aprendemos a linguagem Pine. A sequência de tempo não é um tipo, mas sim uma estrutura básica para armazenar valores contínuos de variáveis ao longo do tempo. Sabemos que o script Pine é baseado em gráficos, e o conteúdo mais básico exibido nos gráficos é o gráfico de linha K.openÉ uma variável interna da linguagem Pine, construída para armazenar a sequência de tempo do preço de abertura de cada linha K de Bar. Pode ser interpretada comoopenEsta estrutura de sequência de tempo representa o preço de abertura de todas as barras de linha K no diagrama K atual, desde o primeiro Bar no início até o Bar executado no script atual. Se o diagrama K atual for de um período de 5 minutos, então nós referimos o código de estratégia do Pine (ou usamos)openÉ o preço de abertura da barra de linha K na execução atual do código da estratégia. É necessário usar o valor de abertura da barra de linha K para referenciar o valor histórico na sequência de tempo.[]O operador <unk> é usado quando a estratégia Pine é executada em uma barra de linha K.open[1]Indicar referênciaopenO preço de abertura do Bar de linha K anterior executado pelo script atual na sequência de tempo (ou seja, o preço de abertura do ciclo de linha K anterior).
-
As variáveis na sequência de tempo são muito fáceis de calcular.
Nós construímos uma funçãota.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.sumCódigo de teste:
pinev1 = 1 v2 = ta.cum(v1) plot(v1, title="v1") plot(v2, title="v2") plot(bar_index+1, title="bar_index")Há muitas semelhanças.
ta.cumEssa função embutida pode processar dados diretamente na sequência de tempo, comota.cumÉ somar os valores correspondentes das variáveis de entrada em cada barra de linha K, e depois usamos um gráfico para facilitar a compreensão.O processo de execução da estratégia é a variável interna bar_index
| - | - | - | - |
A estratégia é executada na primeira linha de K-bar, onde 0 é a raiz de 1 e 1 é a raiz de 1.
A estratégia é executada na linha K2 Bar <unk> 1 <unk> 1 <unk> 2 <unk>
A estratégia é executada na terceira raiz K.
|...|...|...|...|
A estratégia é executada em N+1 raiz K.Pode-se ver que, na verdade, v1, v2 e até mesmo bar_index são estruturas de sequência de tempo, com dados correspondentes em cada barra. Este código de teste não se diferencia entre o "modelo de preço em tempo real" ou o "modelo de preço de encerramento" apenas para mostrar o Bar real no gráfico. Para medir a velocidade, usamos o teste de retorno do "modelo de preço de encerramento".
Porque v1 é igual a 1 em cada barra.
ta.cum(v1)A função é executada na primeira linha de K, Bar, e, como só existe a primeira linha de Bar, o resultado é 1, atribuindo o valor à variável v2。
Quandota.cum(v1)Quando executado na segunda linha K Bar, já existem 2 linhas K Bar ((a primeira correspondente à variável interna bar_index é 0, a segunda correspondente à variável interna bar_index é 1), então o resultado é 2, atribuindo o valor à variável v2, e assim por diante. Na verdade, pode-se observar que v2 é o número de linhas K Bar no gráfico, já que o índice das linhas Kbar_indexEntão isso é a partir de zero.bar_index + 1Na verdade, é o número de linhas de K. O gráfico de observação também mostra as linhas.v2ebar_indexÉ verdade.Também posso usar
ta.cumA função embutida calcula a soma dos preços de fechamento de todas as barras no gráfico atual, e então pode ser escrita apenas como:ta.cum(close), quando a estratégia é executada na barra de tempo real do lado direitota.cum(close)O resultado do cálculo é a soma dos preços de fechamento de todas as barras no gráfico (sem a operação para o lado mais à direita, somando-se apenas à barra atual).As variáveis de uma sequência de tempo também podem ser calculadas usando operadores, como o código:
ta.sma(high - low, 14)A variável interna.high(o preço máximo da linha K Bar) menoslow(Preço mínimo do K-Line Bar), uso finalta.smaA função busca a média. -
Os resultados das chamadas de funções também deixam um rastro de valores na sequência de tempo
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 foi executado durante a retomada e pode ser observado
v1ev2Se os valores dos números são idênticos, as linhas desenhadas no gráfico também se sobrepõem completamente. O resultado do cálculo de uma chamada de função deixará um rastro de valores na sequência de tempo, como o códigota.highest(high, 10)[1]Dentre eles,ta.highest(high, 10)O resultado da chamada de uma função também pode ser usado[1] para citar seu valor histórico.ta.highest(high, 10)O resultado é o seguinte:ta.highest(high[1], 10)Por isso.ta.highest(high[1], 10)eta.highest(high, 10)[1]É a mesma coisa.Avaliar a informação de saída usando outra função gráfica:
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)Pode-se ver os valores das variáveis a e b na sequência de tempo mostrados acima e abaixo da correspondente barra. Este código de desenho pode ser mantido durante o aprendizado, pois pode ser necessário frequentemente a saída de informações no gráfico para observação durante testes e experimentos.
Estrutura do script
Estrutura geral
Na parte inicial do tutorial, resumimos algumas diferenças entre o uso da linguagem Pine no FMZ e o uso da linguagem Pine no Trading View.indicator()、strategy()E, temporariamente, não.library()É claro que, para ser compatível com as versões anteriores do script Pine, a estratégia foi escrita com coisas como://@version=5,indicator(),strategy()Também é possível. Algumas configurações de política também podem ser feitas emstrategy()Configuração de parâmetros na função.
<version>
<declaration_statement>
<code>
<version>Informações de controle de versão podem ser omitidas.
Comentários
Língua Pine//Como notação de linha única, a notação de linha é ampliada pela FMZ, uma vez que a linguagem Pine não possui notação de linha múltipla./**/Para notas de várias linhas.
Código
As linhas de um script que não são comentários ou instruções do compilador são sentenças, que implementam o algoritmo do script. Uma sentença pode ser um desses conteúdos.
- Declaração de variáveis
- Reatribuição de variáveis
- Declaração de função
- Chamadas de funções embutidas, chamadas de funções definidas pelo usuário
if,for,whileouswitchEstrutura de equivalência
As frases podem ser organizadas de várias maneiras.
- Algumas declarações podem ser expressas em uma única linha, como a maioria das declarações de variáveis, que contêm apenas uma linha de chamada de função ou uma declaração de função de uma única linha. Outras, como as estruturas, sempre precisam de várias linhas, pois precisam de um bloco local.
- As declarações de âmbito global do script (ou seja, as partes que não pertencem ao bloco local) não podem ser consideradas como
空格ou制表符Comece (tabulação) <unk> O primeiro caractere deles também deve ser o primeiro caractere da linha <unk> As linhas que começam na primeira posição da linha, por definição, fazem parte do alcance global do script <unk> - Uma estrutura ou uma declaração de função de várias linhas sempre requer um
local blockUm bloco local deve ser encolhido em um marcador ou em quatro espaços (caso contrário, será analisado como uma sequência da linha anterior de código, ou seja, julgado como um contínuo da linha anterior de código), e cada bloco local define um alcance local diferente. - Várias frases de uma linha podem ser seqüenciadas em uma linha usando a vírgula ((,) como separador.
- Uma linha pode conter comentários ou apenas comentários.
- As linhas também podem ser fechadas (continua em linhas múltiplas).
Por exemplo, incluindo três blocos locais, um na declaração de função personalizada e dois usando a estrutura if na declaração de variável, com o seguinte código:
pine
indicator("", "", true) // 声明语句(全局范围),可以省略不写
barIsUp() => // 函数声明(全局范围)
close > open // 本地块(本地范围)
plotColor = if barIsUp() // 变量声明 (全局范围)
color.green // 本地块 (本地范围)
else
color.red // 本地块 (本地范围)
runtime.log("color", color = plotColor) // 调用一个内置函数输出日志 (全局范围)
Código de mudança de linha
As linhas longas podem ser divididas em várias linhas, ou "enroladas". As linhas enroladas devem ser encolhidas em qualquer número de espaços, desde que não sejam múltiplos de 4.
pine
a = open + high + low + close
Pode ser embalado como ((observe que o número de espaços em cada linha não é um múltiplo de 4):
pine
a = open +
high +
low +
close
Uma chamada longa de plot ((() pode ser embalada como ▽.
pine
close1 = request.security(syminfo.tickerid, "D", close) // syminfo.tickerid 当前交易对的日线级别收盘价数据系列
close2 = request.security(syminfo.tickerid, "240", close) // syminfo.tickerid 当前交易对的240分钟级别收盘价数据系列
plot(ta.correlation(close, open, 100), // 一行长的plot()调用可以被包装
color = color.new(color.purple, 40),
style = plot.style_area,
trackprice = true)
A frase na declaração de função definida pelo usuário também pode ser empacotada. No entanto, como o bloco local deve começar gramaticalmente com uma contração (((4 espaços ou 1 símbolo), quando dividido para a linha seguinte, a parte de continuação da frase deve começar com uma contração ou mais (((não é igual ao múltiplo de 4 espaços)).
pine
test(c, o) =>
ret = c > o ?
(c > o+5000 ?
1 :
0):
(c < o-5000 ?
-1 :
0)
a = test(close, open)
plot(a, title="a")
Identificadores e operadores
Identificador
Antes de entendermos a variável, devemos primeiro entender o conceito de um identificador de barra. O identificador de barra comum é usado comofunçãoeVariaçõesO nome de uma função é usado para nomear uma variável.funçãoComo veremos em outros tutoriais, primeiro aprendemos o símbolo de identificação de um alfinete: <unk>。
- 1 - Os identificadores devem ser em maiúsculas
(A-Z)Ou em letras minúsculas.(a-z)Letras ou sublinhados(_)Início, como o primeiro caractere do identificador. - 2 - O primeiro caractere do identificador pode ser seguido por outroAlfabeto、SublinheouNúmeros。
-
- O nome do identificador é em maiúsculas e minúsculas.
Por exemplo, o identificador com os seguintes nomes:
pine
fmzVar
_fmzVar
fmz666Var
funcName
MAX_LEN
max_len
maxLen
3barsDown // 错误的命名!使用了数字字符作为标识符的开头字符
Como a maioria das linguagens de programação, a linguagem Pine também tem recomendações de escrita. Geralmente, é recomendado quando se nomeia um identificador:
- 1, todas as letras maiúsculas são usadas para nomear as constantes.
-
- UsarRegra do Pico do CameloNome para outros identificadores.
pine
// 命名变量、常量
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7
// 命名函数
zeroOne(boolValue) => boolValue ? 1 : 0
Operador
Os operadores são alguns símbolos de operação usados em linguagens de programação para construir expressões, enquanto expressões são regras de cálculo projetadas para algum tipo de computação quando escrevemos uma estratégia. Os operadores da linguagem Pine são classificados por função como:
Operadores de atribuição de valor, operadores de aritmética, operadores de comparação, operadores lógicos,? : Os operadores de tríade.[]O operador de referência histórica.
O operador aritmético*Por exemplo, para distinguir o tipo de problema causado pelo resultado do operador de linguagem Pine no Trading View, há o seguinte código de teste:
pine
//@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)
O erro de compilação é causado por um erro de compilação de um script executado no Trading View.adjustedLength = lenInput * factorDepois da multiplicação, temosseries intTipo (série), no entantota.emaO segundo parâmetro da função não suporta esse tipo de função. No entanto, não há restrições rigorosas desse tipo no FMZ, e o código acima pode funcionar normalmente.
A seguir, vamos ver o uso de vários operadores.
Operador de atribuição
Existem dois tipos de operadores de atribuição:=、:=A partir de agora, você pode fazer o que você quiser, mas não é o que você quer fazer.
=O operador é usado para atribuir um valor a uma variável quando esta é iniciada ou declarada.=Inicialização, declaração e atribuição de valor de uma variável começam com esse valor em cada barra subsequente. Estas são declarações de variáveis válidas:
a = close // 使用内置变量赋值给a
b = 10000 // 使用数值赋值
c = "test" // 使用字符串赋值
d = color.green // 使用颜色值赋值
plot(a, title="a")
plot(b, title="b")
plotchar(true, title="c", char=str.tostring(c), color=d, overlay=true)
Percebera = closeDeclaração de atribuição, onde a variável a em cada Bar é o preço de fechamento atual do Bar (closed) ⋅ outras variáveisb、c、dÉ invariável e pode ser testado em um sistema de feedback no FMZ, e os resultados podem ser vistos em um gráfico.
:=Usado para atribuir valores a variáveis existentes, pode ser entendido simplesmente como o uso de:=Os operadores são usados para modificar o valor de uma variável declarada e inicializada.
Se for usado:=Um operador atribui um valor a uma variável não iniciada ou declarada, gerando erros, como:
pine
a := 0
Então,:=Os operadores de atribuição são geralmente usados para re-atribuir valores a variáveis já existentes, como:
pine
a = close > open
b = 0
if a
b := b + 1
plot(b)
A julgar seclose > open(ou seja, o BAR atual é a linha do sol), a variável é o valor verdadeiro (true) <unk> executa o código no bloco local da declaração ifb := b + 1, usando o operador de atribuição:=Re-atribuir a b, acrescentando um 1 ◦ e, em seguida, usar a função plot para desenhar no gráfico o valor da variável b em cada BAR da sequência de tempo, em linha ◦
Será que nós pensamos que se houver um solenoide BAR, b continuará acumulando 1? Claro que não, aqui nós não usamos nenhuma palavra-chave para declarar a variável b, quando a inicialização é 0.b=0E isso é executado em cada BAR, então você pode ver que o resultado de executar este código é que a variável b é redefinida para 0 a cada vez que a variável a é verdadeira, então isso corresponde aclose > openEntão, nesta rodada de execução do código, b é adicionado 1, quando a função de plot é 1, mas na próxima rodada de execução do código, b é re-atribuído a 0.
Quando falamos de operadores de atribuição, temos que estender a explicação a duas palavras-chave:var、varip
-
var
Na verdade, essa palavra-chave, que já vimos e usamos em tutoriais anteriores, só que não foi explorada em detalhes.
var é uma palavra-chave usada para alocar e inicializar variáveis de uma só vez. Normalmente, a sintaxe de atribuição de variáveis que não contém a palavra-chave var leva a que os valores das variáveis sejam cobertos sempre que os dados são atualizados. Por outro lado, quando as variáveis de alocamento são usadas com a palavra-chave var, elas podem ser mantidas em um estado semelhante, mesmo que os dados sejam atualizados.
Nós ainda usamos esse exemplo, mas nós usamos para atribuir um valor a b
varPalavras-chave:pinea = close > open var b = 0 if a b := b + 1 plot(b)varA palavra-chave permite que a variável b execute apenas a primeira atribuição inicial, sem que a lógica da estratégia de execução de cada vez que b seja redefinida para 0, de modo que a linha desenhada no tempo de execução pode ser observada para obter b, ou seja, o número de BARs de linha de onda que ocorreram quando o atual K BAR foi medido.As variáveis de uma declaração var podem ser escritas não apenas em escala global, mas também em blocos de código, como neste exemplo:
pinestrategy(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' mantém o preço de fechamento da primeira linha da série.
A variável 'b' mantém o preço de fechamento da primeira barra de preço de alumínio verde da série.
A variável 'c' mantém o preço de fechamento da décima chiclete verde da série. -
varip
varipA primeira vez que vimos a palavra-chave, podemos ver a descrição da palavra-chave:varip ((var intrabar persist) é uma palavra-chave usada para a distribuição e inicialização de variáveis de uma só vez. É semelhante à palavra-chave var, mas a variável que usa a declaração varip mantém seu valor entre as atualizações de linha K em tempo real.
Não é difícil de entender, mas é fácil de entender com um exemplo.
strategy(overlay=true) // 测试 var varip var i = 0 varip ii = 0 // 将策略逻辑每轮改变的i、ii打印在图上 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) // 每轮逻辑执行都给i、ii递增1 i := i + 1 ii := ii + 1Este código de teste funciona de forma diferente em modelos de preços de fechamento e modelos de preços em tempo real:
Modelos de preços em tempo real:
Lembram-se de quando nós dissemos que a estratégia era executada em um período de BAR histórico e um período de BAR em tempo real?var、varipVariáveis declaradasi、iiCada vez que o código da estratégia é executado, uma operação de incremento é executada. Assim, é possível ver que os números exibidos na linha KBAR do resultado da retomada são incrementados um por um. Quando a fase histórica da linha K termina, começa a fase de linha K em tempo real. As variáveis das declarações var e varip começam a mudar.i := i + 1eii := ii + 1A diferença é que ii é modificado a cada vez. Embora i seja modificado a cada vez, a próxima rodada de execução da lógica da estratégia retorna ao valor anterior (lembre-se do mecanismo de rolagem que explicamos no capítulo anterior "Execução do modelo"?), até que o atual KBAR seja executado e o valor de i seja atualizado (ou seja, a próxima rodada de execução da lógica da estratégia não retorna ao valor anterior). Portanto, pode-se ver que a variável i ainda é aumentada a cada BAR.1.Modelo de fechamento:
Como o modelo de preço de fechamento executa uma lógica de estratégia para cada linha K BAR. Assim, no modelo de preço de fechamento, a fase de linha K histórica e a fase de linha K em tempo real, as variáveis das declarações var, varp apresentam um desempenho excessivo perfeitamente consistente no exemplo acima, aumentando 1 para cada linha K BAR.
Operador de aritmética
| Operador | ilustrar |
|---|---|
| + | Adição |
| - | Subtração |
| * | Multiplicação |
| / | Eliminação |
| % | Buscar exemplos |
+、-Um operador pode ser usado como um operador binário ou como um operador unitário. Outros operadores aritméticos só podem ser usados como um operador binário.
1 , o operador de aritmética é de ambos os lados do tipo de valor, o resultado é o tipo de valor, inteiro ou número de ponto flutuante, dependendo do resultado da operação.
2. Se um número de operações é uma string, o operador é+, o resultado é uma string, o valor é convertido em forma de string, e depois as strings são empilhadas. Se for outro operador de aritmética, tentaremos converter a string em um valor, e depois operar.
3. Se um dos números de operação for na, o resultado será nulo, mostrando NaN quando impresso no FMZ.
pine
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 exigente quanto aos requisitos de tipo de variável. Por exemplo:
pine
a = 1 * "1.1"
b = "1" / "1.1"
c = 5 % "A"
plot(a)
plot(b)
plot(c)
Pode ser executado no FMZ, mas será reportado um erro de tipo no trading view. Para um operador de aritmética, quando os números de operação de ambos os lados são strings, o sistema irá converter a string em um valor numérico e depois calcular. Se uma string não-numérica não puder ser calculada, o resultado da operação do sistema será um valor nulo na.
Comparar o operador
Os operadores de comparação são binários.
| Operador | ilustrar |
|---|---|
| < | Menos de |
| > | Maior do que |
| <= | Menos que igual a |
| >= | Maior do que igual a |
| == | Equivalência |
| != | Desigualdade |
Exemplo de teste:
pine
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 você pode ver, o operador de comparação é muito simples de usar, mas é o operador que mais usamos quando escrevemos estratégias. Você pode comparar valores, mas também pode comparar variáveis internas, comoclose、openespere.
Como um operador de cálculo, o FMZ é diferente do Pine no Trading View, o FMZ não tem um tipo de requisição particularmente rigoroso, então declarações como essad = "1" >= 2 No FMZ, não há erro, a execução é feita primeiro com a conversão da string para um valor numérico e depois a operação de comparação. No Trading View, há erro.
Operador lógico
| Operador | Código de identificação | ilustrar |
|---|---|---|
| Não. | not | Operador unitário, não operativo |
| e | and | Operador binário, com () e () |
| ou | or | Operador binário, ou operação |
Quando falamos de operadores lógicos, temos que falar de tabelas de valores reais. Tal como aprendemos quando estávamos no ensino médio, só que aqui estamos testando e aprendendo no sistema de feedback:
pine
a = 1 == 1 // 使用比较运算符构成的表达式,结果为布尔值
b = 1 != 1
c = not b // 逻辑非操作符
d = not a // 逻辑非操作符
runtime.log("测试逻辑操作符: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("测试逻辑操作符: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")
Para evitar que o sistema de rastreamento fosse influenciado pela impressão contínua de informações, usamosruntime.error("stop")A frase é executada uma vez que a impressão é executada, e o erro anormal é lançado para que a detecção seja interrompida. Depois, você pode observar a informação de saída e descobrir que o conteúdo da impressão e a tabela de valores reais são na verdade os mesmos.
Operador de terciário
Utilização do operador triangular? : Expressões trigonométricas combinadas com números operacionaiscondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalseNós já vimos isso nos cursos anteriores. O que chamamos de expressão triangular, um operador triangular significa que há um total de três operações.
condition ? valueWhenConditionIsTrue : valueWhenConditionIsFalseO que está acontecendo?conditionÉ um critério de julgamento, se o valor da expressão de verdade for:valueWhenConditionIsTrueSeconditionO valor da expressão para o caso évalueWhenConditionIsFalse。
O blogueiro, que não tem nenhuma utilidade prática, mas é fácil de demonstrar:
pine
a = close > open
b = a ? "阳线" : "阴线"
c = not a ? "阴线" : "阳线"
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)
A expressão triangular também pode ser embutida, como fizemos no tutorial anterior.
pine
a = close > open
b = a ? math.abs(close-open) > 30 ? "阳线" : "十字星" : math.abs(close-open) > 30 ? "阴线" : "十字星"
c = not a ? math.abs(close-open) > 30 ? "阴线" : "十字星" : math.abs(close-open) > 30 ? "阳线" : "十字星"
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)
Isso é o equivalente acondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalseEmvalueWhenConditionIsTrue、valueWhenConditionIsFalseEm vez disso, usamos outras expressões tridimensionais.
Operador Histórico
Utilização do operador Histórico[], referindo-se aos valores históricos na sequência de tempo. Estes valores históricos são os valores da variável na linha K BAR anterior à linha K BAR atual no tempo de execução do script.[]O operador é usado para chamadas de variáveis, expressões e funções.[]O valor entre parênteses é o desvio do histórico que queremos citar do BAR da linha K atual. Por exemplo, se eu quiser citar o preço de fechamento de uma linha K BAR anterior, escrevo:close[1]。
A forma de escrever a palavra é a mesma que a forma de escrever a palavra em português.
pine
high[10]
ta.sma(close, 10)[1]
ta.highest(high, 10)[20]
close > nz(close[1], open)
[]O operador só pode ser usado uma vez no mesmo valor, então isso é errado, e vai falhar:
pine
a = close[1][2] // 错误
Talvez você veja aqui, alguns colegas de aula dizem que os operadores[]E o problema é que o que é usado para construções de séries é muito parecido com o que é usado para construções de séries.
Em seguida, vamos usar um exemplo para ilustrar a diferença entre uma série (series) e um conjunto (array) na linguagem Pine.
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")
Embora dizendoa = close[1][2]O blogueiro também escreve sobre o assunto:
pine
b = close[1]
c = b[1]
A partir daí, a sequência de números, que é a sequência de números, não é um erro, se entendermos a sequência de números como uma sequência normal.b = close[1]Depois de atribuir um valor, b deve ser um número, masc = b[1], b ainda pode ser novamente usado para usar o operador de referência histórico. Como pode ser visto na linguagem Pine, o conceito de série não é tão simples quanto o de arquivos. Pode ser entendido como 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.na、nzA função embutida é a função que julga a função embutida, e a função embutida julga a função embutida.nz、naFunções, lembra-se em que capítulo?) que tratam especificamente de valores vazios, como:
pine
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。
3. Não escreva nada.var、varipA 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.1Os 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! -
Identificador
O identificador é o nome da variável, o nome do identificador já foi mencionado no capítulo anterior, e pode ser consultado em: https://www.fmz.com/bbs-topic/9390#标识符
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.if、 for、whileouswitchEstrutura 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:
pine
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.
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:
pine
[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.
pine
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.
pine
strategy("test", overlay=true)
if close > open
plot(close, title="close")
else
plot(open, title="open")
Declaração if
Exemplo:
pine
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.
pine
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:
pine
// 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.string、input.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.
pine
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.
- Sem expressão
switch
Vamos ver.switchOutra forma de escrever o termo é sem expressão.
pine
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 < open Alé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) <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk>
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:
pine
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.
pine
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:
pine
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:
pine
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:
pine
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:
pine
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:
pine
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:
pine
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:
pine
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> a、float[] bUma variável pode ser atribuída a uma matriz, declarando uma matriz ou declarando apenas uma variável, por exemplo:
pine
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:
pine
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:
pine
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.
pine
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")
pine
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")
pine
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.
pine
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()。
- 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.
pine
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.
pine
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:
pine
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:
pine
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:
- 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. - 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:
pine
barIsUp() => close > open
Esta função retorna se o BAR atual é o diagrama.
Concebido como uma função personalizada de várias linhas:
pine
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:
pine
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 encomendastopPreç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çãopyramiding、default_qty_valueParâmetros. Teste com o seguinte código:
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(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: <unk>long3<unk>.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 ID<unk>long3<unk>.
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:id,when,qty,qty_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çãoqtyNú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.
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("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<unk> long e 1<unk> long, e que a entrada foi feita usando o ID de entrada de 1<unk> long e 1<unk> 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:
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("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.
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"}]
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), <unk>long1 <unk> 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_price、trail_points、trail_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 é:id、when。
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.
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, 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, 0.2, limit=2)
strategy.entry("long3", strategy.long, 0.3, limit=3)
if not barstate.ishistory and close < open
strategy.cancel_all()
isStop := true
7、strategy.order
strategy.orderFunções de funções, configuração de parâmetros, etc.strategy.entryConformidade, diferença entrestrategy.orderA função não é protegida.strategyFunção depyramidingA configuração dos parâmetros tem efeito, sem restrição de sequências.
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 encomendastopPreço de parada:
Nós usamosstrategy.orderNão há restrição quanto ao número de sequências.strategy.exitA função de saída condicional. A construção de um script de transação semelhante a uma grelha. O exemplo é muito simples e apenas para aprendizagem:
pine
/*backtest
start: 2021-03-01 00:00:00
end: 2022-08-30 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"ETH_USDT"}]
args: [["ZPrecision",0,358374]]
*/
varip beginPrice = -1
if not barstate.ishistory
if beginPrice == -1 or (math.abs(close - beginPrice) > 1000 and strategy.opentrades == 0)
beginPrice := close
for i = 0 to 20
strategy.order("buy"+i, strategy.long, 0.01, limit=beginPrice-i*200, when=(beginPrice-i*200)<close)
strategy.exit("coverBuy"+i, "buy"+i, qty=0.01, profit=200)
strategy.order("sell"+i, strategy.short, 0.01, limit=beginPrice+i*200, when=(beginPrice+i*200)>close)
strategy.exit("coverSell"+i, "sell"+i, qty=0.01, profit=200)
Exemplos de estratégias
Os exemplos de estratégias neste tutorial são apenas para estratégias de ensino, estratégias de orientação e ideias de design, não fazem qualquer orientação ou recomendação de negociação.
Estratégias de indicadores de super tendências
pine
strategy("supertrend", overlay=true)
[supertrend, direction] = ta.supertrend(input(5, "factor"), input.int(10, "atrPeriod"))
plot(direction < 0 ? supertrend : na, "Up direction", color = color.green, style=plot.style_linebr)
plot(direction > 0 ? supertrend : na, "Down direction", color = color.red, style=plot.style_linebr)
if direction < 0
if supertrend > supertrend[2]
strategy.entry("entry long", strategy.long)
else if strategy.position_size < 0
strategy.close_all()
else if direction > 0
if supertrend < supertrend[3]
strategy.entry("entry short", strategy.short)
else if strategy.position_size > 0
strategy.close_all()
É muito simples escrever estratégias de tendências usando a linguagem Pine, aqui nós desenhamos uma estratégia de acompanhamento de tendências simples com um indicador de tendências super. Vamos analisar o código-fonte da estratégia.
Primeiro, o código da estratégia.strategyA função faz algumas configurações simples:strategy("supertrend", overlay=true)A única coisa que você pode fazer é definir uma estratégia de título de tendência.overlayOs parâmetros sãotrueQuando desenhamos uma estratégia de Pine ou aprendemos um script de estratégia de Pine, primeiro olhamos para o design de parâmetros da interface da estratégia, olhamos para o código fonte de "Super Trend Indicator Strategy", que incluiu o que aprendemos no curso anterior.inputfunção
[supertrend, direction] = ta.supertrend(input(5, "factor"), input.int(10, "atrPeriod"))
inputA chamada de função é usada diretamente comota.supertrendOs parâmetros da função indicador são usados para calcular o indicador de tendência superficial.
- input(5, "factor")
- input.int(10, "atrPeriod")
Por padrão, a função define dois controles de parâmetros na interface de política da linguagem Pine, como mostrado na figura:
Como você pode ver, o padrão no controle éinputFunções einputA função de série ((input.intO primeiro parâmetro de () é explicado nos capítulos anteriores. Com esses dois parâmetros podemos definir a interface de políticata.supertrendOs parâmetros da função são: A função Supertrend Indicator calcula um dado de preçosupertrende um dado de direçãodirection- Então, usa.plotDesenhar um gráfico de função, observe que o desenho é feito de acordo com a direção do indicador de tendência super, desenhando apenas a direção atual.directionPara -1, a tendência atual é para cima.directionPor 1 hora, a tendência é para baixo.plotQuando a função é desenhadadirectionMaior que, menor que 0 <unk>
A seguir,if ... else ifA lógica é o julgamento dos sinais de transação, quando a expressãodirection < 0O indicador de tendência super é um indicador em tempo real que indica que a tendência atual está em fase ascendente, quando os dados de preços no indicador de tendência super são mostrados em um intervalo de tempo real.supertrendPreço do indicador de tendência super superior a 2 BARs para a frentesupertrend[2],还记得历史操作符引用某个变量历史数据吧Se houver uma posição atual, a chamada da função de pedido inverso será feita para liquidar a posição anterior e abrir a posição de acordo com a direção da negociação atual.supertrend > supertrend[2]A condição não foi atingida, desde que neste momentostrategy.position_size < 0A posição em aberto também pode desencadear uma crise.strategy.close_all()Executar a função, executar o total de equilíbrio.
direction > 0O mesmo acontece quando estamos em uma fase de tendência de baixa, se houver mais detentores de posições, todas as posições serão liquidadas e, em seguida, serão qualificadas.supertrend < supertrend[3]Por que é que a configuração é[3]Pode ser intencional para os estrategistas, afinal, alguns mercados, como o mercado de negociação de contratos, têm um risco menor de queda do que o risco de aumento.
parata.supertrendO indicador, alguns dos meus colegas estavam interessados em saber como é que ele determina se a tendência atual é ascendente ou descendente?
Na verdade, este indicador também pode ser implementado na forma de funções personalizadas da linguagem Pine:
pine
pine_supertrend(factor, atrPeriod) =>
src = hl2
atr = ta.atr(atrPeriod)
upperBand = src + factor * atr
lowerBand = src - factor * atr
prevLowerBand = nz(lowerBand[1])
prevUpperBand = nz(upperBand[1])
lowerBand := lowerBand > prevLowerBand or close[1] < prevLowerBand ? lowerBand : prevLowerBand
upperBand := upperBand < prevUpperBand or close[1] > prevUpperBand ? upperBand : prevUpperBand
int direction = na
float superTrend = na
prevSuperTrend = superTrend[1]
if na(atr[1])
direction := 1
else if prevSuperTrend == prevUpperBand
direction := close > upperBand ? -1 : 1
else
direction := close < lowerBand ? 1 : -1
superTrend := direction == -1 ? lowerBand : upperBand
[superTrend, direction]
Esta função é uma função personalizada e uma função embutida.ta.supertrendO mesmo algoritmo e, claro, os mesmos indicadores.
A partir deste algoritmo de funções personalizadas, podemos ver que o indicador de tendência super incorporado em Pine é calculado usandohl2A variável interna ((o preço mais alto, o preço mais baixo são somados e divididos por 2, ou seja, o valor médio do preço mais alto, o preço mais baixo), e depois calcula-se o indicador ATR de um determinado período com base no parâmetroatrPeriod ((amplitude da onda)). Em seguida, use hl2 e ATR para construir o caminho de cima, o caminho de baixo.
Atualização baseada em expressões tridimensionais no códigolowerBandeupperBand。
pine
lowerBand := lowerBand > prevLowerBand or close[1] < prevLowerBand ? lowerBand : prevLowerBand
upperBand := upperBand < prevUpperBand or close[1] > prevUpperBand ? upperBand : prevUpperBand
lowerBand: linha descendente, usada para determinar se a tendência ascendente mudou. upperBand: linha ascendente, usada para determinar se a tendência descendente mudou. lowerBand e upperBand são sempre calculados, mas apenas nesta função personalizada para determinar a direção da tendência atual.
pine
else if prevSuperTrend == prevUpperBand
direction := close > upperBand ? -1 : 1
else
direction := close < lowerBand ? 1 : -1
Então, se o preço de uma tendência super alta no BAR anterior forprevUpperBand, que é a linha ascendente, indica que a tendência atual é descendente.closeMais do queupperBandA partir daí, o preço passa a ser uma tendência ascendente.directionA variável de direção é definida como -1 (a tendência ascendente). Caso contrário, é definida como 1 (a tendência descendente).if direction < 0Quando a condição de sinal é ativada, faça mais.direction > 0Quando a condição de sinal é ativada, faça um vazio.
pine
superTrend := direction == -1 ? lowerBand : upperBand
[superTrend, direction]
Por fim, retornam dados de preços e dados de direção de indicadores de tendências super específicas, de acordo com a direção escolhida.
Estratégias de equilíbrio dinâmico
pine
/*backtest
start: 2021-03-01 00:00:00
end: 2022-09-08 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Binance","currency":"ETH_USDT"}]
args: [["v_input_1",4374],["v_input_2",3],["v_input_3",300],["ZPrecision",0,358374]]
*/
varip balance = input(50000, "balance")
varip stocks = input(0, "stocks")
maxDiffValue = input(1000, "maxDiffValue")
if balance - close * stocks > maxDiffValue and not barstate.ishistory
// more balance , open long
tradeAmount = (balance - close * stocks) / 2 / close
strategy.order("long", strategy.long, tradeAmount)
balance := balance - tradeAmount * close
stocks := stocks + tradeAmount
runtime.log("balance:", balance, ", stocks:", stocks, ", tradeAmount:", tradeAmount)
else if close * stocks - balance > maxDiffValue and not barstate.ishistory
// more stocks , open short
tradeAmount = (close * stocks - balance) / 2 / close
strategy.order("short", strategy.short, tradeAmount)
balance := balance + tradeAmount * close
stocks := stocks - tradeAmount
runtime.log("balance:", balance, ", stocks:", stocks, ", tradeAmount:", tradeAmount)
plot(balance, title="balance value(quoteCurrency)", color=color.red)
plot(stocks*close, title="stocks value(quoteCurrency)", color=color.blue)
Continuamos a estudar alguns exemplos de estratégias de design da linguagem Pine, desta vez vamos ver uma estratégia de equilíbrio dinâmico.BaseCurrency(Variedade de transação) eQuoteCurrencyO valor da moeda de cálculo é sempre tratado em equilíbrio. Se o preço relativo de um ativo aumenta, o valor detido na conta aumenta e o ativo é vendido. Se o preço relativo de um ativo diminui, o valor detido na conta diminui, o ativo é comprado. Esta é a chamada estratégia de equilíbrio dinâmico.
A desvantagem, como mostrado no gráfico de retrospectiva desta estratégia, é que a estratégia de flutuação é maior na fase em que a tendência de preços aumenta (ou cai). Portanto, esta estratégia é boa para a estratégia de caixa, mas o uso em futuros requer um bom controle de risco.
Vamos dar uma olhada na estratégia de criação de código:
Nós usamos um design simplificado para simular uma estratégiabalance(ou seja, quantidade de ativos da CotaCurrency) estocks(ou seja, a quantidade de ativos BaseCurrency) informações sobre o equilíbrio. Nós não vamos ler a quantidade real de ativos na conta, nós apenas usamos o montante simulado para calcular a compra e venda apropriada.maxDiffValue, este parâmetro é o critério de julgamento para o equilíbrio.BaseCurrencyeQuoteCurrencyO desvio é superior amaxDiffValueO equilíbrio deve ser feito vendendo os ativos mais caros e comprando os ativos mais baratos para equilibrar os ativos.
A ação do sinal de negociação estratégica deve ter sentido no estágio BAR em tempo real, então as condições de negociação estratégica são definidas no julgamento ifnot barstate.ishistory◦ A taxa de juros é de US$ 1 milhão, calculada a preços atuais.balanceO valor ultrapassastocksComprar no momento do valor. Em vez disso, vender. Atualizar após a execução da transação.balanceestocksA variável, então, espera o próximo equilíbrio para ser acionado.
A informação do retorno de estratégia acima contém o preço da variedade no início do retorno de estratégia, o preço é de 1458, então eu ajustei os parâmetros de propósitobalanceComo: 43741458*3), configuração de parâmetrosstocksPara: 3. Deixar os ativos em equilíbrio no início.
Estratégias de super tendências com rastreamento de stop loss
Em uma aula anterior, nós aprendemosstrategy.exitA função de saída de posição, cuja função de tracking stop loss stop function não temos exemplos para explicar.strategy.exitA função de rastreamento de stop loss para otimizar uma estratégia de super tendência.
Primeiro, vamos ver.strategy.exitParâmetros de tracking stop loss para função:
1、trail_priceParâmetro: A posição que desencadeia o comportamento lógico de colocar o stop loss de rastreamento na posição de liquidação (a posição especificada pelo preço).
2、trail_offsetParâmetro: a distância entre o preço mais alto (quando é feito) ou o preço mais baixo (quando é feito) após a execução do tracking stop loss.
3、trail_pointsParâmetros:trail_priceParâmetros, mas apenas o número de pontos de vantagem como a posição designada.
Não importa se é difícil de entender, vamos entender o que é aprendizado através de uma estratégia de retrospectiva, que é muito simples.
pine
/*backtest
start: 2022-09-23 00:00:00
end: 2022-09-23 08:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Binance","currency":"ETH_USDT"}]
args: [["RunMode",1,358374],["ZPrecision",0,358374]]
*/
strategy("test", overlay = true)
varip a = na
varip highPrice = na
varip isTrade = false
varip offset = 30
if not barstate.ishistory and not isTrade
strategy.entry("test 1", strategy.long, 1)
strategy.exit("exit 1", "test 1", 1, trail_price=close+offset, trail_offset=offset)
a := close + offset
runtime.log("每点价格为:", syminfo.mintick, ",当前close:", close)
isTrade := true
if close > a and not barstate.ishistory
highPrice := na(highPrice) ? close : highPrice
highPrice := close > highPrice ? close : highPrice
plot(a, "trail_price 触发线")
plot(strategy.position_size>0 ? highPrice : na, "当前最高价")
plot(strategy.position_size>0 ? highPrice-syminfo.mintick*offset : na, "移动止损触发线")
Quando a estratégia começa a ser executada, a entrada imediata de múltiplos cabeçalhos e a entrada imediata da seguintestrategy.exitA ordem de saída ((especificou o parâmetro de parada de perda de rastreamento), quando o preço de mudança de mercado sobe acima da linha de gatilho trail_price, começa a executar a lógica de parada de perda de rastreamento, a linha de parada de perda ((azul) começa a seguir o ajuste de preços mais altos, a localização da linha azul é o preço em que a parada de perda de parada aciona a posição de equilíbrio, e finalmente, quando o preço de mudança de mercado cai abaixo da linha azul, a posição de equilíbrio é acionada.
Então nós usamos esse recurso para otimizar uma estratégia de super tendência, e nós apenas especificamos uma ordem de entrada para a estratégia.strategy.exitA função Stop Loss Stop pode ser adicionada ao plano de saída.
pine
if not barstate.ishistory and findOrderIdx("open") >= 0 and state == 1
trail_price := strategy.position_size > 0 ? close + offset : close - offset
strategy.exit("exit", "open", 1, trail_price=trail_price, trail_offset=offset)
runtime.log("每点价格为:", syminfo.mintick, ",当前close:", close, ",trail_price:", trail_price)
state := 2
tradeBarIndex := bar_index
O código de estratégia completo:
pine
/*backtest
start: 2022-05-01 00:00:00
end: 2022-09-27 00:00:00
period: 1d
basePeriod: 5m
exchanges: [{"eid":"Binance","currency":"ETH_USDT"}]
args: [["RunMode",1,358374],["ZPrecision",0,358374]]
*/
varip trail_price = na
varip offset = input(50, "offset")
varip tradeBarIndex = 0
// 0 : idle , 1 current_open , 2 current_close
varip state = 0
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
if strategy.position_size == 0
trail_price := na
state := 0
[superTrendPrice, dir] = ta.supertrend(input(2, "atr系数"), input(20, "atr周期"))
if ((dir[1] < 0 and dir[2] > 0) or (superTrendPrice[1] > superTrendPrice[2])) and state == 0 and tradeBarIndex != bar_index
strategy.entry("open", strategy.long, 1)
state := 1
else if ((dir[1] > 0 and dir[2] < 0) or (superTrendPrice[1] < superTrendPrice[2])) and state == 0 and tradeBarIndex != bar_index
strategy.entry("open", strategy.short, 1)
state := 1
// 反向信号,全平
if strategy.position_size > 0 and dir[2] < 0 and dir[1] > 0
strategy.cancel_all()
strategy.close_all()
runtime.log("趋势反转,多头全平")
else if strategy.position_size < 0 and dir[2] > 0 and dir[1] < 0
strategy.cancel_all()
strategy.close_all()
runtime.log("趋势反转,空头全平")
if not barstate.ishistory and findOrderIdx("open") >= 0 and state == 1
trail_price := strategy.position_size > 0 ? close + offset : close - offset
strategy.exit("exit", "open", 1, trail_price=trail_price, trail_offset=offset)
runtime.log("每点价格为:", syminfo.mintick, ",当前close:", close, ",trail_price:", trail_price)
state := 2
tradeBarIndex := bar_index
plot(superTrendPrice, "superTrendPrice", color=dir>0 ? color.red : color.green, overlay=true)
- 1




















