Ce fil est resté sans conclusion technique, et madjes avait raison au message #4 : Tyrese avait effectivement les deux versions sous les yeux. Comme cet indicateur circule toujours et qu'il illustre bien un piège classique de conversion entre plateformes, ça vaut la peine de le décortiquer.
1. Ce que fait réellement cet indicateur
Le principe est astucieux et tient en trois lignes.
- Un CCI 50 sur le prix typique sert uniquement de filtre de régime : positif, on est en mode haussier ; négatif, en mode baissier.
- En mode haussier, on trace une ligne sous le marché, à un ATR(5) sous le plus bas de la bougie. En mode baissier, une ligne au-dessus, à un ATR(5) au-dessus du plus haut.
- Et surtout : cette ligne ne peut jamais reculer. En mode haussier elle ne descend pas, en mode baissier elle ne monte pas.
C'est donc un
stop suiveur adaptatif à la volatilité, piloté par un filtre de tendance. La famille est la même que le Parabolic SAR ou le SuperTrend, avec deux différences : le déclencheur de retournement est un oscillateur plutôt que le prix lui-même, et l'écartement est proportionnel à l'ATR, donc il s'élargit quand le marché s'agite et se resserre quand il se calme.
C'est ce qui explique l'enthousiasme du message #1 : visuellement, une ligne qui suit proprement la tendance et qui bascule de l'autre côté aux retournements, ça a l'air magique. D'où le nom.
2. Les deux codes ne font pas exactement la même chose
C'est le point intéressant, et personne ne l'a relevé à l'époque.
Le code ProRealTime utilise
un seul tampon (la variable magic), qui change simplement de côté du prix selon le signe du CCI. Le code MQL4 en utilise
deux, bufferUp et bufferDn, plus deux lignes de raccordement :
Code : Tout sélectionner
if (thisCCI >= 0 && lastCCI < 0) bufferUp[shift + 1] = bufferDn[shift + 1];
if (thisCCI <= 0 && lastCCI > 0) bufferDn[shift + 1] = bufferUp[shift + 1];
Ces deux lignes servent uniquement à l'esthétique : au moment du basculement, elles recopient la dernière valeur de l'ancien tampon dans le nouveau, pour que la ligne bleue et la ligne rouge se rejoignent au lieu de laisser un trou. C'est de la cosmétique de tracé, pas de la logique.
En revanche, il y a une vraie divergence fonctionnelle. Regardez la condition de réinitialisation. En PRT :
Code : Tout sélectionner
if (CCI[50](typicalPrice)>=0 and magic<magic[1]) then
magic=magic[1]
endif
La variable magic est unique. Quand le CCI repasse positif après une phase baissière, magic[1] contient la
valeur baissière de la bougie précédente, située au-dessus du prix. La contrainte "ne pas descendre" s'applique donc à partir d'un niveau haut, et la ligne met plusieurs bougies à redescendre coller au marché.
En MQL4, bufferUp[shift+1] contient la dernière valeur haussière connue, qui peut dater de très longtemps. Le comportement au retournement diffère donc, parfois de façon visible.
Conséquence pratique : si vous comparez les deux versions sur le même graphique, ne vous étonnez pas de voir les lignes diverger juste après chaque basculement. Aucun des deux codes n'est faux — ils n'implémentent simplement pas la même règle de reprise.
3. Le vrai bug du code MQL4, qui est resté dans toutes les copies
Celui-là mérite d'être signalé, parce que le code circule encore.
Les tampons ne sont jamais réinitialisés à EMPTY_VALUE. Résultat : quand le marché est en mode haussier, bufferDn conserve sa dernière valeur au lieu d'être vide, et le tracé garde une ligne rouge horizontale fantôme sur toute la période. Beaucoup d'utilisateurs l'ont interprétée comme un "niveau de résistance mémorisé" alors que c'est un artefact.
La correction tient en trois lignes, à l'intérieur de la boucle :
Code : Tout sélectionner
if (thisCCI >= 0)
{
bufferUp[shift] = Low[shift] - iATR(NULL, 0, ATR, shift);
if (bufferUp[shift] < bufferUp[shift + 1] && bufferUp[shift + 1] != EMPTY_VALUE)
bufferUp[shift] = bufferUp[shift + 1];
bufferDn[shift] = EMPTY_VALUE; // <-- ligne manquante
}
else
{
bufferDn[shift] = High[shift] + iATR(NULL, 0, ATR, shift);
if (bufferDn[shift] > bufferDn[shift + 1] && bufferDn[shift + 1] != EMPTY_VALUE)
bufferDn[shift] = bufferDn[shift + 1];
bufferUp[shift] = EMPTY_VALUE; // <-- ligne manquante
}
À compléter dans init() par :
Code : Tout sélectionner
SetIndexEmptyValue(0, EMPTY_VALUE);
SetIndexEmptyValue(1, EMPTY_VALUE);
Un mot aussi sur la structure d'origine : la variable externe s'appelle CCI et la fonction native s'appelle iCCI. Ça compile, mais nommer une variable comme un concept du langage est une mauvaise habitude qui produit des erreurs difficiles à repérer dès que le code grossit. Préférez CCIPeriod et ATRPeriod.
4. Ce que cet indicateur peut et ne peut pas faire
Puisque le message #2 demandait en quoi il consiste, autant conclure sur l'usage.
Ce pour quoi il est bon :
- Un stop suiveur objectif et adaptatif. C'est son vrai métier, et il le fait bien. Placer son stop sur la ligne dans le sens de la position est un usage parfaitement défendable.
- Un filtre de régime binaire pour un système plus large : on ne prend que les achats quand la ligne est sous le prix.
- Une lecture visuelle propre de la structure de tendance.
Ce pour quoi il n'est pas bon :
- Comme signal d'entrée autonome. Il est entièrement construit sur des données passées et il n'anticipe rien. En marché sans direction, le CCI 50 oscille autour de zéro et la ligne bascule sans arrêt : c'est le hachoir garanti.
- Comme "indicateur sublime". Aucun indicateur dérivé du prix n'ajoute d'information au prix — il la réorganise. Sa valeur vient de la discipline qu'il impose, pas d'une capacité prédictive.
Et le test qui tranche, valable pour n'importe quel indicateur qu'on découvre : ne demandez pas s'il "a l'air bon" sur le graphique, où tout paraît évident a posteriori. Faites-le tourner sur trois cents signaux, mesurez l'espérance par trade en multiples de R et le drawdown, et comparez à une règle mécanique bête — par exemple un stop suiveur à 2 ATR sans aucun filtre. Si l'indicateur ne bat pas cette référence, il ne sert qu'à rendre le graphique plus joli.
5. Deux remarques de contexte, seize ans après
- MetaQuotes ne délivre plus de nouvelles licences MT4 et l'écosystème est passé à MT5. Ce code est en MQL4 ; en MQL5, la structure diffère sensiblement — OnCalculate au lieu de start, handles d'indicateurs à créer dans OnInit, tampons indexés dans l'autre sens par défaut. Si vous reprenez ce projet aujourd'hui, autant le réécrire directement pour MT5.
- Sur la conversion PRT vers MQL en général : les deux pièges récurrents sont le sens d'indexation des barres (l'indice 0 est la bougie courante dans les deux, mais la logique de boucle diffère) et la définition exacte des indicateurs natifs. Le prix typique, le mode de lissage de l'ATR ou la constante du CCI ne sont pas toujours identiques d'une plateforme à l'autre. Avant de conclure qu'une conversion est ratée, comparez d'abord les valeurs brutes des indicateurs sous-jacents sur quelques bougies — c'est là que se cache la différence neuf fois sur dix.
Si quelqu'un veut la version MQL5 corrigée, je peux la poster : c'est une trentaine de lignes et ça fera un bon exercice de comparaison pour ceux qui migrent.