Codes et pratiques élégants

 

Bonjour,

Pour changer un peu, je vous propose ce sujet.

Pas de question, pas de besoin particulier — juste l’envie de montrer un morceau de code que j’ai écrit, et que je trouve, pour une fois… élégant.

Je me lance, puisque c’est moi qui ai proposé l’idée.

Je devais gérer une sélection de trades dans une classe qui centralise toutes les positions d’un EA, pour pouvoir ensuite appliquer des modifications ciblées.

Très vite, je me suis heurté à la diversité des filtres et à la manière de les traiter.

Globalement, certains filtres conduisent à des modifications de SL/TP sans lien direct avec une notion monétaire.

D’autres en revanche ne portent que sur le SL — typiquement ceux liés au break-even, aux profits minimums, ou aux pertes maximales.


J’ai donc fini par écrire ce pseudo-code :

char Dispatch_Modify_Position( const string p_Filter,
                               const double           p_LevelSL,
                               const double           p_LevelTP,
                               const double           p_Threshold = 0.0 )
{
  //--- Position-level filters (without monetary directive)
  if ( p_Filter has no monetary intent )
    return this.My_Modify_Position( p_Filter, p_LevelSL, p_LevelTP );

  //--- Monetary directive filters (threshold-based)
  if ( p_Filter implies monetary logic )
    return this.My_Modify_Position( p_Filter, p_Threshold );
}

Ce que je trouve élégant, c’est cet "échangeur" :

Selon le filtre reçu, il redirige vers la bonne surcharge.

Chaque version applique ses propres règles métier, avec des contraintes de validation adaptées.

Pas de logique en spaghetti,

pas de tests empilés

juste une bifurcation nette.


Voilà.

Si vous avez des morceaux de code que vous trouvez beaux (même si personne d’autre ne vous croit 🤭), ce sujet est pour ça !

 
Salut

Dans certains scénarios, on souhaite appeler une fonction tant qu’elle retourne 1 (succès),

mais sans perdre la gestion explicite des cas 0 (rien à faire) ou <0 (erreur métier).

L’objectif est aussi de conserver une syntaxe claire en while(...) plutôt qu’un enchaînement de do...while , break , ou autres drapeaux.

Une astuce simple consiste à utiliser une valeur sentinelle fictive (ex. 999 ) que la fonction ne retourne jamais.

Ainsi, on force la gestion de tous les cas dans le corps de la boucle y compris un traitement comme une for()

int Process_Next()
{
  static int index = 0;

  if (index < 3)
  {
    Print("Processed unit #" + IntegerToString(index++));
    return 1;
  }

  if (index == 3)
  {
    Print("No more items to process.");
    return 0;
  }

  Print("Processing error occurred.");
  return -1;
}

void Ontick()
{
  int result    = 0;
  int processed = 0;

  // Loop continues as long as result ≠ 999 (sentinel value never returned)
  while ((result = Process_Next()) != 999)
  {
    if (result < 0)
    {
      Print("Error: processing aborted.");
      break;
    }

    if (result == 0)
    {
      PrintFormat("All %d items processed successfully.", processed);

      // Example: post-processing loop after full success
      for (int i = 0; i < processed; i++)
        PrintFormat(" → Finalization step on item #%d", i);

      break;
    }

    // Normal successful processing
    processed++;
  }
}

Ce code, je sûr de pouvoir le lire dans 6 mois sans trop me poser de question. Pas mal du tout cette sentinelle 
 

Le saviez-vous ?

Il vous sera presque impossible de créer une librairie pour gérer plusieurs symboles en trading.

Et pourquoi donc ?

Parce que la seule manière fiable de le faire, c’est… une classe mono-symbole. 😅


Le plus drôle, c’est que

créer une librairie mono-symbole n’a aucun sens.


Donc, pour faire du multi, il faut penser mono.


C’est un peu comme devoir créer un robot pour un seul instrument de musique… pour avoir un orchestre.

Vive la POO

 
Le code du jour

Des oncalculate comme ça ce n'est pas tous les jours et j'en suis très content.

très concis et avec les if ternaires on gagne vraiment car au final ce code gère beaucoup de possibilités

Vous en pensez quoi ? moi je trouve ça élégant, le sujet du post

int OnCalculate( const int        rates_total,
                 const int        prev_calculated,
                 const datetime & time[],
                 const double   & open[],
                 const double   & high[],
                 const double   & low[],
                 const double   & close[],
                 const long     & tick_volume[],
                 const long     & volume[],
                 const int      & spread[] ) {

  static const char Err_INIT_BUFF_FAIL     = -1;
  static const char Err_IMA_FAILED         = -2;
  static const char Err_DONCHIAN_FAIL      = -3;

  char i_Result = 0;

  //--- Tick gap check
  if ( Detect_TickGap( s_DevPrefs.TimeGap ) == 1 )
    return 0;

  //--- Display header logs
  i_Msg_TA.Display();

  //--- Compute starting index
  const bool is_Init = ( prev_calculated == 0 );
  int i_Start       = is_Init
                      ? s_UserPrefs.Period_Ind * 2
                      : prev_calculated - 1;

  //--- Initialize buffers
  if ( is_Init ) {
    i_Result = Init_Buffers();
    if ( i_Result < 0 ) {
      i_Msg_TA.Error( "Failed to initialize output buffers.",
                      Function_Class( __FUNCTION__ ),
                      Err_INIT_BUFF_FAIL );
      return 0;
    }
  }

  //--- MA processing
  i_Result = ( gf_IsTFEgal )
             ? ( is_Init
                 ? MA_Values_Full_STD( rates_total )
                 : MA_Values_Single_STD( i_Start, rates_total ) )
             : ( is_Init
                 ? MA_Values_Full_MTF( rates_total, time )
                 : MA_Values_Single_MTF( i_Start, rates_total, time ) );

  if ( i_Result < 0 ) {
    i_Msg_TA.Error( "Failed to process MA values.",
                    Function_Class( __FUNCTION__ ),
                    Err_IMA_FAILED );
    return 0;
  }

  //--- Donchian processing
  i_Result = ( gf_IsTFEgal )
             ? Donchian_STD( i_Start, rates_total, high, low )
             : Donchian_MTF( i_Start, rates_total, time );

  if ( i_Result < 0 ) {
    i_Msg_TA.Error( "Failed to process Donchian bounds.",
                    Function_Class( __FUNCTION__ ),
                    Err_DONCHIAN_FAIL );
    return 0;
  }

  return rates_total;
}
 
Gerard William G J B M Dinh Sy #:

Le saviez-vous ?

Il vous sera presque impossible de créer une librairie pour gérer plusieurs symboles en trading.

Et pourquoi donc ?

Parce que la seule manière fiable de le faire, c’est… une classe mono-symbole. 😅


Le plus drôle, c’est que

créer une librairie mono-symbole n’a aucun sens.


Donc, pour faire du multi, il faut penser mono.


C’est un peu comme devoir créer un robot pour un seul instrument de musique… pour avoir un orchestre.

Vive la POO

Qu'est-ce qui vous permet d'en arriver à un telle conclusion ?

 
Alain Verleyen #:

Qu'est-ce qui vous permet d'en arriver à un telle conclusion ?

Bonjour,

Mon expérience n'est pas comparable à la vôtre.

Quoi qu'il en soit, ce fil n'est pas destiné à un débat sur qui a raison ou tort, aujourd'hui ou demain. Le sujet est l'élégance des codes.

Si vous trouvez de l'élégance dans une librairie ou une classe multi symbole, montrez-la avec plaisir !

Si vous souhaitez présenter la partie que vous trouvez la plus jolie et la mieux conçue, allez-y, postez-la.

Si vous avez tout simplement envie de montrer quelque chose de joli, ce sujet est fait pour ça.

Passez une belle journée !

 

Dans le développement algorithmique, l’élégance ne se mesure pas à la complexité du code, mais à sa lisibilité, sa cohérence, et sa capacité à ne pas fatiguer le cerveau.


C’est cette cohérence qui permet de gérer plus facilement la complexité sans devenir dingue.


En MQL5, j’ai adopté une convention de nommage stricte, presque obsessionnelle.

Non pas pour le style, mais pour réduire la charge cognitive et fluidifier la lecture.


Reconnaître une variable et sa portée instantanément est une arme très efficace.

Je vous en parle parce que ce nommage est mature. C’est celui que j’utilise depuis des années.


Voici quelques principes que j’applique systématiquement, indépendamment des types :


Structuration & Nommage : une grammaire visuelle du code


En Programmation Orientée Objet :


Tous les membres de classe sont accédés via this. même si l’accès est implicite.

Cela rend les responsabilités visibles et les intentions claires.


Préfixes standardisés :

this.     Membre privé d’une classe           this.Membre
this.s_   Structure interne métier            this.s_Structure
this.i_   Instance de classe métier           this.i_Msg_TB

g_        Variable globale                    g_Variable_Globale
i_        Variable locale                     i_Variable_Locale
ir_       Variable locale passée par ref.     ir_Valeur_Reference

Pourquoi cette rigueur ?

Moins de friction mentale : Le cerveau identifie instantanément le rôle d’une variable.

Lisibilité transversale : Même dans un projet complexe, le code reste fluide.

Documentation implicite : On connaît tout de suite la portée de la variable, et c’est méga important.

Refactoring facilité : Les outils de recherche deviennent des scalpels. L’éditeur vous donne tout de suite les bonnes valeurs — j’insiste, si la portée est assurée.


En conclusion

L’élégance en code, ce n’est pas l’absence de bugs.

C’est la capacité à écrire du code qui respire, qui se lit comme une langue, et qui respecte l’attention du développeur.


Et vous, quelles sont vos pratiques élégantes en MQL5 ou ailleurs ?

 

Qu’est-ce qu’un code pratique et élégant ?

Avec les langages modernes, presque tout est possible.

La puissance de réalisation brute n’est plus la limite.

Mais cette puissance, livrée sans maîtrise, ne vaut pas grand-chose.

Comme le disait cette vieille publicité : “Sans maîtrise, la puissance n’est rien.”

Et dans le code, c’est encore plus vrai.


On peut produire des algorithmes puissants, des fonctions ultra polyvalentes, des classes d’une complexité vertigineuse…

Mais si le code devient trop complexe à lire, trop lourd à utiliser, ou trop éloigné de son intention métier, alors il perd sa valeur réelle : il devient fragile, opaque, et surtout difficile à faire évoluer.

En clair, il ne sert à rien !!!

Un code pratique et élégant, ce n’est pas juste un bel agencement syntaxique.

C’est un code qui respire la clarté, qui se laisse apprivoiser, et qui permet de garder une charge cognitive basse.

Pourquoi la charge cognitive basse est-elle cruciale ?

  • Maintenabilité accrue Plus un code est compréhensible, plus il est facile à maintenir, à corriger, à améliorer. Moins de temps est consacré à le déchiffrer, plus le temps est investi dans l’évolution métier.

  • Réduction des erreurs Un code clair limite les effets de bord. Il diminue les risques de bugs quand on le modifie ou qu’on le étend.

  • Clarté des intentions Un code bien structuré reflète l’intention du développeur. On comprend ce qu’il fait, pourquoi il le fait, et comment il peut être réutilisé ou adapté.

La vraie élégance ne vient pas de la solution, mais de la façon dont elle laisse de l’espace à l’esprit.

Un bon code ne se lit pas avec les dents serrées et une charge élevée — il se traverse comme une idée fluide et facilement. C’est ça, l’élégance.

Méfiez vous des concours de cons qui essaye de démontrer qui peut supporter la plus grosse charge cognitive. 
"les gagnants seront les perdants"


 

Les opérateurs ternaires : élégants ou trop abstraits ?

Bonjour à tous !

Je voulais ouvrir un petit débat sur l'utilisation des if ternaires dans nos codes.

Je les trouve très élégants quand ils gèrent un simple choix binaire — un vrai plaisir visuel et concis.

auto status = (isValid) ? "OK" : "ERROR";

Claire, nette, efficace. Le cerveau n’a aucun mal à suivre la logique.

Mais dès qu’on commence à gérer trois cas ou plus, là...

le charme s’effondre et la charge cognitive grimpe.

On se retrouve à décortiquer des imbrications, à jouer avec les parenthèses, et la lisibilité en prend un coup.


Regardez

auto level = (score > 90) ? "Excellent"
            : (score > 75) ? "Good"
            : (score > 60) ? "Average"
            : "Poor";

En une ligne, on gère quatre cas...

mais il faut se concentrer pour ne pas se tromper.

Un if-else classique serait plus long, mais tellement plus clair à maintenir !

Et vous, comment utilisez-vous les opérateurs ternaires ? Avez-vous des règles perso pour savoir quand basculer vers un if classique ? Ou peut-être des astuces pour garder le ternaire lisible, même quand ça se complexifie ?

Hâte de lire vos avis 

 
Y a foule.... Je me sens un peu seul pour le code. A ce demander si seul les anglophones développent ?
 

Hello les devs,


Dans 90% des codes qu’on écrit, on finit par avoir une fonction qui retourne une “valeur de statut”

ça peut être :

return 0; // OK

return -1; // Erreur

return +1; // Signal positif

Et sur le moment, c’est rapide, efficace, tout roule.  

Sauf que… 3 semaines plus tard, avec une base de code qui a évolué, 

vous cherchez ce que signifie ce -1 là au fond d’un bloc d’analyse, 

sans doc, 

sans trace. 

Et là… 


La solution : enum métier

Depuis que j’utilise des enum pour centraliser mes codes de retour ou mes états métier, refactoriser mon code est devenu fluide et lisible.

enum ENUM_TREND {

  TREND_FLAT = 0,

  TREND_BULLISH = 1,

  TREND_BEARISH = -1

};



// Utilisation claire :

if (GetTrend(...) == TREND_BEARISH) 

}


Pas besoin de se souvenir que -1 signifie “bearish”, ou que 0 veut dire “flat”. 

Le jour où je veux changer les valeurs internes ou ajouter des cas → un seul endroit à modifier

 Bonus

Meilleur auto-complétion dans l’éditeur

Moins d’erreurs de typo

Lisibilité instantanée quand on relit du code plusieurs mois plus tard

Bref, c’est un investissement minuscule qui me sauve des heures de débogage et de recodage.


Vous êtes team enum  ou  struct car une structure peut faire la même chose , ou vous continuez à cartographier les abysses de la jungle des -42 , 7 , et 3.14  ?

Franchement, centraliser ses codes métier, c’est pas du luxe