Erreur n° 1 lors de la modification des commandes - page 4

 
hoz:

Boris, supposons que c'est le cas... Assumant. Mais, si la fonction renvoie un ordre pour modifier l'ordre, le significantton doit être modifié. Et avec moi, ça ne change pas du tout. Même si nous regardons le journal dans le journal, ce que nous voyons est ceci :

Pourquoi la commande est-elle envoyée ? S'il n'avait pas les bons paramètres, la fonction aurait explosé... C'est comme OK... ...il a été envoyé. Puis il s'est avéré qu'il y avait une erreur... Quelle est la logique derrière cela ?

Je viens de l'allumer ! Victor, si "ok", cela signifie qu'un paramètre a été modifié, mais l'erreur 1 signifie qu'un paramètre a été déclaré modifié, mais s'est avéré inchangé. C'est pourquoi nous devons corriger votre logique pour éviter de tels cas, et toutes ces imprécisions entraîneront des requêtes et beaucoup d'erreurs sur le marché réel !

Vous savez que je n'utilise pas ce genre de style de programmation, que tout est éparpillé un peu partout. J'écris le programme comme un script logique, tous les événements se déroulent de manière séquentielle, et tout est toujours à portée de main avec toutes les conditions sans avoir à fouiller partout pour trouver quelque chose. Et j'utilise des fonctions externes pour effectuer l'action finale et vérifier les erreurs.

Mais dans votre cas, les non-initiés ne savent pas ce que vous avez et où c'est coché, tout est caché, et vous devez deviner si vous avez coché le prérequis ou non ! C'est beaucoup de mots, comme le mien maintenant, mais votre programme ne fait pas le poids. Il doit être clair et concis !

 

Boris, je réalise bien sûr que je n'ai pas tout en un seul endroit. Mais les paramètres d'entrée juste avant la fonction de modification sont ancrés. Je viens d'en faire une capture d'écran. Il y a des PLOs actuels et nouveaux, SL et TP. Et ils sont tous différents. Pourquoi devrais-je entrer dans les détails alors que tout est à proximité ? Si elle n'est pas imprimée et que vous pouvez voir que les paramètres sont différents, cela signifie qu'ils sont effectivement différents. Ou peut-être que vous ne devriez pas faire confiance à l'empreinte non plus ?

2013.08.26 00:00  Kevin Martens_Moi_Myrei EURUSD,M15: PositionsManipulations::fOrderModify: Тикет № 2; New_OOP = 1.34048; New_SL = 1.34125; New_TP = 1.33362
2013.08.26 00:00  Kevin Martens_Moi_Myrei EURUSD,M15: PositionsManipulations::fOrderModify:  Тикет № 2; OrderOpenPrice() = 1.34048; OrderStopLoss() = 0.0; OrderTakeProfit() = 0.0
2013.08.26 00:00  Kevin Martens_Moi_Myrei EURUSD,M15: PositionsManipulations::fOrderModify: Тикет № 2; New_OOP = 1.34048; New_SL = 1.34125; New_TP = 1.33362
2013.08.26 00:00  Kevin Martens_Moi_Myrei EURUSD,M15: PositionsManipulations::fOrderModify: _LastError = 0
2013.08.26 00:00  Kevin Martens_Moi_Myrei EURUSD,M15: modify #2  sell limit 0.10 EURUSD at 1.34048 sl: 1.34125 tp: 1.33362 ok
2013.08.26 00:00  Kevin Martens_Moi_Myrei EURUSD,M15: OrderModify error 1

Et avant cela, comme je l'ai montré ci-dessus, il y a un test :

 if ((ND (OrderOpenPrice()) != fd_OpenPrice) || ND ((OrderStopLoss()) != fd_NewSL) || (ND (OrderTakeProfit()) != fd_NewTP))
{
 ... Если какой-то из параметров отличается от нового, значит выполняется это условие...
}

Comment pouvez-vous être plus précis ? Il y a un couple de clowns au sommet qui a commencé à inventer des histoires. Apparemment, ils ne pouvaient pas ou ne voulaient pas comprendre ce que je demandais. Donc ils rient sans raison. Mais la question est intéressante.

Si nous insérons par exemple un paramètre d'un autre type ou une mauvaise quantité dans OrderModify(), des erreurs se produisent immédiatement. Et là, il est exécuté, dit comme OK, mais ensuite il s'avère qu'aucun paramètre n'a été modifié.

La question est de savoir comment nous pouvons savoir ce qui ne va pas là-bas. J'ai exposé ma fonction. Tout devrait y être clair. C'est ici :

// 1.2 Функция модифицирует выделенный ордер (установка TP и SL). =========================================================================
bool PositionsManipulations::fOrderModify (int       fi_Ticket,             // OrderTicket()
                                           double    fd_OpenPrice,          // OpenPrice
                                           double    fd_NewSL,              // Новый StopLoss (УРОВЕНЬ !!!)
                                           double    fd_NewTP,              // Новый TakeProfit (УРОВЕНЬ !!!)
                                           datetime  fdt_Expiration = 0,    // Время истечения отложенного ордера
                                           color     fc_Arrow = CLR_NONE)   // Цвет стрелок модификации StopLoss и/или TakeProfit на графике
{
   Print (__FUNCTION__, ": ", "Вошли в функцию ");

//---- Проверяем необходимость модификации
   if (fd_NewSL == OrderStopLoss() && fd_NewTP == OrderTakeProfit())
       return (false);
//----
   string ls_fName = "fOrderModify()";
   int    li_Cnt = 0;
   double ld_Price;
   bool   lb_InvalidSTOP = false,
          lb_FixInvalidPrice = false;    // Флаг первоначальной коррекции отложки

//---- Получаем актуальную информацию по символу и текущему ордеру
   CBase.GetMarkerInfo (OrderSymbol(), fi_Ticket);
   
   Print (__FUNCTION__, ": ", "До проверки на FREEZELEVEL и STOPLEVEL  тикета № ", fi_Ticket);
   Print (__FUNCTION__, ": ", "Тикет № ", OrderTicket(), "; OrderOpenPrice() = ", OrderOpenPrice(),
             "; OrderStopLoss() = ", OrderStopLoss(), "; OrderTakeProfit() = ", OrderTakeProfit());
   Print (__FUNCTION__, ": ", "Тикет № ", fi_Ticket, "; New_OOP = ", fd_OpenPrice, "; New_SL = ", fd_NewSL, "; New_TP = ", fd_NewTP);
//---- Проверяем на условия FREEZELEVEL и STOPLEVEL
   if (!CheckLevelsBLOCK (3, SPos.gs_Symbol, SPos.gi_Type, fd_OpenPrice, fd_NewSL, fd_NewTP, lb_FixInvalidPrice))
   {
      if (StringLen (CErrs.ErrInf) > 0)      // Если переменная для хранения ошибок не пустая..
      {
         CLogs.WriteLog (CErrs.ErrInf);      //.. то, запишем в лог и выйдем из функции
         return (false);
      }
   }
   Print (__FUNCTION__, ": ", "После проверки на FREEZELEVEL и STOPLEVEL  тикета № ", fi_Ticket);
   Print (__FUNCTION__, ": ", "Тикет № ", fi_Ticket, "; New_OOP = ", fd_OpenPrice, "; New_SL = ", fd_NewSL, "; New_TP = ", fd_NewTP);
   
   ResetLastError();
   
//---- Определяем цвет значков модификации ордеров
   if (fc_Arrow == CLR_NONE)
       fc_Arrow = ColorByModify [OrderType() % 2];
   
//---- Выполняем модификацию в тестере
   if (!CBase.GetRealTrade())
   {
      if ((ND (OrderOpenPrice()) != fd_OpenPrice) || ND ((OrderStopLoss()) != fd_NewSL) || (ND (OrderTakeProfit()) != fd_NewTP))
      {
      ResetLastError();
      
      Print (__FUNCTION__, ": ", " Тикет № ", OrderTicket(), "; OrderOpenPrice() = ", OrderOpenPrice(),
             "; OrderStopLoss() = ", OrderStopLoss(), "; OrderTakeProfit() = ", OrderTakeProfit());
      Print (__FUNCTION__, ": ", "Тикет № ", fi_Ticket, "; New_OOP = ", fd_OpenPrice, "; New_SL = ", fd_NewSL, "; New_TP = ", fd_NewTP);
      Print (__FUNCTION__, ": ", "_LastError = ", _LastError);
      
      if (!OrderModify (fi_Ticket, fd_OpenPrice, fd_NewSL, fd_NewTP, fdt_Expiration, fc_Arrow))
      {
         CLogs.WriteLog (StringConcatenate ("fOrderModify(): ", CErrs.ErrorToString (_LastError)));
         Print (__FUNCTION__, ": ", "После модификации тикета № ", fi_Ticket);
         Print (__FUNCTION__, ": ", "Тикет № ", fi_Ticket, "; New_OOP = ", fd_OpenPrice, "; New_SL = ", fd_NewSL, "; New_TP = ", fd_NewTP);
         return (false);
      }
      }
   }
   

J'ai volontairement commenté toutes les lignes. Vous pourriez gâcher le code. Si vous avez des commentaires, merci de nous aider.

 
hoz:

Boris, je réalise bien sûr que je n'ai pas tout en un seul endroit. Mais les paramètres d'entrée juste avant la fonction de modification sont ancrés. Je viens d'en faire une capture d'écran. Il y a des PLOs actuels et nouveaux, SL et TP. Et ils sont tous différents. Pourquoi devrais-je entrer dans les détails alors que tout est à proximité ? Si elle n'est pas imprimée et que vous pouvez voir que les paramètres sont différents, cela signifie qu'ils sont effectivement différents. Ou peut-être que vous ne devriez pas faire confiance à l'empreinte non plus ?

Et avant cela, comme je l'ai montré ci-dessus, il y a un test :


Tout

Désolé, je ne parviens pas à comprendre ce qui se passe car je ne vois aucune condition dans la boucle qui garantit que les paramètres de commande ne sont pas confondus entre eux !

La présence d'une erreur vous indique que vous commettez une erreur logique quelque part. Mais il est également dit que le programme fonctionne, mais ce qui nous intéresse, c'est la qualité du programme !

 
borilunad:

Désolé, je n'arrive pas à comprendre car je ne vois aucune condition dans la boucle qui assure que les paramètres de commande ne sont pas confondus entre eux !

La présence d'une erreur vous indique que vous commettez une erreur logique quelque part. Mais il est également dit que le programme fonctionne, mais ce qui nous intéresse, c'est la qualité du programme !


Toutes les opérations avec les commandes ont lieu dans une boucle ! C'est là qu'est appelée la méthode fOrderModify(), dont j'ai cité le code ci-dessus :

//+---------------------------------------------------------------------------------------------------------------------------------------+
//|                                                   Перенос лимитных ордеров по сигналу                                                 |
//+---------------------------------------------------------------------------------------------------------------------------------------+
void MovingLimitPositions (const double& fd_MurreyLevelsValue[])
{
   double New_OOP = 0.0,
          New_SL = 0.0,
          New_TP = 0.0;
   int    Ticket = -1;
   
   for (int i=0; i<13; i++)
   {
      Print (__FUNCTION__, ": ", "fd_MurreyLevelsValue["+IToS (i)+"] = ", DToS (fd_MurreyLevelsValue[i]));
   }

   for (int i=OrdersTotal()-1; i>=0; i--)
   {
      New_OOP = 0.0;
      New_SL = 0.0;
      New_TP = 0.0;
      
      ResetLastError();
      
      if (!CPosMan.CheckMyOrdersBased(i, 7)) continue;
      if (!OrderSelect (i, SELECT_BY_POS, MODE_TRADES)) continue;

      Ticket = OrderTicket();

      Print (__FUNCTION__, ": ", "Выбран тикет № ", OrderTicket());
      Print (__FUNCTION__, ": ", "Тикет № ", OrderTicket(), "; OrderOpenPrice() = ", OrderOpenPrice(),
             "; OrderStopLoss() = ", OrderStopLoss(), "; OrderTakeProfit() = ", OrderTakeProfit());
          
      if (OrderType() == OP_BUYLIMIT)
      {
         if (ND (OrderOpenPrice()) != ND (fd_MurreyLevelsValue[1]))
            New_OOP = ND (fd_MurreyLevelsValue[1]);
         else New_OOP = ND (OrderOpenPrice());
         if (ND (OrderStopLoss()) != ND (fd_MurreyLevelsValue[0]))
            New_SL = ND (fd_MurreyLevelsValue[0]);
         else New_SL = ND (OrderStopLoss());
         if (ND (OrderTakeProfit()) != ND (fd_MurreyLevelsValue[10]))
            New_TP = ND (fd_MurreyLevelsValue[10]);
         else New_TP = ND (OrderTakeProfit());
         
         Print (__FUNCTION__, ": ", "New_OOP = ", New_OOP, "; New_SL = ", New_SL, "; New_TP = ", New_TP);
         
         if (ND (OrderOpenPrice()) != fd_MurreyLevelsValue[1])
         {
            if (!CPosMan.fOrderModify (OrderTicket(), New_OOP, New_SL, New_TP))
            {
               if (_LastError > 0)
                  CLogs.WriteLog (StringConcatenate (__FUNCTION__, ". В строке ", __LINE__, " ", CErrs.ErrorToString (_LastError)),
                                  CLogs.GetNeedLogs(), CLogs.GetPrintUP(), CLogs.GetCommentUP());
            }
            else
            {Print (__FUNCTION__, ": ", "Модификация тикета №", OrderTicket(), " успешно завершена!");}
         }
      }
      
      if (OrderType() == OP_SELLLIMIT)
      {
         if (ND (OrderOpenPrice()) != ND (fd_MurreyLevelsValue[11]))
            New_OOP = ND (fd_MurreyLevelsValue[11]);
         else New_OOP = ND (OrderOpenPrice());
         if (ND (OrderStopLoss()) != ND (fd_MurreyLevelsValue[12]))
            New_SL = ND (fd_MurreyLevelsValue[12]);
         else New_SL = ND (OrderStopLoss());
         if (ND (OrderTakeProfit()) != ND (fd_MurreyLevelsValue[2]))
            New_TP = ND (fd_MurreyLevelsValue[2]);
         else New_TP = ND (OrderTakeProfit());
         
         Print (__FUNCTION__, ": ", "New_OOP = ", New_OOP, "; New_SL = ", New_SL, "; New_TP = ", New_TP);
         Print (__FUNCTION__, ": ", "Тикет № ", OrderTicket(), "; OrderOpenPrice() = ", OrderOpenPrice(),
             "; OrderStopLoss() = ", OrderStopLoss(), "; OrderTakeProfit() = ", OrderTakeProfit());
             
         if (ND (OrderOpenPrice()) != fd_MurreyLevelsValue[11])
         {
            if (!CPosMan.fOrderModify (Ticket,  New_OOP, New_SL, New_TP))
            {
               if (_LastError != 0)
                  CLogs.WriteLog (StringConcatenate (__FUNCTION__, ". В строке ", __LINE__, " ", CErrs.ErrorToString (_LastError)),
                                  CLogs.GetNeedLogs(), CLogs.GetPrintUP(), CLogs.GetCommentUP());
            }
            else
            {
               Print (__FUNCTION__, ": ", "Модификация тикета №", OrderTicket(), " успешно завершена!");
               Print (__FUNCTION__, ": ", "Тикет № ", OrderTicket(), "; OrderOpenPrice() = ", OrderOpenPrice(),
             "; OrderStopLoss() = ", OrderStopLoss(), "; OrderTakeProfit() = ", OrderTakeProfit());
            }
         }
      }
   }
   Print (__FUNCTION__, ": ", "OUT OF FUNCTION");
}

On peut tout y voir... Vous pouvez également voir qu'après chaque itération de la boucle, l'erreur est effacée. Ainsi, le fait est garanti qu'une erreur dans l'ordre précédent, s'il y en a eu une, ne sautera pas au suivant (je parle de sa valeur, bien sûr).

Comment cela peut-il être plus facile ? C'est si simple...

 

J'ai trouvé un message.

C'est le même bug du terminal. Je n'ai jamais eu un tel bug avant. Je n'ai jamais essayé de modifier les 3 paramètres (OOP, SL et TP) des ordres en attente auparavant. Mais j'ai dû le faire. Et je suis tombé sur un bug.

Je vois, si, par exemple, le prix d'ouverture et le Stop Loss n'ont pas changé et à leur place nous obtenons les mêmes valeurs, mais les Take Points ont changé. Cela provoque-t-il également l'erreur ? Puis il s'avère que la documentation est tordue. Et ce point n'est pas soutenu ou quoi ?

 
hoz:

J'ai trouvé un message.

C'est le même bug du terminal. Je n'ai jamais eu un tel bug avant. Je n'ai jamais essayé de changer les 3 paramètres (OOP, SL et TP) des ordres en attente avant. Mais j'ai dû le faire. Et je suis tombé sur un bug.

Je vois, si, par exemple, le prix d'ouverture et le Stop Loss n'ont pas changé et à leur place nous obtenons les mêmes valeurs, mais les Take Points ont changé. Cela provoque-t-il également l'erreur ? Puis il s'avère que la documentation est tordue. Et ce point n'est pas soutenu ou quoi ?

Vérifiez-vous également la distance à chaque tic ? J'ai depuis longtemps adopté la règle d'ouvrir les ordres à l'ouverture de la barre TF et de ne les modifier et fermer qu'à l'ouverture de la barre M1 ! Le code ci-dessus me fait penser à un rapport d'activité, qui semble tout contenir, mais rien de concret ! Je ne vois pas de boucle, dans laquelle vous définissez toutes les actions par des conditions spécifiques ! Je vois seulement une boucle qui me dit qu'il n'y a rien à modifier, alors il suffit de ne pas modifier, et il n'y aura pas d'erreurs.

Notez l'observation importante de Renat que vos erreurs peuvent provenir d'une confusion entre ce qu'il faut faire globalement et ce qu'il faut faire localement, et les erreurs sont toujours les dernières, les précédentes sont réinitialisées sans que vous n'ayez à intervenir avec la sortie de la fonction !

 
borilunad:

Vous vérifiez aussi la distance à chaque tic tac ? !

Non ! Je n'autorise les modifications que si une condition est remplie. Dans ce cas, la condition de modification est de changer les niveaux calculés. Juste comme ça :

void OnTick()
{
   static double FirstInd = 0.0;                    // Переменная, которая хранит значение 1-ого индекса массива
   double MurreyLevelsValue[13];                    // Массив значений уровней
   GetDataFromIndicator (MurreyLevelsValue);        // Функция, которая получает значения из индикатора уровней
   
   if (gdt_LastBarTime <= Time[0])
   {
      OTotal = CPosInf.FindPositions (AmountPosByType);   // Получаем количество позиций( по типам ).
      
      GetTradeSignal (MurreyLevelsValue);
      
      gdt_LastBarTime = Time[0];
   }

   if (FirstInd != ND (MurreyLevelsValue[1]))      // Если значений текущего состояния переменной FirstInd не равно новому рассчитанном, то.. 

   {
      MovingLimitPositions (MurreyLevelsValue);     // ..модифицируем ордер новым значением
      FirstInd = ND (MurreyLevelsValue[1]);
   }
}

Simple ? Juste....

Borilunad:


J'ai pris pour règle, il y a longtemps, d'ouvrir des ordres à l'ouverture d'une barre en TF, et de ne modifier et fermer qu'à l'ouverture d'une barre en M1 ! Le code ci-dessus me fait penser à un rapport d'activité, qui semble tout contenir, mais rien de précis ! Je ne vois pas de boucle, dans laquelle vous définissez toutes les actions par des conditions spécifiques ! Je vois seulement une boucle qui me dit qu'il n'y a rien à modifier, alors il suffit de ne pas modifier et il n'y aura pas d'erreurs.

J'ai aussi eu des pensées similaires, pour modifier seulement à l'ouverture de M1, et, ceci est applicable si la modification est faite par une valeur prédéfinie. Mais il y a des situations où je n'ai pas besoin de vérifier ces données sur M1. Par exemple, j'ai un stop tiré à un niveau qui a déjà été calculé. Ensuite, comme je l'ai montré ci-dessus, j'ai une vérification dans la fonction OnInit() :

if (FirstInd != ND (MurreyLevelsValue[1]))
{
   MovingLimitPositions (MurreyLevelsValue);
   FirstInd = ND (MurreyLevelsValue[1]);
}
C'est-à-dire que si le niveau a changé, il... modifie. Cela évite les tentatives de modification inutiles. En quelque sorte, une modification par signal et non par minuterie. Compris ici ?


borilunad:


Je ne vois pas de boucle où l'on définit toutes les actions par des conditions spécifiques ! Je ne vois qu'une boucle qui me dit qu'il n'y a rien à modifier, alors ne modifie pas et il n'y a pas d'erreur.

J'ai tout déshabillé là-bas. Qu'est-ce qu'il n'y a pas à comprendre... :(

borilunad:

Notez le point important de Renat que vos erreurs peuvent provenir d'une confusion entre ce qu'il faut faire globalement et ce qu'il faut faire localement, et les erreurs sont toujours les dernières, les premières sont réinitialisées sans votre implication avec la sortie de fonction !

Je ne suis pas le seul à avoir ce problème, comme je l'ai découvert. Voici un exemple...

Et il semble qu'il n'ait pas été résolu et qu'il ne le sera pas. Peut-être que les développeurs sont paresseux ? Si je ne travaille pas correctement avec des erreurs, quelqu'un, y compris Renat, aurait pu fouiller dans le code, et ne pas simplement dire que je me trompe.

Après tout, si les valeurs nouvelles et actuelles des paramètres à modifier sont imprimées avant la fonction de modification, il est clair que ces valeurs sont présentes. Pourquoi aller plus haut ? Il y a des valeurs, vous pouvez voir qu'il n'y a pas d'erreurs (j'ai imprimé là pour les erreurs). Cela signifie que tout est bien dans la logique. Donc la faute à la fonction de modification.

 
hoz:

Non ! Je n'autorise les modifications que si une condition est remplie. Dans ce cas, la condition de modification est de changer les niveaux calculés. Juste comme ça :

Simple ? Juste....

J'ai eu aussi des pensées similaires, de modifier seulement à l'ouverture de M1, et, ceci est applicable si la modification est effectuée par une valeur prédéterminée. Mais il y a des situations où je n'ai pas besoin de vérifier ces données sur M1. Par exemple, j'ai un stop tiré à un niveau qui a déjà été calculé. Ensuite, comme je l'ai montré ci-dessus, il y a une vérification dans la fonction OnInit() :

C'est-à-dire que si le niveau a changé, il... modifie. Cela évite les tentatives de modification inutiles. En quelque sorte, une modification par signal et non par minuterie. Compris ici ?


J'ai tout débranché là-bas. Qu'est-ce qu'il n'y a pas à comprendre... :(

Je ne suis pas le seul à avoir ce problème, comme je l'ai découvert. Voici un exemple...

Et il semble qu'il n'ait pas été résolu et qu'il ne le sera pas. Peut-être que les développeurs sont paresseux ? Si je ne travaille pas correctement avec des erreurs, quelqu'un, y compris Renat, pourrait intervenir dans le code, et pas seulement dire que je me trompe.

Après tout, si les valeurs nouvelles et actuelles des paramètres à modifier sont imprimées avant la fonction de modification, il est clair que ces valeurs sont présentes. Pourquoi aller plus haut ? Il y a des valeurs, il est clair qu'il n'y a pas d'erreurs (j'ai imprimé là pour les erreurs). Cela signifie que tout est bien dans la logique. La faute est donc dans la fonction de modification.

J'ai déjà examiné cet exemple. Mais nous devons nous appliquer à toutes les conditions, jusqu'à ce qu'il n'y ait rien de mieux. Votre code ne me convainc pas. Maintenant, je vais déjeuner, puis je vous donnerai un exemple de boucle, qui fonctionne bien pour le réglage de SL, la conversion en B/S et le chalutage pour un seul appel à la fonction de modification, où les erreurs sont traitées, si elles surviennent soudainement dans le travail, chez le testeur ne surviennent pas.

Voulez-vous la fonction Modify() ?

 
hoz:

Après tout, si les nouvelles valeurs et les valeurs actuelles des paramètres à modifier sont imprimées avant la fonction de modification, il est clair que ces valeurs sont présentes. Pourquoi aller plus haut ? Il y a des valeurs, il est clair qu'il n'y a pas d'erreurs (j'ai imprimé là pour les erreurs). Cela signifie que tout est bien dans la logique. La faute est donc dans la fonction de modification.

Le code semble correct... et sur le vrai (pas la démo) il fonctionne ?
 

Victor, pourquoi avez-vous modifié le SL et le TP dans les positions en attente ? En général, il est judicieux de fixer le SL seulement après avoir ouvert une position, et le TP après le transfert du SL vers le B/S ! Alors pourquoi déranger le serveur autant pour rien et pourquoi vous devez vous donner tout ce mal !

Vous devez minimiser et simplifier le code, pour qu'il fonctionne rapidement et clairement, et il sera ensuite plus facile de le modifier en raison des aléas du marché ! Réfléchissez bien à toutes les nuances liées aux réalités du marché !