Avançado: como funcionam os indicadores?
Aprenda com o código-fonte de SMA, EMA, RSI, MACD, ATR e bandas de Bollinger e crie sua própria versão de um indicador.
É mais fácil pensar em novas versões de um indicador quando você entende como seu valor é produzido. O que entra no cálculo? O que ele guarda para o próximo candle? O que muda ao usar outra média ou outra fonte de preços? O código-fonte também responde a essas perguntas.
A biblioteca ta do PyneCore é escrita em Pyne code. As variáveis Persistent, as chamadas de função e as condições que você encontra nela usam o funcionamento estudado no livro. Todo o código-fonte está disponível, por isso você pode acompanhar um indicador pronto assim como faz com seu próprio programa.
Este é um capítulo avançado: ensina a ler o código-fonte e a experimentar. Você não precisa entender a biblioteca inteira de uma vez. Comece com uma função curta e depois siga as chamadas das quais ela depende.
Abra a implementação real
- Abra no PyneIDE o arquivo
moving_averages.pycriado anteriormente. - Na linha
ta.ema(close, 10), mantenha Command pressionado no macOS ou Ctrl no Windows e Linux e clique no nomeema. A opção Go to Definition do menu do botão direito também funciona. - O editor abre o arquivo
ta.pydo PyneCore instalado, na definição deema. - Leia a descrição da função e depois seu corpo. Se ela chamar outra função, você também pode ir até a definição dessa chamada.

Você já experimentou a consulta de parâmetros e a navegação até a definição no capítulo Ajuda enquanto você escreve código. A mesma operação leva ao código-fonte de RSI, MACD e das outras funções da biblioteca. A referência completa de ta ajuda a encontrar funções e entender seus parâmetros.
O código da biblioteca e Edge: no início de ta.py, você encontra a marcação @pyne lib. A biblioteca não segue o conjunto restrito de regras de Edge: também usa recursos gerais de Python, funções auxiliares internas e outras anotações de tipo. Porém, seu funcionamento candle a candle e sua gestão de estado já serão familiares. PyneFloat, por exemplo, é a anotação de tipo numérico da biblioteca; na_float indica um valor numérico ausente.
Os trechos abaixo mostram os cálculos da biblioteca, não programas Edge completos. Para criar uma versão própria em Edge, escreva o algoritmo que entendeu com os recursos permitidos. Preserve o arquivo ta.py instalado e faça os experimentos em arquivos próprios.
SMA: a soma por trás da fórmula
Duas linhas do cálculo de ta.sma:
length = int(length)
return lib_math.sum(source, length) / lengthA média é a soma da janela dividida pelo número de elementos. Depois de converter o comprimento em inteiro, a soma fica a cargo de lib_math.sum. Aqui, o estado não aparece no corpo de sma: quem o mantém é o cálculo de soma chamado. Se continuar seguindo a chamada, você chegará à implementação de _math_stateful.py por meio do módulo math.py.
Esse é um hábito importante de leitura: uma função curta ainda pode usar estado. O comportamento das funções chamadas também faz parte do cálculo. A média móvel escrita à mão somava os valores dessa mesma janela com um laço próprio.
EMA e RMA: atualize a média preservada
Depois do aquecimento, a EMA realiza este passo:
last_val = last_val + alpha * (source - last_val)
return last_vallast_val é a média anterior; source, o valor atual. Ajustamos a média antiga por uma fração da diferença entre os dois. Na EMA, alpha vale 2 / (length + 1). Com comprimento dez, portanto, acrescentamos à média antiga aproximadamente 18,18 por cento da diferença entre o novo preço e a média anterior.
No código-fonte, tanto alpha quanto last_val são variáveis Persistent. last_val começa ausente, por isso a função primeiro tenta inicializá-la com uma SMA do mesmo comprimento:
if not (last_val == last_val):
last_val = sma(source, length)
return last_valEnquanto não houver dados suficientes nem para a SMA, o resultado fica ausente. Depois que a média inicial é obtida, passamos a atualizar o valor anterior. not (last_val == last_val) é uma verificação de ausência: um valor ausente não é igual a si mesmo. Nos seus exemplos, você usou na(value) para essa finalidade.
A RMA, ou suavização de Wilder, começa de forma parecida, mas é atualizada de outro modo:
last_val = (last_val * (length - 1) + source) / length
return last_valCom comprimento dez, o peso do novo valor é um décimo. Na EMA com esse mesmo comprimento, o peso é maior. Por isso, as duas suavizações reagem de formas diferentes à mesma série de preços.
No código completo, você também encontra o tratamento de entradas ausentes e o atalho para comprimento um. Mesmo duas versões próprias com a mesma fórmula podem produzir resultados diferentes se começarem com valores distintos, tratarem a ausência de outra forma ou não forem executadas nos mesmos candles. A ordem das operações de ponto flutuante também importa; preserve-a para uma comparação exata.
RSI: duas variações suavizadas separadamente
O RSI trata separadamente as altas e as baixas da fonte. No PyneCore, estas são as principais linhas do cálculo:
rma_u = rma(builtins.max(source - prev_src, 0.0), length)
rma_d = rma(builtins.max(prev_src - source, 0.0), length)
prev_src = sourceprev_src é o valor válido anterior da fonte, preservado em uma variável Persistent. Se o preço subir de 100 para 103, a variação para cima será 3 e a para baixo, 0. Se cair de 103 para 101, elas serão 0 e 2, respectivamente. A RMA suaviza as duas sequências separadamente; cada local de chamada tem seu próprio estado.
A atualização de prev_src acontece depois do cálculo das duas diferenças. É a mesma ordem usada no exercício extra com um valor anterior persistente.
A relação entre as duas variações suavizadas produz o RSI:
if rma_d <= 1e-10:
return 100.0
if rma_u <= 1e-10:
return 0.0
return 100 - 100 / (1 + rma_u / rma_d)A ausência de movimento para baixo produz 100; a ausência de movimento para cima produz 0. O código também trata valores muito pequenos e examina primeiro o lado das baixas: se ambos estiverem abaixo do limite, o resultado será 100. Esses detalhes não ficariam claros olhando apenas uma fórmula.
O primeiro preço válido ainda não produz uma variação. Por isso, o RSI o armazena primeiro e só consegue iniciar as duas suavizações com o próximo valor. O número de variações determina o aquecimento do indicador. O RSI não mostra uma probabilidade: um valor 70 não significa 70 por cento de chance de alta do preço.
MACD: um novo indicador a partir de médias móveis
O MACD calcula a diferença entre duas EMAs e depois uma EMA dessa diferença. Os principais passos, sem as verificações de ausência:
fast = ema(source, fastlen)
slow = ema(source, slowlen)
macd_val = fast - slow
signal = ema(macd_val, siglen)Os três valores devolvidos:
return macd_val, signal, macd_val - signalA linha MACD é a diferença entre a média rápida e a lenta. A linha de sinal é uma versão suavizada dessa diferença. O histograma é a diferença entre as duas linhas, por isso vale zero quando elas têm o mesmo valor.
Uma chamada fornece três resultados, que você pode receber em três variáveis:
macd_line, signal_line, histogram = ta.macd(close, 12, 26, 9)A ordem importa: linha MACD, linha de sinal, histograma. Enquanto as duas médias de preços não estiverem prontas, sua diferença também ficará ausente. Depois, a linha de sinal ainda tem seu próprio aquecimento. Assim, as ausências iniciais de um indicador composto também decorrem dos cálculos internos.
ATR: o salto de preço também faz parte da amplitude
A implementação de ATR é curta:
return rma(tr(True), length)Você já conhece a RMA; aqui, ela suaviza o True Range, ou amplitude verdadeira. Esse valor é o máximo entre estas três distâncias:
- a diferença entre a máxima e a mínima atuais;
- a distância entre a máxima atual e o fechamento anterior;
- a distância entre a mínima atual e o fechamento anterior.
Se o fechamento anterior for 100, a mínima atual for 108 e a máxima for 110, a amplitude do próprio candle será apenas 2. Já a amplitude verdadeira será max(2, 10, 8), ou seja, 10: ela também considera o salto de preço desde o fechamento anterior.
No primeiro candle, sem um fechamento anterior, tr(True) usa high - low. No código-fonte, ela lê o fechamento anterior em um dado interno fornecido pelo ambiente de execução. O ATR mostra a magnitude do movimento na unidade do preço; não informa sua direção.
Bandas de Bollinger: média e desvio padrão
A linha central de ta.bb é uma SMA; os dois limites ficam afastados dela por um múltiplo definido do desvio padrão. O código primeiro calcula o desvio e a média:
std_dev = stdev(series, length)
middle = sma(series, length)O retorno quando há um valor central válido:
std_dev *= mult
return middle, middle + std_dev, middle - std_devO desvio padrão descreve quanto os valores da janela se dispersam em torno da média. Se o centro for 100, o desvio for 3 e o multiplicador for 2, o limite superior será 106 e o inferior, 94. A chamada também devolve três valores, mas em uma ordem diferente da de MACD: centro, limite superior, limite inferior.
Sua própria versão: reproduza primeiro a original
Crie o arquivo bands_workshop.py. Nossa função monta as bandas com os componentes mais simples da biblioteca. Também calculamos o limite superior da função pronta ta.bb, para ter uma referência de comparação.
"""
@pyne edge
"""
from pynecore.lib import close, color, input, plot, script, ta
def custom_bands(source: float, length: int, multiplier: float) -> tuple[float, float, float]:
middle = ta.sma(source, length)
deviation = ta.stdev(source, length)
upper = middle + multiplier * deviation
lower = middle - multiplier * deviation
return middle, upper, lower
@script.indicator("Bands Workshop", overlay=True)
def main(
length: int = input.int(20, title="Length", minval=2, maxval=200),
multiplier: float = input.float(2.0, title="Deviation multiplier", minval=0.1, maxval=5.0),
):
builtin_middle, builtin_upper, builtin_lower = ta.bb(close, length, multiplier)
own_middle, own_upper, own_lower = custom_bands(close, length, multiplier)
plot(builtin_upper, "Built-in upper", color=color.blue, linewidth=3)
plot(own_upper, "Custom upper", color=color.orange)
plot(own_middle, "Custom middle", color=color.green)
plot(own_lower, "Custom lower", color=color.gray)tuple[float, float, float] indica o tipo de um resultado formado por três números. A linha return middle, upper, lower devolve esse trio, e os três nomes de variáveis à esquerda da chamada recebem os valores separadamente. Não desenhamos builtin_middle nem builtin_lower agora, mas você também pode usá-los depois na conferência.
Execute com os dados de demonstração. O limite superior próprio e o pronto coincidem: a linha laranja fina passa sobre a azul mais grossa. Passe o ponteiro sobre um candle e compare os valores Built-in upper e Custom upper. Com comprimento vinte, os dezenove primeiros candles ainda não têm uma janela completa.

Para uma conferência mais precisa, abra os valores exportados com Open plot output CSV, na barra de ferramentas do gráfico. Em uma versão própria, confira tanto o primeiro ponto válido quanto os valores posteriores. Mesmo linhas visualmente iguais podem ter pequenas diferenças.
Tente por conta própria: linha central de EMA
Altere apenas a linha central de custom_bands: use EMA no lugar de SMA. Preserve o cálculo do desvio padrão e a chamada pronta de ta.bb como referência.
Antes de executar, faça uma previsão: seu limite superior vai coincidir com o pronto o tempo todo? O que acontece depois de uma mudança brusca de preço?
Solução do exercício
A primeira linha de cálculo da função própria:
middle = ta.ema(source, length)Medimos os dois limites a partir dessa EMA, mantendo o cálculo de ta.stdev. Assim, a linha central e os dois limites próprios geralmente se deslocam em relação às bandas originais. A diferença entre os dois limites superiores é igual à diferença entre EMA e SMA, pois somamos a ambos o mesmo desvio com o mesmo multiplicador.
Essa já é sua versão experimental. A linha diferente, por si só, não informa se uma regra de trading produzirá um resultado melhor com ela. Você pode examinar isso em uma estratégia separada, com os mesmos dados e configurações.
Como continuar estudando a biblioteca
Escolha um indicador e descreva com suas palavras quais dados ele recebe, o que preserva e qual resultado produz. Siga as chamadas internas até entender como o cálculo se encaixa. No RSI, por exemplo, o caminho rsi → rma → sma leva das diferenças à suavização e ao valor inicial.
Leia também os ramos que tratam de valores ausentes, aquecimento e casos-limite. Se criar uma versão própria, altere primeiro um único detalhe e depois compare os resultados com a original. Assim, você consegue distinguir uma mudança intencional de um erro de cálculo.
Você pode abrir o código-fonte das outras funções da biblioteca com o mesmo Command ou Ctrl + clique. Um indicador mais longo pode exigir mais tempo, mas a forma de leitura é a mesma: entradas, dados preservados, cálculo e resultado.