Les repères de croisement montrent où un événement a eu lieu. Pour savoir combien se sont produits depuis le début des données, le programme doit mémoriser un compteur.

Pourquoi une variable ordinaire ne suffit-elle pas ?

Ces deux lignes recommencent le compte à chaque bougie :

count = 0
count += 1

La première donne zéro à la variable à chaque exécution de main(). La seconde ajoute un : le résultat vaut donc 1 à chaque bougie.

+= est une affectation abrégée : count += 1 exprime la même chose que count = count + 1. Elle lit l’ancienne valeur, ajoute un et replace le résultat dans la variable.

Pour cumuler des événements, il faut une autre initialisation. Nous voulons remettre à zéro seulement lors de la première création du compteur.

La variable Persistent

count: Persistent[int] = 0

Persistent demande de conserver la valeur entre les bougies. Le zéro initial initialise la variable une seule fois. À la bougie suivante, le calcul continue à partir de la valeur conservée à la clôture précédente.

int désigne ici le type entier du compteur. Nous importons Persistent depuis pynecore, comme nous l’avions fait pour Series.

Cette annotation n’enregistre rien sur le disque. Une nouvelle exécution repart du début des données avec un compteur nul. Il s’agit d’une conservation entre bougies au sein du programme.

Compter les croisements haussiers

Créez 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)

Exécutez-le sur les données de démonstration. Le compteur du panneau séparé commence à zéro. Il augmente d’un à chaque croisement haussier et reste inchangé entre les événements.

À côté d’event_counter.py, le graphique de PyneIDE affiche la ligne du nombre cumulé de croisements.
Le compteur augmente d’un à chaque croisement haussier et conserve sa valeur aux autres bougies.

Si sa dernière valeur est par exemple 12, cela signifie douze croisements haussiers dans l’ensemble des données traitées. La courte période visible à l’écran peut contenir moins de repères. Zoomer ne réinitialise pas le compteur.

État et historique

Les deux annotations répondent à des besoins différents :

AnnotationQue demandons-nous à l’environnement d’exécution ?
Series[float]Pouvoir retrouver l’historique de la valeur par bougie
Persistent[int]Reprendre à la bougie suivante avec la valeur conservée

L’affectation d’une variable Series s’exécute à chaque bougie et les valeurs obtenues constituent un historique. L’affectation initiale d’une variable Persistent n’a lieu qu’une fois ; les modifications du code font ensuite évoluer son état.

Pour consulter directement une ancienne valeur du compteur, vous pouvez placer sa valeur actuelle dans une variable Series distincte. Conserver une valeur et indexer son passé sont deux questions différentes.

Historique et état conservéSeries permet de retrouver les valeurs passées par leur indice. Persistent conserve le compteur pour la bougie suivante ; il augmente seulement quand crosses_up vaut True. Les valeurs et les signaux sont donnés à titre d’illustration. price: 102; price[1]: 99; price[2]: 101; price[3]: 100. crosses_up: False → True → False → True. count: 0 → 1 → 1 → 2.price: Series[float] = closeHistorique après la quatrième bougieBougie1100price[3]2101price[2]399price[1]4102pricecount: Persistent[int] = 0Valeur initiale : définie une foisBougiecrosses_upcount1False02True13False14True2
Historique et état conservéSeries permet de retrouver les valeurs passées par leur indice. Persistent conserve le compteur pour la bougie suivante ; il augmente seulement quand crosses_up vaut True. Les valeurs et les signaux sont donnés à titre d’illustration.

Où déclarer la variable persistante ?

Nous déclarons le compteur avant la condition, à un endroit atteint à chaque bougie. Il a ainsi une valeur même sans événement et plot peut l’afficher à chaque bougie.

Ne créez pas un compteur global ordinaire modifiable au-dessus de main(). Décrivez l’état entre bougies avec l’annotation, là où le programme l’utilise.

L’exécution historique étudiée ici calcule une fois par bougie. Dans un mode qui évalue plusieurs fois la même bougie, Persistent repart de l’état de la bougie clôturée précédente. Il ne compte donc pas toutes les exécutions techniques. Le compteur du livre sert aux événements de bougies clôturées.

Une condition peut aussi réinitialiser une valeur

Pour compter les bougies haussières consécutives, vous pouvez revenir à zéro sur une bougie baissière ou sans variation entre ouverture et clôture :

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

Il faut d’abord créer streak avec l’annotation Persistent[int]. Ici, la remise à zéro est une opération conditionnelle volontaire. Elle n’a pas lieu automatiquement au début de chaque bougie.

À vous de jouer

Ajoutez un deuxième compteur persistant. Il doit compter les croisements baissiers et s’afficher en orange. Les deux calculs doivent s’exécuter à chaque bougie.

Solution de l’exercice

À côté du calcul du croisement haussier :

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)

Conservez la variable count existante et son tracé. Les deux compteurs sont indépendants. Une nouvelle exécution les reconstruit tous les deux à partir de zéro.

Vérifier un signal

Comparez les repères du chapitre sur les croisements aux changements du compteur sur les mêmes données. Sur la bougie d’un croisement haussier, il doit augmenter exactement d’un. Il ne peut augmenter à la bougie suivante que si un nouveau croisement haussier a lieu.