A marcação de cruzamento mostra onde um evento ocorreu. Para saber também quantos eventos aconteceram desde o início dos dados, o programa precisa guardar o valor de um contador.

Por que uma variável simples não basta?

Estas duas linhas reiniciam a contagem a cada candle:

count = 0
count += 1

A primeira linha atribui zero à variável em toda execução de main(). A segunda soma um; assim, o resultado final é 1 em todos os candles.

+= é uma atribuição abreviada: count += 1 expressa o mesmo que count = count + 1. Primeiro, lê o valor antigo, soma um e armazena o resultado na variável.

Para acumular eventos, precisamos de outra inicialização. Queremos zerar o contador apenas quando ele for criado pela primeira vez.

A variável Persistent

count: Persistent[int] = 0

A anotação Persistent informa que o valor da variável deve ser preservado entre candles. O zero inicial define a variável uma única vez. No próximo candle, continuamos com o valor preservado do candle encerrado anterior.

Aqui, int indica o tipo inteiro do contador. Importamos Persistent do módulo pynecore, como fizemos antes com Series.

Essa anotação não salva dados no disco. Uma nova execução começa novamente do início dos dados, com o contador zerado. Estamos falando de preservação entre candles dentro da execução do programa.

Conte os cruzamentos para cima

Crie o arquivo event_counter.py:

"""
@pyne edge
"""
from pynecore import Persistent
from pynecore.lib import close, color, plot, script, ta


@script.indicator("Crossing Counter", overlay=False)
def main():
    fast_average = ta.sma(close, 5)
    slow_average = ta.sma(close, 20)
    crosses_up = ta.crossover(fast_average, slow_average)
    count: Persistent[int] = 0
    if crosses_up:
        count += 1
    plot(count, "Crossing count", color=color.blue, linewidth=2)

Execute com os dados de demonstração. O contador no painel separado começa em zero. Quando ocorre um cruzamento para cima, aumenta em um; nos candles intermediários, fica igual.

Ao lado de event_counter.py, o gráfico do PyneIDE mostra uma linha com a contagem acumulada dos cruzamentos.
O contador aumenta em um a cada cruzamento para cima e preserva seu valor nos demais candles.

Se o último valor do gráfico for 12, por exemplo, isso representa doze cruzamentos para cima em todo o conjunto de dados processado. Pode haver menos sinais no curto intervalo visível na tela. Dar zoom no gráfico não reinicia o contador.

Estado e histórico

As duas anotações têm tarefas diferentes:

AnotaçãoO que pedimos ao ambiente de execução?
Series[float]Permitir a consulta do histórico do valor em cada candle
Persistent[int]Começar o próximo candle com o valor armazenado na variável

A atribuição da variável Series é executada em cada candle, e os valores obtidos formam um histórico. A atribuição inicial de uma variável Persistent acontece uma única vez; depois, as alterações do código levam seu estado adiante.

Se também quiser consultar diretamente um valor anterior do contador, você pode colocar o valor atual em uma variável Series separada. Preservar o estado e indexar valores passados são duas questões diferentes.

Onde declarar a variável preservada

Declaramos o contador antes da condição, em um lugar acessível em todos os candles. Assim, ele tem um valor mesmo quando a condição não é atendida, e plot consegue desenhá-lo em cada candle.

Não crie um contador global comum e mutável acima de main(). Descreva o estado entre candles com a anotação, no lugar em que o programa o utiliza.

A execução histórica estudada aqui calcula uma vez por candle. Em um modo que avalia um candle várias vezes, Persistent recomeça do estado do candle encerrado anterior. Por isso, não armazena o número total de execuções técnicas. Usamos o contador do livro para eventos de candles encerrados.

Uma condição também pode zerar o valor

Ao contar candles de alta consecutivos, você pode zerar o contador quando aparecer um candle de baixa ou com abertura igual ao fechamento:

if close > open:
    streak += 1
else:
    streak = 0

Antes disso, a variável streak precisa ser criada com a anotação Persistent[int]. Aqui, zerar o contador é uma operação condicional intencional. Isso não acontece automaticamente no início de cada candle.

Tente por conta própria

Acrescente um segundo contador preservado ao programa. Ele deve contar os cruzamentos para baixo e aparecer como uma linha laranja. Os dois cálculos precisam ser executados em cada candle.

Solução do exercício

Ao lado do cálculo de cruzamento para cima:

crosses_down = ta.crossunder(fast_average, slow_average)
down_count: Persistent[int] = 0
if crosses_down:
    down_count += 1
plot(down_count, "Downward count", color=color.orange, linewidth=2)

Mantenha a variável count existente e seu desenho. Os dois contadores são independentes. Uma nova execução constrói ambos novamente a partir de zero.

Confira em um sinal

Compare as marcações do capítulo sobre cruzamentos com as mudanças do contador, usando os mesmos dados. No candle do cruzamento para cima, o contador deve aumentar exatamente em um. No próximo candle, só poderá aumentar de novo se ocorrer outro cruzamento para cima.