[TOC]
Video tutorial de suporte:https://www.youtube.com/watch?v=CA3SwJQb_1g
FMZ Quant Trading Platform suporta a escrita de estratégias de linguagem Pine, backtesting e negociação ao vivo de estratégias de linguagem Pine, e é compatível com versões mais baixas da linguagem Pine.Praça da Estratégiana plataforma de negociação quantitativa FMZ (FMZ.COM).
O FMZ suporta não apenas a linguagem Pine, mas também a poderosa função de desenho da linguagem Pine. As várias funções, ferramentas ricas e práticas, gerenciamento eficiente e conveniente na plataforma FMZ aumentam ainda mais a praticidade da estratégia Pine (script). Com base na compatibilidade com a linguagem Pine, o FMZ também expande, otimiza e corrige a linguagem Pine até certo ponto. Antes de entrar no tutorial oficialmente, vamos dar uma olhada em quais mudanças foram feitas na linguagem Pine no FMZ em comparação com a versão original.
Um breve resumo de algumas das diferenças óbvias:
//@version
e ostrategy
, indicator
declarações no início do código não são obrigatórias para escrever, FMZ não suportaimport
importarlibrary
Funciona por enquanto.Pode-se ver que algumas estratégias escritas assim:
//@version=5
indicator("My Script", overlay = true)
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue)
plot(b, color = color.black)
plotshape(c, color = color.red)
Ou escreva assim:
//@version=5
strategy("My Strategy", overlay=true)
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
Na FMZ, pode ser simplificado para:
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue, overlay=true)
plot(b, color = color.black, overlay=true)
plotshape(c, color = color.red, overlay=true)
Ou:
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
Modelo de preço de fechamento e modelo de preço em tempo real
Na visão de negociação, podemos usar ocalc_on_every_tick
Parâmetro dostrategy
função para definir o script estratégia para executar a lógica estratégia em tempo real quando o preço muda sempre.calc_on_every_tick
o parâmetro deve ser definido emtrue
. Ocalc_on_every_tick
O parâmetro padrão éfalse
, ou seja, a lógica da estratégia é executada somente quando o atual BAR da linha K da estratégia está completamente concluído.
No FMZ, é definido pelos parâmetros do modelo
O controlo de precisão numérica, como o preço e o montante da ordem quando a estratégia é executada, deve ser especificado na FMZ Na visão de negociação, não há problema de precisão ao colocar ordens de negociação reais, porque só pode ser testado em simulação. No FMZ, é possível executar a estratégia Pine em negociação real. Em seguida, a estratégia precisa ser capaz de especificar a precisão do preço e a precisão do valor da ordem da variedade de negociação de forma flexível. As configurações de precisão controlam o número de casas decimais nos dados relevantes para evitar que os dados não atendam aos requisitos de ordem da bolsa e, portanto, não possam fazer uma ordem.
Código do contrato de futuros
Se o produto de negociação no FMZ for um contrato, ele tem dois atributos, eles são swap
Por exemplo, algumas bolsas têm contratos trimestrais, você pode preencherquarter
Estes códigos de contrato são consistentes com os códigos de contrato de futuros definidos no documento da API da linguagem Javascript/python/c++ da FMZ
Para outras definições, tais como o montante mínimo da ordem, o montante da ordem por defeito, etc., consulte a introdução do parâmetro no
runtime.debug
, runtime.log
, runtime.error
utilizadas para depuração.Foram adicionadas 3 funções à plataforma FMZ para depuração.
runtime.debug
: Imprimir informação sobre variáveis no console, que geralmente não é utilizada com esta função.
runtime.log
Funções específicas da língua PINE na FMZ.
runtime.log(1, 2, 3, close, high, ...), Multiple parameters can be passed.
runtime.error
: Resultará num erro de execução com a mensagem de erro especificada no parâmetro de mensagem quando chamada.
runtime.error(message)
overlay
Parâmetro é estendido em algumas das funções de desenhoNa linguagem Pine no FMZ, as funções de desenhoplot
, plotshape
, plotchar
, etc. acrescentaram ooverlay
suporte de parâmetros, permitindo especificar o desenho no gráfico principal ou no sub-gráfico.overlay
está definido emtrue
para desenhar no gráfico principal, efalse
é definido para utilizar o sub-gráfico, o que permite à estratégia Pine na FMZ desenhar simultaneamente o gráfico principal e o sub-gráfico.
syminfo.mintick
variável incorporadaA variável integrada desyminfo.mintick
Este valor pode ser controlado pelo parâmetro modelo de precificação de preços da moeda na syminfo.mintick
é 0,01.
Por exemplo: o preço da encomenda é 8000, a direcção de venda, a quantidade é 1 lote (peça, folha), o preço médio após a transacção não é 8000, mas inferior a 8000 (o custo inclui a taxa de manipulação).
Ao começar a aprender os fundamentos da linguagem Pine, pode haver alguns exemplos de instruções e gramática de código com os quais não estamos familiarizados. Não importa se você não entende, podemos familiarizar-nos com os conceitos primeiro e entender o propósito do teste, ou você pode verificar a documentação da linguagem Pine no FMZ para instruções. Em seguida, siga o tutorial passo a passo para se familiarizar com várias gramáticas, instruções, funções e variáveis incorporadas.
Quando se começa a aprender a linguagem Pine, é muito necessário entender os conceitos relacionados, como o processo de execução do programa de script da linguagem Pine. A estratégia da linguagem Pine é executada com base no gráfico. Pode-se entender que a estratégia da linguagem Pine é uma série de cálculos e operações, que são executadas no gráfico na ordem de séries temporais a partir dos dados mais antigos que foram carregados no gráfico. A quantidade de dados que o gráfico inicialmente carrega é limitada. Na negociação real, a quantidade máxima de dados é geralmente determinada com base no volume máximo de dados retornado pela interface de troca, e a quantidade máxima de dados durante o backtesting é determinada com base nos dados fornecidos pela fonte de dados do sistema de backtesting. A barra de linha K mais à esquerda do gráfico, ou seja, o primeiro conjunto de dados do gráfico, tem um valor de índice de 0.bar_index
na língua Pine.
plot(bar_index, "bar_index")
Oplot
A função é uma das funções que vamos usar mais no futuro. o uso é muito simples, é desenhar uma linha no gráfico de acordo com os parâmetros de entrada, os dados de entrada ébar_index
, e a linha é denominada comobar_index
. Pode-se ver que o valor da linha chamada bar_index no primeiro Bar é 0, e aumenta em 1 para a direita à medida que o Bar aumenta.
Como as configurações da estratégia são diferentes, os métodos de execução do modelo da estratégia são diferentes, eles podem ser divididos emclosing price model
ereal-time price model
Também apresentamos brevemente os conceitos deles antes.
Modelo de preço de encerramento
Quando o código de estratégia é executado, o período da barra de linha K atual é completamente executado, e quando a linha K é fechada, o período de linha K foi concluído.
Modelo de preços em tempo real
Quando o código de estratégia é executado, independentemente de a barra de linha K atual estar fechada ou não, a lógica da estratégia Pine será executada quando o mercado mudar toda vez, e o sinal de negociação desencadeado será executado imediatamente.
Quando a estratégia da linguagem Pine é executada da esquerda para a direita no gráfico, as barras da linha K no gráfico são divididas emHistorical Bars
eReal-time Bars
:
Bar Histórico
Quando a estratégia é definida como Historical Bars
A lógica da estratégia é executada apenas uma vez em cadahistorical bar
- Não.
Quando a estratégia é definida como historical bars
A lógica da estratégia é executada apenas uma vez em cadahistorical bar
.
Cálculo baseado em Bars históricos: O código de estratégia é executado uma vez no estado de fechamento da barra histórica e, em seguida, o código de estratégia continua a ser executado na próxima barra histórica até que todas as barras históricas sejam executadas uma vez.
Bar em tempo real
Quando a estratégia é executada para a última barra de linha K no extremo direito, a barra é uma barra em tempo real.
Quando a estratégia for definida em
Cálculo baseado em Bar em tempo real:
Se a estratégia estiver definida em high
, low
, close
Os valores de mercado são determinados em barras históricas, e estes valores podem mudar cada vez que o mercado muda em barras em tempo real.close
Representa sempre o último preço corrente, ehigh
elow
representam sempre o ponto mais alto e o ponto mais baixo alcançado desde o início da barra em tempo real atual. Estas variáveis embutidas representam o valor final da barra em tempo real quando foi atualizada pela última vez.
Mecanismo de reversão ao executar estratégias em Bar em tempo real (modelo de preços em tempo real): Durante a execução de Bar em tempo real, a reinicialização de variáveis definidas pelo usuário antes de cada nova iteração da estratégia é chamada de rollback.
Atenção:
/*backtest
...
..
.
*/
O conteúdo do pacote é a informação de configuração do backtest guardada sob a forma de código na plataforma FMZ.
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := n + 1
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Nós só examinar a cena executada durante o tempo real Bars, por isso usamos onot barstate.ishistory
expressão para limitar o acúmulo da variável n apenas no Bar em tempo real, e usarruntime.log
Função para extrair as informações no log da estratégia antes e depois da operação de acumulação.plot
, pode-se ver que n é sempre 0 quando a estratégia está sendo executada em Bars históricos. Quando a Barra em tempo real é executada, a operação de adição de 1 para n é acionada, e a operação de adição de 1 para n é executada quando a estratégia é executada em cada rodada da Barra em tempo real. Pode-se observar a partir da mensagem de log que n será redefinido para o valor finalmente enviado pela estratégia de execução da Barra anterior quando o código de estratégia é re-executado em cada rodada. A atualização do valor n será enviada quando o código de estratégia é executado na Barra em tempo real pela última vez, então você pode ver que o valor da curva n aumenta em 1 com cada aumento de Bar começando da Barra em tempo real no gráfico.
Resumo:
Devido ao rollback de dados, as operações de desenho, como curvas no gráfico, também podem causar redesenho.
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := open > close ? n + 1 : n
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Captura de tela do tempo A
Captura de tela do tempo B
Só modificamos a frase:n := open > close ? n + 1 : n
, somente adicionar 1 a n quando a barra em tempo real atual é uma linha negativa (ou seja, o preço de abertura é maior que o preço de fechamento). Pode-se ver que no primeiro gráfico (tempo A), uma vez que o preço de abertura era maior que o preço de fechamento (linha negativa) naquele momento, n foi acumulado por 1, e o valor de n exibido na curva do gráfico foi 5. Então o mercado mudou e o preço foi atualizado como mostrado no segundo gráfico (tempo B). Neste momento, o preço de abertura é menor que o preço de fechamento (linha positiva), e o valor de n retrocede sem aumentar 1. A curva de n no gráfico também é redesenhada imediatamente, e o valor de n na curva é 4. Portanto, os sinais, como cruzamento e descida exibidos nas barras em tempo real, são incertos e podem mudar.
Contexto variável em funções
De acordo com algumas descrições em tutoriais de Pine, as variáveis na função têm as seguintes diferenças das variáveis fora da função:
O histórico das variáveis de série usadas na função Pine é criado com cada chamada sucessiva à função. Se a função não for chamada em cada barra em que o script é executado, isso resultará em uma discrepância entre os valores históricos da série dentro e fora do bloco local da função. Portanto, se a função não for chamada em cada barra, a série referenciada dentro e fora da função com o mesmo valor de índice não se referirá ao mesmo ponto histórico.
Não importa, vamos descobrir com um código de teste a correr no FMZ:
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
f(a) => a[1]
f2() => close[1]
oneBarInTwo = bar_index % 2 == 0
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
plot(close[2], title = "close[2]", color = color.red, overlay = true)
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Captura de tela do backtest em execução
O código de ensaio é relativamente simples, principalmente para examinar os dados referenciados por dois métodos, a saber:f(a) => a[1]
ef2() => close[1]
.
f(a) => a[1]
: Utilize o método de passagem de parâmetros, a função retorna paraa[1]
finally.
f2() => close[1]
: Utilize a variável embutidaclose
diretamente, e a função retorna paraclose[1]
finally.
O[]
O símbolo é usado para se referir ao valor histórico da variável da série de dados, e fechar[1] refere-se aos dados do preço de fechamento na barra antes do preço de fechamento atual.
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
Desenhe um caractere f(close)
.
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
Desenhe um caractere f2()
.
plot(close[2], title = "close[2]", color = color.red, overlay = true)
Desenhe uma linha, a cor é vermelha, e a posição desenhada (no eixo Y) é:close[2]
, que é o preço de encerramento da segunda barra antes da barra corrente (contando 2 barras à esquerda).
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Desenhe uma linha, a cor é verde, e a posição desenhada (no eixo Y) é:close[1]
, que é o preço de encerramento da primeira barra antes da barra corrente (contando 1 barra para a esquerda).
Pode ser visto a partir da captura de tela da estratégia backtesting que, embora tanto a funçãof(a) => a[1]
usado para desenhar o marcador A e a funçãof2() => close[1]
Usado para desenhar o marcador B [1] para se referir aos dados históricos da série de dados, as posições do marcador plot(close[2], title = "close[2]", color = color.red, overlay = true)
, os dados utilizados para desenhar a linha são:close[2]
.
A razão é calcular se deve desenhar os marcadores bar_index
Os marcadores f(a) => a[1]
não será o mesmo que o valor referenciado pela funçãof2() => close[1]
se a função não for chamada em cada Bar (mesmo que ambos usem o mesmo índice como [1]).
Algumas funções integradas precisam ser calculadas em cada barra para calcular os seus resultados corretamente
Para ilustrar esta situação com um exemplo simples:
res = close > close[1] ? ta.barssince(close < close[1]) : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
Nós escrevemos o código de chamada de funçãota.barssince(close < close[1])
num operador ternáriocondition ? value1 : value2
. Isso faz com que a função ta.barssince seja chamada somente quandoclose > close[1]
Mas o...ta.barssince
Função é calcular o número de K-linhas desde a última vezclose < close[1]
Quando a função ta.barssince é chamada, ela é sempre close > close[1], ou seja, o preço de fechamento atual é maior que o preço de fechamento do Bar anterior. Quando a função ta.barssince é chamada, a condição close < close[1] não é estabelecida e não há posição recente onde ela se mantenha.
ta.barssince: Quando chamada, a função retorna na se a condição nunca tiver sido atendida antes da linha K atual.
Como mostra o gráfico:
Então, quando o gráfico é desenhado, apenas os dados com um valor para a variável res (-1) é desenhado.
Para evitar este problema, basta tomar ota.barssince(close < close[1])
função chamar para fora do operador ternário e escrevê-lo fora de quaisquer possíveis ramos condicionais, fazendo-o realizar cálculos em cada K-line Bar.
a = ta.barssince(close < close[1])
res = close > close[1] ? a : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
O conceito de série de tempo é muito importante na linguagem Pine, e é um conceito que devemos entender quando aprendemos a linguagem Pine. A série de tempo não é um tipo, mas uma estrutura básica para armazenar valores contínuos de variáveis ao longo do tempo. Sabemos que os scripts Pine são baseados em gráficos e o conteúdo mais básico exibido no gráfico é o gráfico de linha K. Série de tempo onde cada valor está associado a um carimbo de tempo de uma barra de linha K.open
é uma variável embutida (embutida) da linguagem Pine, e sua estrutura é armazenar a série de tempo do preço de abertura de cada K-line Bar.open
representa os preços de abertura de todos os K-line Bars a partir da primeira barra no início do gráfico de linha K atual para a barra onde o script atual é executado.open
no código de estratégia Pine, é o preço de abertura da barra da linha K quando o código de estratégia é executado atualmente.[]
Quando a estratégia Pine é executada em uma certa K-line Bar, useopen[1]
referir-se ao preço de abertura da barra da linha K anterior (ou seja, o preço de abertura do período da linha K anterior) que se refere aoopen
série de tempo em que esta barra de linha K está sendo executada atualmente pelo script.
Variaveis em séries de tempo são muito convenientes para computação
Vamos tomar a função embutidata.cum
Por exemplo:
ta.cum
Cumulative (total) sum of `source`. In other words it's a sum of all elements of `source`.
ta.cum(source) → series float
RETURNS
Total sum series.
ARGUMENTS
source (series int/float)
SEE ALSO
math.sum
Código de ensaio:
v1 = 1
v2 = ta.cum(v1)
plot(v1, title="v1")
plot(v2, title="v2")
plot(bar_index+1, title="bar_index")
Há muitas funções embutidas comota.cum
que pode processar dados sobre séries temporais diretamente.ta.cum
é a acumulação dos valores correspondentes às variáveis passadas em cada barra de linha K, e depois usamos um gráfico para facilitar a compreensão.
Processo de operação da estratégia | Variavel incorporada bar_index | V1 | V2 |
---|---|---|---|
A estratégia é executada na primeira barra da linha K. | 0 | 1 | 1 |
A estratégia é executada na segunda barra da linha K. | 1 | 1 | 2 |
A estratégia funciona na terceira barra da linha K. | 2 | 1 | 3 |
… | … | … | … |
A estratégia é executada na N + 1a K-line Bar | N | 1 | N+1 |
Pode-se ver que v1, v2 e até mesmo bar_index são todas estruturas de séries temporais, e há dados correspondentes em cada barra. Se o código de teste usa o modelo
Porque a variável v1 é 1 em cada Bar, quando ota.cum(v1)
A função é executada na primeira linha K Bar, há apenas a primeira Bar, então o resultado do cálculo é 1 e atribuído à variável v2.
Quando?ta.cum(v1)
O resultado do cálculo é 2, que é atribuído à variável v2, e assim por diante. De fato, pode-se observar que v2 é o número de barras de linha K no gráfico, já que o índice da linha K é o número de barras de linha K no gráfico.bar_index
é incrementado a partir de 0, entãobar_index + 1
No gráfico, também podemos ver que as linhasv2
ebar_index
de facto se sobrepõem.
Da mesma forma, também posso usar ota.cum
Tudo o que eu preciso fazer é escrever assim:ta.cum(close)
, Quando a estratégia é executada para a barra em tempo real no extremo direito, o resultado calculado porta.cum(close)
é a soma dos preços de fechamento de todas as Barras no gráfico (se não correr para a extrema direita, só se acumula até a Bar atual).
As variáveis da série temporal podem também ser calculadas utilizando operadores, como o código:ta.sma(high - low, 14)
, subtrair a variável incorporadahigh
(o preço mais elevado da barra da linha K)low
(o preço mais baixo de K-line Bar), e finalmente usar ota.sma
Função para calcular o valor médio.
O resultado de uma chamada de função também deixará vestígios de valores na série temporal.
v1 = ta.highest(high, 10)[1]
v2 = ta.highest(high[1], 10)
plot(v1, title="v1", overlay=true)
plot(v2, title="v2", overlay=true)
O código de teste é executado durante o backtesting, e pode-se observar que os valores dev1
ev2
O resultado calculado pela chamada da função deixará vestígios do valor na série temporal, tais como ota.highest(high, 10)
no códigota.highest(high, 10)[1]
O resultado calculado pela chamada da função também pode usar [1] para se referir ao seu valor histórico.ta.highest(high, 10)
correspondente à barra anterior da barra atual, o resultado do cálculo éta.highest(high[1], 10)
Então...ta.highest(high[1], 10)
eta.highest(high, 10)[1]
são exatamente equivalentes.
Utilize outra função de desenho para a verificação de informações de saída:
a = ta.highest(close, 10)[1]
b = ta.highest(close[1], 10)
plotchar(true, title="a", char=str.tostring(a), location=location.abovebar, color=color.red, overlay=true)
plotchar(true, title="b", char=str.tostring(b), location=location.belowbar, color=color.green, overlay=true)
Podemos ver que os valores da variável a e da variável b na série de tempo são exibidos acima e abaixo das Bars correspondentes. Podemos manter este código de desenho durante o processo de aprendizagem, porque muitas vezes podemos precisar de extrair informações no gráfico para observação durante backtesting e experimentação.
Na parte inicial do tutorial, resumimos algumas diferenças no uso da linguagem Pine no FMZ e no Trading View.indicator()
, strategy()
, elibrary()
É claro que, a fim de ser compatível com versões anteriores de scripts Pine, estratégias como://@version=5
, indicator()
, strategy()
Algumas configurações de estratégia também podem ser definidas passando parâmetros nostrategy()
function.
<version>
<declaration_statement>
<code>
O<version>
As informações de controlo de versão podem ser omitidas.
A língua do pinheiro usa//
como um símbolo de comentário de linha única, uma vez que a língua Pine não tem um símbolo de comentário de várias linhas. FMZ estende o símbolo de comentário/**/
para comentários de várias linhas.
As linhas no script que não são comentários ou diretrizes do compilador são instruções, que implementam o algoritmo do script.
if
, for
, while
ouswitch
EstruturaAs declarações podem ser organizadas de várias maneiras.
space
As linhas que começam na primeira posição, por definição, tornam-se parte do escopo global do script.local block
Um bloco local deve ser recuado por uma aba ou quatro espaços (caso contrário, será analisado como o código concatenado da linha anterior, ou seja, julgado como o conteúdo contínuo da linha anterior de código), e cada bloco local define um escopo local diferente.Por exemplo, inclui três blocos locais, um na declaração da função personalizada e dois na declaração da variável usando a estrutura if, da seguinte forma:
indicator("", "", true) // declaration statement (global scope), can be omitted
barIsUp() => // function declaration (global scope)
close > open // local block (local scope)
plotColor = if barIsUp() // variable declaration (global scope)
color.green // local block (local scope)
else
color.red // local block (local scope)
runtime.log("color", color = plotColor) // Call a built-in function to output the log (global scope)
As linhas longas podem ser divididas em várias linhas, ou
a = open + high + low + close
Pode ser embalado como (observe-se que o número de espaços recuados por linha não pode ser múltiplo de 4):
a = open +
high +
low +
close
Uma chamada longa pode ser enrolada como:
close1 = request.security(syminfo.tickerid, "D", close) // syminfo.tickerid daily level closing price data series for the current trading pair
close2 = request.security(syminfo.tickerid, "240", close) // syminfo.tickerid 240-minute level closing price data series for the current trading pair
plot(ta.correlation(close, open, 100), // line-long plot() calls can be wrapped
color = color.new(color.purple, 40),
style = plot.style_area,
trackprice = true)
As declarações em declarações de funções definidas pelo usuário também podem ser enroladas. No entanto, uma vez que um bloco local deve começar com uma hendidura na gramática (4 espaços ou 1 guia), ao dividi-lo na próxima linha, a continuação de uma declaração deve começar com mais de uma hendidura (não igual a 4 múltiplos de espaços).
test(c, o) =>
ret = c > o ?
(c > o+5000 ?
1 :
0):
(c < o-5000 ?
-1 :
0)
a = test(close, open)
plot(a, title="a")
Antes de reconhecer as variáveis, devemos entender o conceito de
(A-Z)
ou em minúsculas(a-z)
letra ou sublinhação(_)
como o primeiro carácter do marcador.Tal como os seguintes marcadores denominados:
fmzVar
_fmzVar
fmz666Var
funcName
MAX_LEN
max_len
maxLen
3barsDown // Wrong naming! It used a numeric character as the leading character of the marker
Como a maioria das linguagens de programação, a linguagem Pine também tem sugestões de escrita.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7
// name functions
zeroOne(boolValue) => boolValue ? 1 : 0
Os operadores são alguns símbolos de operação usados em linguagens de programação para construir expressões, e expressões são regras computacionais projetadas para certos propósitos computacionais quando escrevemos estratégias.
Operadores de atribuição, operadores aritméticos, operadores de comparação, operadores lógicos,? :
operadores ternários,[]
operadores de referência históricos.
Tomando o operador aritmético*
como exemplo, é diferente do problema de tipo causado pelo resultado de retorno do operador da linguagem Pine no Trading View.
//@version=5
indicator("")
lenInput = input.int(14, "Length")
factor = year > 2020 ? 3 : 1
adjustedLength = lenInput * factor
ma = ta.ema(close, adjustedLength) // Compilation error!
plot(ma)
Ao executar este script no Trading View, um erro de compilação ocorrerá.adjustedLength = lenInput * factor
, o resultado é:series int
tipo (série), mas o segundo parâmetro da funçãota.ema
Mas não há tais restrições rigorosas no FMZ, o código acima pode ser executado normalmente.
Vamos dar uma olhada na utilização de vários operadores juntos.
Existem 2 tipos de operadores de atribuição:=
, :=
, que vimos em vários exemplos no início do tutorial.
O=
O operador é usado para atribuir um valor a uma variável quando ela é inicializada ou declarada.=
Estas são declarações válidas de variáveis:
a = close // Use built-in variables to assign values to a
b = 10000 // Use numerical assignment
c = "test" // Use string assignment
d = color.green // Use color value assignment
plot(a, title="a")
plot(b, title="b")
plotchar(true, title="c", char=str.tostring(c), color=d, overlay=true)
Observe que a declaração de atribuiçãoa = close
, a variável a de cada barra é o preço de fechamento (fechamento) corrente da barra. Outras variáveisb
, c
, d
são inalteradas e podem ser testadas no sistema de backtest na FMZ, e os resultados podem ser vistos no gráfico.
:=
A definição de valor de uma variável pode ser simplesmente entendida como a:=
O operador é usado para modificar os valores das variáveis que foram declaradas e inicializadas.
Se usarmos o:=
Operador para atribuir um valor a uma variável não inicializada ou declarada, ele causará um erro, por exemplo:
a := 0
Por conseguinte, o:=
O operador de atribuição é geralmente usado para reatribuir variáveis existentes, por exemplo:
a = close > open
b = 0
if a
b := b + 1
plot(b)
Julgando seclose > open
(ou seja, o BAR atual é uma linha positiva), a variável a é verdadeira.b := b + 1
é executado, e o operador de atribuição:=
Em seguida, usamos a função de gráfico para desenhar o valor da variável b em cada BAR da série de tempo no gráfico, e conectá-los em uma linha.
Pensamos que quando uma linha positiva BAR aparece, b continuará a acumular por 1? Claro que não, aqui declaramos e inicializamos a variável b como 0 sem usar qualquer designação de palavra-chave.b=0
é executado em cada BAR, então podemos ver que o resultado deste código é redefinir a variável b para 0 toda vez, se a variável a for verdadeira, ou seja, em linha comclose > open
, então b será incrementado em 1 quando o código é executado nesta rodada, e b é 1 quando a função de gráfico desenha, mas b é reatribuído a 0 quando o código é executado na próxima rodada.
Quando se trata de operadores de atribuição, devemos expandir em duas palavras-chave:var
, varip
Var
Na verdade, vimos e usamos esta palavra-chave em tutoriais anteriores, mas não discutimos em detalhes naquela época.
var é uma palavra-chave usada para atribuir e inicialização de uma única vez de variáveis. em geral, gramática de atribuição de variáveis que não contém a palavra-chave var faz com que o valor da variável
s seja substituído toda vez que os dados são atualizados.
Nós ainda usamos este exemplo, mas usamos ovar
palavra-chave ao atribuir um valor a b aqui.
a = close > open
var b = 0
if a
b := b + 1
plot(b)
Ovar
palavra-chave permite que a variável b para executar a atribuição inicial apenas, e então ele não irá redefinir b para 0 toda vez que a lógica de estratégia é executada, por isso pode ser observado a partir da linha desenhada no tempo de execução que b é o número de linhas positivas BARs que apareceram quando o atual K linha BAR foi backtested.
As variáveis declaradas por var podem ser escritas não apenas no escopo global, mas também em blocos de código, como este exemplo:
strategy(overlay=true)
var a = close
var b = 0.0
var c = 0.0
var green_bars_count = 0
if close > open
var x = close
b := x
green_bars_count := green_bars_count + 1
if green_bars_count >= 10
var y = close
c := y
plot(a, title = "a")
plot(b, title = "b")
plot(c, title = "c")
A variável
variável
Vemos a palavra chavevarip
pela primeira vez, podemos olhar para a descrição desta palavra-chave:
varip (var intrabar persist) é uma palavra-chave para atribuição e inicialização única de variáveis.
É difícil de entender? Não importa, explicamos através de um exemplo, é fácil de entender.
strategy(overlay=true)
// test var varip
var i = 0
varip ii = 0
// Print the i and ii changed in each round of the strategy logic on the chart
plotchar(true, title="ii", char=str.tostring(ii), location=location.abovebar, color=color.red)
plotchar(true, title="i", char=str.tostring(i), location=location.belowbar, color=color.green)
// Increment i and ii by 1 for each round of logic execution
i := i + 1
ii := ii + 1
Este código de teste tem diferentes desempenhos no
Modelo de barra:
Você se lembra que a execução da estratégia que explicamos anteriormente é dividida em estágio histórico BAR e estágio BAR em tempo real? no Modelo de Bar, o estágio histórico da linha K, as variáveisi
, ii
declarado em:var
, varip
executar operações incrementais em cada rodada de execução do código de estratégia. Portanto, pode-se ver que os números exibidos no K-line BAR do resultado do backtest são incrementados por 1 um por um. Quando o estágio histórico da linha K termina, o estágio da linha K em tempo real começa. As variáveis declaradas por var e varip começam a sofrer mudanças diferentes. Como é o Modelo de Bar, o código de estratégia será executado uma vez para cada mudança de preço em um BAR de linha K,i := i + 1
eii := ii + 1
A diferença é que ii é modificado toda vez. Embora i seja modificado toda vez, o valor anterior será restaurado quando a lógica de estratégia é executada na próxima rodada (lembre-se do mecanismo de rollback que explicamos no capítulo anterior
Modelo da marca: Uma vez que o modelo Tick executa a lógica da estratégia apenas uma vez por K-line BAR. Assim, no modelo de preço de fechamento, as variáveis declaradas por var e varip se comportam exatamente da mesma forma no exemplo acima incrementando em 1 para cada K-line BAR durante o estágio histórico de K-line e o estágio de K-line em tempo real.
Operadores | Descrição |
---|---|
+ | Adição |
- | Subtração |
* | Multiplicação |
/ | Divisão |
% | Modulo |
O+
e-
Outros operadores aritméticos só podem ser usados como operadores binários e ele irá relatar um erro se ele foi usado como operadores unários.
+
, o resultado do cálculo é uma cadeia, o valor será convertido para a forma de cadeia, e então as cadeias são costurados juntos.a = 1 + 1
b = 1 + 1.1
c = 1 + "1.1"
d = "1" + "1.1"
e = 1 + na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e)
// a: 2 , b: 2.1 , c: 11.1 , d: 11.1 , e: NaN
A linguagem Pine no FMZ é um pouco diferente da linguagem Pine no Trading View, a linguagem Pine no FMZ não é muito rigorosa sobre tipos de variáveis.
a = 1 * "1.1"
b = "1" / "1.1"
c = 5 % "A"
plot(a)
plot(b)
plot(c)
Ele funciona no FMZ, mas relata um erro de tipo na Visualização de Negociação. Se ambos os operandos do operador aritmético forem cadeias, o sistema converte as cadeias em valores numéricos e depois os calcula. Se uma cadeia não numérica não puder ser calculada, o resultado da operação do sistema é um valor nulo
Os operadores de comparação são todos operadores binários.
Operadores | Descrição |
---|---|
< | < |
> | > |
<= | <= |
>= | >= |
== | == |
!= | != |
Exemplo de ensaio:
a = 1 > 2
b = 1 < 2
c = "1" <= 2
d = "1" >= 2
e = 1 == 1
f = 2 != 1
g = open > close
h = na > 1
i = 1 > na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e, ", f:", f, ", g:", g, ", h:", h, ", i:", i)
// a: false , b: true , c: true , d: false , e: true , f: true , g: false , h: false , i: false
Como podemos ver, o operador de comparação é muito simples de usar, mas este também é o operador que mais usamos quando escrevemos estratégias.close
, open
, etc.
Como com o operador, há uma diferença sobre a linguagem Pine entre FMZ e Trading View.d = "1" >= 2
não irá relatar um erro no FMZ, e será executado convertendo a cadeia para um valor primeiro e depois comparando a operação. no Trading View, irá relatar um erro.
Operadores | Símbolos de código | Descrição |
---|---|---|
Não, não. | Não, não. | Operador unário, não operações |
e | e | Operadores binários e operações |
ou | ou | Operadores binários ou operações |
Quando se trata de operadores lógicos, então devemos falar sobre tabelas de valores verdadeiros.
a = 1 == 1 // An expression formed by using comparison operators, the result is a Boolean value
b = 1 != 1
c = not b // Logical not operators
d = not a // Logical not operators
runtime.log("test the logical operator:and", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a and c:", a and c)
runtime.log("a:", a, ", b:", b, ", a and b:", a and b)
runtime.log("b:", b, ", c:", c, ", b and c:", b and c)
runtime.log("d:", d, ", b:", b, ", d and b:", d and b)
runtime.log("test the logical operator:or", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a or c:", a or c)
runtime.log("a:", a, ", b:", b, ", a or b:", a or b)
runtime.log("b:", b, ", c:", c, ", b or c:", b or c)
runtime.log("d:", d, ", b:", b, ", d or b:", d or b)
runtime.error("stop")
A fim de não sobreimprimir mensagens, lançamos um erro com oruntime.error("stop")
Depois disso, podemos observar as informações de saída, e podemos descobrir que o conteúdo impresso é realmente o mesmo que a tabela de valores reais.
Expressões ternárias usando o operador ternário? :
combinado com operandoscondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
A chamada expressão ternária, operador ternário significa que há três operandos nela.
Emcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
, condition
é a condição de julgamento. Se for verdadeira, o valor da expressão é:valueWhenConditionIsTrue
Se...condition
é falso, então o valor da expressão évalueWhenConditionIsFalse
.
Exemplo de uma demonstração conveniente, embora de pouca utilidade prática:
a = close > open
b = a ? "positive line" : "negative line"
c = not a ? "negative line" : "positive line"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
O que fazer se encontrarmos um doji? Não importa! Expressões ternais também podem ser aninhadas, como fizemos no tutorial anterior.
a = close > open
b = a ? math.abs(close-open) > 30 ? "positive line" : "doji" : math.abs(close-open) > 30 ? "negative line" : "doji"
c = not a ? math.abs(close-open) > 30 ? "negative line" : "doji" : math.abs(close-open) > 30 ? "positive line" : "doji"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
De facto, é equivalente a substituir avalueWhenConditionIsTrue
evalueWhenConditionIsFalse
emcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
com outra expressão ternária.
Use o operador histórico[]
Para se referir a valores históricos em uma série de tempo. Estes valores históricos são os valores da variável na barra de linha K antes da barra de linha K atual quando o script estava sendo executado.[]
O operador é usado após variáveis, expressões e chamadas de funções.[]
Parênteses quadrados é o deslocamento dos dados históricos que queremos referenciar do atual K-line BAR. Por exemplo, se eu quiser citar o preço de fechamento do último K-line BAR, nós escrevemos como:close[1]
.
Já vimos algo assim nas lições anteriores:
high[10]
ta.sma(close, 10)[1]
ta.highest(high, 10)[20]
close > nz(close[1], open)
O[]
O operador só pode ser usado uma vez no mesmo valor, por isso é errado escrevê-lo assim, e um erro será relatado:
a = close[1][2] // error
Aqui, alguém pode dizer que o operador[]
é usado para a estrutura de série, parece que a estrutura de série (série) é semelhante à matriz!
Vamos usar um exemplo para ilustrar a diferença entre séries e matrizes na linguagem Pine.
strategy("test", overlay=true)
a = close
b = close[1]
c = b[1]
plot(a, title="a")
plot(b, title="b")
plot(c, title="c")
a = close[1][2]
Informará de um erro, mas:
b = close[1]
c = b[1]
Mas se for escrito separadamente, não irá relatar um erro.b = close [1]
, b deve ser um valor, masc = b[1]
Pode-se ver que o conceito de série na linguagem Pine não é tão simples quanto uma matriz. Pode ser entendido como o valor histórico na última barra de fechamento (atribuída a b), b também é uma estrutura de série temporal (série temporal), e seu h