Aller au contenu
Accueil/Articles/MQL5 et automatisation
MQL5 et automatisation

ZoneMartingale EA : le robot de récupération par zone, expliqué ligne par ligne

Moins de 200 lignes de MQL5 pour ouvrir une position sur le RSI, tracer une zone et la récupérer en doublant le lot. Ce que fait chaque bloc, pourquoi le cycle finit en gain, et les cinq corrections à faire avant de s'en servir.

ZoneMartingale EA est le robot de « zone recovery » codé en direct sur la chaîne, et dont le code source complet est distribué gratuitement. C'est un bon objet d'étude : le fichier tient en moins de 200 lignes, il n'a que quatre paramètres, et il montre sans détour comment fonctionne une martingale à zone. Il montre aussi pourquoi ce type de robot demande de la prudence.

Cet article reprend le code de la version 1.0 bloc par bloc. À la fin, vous saurez lire chaque décision du robot, refaire le calcul qui explique ses gains, et corriger les points faibles de cette première version.

Le principe en une minute

Le robot ouvre une première position quand le RSI sort d'une zone extrême. Autour du prix d'entrée, il trace une zone de largeur fixe et deux objectifs, un au-dessus et un en dessous.

  • Si le prix atteint l'un des deux objectifs, toutes les positions sont fermées : le cycle est terminé.
  • Si le prix traverse la zone dans l'autre sens, le robot ouvre une position opposée, avec un lot multiplié par un facteur (2 par défaut).
  • Chaque nouvelle traversée ajoute une position, toujours plus grosse, dans le sens du mouvement.

Tant que le prix finit par atteindre un objectif, le cycle se termine en gain. Le risque est ailleurs : si le prix fait des allers-retours dans la zone, le lot double à chaque fois.

Les paramètres

MQL5
input double lot = 0.01;
input double Range = 2000;
input double Target = 4000;
input double factor = 2.0;
  • lot : le volume de la première position.
  • Range : la largeur de la zone, en points.
  • Target : la distance entre le bord de la zone et chaque objectif, en points.
  • factor : le multiplicateur appliqué à chaque nouvelle position.

Un point, c'est le plus petit pas de prix du symbole (_Point). Sur un symbole coté avec deux décimales, 2 000 points font 20,00 de prix ; avec cinq décimales, 0,02000. Les distances se règlent donc symbole par symbole.

Le signal d'entrée : le RSI qui sort d'un extrême

MQL5
int bars = iBars(_Symbol, PERIOD_CURRENT);
if(totalBars == bars) return;
totalBars = bars;
if(PositionsTotal() > 0) return;

if(!CopyBuffer(rsi_handle, 0,1,2, rsiData)) return;

if(rsiData[1] < OverSoldLevel && rsiData[0] > OverSoldLevel) {
   MQL5_Lab_trade.Buy(lot, _Symbol, Bid, 0,0, "MQL5 Lab");

Trois choses à remarquer :

  1. Le signal n'est cherché qu'une fois par bougie, à l'ouverture d'une nouvelle barre. Le robot ne réagit donc pas aux mouvements à l'intérieur d'une bougie pour entrer.
  2. Aucun signal n'est cherché tant qu'une position est ouverte : un seul cycle à la fois.
  3. CopyBuffer(rsi_handle, 0, 1, 2, rsiData) copie le RSI des deux dernières bougies clôturées. Sans ArraySetAsSeries, le tableau garde l'ordre chronologique : rsiData[0] est la plus ancienne, rsiData[1] la plus récente. La condition rsiData[1] < 30 && rsiData[0] > 30 signifie donc : le RSI était au-dessus de 30 il y a deux bougies et il est passé sous 30 sur la dernière bougie clôturée.

Ce détail compte : le commentaire du code parle d'une « sortie de survente », mais la condition, lue dans l'ordre réel du tableau, détecte une entrée en survente. Le robot achète donc quand le RSI vient de passer sous 30, pas quand il en ressort. C'est un comportement défendable (acheter l'excès), mais ce n'est pas celui que le commentaire annonce. Si vous voulez l'achat sur la sortie de survente, inversez les indices. Le même raisonnement vaut pour la vente autour de 70.

La zone et les objectifs

Une fois l'achat exécuté, le robot lit le prix d'ouverture réel et trace ses niveaux :

MQL5
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
zoneHigh = NormalizeDouble(openPrice, _Digits);
zoneLow = NormalizeDouble(zoneHigh - zoneRange, _Digits);
zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget, _Digits);
zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget, _Digits);

Pour un achat, le haut de la zone est le prix d'entrée et le bas se trouve Range points plus bas. Les objectifs sont à Target points au-delà de chaque bord. Pour une vente, c'est le symétrique : le bas de la zone est le prix d'entrée.

La récupération : une position opposée à chaque traversée

MQL5
if(Bid > zoneHigh) {
   if(isBuyDone == false || lastDirection < 0) {
      MQL5_Lab_trade.Buy(lots_Rec, _Symbol, Bid, 0,0, "MQL5 Lab");
      lastDirection = 1;
      recovery_lot = recovery_lot*factor;

À chaque tick, le robot regarde où se trouve le prix. S'il passe au-dessus du haut de la zone alors que la dernière position était une vente, il achète. S'il passe sous le bas alors que la dernière était un achat, il vend. Le lot de récupération démarre à lot × factor et est multiplié par factor à chaque nouvelle position.

La sortie : tout fermer sur un objectif

MQL5
if (Bid > zoneTargetHigh || Bid < zoneTargetLow) {
   deleteZoneLevels();
   for (int i=PositionsTotal()-1; i>=0; i--) {
      ulong ticket = PositionGetTicket(i);
      ...
      MQL5_Lab_trade.PositionClose(ticket);

Dès que le prix dépasse l'un des deux objectifs, toutes les positions sont fermées et les variables remises à zéro. Le robot attend alors un nouveau signal RSI.

Pourquoi le cycle finit en gain

Prenons les réglages par défaut : zone de largeur Z, objectifs à 2Z, facteur 2, et un premier lot de 1 (pour simplifier les calculs). On compte en multiples de Z, sans le spread.

ScénarioPositions ouvertesRésultat à l'objectif
Le prix monte directement à l'objectif hautAchat 1+2
Il descend, puis atteint l'objectif basAchat 1, vente 2−3 + 4 = +1
Bas, haut, puis objectif hautAchat 1, vente 2, achat 4+2 − 6 + 8 = +4
Bas, haut, bas, puis objectif basAchat 1, vente 2, achat 4, vente 8−3 + 4 − 12 + 16 = +5

Chaque nouvelle position, deux fois plus grosse que la précédente et dans le sens du mouvement, couvre les pertes des autres. Le cycle finit donc toujours positif si le prix atteint un objectif, si les ordres s'exécutent aux niveaux prévus, et si le compte a la marge pour tenir.

Le prix à payer : l'exposition

Le volume total ouvert après n positions vaut lot × (2^n − 1) avec un facteur 2 :

Positions ouvertesDernier lotVolume total
10,010,01
40,080,15
60,320,63
81,282,55
105,1210,23

Neuf traversées de la zone d'affilée, ce qui arrive en période de range, transforment un premier lot de 0,01 en plus de 10 lots ouverts. Le gain du cycle, lui, reste de l'ordre de quelques Z au lot initial. Le spread se paie à chaque nouvelle position, et une exécution au-delà du niveau prévu (glissement, ouverture du lundi) entame le calcul du tableau précédent. C'est tout le problème des martingales : beaucoup de petits gains, et un risque rare mais très lourd.

Cinq corrections à faire avant de s'en servir

La version 1.0 a été écrite pour montrer le principe, en direct. Avant de l'utiliser, même en démo, voici ce qu'il faut corriger.

1. Ne gérer que ses propres positions

Le robot compte et ferme toutes les positions du compte, quel que soit le symbole. Une position manuelle sur un autre actif bloque les nouveaux signaux, et un objectif atteint la fermerait. Ajoutez un numéro magique et filtrez :

MQL5
input long Magic = 2601;
// dans OnInit()
MQL5_Lab_trade.SetExpertMagicNumber(Magic);

// une fonction pour compter seulement les positions du robot
int MesPositions() {
   int n = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--) {
      ulong t = PositionGetTicket(i);
      if(t > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic) n++;
   }
   return n;
}

Utilisez ce filtre partout où le code parcourt les positions, et remplacez PositionsTotal() > 0 par MesPositions() > 0.

2. Plafonner la récupération

Ajoutez un nombre maximal de positions et un lot maximal. Une fois le plafond atteint, le robot n'ajoute plus rien et vous prévient :

MQL5
input int    MaxPositions = 6;
input double LotMax = 0.32;

if(MesPositions() >= MaxPositions || lots_Rec > LotMax) {
   Alert(_Symbol, " : plafond de récupération atteint");
   return;
}

C'est la décision la plus importante : elle fixe à l'avance la perte maximale que vous acceptez sur un cycle.

3. Acheter au bon prix

Les achats passent Bid comme prix. Un achat s'exécute au prix Ask ; sur un compte en exécution instantanée, un prix faux peut faire refuser l'ordre. Passez 0 : la classe CTrade prend alors le prix courant du bon côté.

MQL5
MQL5_Lab_trade.Buy(lots_Rec, _Symbol, 0, 0, 0, "ZoneMartingale");

4. Tracer le bon objectif bas

Une faute de frappe trace l'objectif bas sous le nom ZONE_L au lieu de ZONE_T_L. Comme cet objet existe déjà, la ligne orange n'apparaît jamais. Il suffit de corriger le nom dans les deux appels à drawZoneLevel.

5. Ne pas perdre le fil au redémarrage

La zone, le sens et le lot en cours sont gardés dans des variables. Si MetaTrader redémarre au milieu d'un cycle, le robot oublie tout : les positions restent ouvertes, sans objectif ni récupération. Enregistrez l'état dans des variables globales du terminal (GlobalVariableSet) et relisez-le dans OnInit(), ou fermez le cycle à la main avant tout redémarrage.

Une dernière ligne peut partir : EventSetTimer(60) démarre un minuteur qu'aucune fonction OnTimer() n'utilise.

Comment le tester

  1. Ouvrez le testeur de stratégie (Ctrl+R), choisissez le robot, le symbole et l'unité de temps.
  2. Prenez le mode Chaque tick basé sur les ticks réels : la récupération se joue tick par tick, un mode approximatif fausserait le résultat.
  3. Regardez le drawdown maximal et le volume maximal ouvert, pas seulement le profit. Un profit régulier avec un drawdown de 60 % n'est pas un bon résultat.
  4. Testez au moins une période de range. C'est là que la martingale montre ses vraies limites.

Ce qu'il faut retenir

ZoneMartingale EA est un excellent support pour apprendre : un signal simple, une gestion d'état lisible, une exécution sans détour. Le calcul du cycle explique pourquoi les martingales semblent gagner presque toujours, et le tableau d'exposition explique pourquoi elles finissent parfois mal. Corrigez les cinq points ci-dessus, fixez un plafond que vous acceptez de perdre, et gardez-le en démo tant que vous n'avez pas vu ce qu'il fait dans un marché sans direction.

Une question, un désaccord, un retour d'expérience ?

Le forum est le bon endroit pour en parler avec les autres traders.

En discuter sur le forum
L'outil dont parle l'article