besoins aide amelioration prog avec "Expert Advisor Builder"

Modérateur : Administrateurs

Message
Auteur
philippou
Nouveau
Messages : 2
Inscription : 16 oct. 2013, 16:45

besoins aide amelioration prog avec "Expert Advisor Builder"

#1 Message par philippou »

bonjour,

j'ai fait un EA avec "Expert Advisor Builder for MetaTrader 4" et j'ai besoin d'aide sur plusieurs points :
- insérer un stoploss à "x" pips d'une MA (pour achat et vente)
- fermer vente et achat depuis un custom indcateur qui lui me place des croix sur mon graphique (fermer achat et vente dès qu'une croix apparaît....et si possible retarder cette action avec un paramètre de "x" bougies après achat ou vente.

merci d'avance
n'hésitez pas à me contacter car je suis novice donc il est possible que vous ne me comprenez pas vraiment.

ci-joint les paramètres d'entrée du custom indcateur
Pièces jointes
custom indicator.jpg
custom indicator.jpg (134.81 Kio) Consulté 18072 fois

Avatar de l’utilisateur
Fabien Labrousse
Administrateur
Messages : 18056
Inscription : 17 mars 2008, 19:41
Localisation : Paris, France

Re: besoins aide amelioration prog avec "Expert Advisor Builder"

#2 Message par Fabien Labrousse »

Je t'aurai bien aidé mais la je suis débordé et j'ai du retard dans mon travail. Peut-être que la semaine prochaine je trouverai le temps mais je peu rien te promettre. Désolé.
Comparatif brokers, courtiers, banques, néobanques et exchanges crypto

La Structure (par VideoBourse) : Formation bourse sur mesure et évolutive pour optimiser votre compréhension et vos résultats

📊 Plateforme de Trading : ProRealTime | Broker : Interactive Brokers

🤝 Contact et échanges avec la communauté : Discord, téléphone, LinkedIn, X (Twitter), Youtube...

xvilain

Réponse avec un léger retard — mais la question revient encore régulièrement

#3 Message par xvilain »

Ce fil date de 2013 et je n'avais jamais trouvé le temps d'y revenir. Comme les mêmes deux questions reviennent sans arrêt en message privé et que ce sujet remonte encore dans les recherches Google, autant y répondre proprement une bonne fois. philippou n'est probablement plus dans les parages, mais le contenu servira aux suivants.

Petit rappel de contexte : les générateurs graphiques de type Expert Advisor Builder produisent une ossature correcte, mais ils ne savent pas gérer deux choses — les stops calculés dynamiquement, et la lecture d'un indicateur personnalisé. Ce sont précisément les deux demandes du message #1. Il faut donc passer par un peu de code écrit à la main, et c'est plus simple qu'il n'y paraît.

Le code ci-dessous est en MQL4, comme le générateur de l'époque.

1. Le préalable que tout le monde rate : point n'est pas pip

Sur un courtier à 5 décimales (ou 3 sur les paires en yen), un "point" vaut un dixième de pip. Un stop de 15 pips codé avec Point donne donc un stop de 1,5 pip, qui sera rejeté ou touché immédiatement. C'est la première cause d'échec des EA générés automatiquement.

Code : Tout sélectionner

double PipSize()
  {
   double p = Point;
   if(Digits == 3 || Digits == 5) p = Point * 10;
   return(p);
  }
À utiliser partout où vous exprimez une distance en pips.

2. Le stop-loss à X pips d'une moyenne mobile

Le principe : on lit la valeur de la moyenne mobile sur la bougie clôturée (décalage 1, pas 0 — sinon la valeur bouge à chaque tick et votre stop danse), puis on retranche ou on ajoute la distance voulue.

Code : Tout sélectionner

extern int    MAPeriod    = 50;
extern int    MAMethod    = MODE_SMA;      // MODE_EMA, MODE_SMMA, MODE_LWMA
extern int    MAPrice     = PRICE_CLOSE;
extern double SL_Pips     = 15.0;          // distance sous / au-dessus de la MM

double MaValue(int shift)
  {
   return(iMA(NULL, 0, MAPeriod, 0, MAMethod, MAPrice, shift));
  }

double StopAchat()
  {
   double sl = MaValue(1) - SL_Pips * PipSize();
   return(NormalizeDouble(sl, Digits));
  }

double StopVente()
  {
   double sl = MaValue(1) + SL_Pips * PipSize();
   return(NormalizeDouble(sl, Digits));
  }
Le contrôle indispensable avant d'envoyer l'ordre. Un courtier impose une distance minimale entre le prix et le stop. Si votre moyenne mobile est trop proche du cours, l'ordre sera rejeté avec une erreur 130 — l'erreur la plus fréquente chez les débutants, et celle qui fait croire que "l'EA ne fonctionne pas".

Code : Tout sélectionner

bool StopValide(int type, double sl)
  {
   double distMin = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
   if(type == OP_BUY)  return(sl > 0 && sl < Bid - distMin);
   if(type == OP_SELL) return(sl > 0 && sl > Ask + distMin);
   return(false);
  }
Deux décisions de conception à prendre consciemment :
  • Stop figé ou stop suiveur ? Le code ci-dessus calcule le stop à l'entrée. Si vous voulez qu'il suive la moyenne mobile ensuite, il faut le recalculer et appeler OrderModify — mais uniquement si le nouveau stop est meilleur que l'ancien, sinon vous élargissez votre risque en cours de route, ce qui est exactement ce qu'il ne faut jamais faire.
  • Ne modifiez pas à chaque tick. N'envoyez un OrderModify que si l'écart avec le stop actuel dépasse au moins un pip, sinon vous saturez le serveur et récoltez des erreurs 1 en série.

Code : Tout sélectionner

void SuivreStop(int ticket)
  {
   if(!OrderSelect(ticket, SELECT_BY_TICKET)) return;

   double nouveau = 0;
   if(OrderType() == OP_BUY)
     {
      nouveau = StopAchat();
      if(nouveau > OrderStopLoss() + PipSize() && StopValide(OP_BUY, nouveau))
         OrderModify(ticket, OrderOpenPrice(), nouveau, OrderTakeProfit(), 0, clrNONE);
     }
   if(OrderType() == OP_SELL)
     {
      nouveau = StopVente();
      if((OrderStopLoss() == 0 || nouveau < OrderStopLoss() - PipSize())
          && StopValide(OP_SELL, nouveau))
         OrderModify(ticket, OrderOpenPrice(), nouveau, OrderTakeProfit(), 0, clrNONE);
     }
  }
3. Fermer les positions sur le signal d'un indicateur personnalisé

C'est la partie où il faut être méthodique, parce que tout dépend de la façon dont l'indicateur dessine ses croix. Deux cas, et un seul est exploitable simplement.

Cas A — les croix sont dans des buffers d'indicateur. C'est le cas si l'auteur a utilisé SetIndexBuffer et SetIndexStyle avec DRAW_ARROW. Vous pouvez alors lire les valeurs depuis l'EA avec iCustom.

Comment le vérifier en trente secondes : posez l'indicateur sur un graphique, ouvrez la fenêtre de données (Ctrl+D) et passez la souris sur une bougie portant une croix. Si une ligne affiche une valeur à cet endroit, l'information est dans un buffer, et le rang de cette ligne vous donne l'indice du buffer (le premier vaut 0).

Code : Tout sélectionner

extern string NomIndicateur = "MonIndicateur";
extern int    BufferHaut    = 0;   // buffer de la croix haussiere
extern int    BufferBas     = 1;   // buffer de la croix baissiere

bool CroixPresente(int shift)
  {
   // ATTENTION : les parametres doivent etre passes dans le MEME ORDRE
   // et le MEME NOMBRE que la liste des "extern" de l'indicateur,
   // avant l'indice de buffer et le shift.
   double haut = iCustom(NULL, 0, NomIndicateur,
                         /* param1, param2, param3, */
                         BufferHaut, shift);

   double bas  = iCustom(NULL, 0, NomIndicateur,
                         /* param1, param2, param3, */
                         BufferBas, shift);

   if(haut != EMPTY_VALUE && haut != 0) return(true);
   if(bas  != EMPTY_VALUE && bas  != 0) return(true);
   return(false);
  }
Trois pièges classiques sur iCustom :
  • Le nom passé est celui du fichier, sans extension. Si l'indicateur est rangé dans un sous-dossier de Indicators, il faut écrire le chemin relatif, par exemple "MonDossier\\MonIndicateur".
  • Il faut passer tous les paramètres externes de l'indicateur, dans l'ordre exact de sa déclaration. Un paramètre oublié ou décalé, et vous lisez silencieusement des valeurs fausses. C'est là que sert la copie d'écran des paramètres que philippou avait jointe à son message.
  • Testez toujours contre EMPTY_VALUE et contre 0 : selon la façon dont l'auteur a initialisé ses buffers, les cases vides valent l'un ou l'autre.
Cas B — les croix sont des objets graphiques. Si l'auteur les a dessinées avec ObjectCreate, la fenêtre de données n'affiche rien et iCustom ne renverra jamais quoi que ce soit. Il faut alors balayer les objets du graphique :

Code : Tout sélectionner

bool CroixObjetSurBougie(int shift)
  {
   datetime t = Time[shift];
   int total = ObjectsTotal();
   for(int i = total - 1; i >= 0; i--)
     {
      string nom = ObjectName(i);
      if(ObjectType(nom) != OBJ_ARROW) continue;
      if(ObjectGet(nom, OBJPROP_TIME1) == t) return(true);
     }
   return(false);
  }
C'est fonctionnel mais fragile : ça dépend du nommage des objets, ça ne marche pas dans le testeur de stratégie de la même façon, et ça devient lent si le graphique est chargé. Si vous êtes dans ce cas, la vraie solution est de faire modifier l'indicateur pour qu'il expose ses signaux dans un buffer.

Le point le plus important, avant tout code : vérifiez que votre indicateur ne repeint pas. Beaucoup d'indicateurs à flèches affichent une croix sur la bougie en cours, puis la déplacent ou la font disparaître. En temps réel votre EA sortira sur des signaux qui n'existeront plus ensuite, et le backtest sera magnifique et faux. Le test est simple : laissez tourner en démo une semaine, faites des captures d'écran chaque jour, et comparez avec le graphique une semaine plus tard. Si les croix ont bougé, arrêtez tout — c'est irréparable côté EA.

C'est aussi pour cela que je lis le signal sur shift = 1, donc sur bougie clôturée, dans tout ce qui précède.

4. Le délai de X bougies après l'entrée

C'est la partie la plus simple. On récupère le rang de la bougie d'ouverture de la position, ce qui donne directement le nombre de bougies écoulées.

Code : Tout sélectionner

extern int BougiesAvantSortie = 3;

int BougiesDepuisOuverture()
  {
   return(iBarShift(NULL, 0, OrderOpenTime(), false));
  }
Et la boucle de gestion, à appeler dans start() :

Code : Tout sélectionner

void GererSorties()
  {
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderSymbol() != Symbol())                   continue;
      if(OrderMagicNumber() != MagicNumber)           continue;

      if(BougiesDepuisOuverture() < BougiesAvantSortie) continue;
      if(!CroixPresente(1))                            continue;

      if(OrderType() == OP_BUY)
         OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrNONE);
      if(OrderType() == OP_SELL)
         OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrNONE);
     }
  }
Trois détails qui comptent :
  • La boucle part de la fin vers le début. Si vous fermez des ordres en parcourant dans l'autre sens, vous en sautez, parce que les positions se réindexent après chaque fermeture.
  • Le filtre sur OrderMagicNumber isole vos ordres de ceux d'un autre EA ou de vos ordres manuels. À ne jamais omettre.
  • Le filtre sur OrderSymbol évite d'aller fermer une position ouverte sur une autre paire.
5. Le squelette d'assemblage

Pour éviter que tout ne se déclenche à chaque tick, on ne traite qu'à l'ouverture d'une nouvelle bougie :

Code : Tout sélectionner

datetime derniereBarre = 0;

int start()
  {
   // gestion des sorties et du stop : a chaque tick
   GererSorties();
   // SuivreStop(...) si stop suiveur souhaite

   // logique d'entree : une seule fois par bougie
   if(Time[0] == derniereBarre) return(0);
   derniereBarre = Time[0];

   // ... vos conditions d'entree ici,
   // avec OrderSend(..., StopAchat() ou StopVente(), ...)

   return(0);
  }
6. Deux conseils de méthode, qui valent plus que le code
  • Faites parler l'EA. Avant de le laisser trader, ajoutez des Print() affichant la valeur de la moyenne mobile, le stop calculé et le retour de l'indicateur à chaque bougie, et comparez avec ce que vous lisez sur le graphique. 90 % des bugs se voient en cinq minutes avec cette méthode, et se cherchent pendant des semaines sans elle.
  • Contrôlez toujours le retour d'OrderSend et d'OrderModify. Le générateur ne le fait pas. Sans GetLastError(), un ordre rejeté est un ordre invisible : vous croyez être en position, vous ne l'êtes pas, et vous ne comprenez pas pourquoi.
Et un mot de contexte pour ceux qui arrivent ici en 2026 : MetaQuotes ne délivre plus de nouvelles licences MT4, et l'écosystème a largement basculé vers MT5. Les principes exposés ci-dessus restent identiques, mais la syntaxe diffère — la gestion des positions y passe par PositionSelect et les structures de requête plutôt que par OrderSelect. Si vous démarrez aujourd'hui un projet d'EA, autant le faire directement en MQL5.

Si quelqu'un bloque sur son propre indicateur, postez ici la liste de ses paramètres externes et une capture de la fenêtre de données sur une bougie portant une croix : avec ces deux éléments, l'appel iCustom correct s'écrit en une minute.

Répondre