English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Utiliser MetaTrader 5 comme fournisseur de signaux pour MetaTrader 4

Utiliser MetaTrader 5 comme fournisseur de signaux pour MetaTrader 4

MetaTrader 5Exemples | 12 janvier 2022, 17:08
86 0
Karlis Balcers
Karlis Balcers

Introduction

Pour de multiples raisons, j'ai choisi d'écrire cet article et d'examiner si c'est faisable.

Premièrement, MetaTrader 5 est sorti et disponible depuis longtemps, mais nous attendons tous que nos courtiers préférés nous permettent de trader en réel. Certains ont élaboré des stratégies en utilisant MQL5 et ont obtenu de bonnes performances, et veulent maintenant les appliquer sur des comptes réels. D'autres, peut-être, aiment la façon dont le trading est organisé et veulent trader manuellement, mais en utilisant MetaTrader 5, au lieu de MetaTrader 4.

Deuxième raison, pendant le championnat de trading automatisé, tout le monde a pensé à suivre les leaders sur leurs propres comptes réels. Certains ont créé leur propre façon de suivre les trades, mais certains cherchent encore comment le faire, comment obtenir des résultats aussi proches que possible des traders en championnat, comment appliquer la même gestion de l'argent.

Troisièmement, certaines personnes ont de bonnes stratégies et elles veulent fournir leurs signaux de trading non seulement à elles-mêmes, mais aussi à leurs amis ou à d'autres. Ils doivent pouvoir accepter des connexions multiples sans perte de performance et distribuer les signaux en temps réel.

Ce sont les questions que j'ai toujours eues à l'esprit et je vais essayer de trouver une solution qui couvre ces besoins.


1. Comment suivre les activités du championnat MQL5 ?

Dernièrement, j'ai trouvé plusieurs articles dans MQL5.community qui correspondaient à mon niveau de connaissance et m'ont fait penser que je pouvais le construire. Je vous dirai également que j'ai utilisé l'application qui suivait les activités sur la page d'accueil du championnat et que j'effectuais des trading sur mon compte réel (heureusement, avec des bénéfices). Le problème était - les données sont mises à jour toutes les 5 minutes et que vous pouvez manquer le bon moment pour ouvrir et fermer.

Sur le forum du championnat, j'ai compris qu'il y a d'autres personnes qui font la même chose, que ce n'est pas efficace et que cela génère un énorme trafic sur la page d'accueil du championnat, ce qui pourrait déplaire aux organisateurs. Alors, y a-t-il une solution ? J'ai examiné toutes les solutions et j'ai apprécié la possibilité d'accéder au compte de chaque participant en mode « investisseur » (trading désactivé) via MetaTrader 5.

Peut-on l'utiliser pour recevoir des informations sur chaque activité de trading en temps réel et les transférer en temps réel ? Pour le trouver, j'ai créé Expert Advisor et j'ai essayé de l'exécuter sur un compte qui n'avait qu'un accès en mode « investisseur ». A ma grande surprise, il était possible de le joindre et d'obtenir des informations sur les Positions, les Ordres et les Transactions - qui étaient les portes d'une solution possible !


2. Que suivre - Positions, ordres ou transactions ?

Si nous sommes sur le point de transférer des informations de MetaTrader 5 vers MetaTrader 4, nous devons prendre en considération tous les types d'ordres possibles dans MetaTrader 4. De même, lorsque nous suivons, nous voulons connaître toutes les actions effectuées sur le compte en rapport avec le trading. Par conséquent, les « Positions » ne nous fourniront pas d'informations complètes à moins que nous ne comparions le statut des « Positions » à chaque tick ou seconde.

Par conséquent, il serait préférable de suivre les « Ordres » ou les « Offres ».

J'ai commencé à regarder les ordres :

Ordres

J'ai apprécié qu'ils soient exécutés avant que « Offres » ne soit et qu'ils contiennent également des informations sur les ordres en attente (limites), mais il leur manque une chose importante par rapport aux "« Offres » - le type d'entrée (ENUM_DEAL_ENTRY) :

Offres

DEAL_ENTRY_TYPE permet de comprendre ce qui s'est passé sur le compte des traders, tandis que les « Ordres » nécessitent un calcul en parallèle. Le mieux serait de fusionner « Offres » avec « Ordres », alors nous pourrions avoir des ordres en attente et suivre également chaque action dans le compte de trading. Étant donné que les mouvements de prix diffèrent d'une société de courtage à l'autre, les ordres en attente peuvent en fait entraîner des erreurs et des résultats incorrects.

Si nous suivons uniquement les « Offres », nous exécuterons toujours les ordres en attente, mais avec un petit retard (jusqu'à la connexion réseau). Entre rapidité (ordres en attente) et performances (offres), j'ai choisi d'opter pour la performance (« Offre »).


3. Comment fournir des « signaux » ?

Il y a eu différents articles et discussions sur la façon de communiquer et de transférer des données de MetaTrader 5 vers d'autres applications et ordinateurs. Puisque je veux que d'autres clients puissent se connecter à nous et qu'ils seront très probablement situés sur d'autres ordinateurs, je choisis la connexion par socket TCP.

Étant donné que MQL5 ne permet pas de le faire avec des fonctions API, nous devons utiliser une bibliothèque externe. Il existe plusieurs articles sur l'implication de la bibliothèque « WinInet.dll » (par exemple « Utilisation de WinInet.dll pour l'échange de données entre terminaux via Internet » et autres) mais aucun d'entre eux ne répond vraiment à nos besoins.

Comme je connais un peu C#, j'ai décidé de créer ma propre bibliothèque. Pour cela, j'ai utilisé l'article « Exposition du code C# à MQL5 à l'aide d'exportations non gérées » pour m'aider à résoudre les problèmes de compatibilité. J'ai créé un serveur avec une interface très simple et la possibilité d'accepter jusqu'à 500 clients en même temps (nécessite .NET framework 3.5 ou plus dans votre PC. Déjà installé dans la plupart des ordinateurs. « Microsoft .NET Framework 3.5 »).

#import "SocketServer.dll"    // Library created on C# (created by using information available on https://www.mql5.com/fr/articles/249)
string About();            // Information about library.
int SendToAll(string msg);  // Sends one text message to all clients.
bool Stop();               // Stops the server.
bool StartListen(int port); // Starts the server. Server will listen from incomming connections (max 500 clients). 
                               // All clients are built on Assync threads.
string ReadLogLine();       // Retrieve one log line from server (can contain erros and other information). 
                               // Reading is optional. Server stores only last 100 lines.
#import

Le serveur lui-même s'exécute en arrière-plan sur des threads séparés et ne bloquera ni ne ralentira le travail de MetaTrader 5 ou de votre stratégie, quel que soit le nombre de clients connectés.

Code source C# : 

         internal static void WaitForClients()
        {
            if (server != null)
            {
                Debug("Cant start lisening! Server not disposed.");
                return;
            }
            try
            {

                IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, iPort);
                server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                server.Bind(localEndPoint);
                server.Listen(500);
                isServerClosed = false;
                isServerClosedOrClosing = false;

                while (!isServerClosedOrClosing)
                {
                    allDone.Reset();
                    server.BeginAccept(new AsyncCallback(AcceptCallback), server);
                    allDone.WaitOne();
                }
                
            }
            catch (ThreadAbortException)
            {
            }
            catch (Exception e)
            {
                Debug("WaitForClients() Error: " + e.Message);
            }
            finally
            {
                if (server != null)
                {
                    server.Close();
                    server = null;
                }
                isServerClosed = true;
                isServerClosedOrClosing = true;
            }
        }

        internal static void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                allDone.Set();
                if (isServerClosedOrClosing)
                    return;
                Socket listener = (Socket)ar.AsyncState;
                Socket client = listener.EndAccept(ar);

                if (clients != null)
                {
                    lock (clients)
                    {
                        Array.Resize(ref clients, clients.Length + 1);
                        clients[clients.Length - 1].socket = client;
                        clients[clients.Length - 1].ip = client.RemoteEndPoint.ToString();
                        clients[clients.Length - 1].alive = true;
                    }
                    Debug("Client connected: " + clients[clients.Length - 1].ip);
                }
            }
            catch (Exception ex)
            {
                Debug("AcceptCallback() Error: " + ex.Message);
            }
        }

Pour en savoir plus sur les sockets de serveur asynchrones en C#, je vous recommande de lire Microsoft MSDN ou certains articles que vous pouvez trouver avec Google.


4. Comment collecter des « signaux » ?

Sur MetaTrader 4, nous aimerions recevoir des informations à tout moment et pas seulement lorsqu'un nouveau tick est généré, c'est pourquoi nous créons un « Script » pour cela, au lieu d'Expert Advisor. De plus, nous devons être en mesure d'ouvrir une connexion socket avec notre fournisseur de signaux - MetaTrader 5.

Pour cela, j'ai choisi de m'aider de MQL4 codebase : « https://www.mql5.com/fr/code/9296 ». Là, j'ai trouvé un très bon fichier d'inclusion (WinSock.mqh) qui permet de travailler avec des sockets de manière très simple. Même si certaines personnes se sont plaintes de la stabilité, je l'ai trouvée suffisamment bonne pour mes besoins et je n'ai rencontré aucun problème pendant mes tests.

#include <winsock.mqh>  // Downloaded from MQ4 homepage
                     // DOWNLOAD:   http://codebase.mql4.com/download/18644
                     // ARTICLE:    http://codebase.mql4.com/6122


5. Traitement des données

Nous avons maintenant notre concept et tout ce que nous devons faire est de nous assurer que les transactions sont traitées et transférées une par une à tous les clients dans un format qu'ils peuvent comprendre et exécuter.

5.1. Côté serveur

Comme nous l'avons précisé, ce sera Expert Advisor mais il ne se soucie pas de la devise sur laquelle il a été ajouté.

Au cours de son démarrage, il lancera également le thread qui attendra les connexions entrantes :

int OnInit()
  {
   string str="";
   Print(UTF8_to_ASCII(About()));
//--- start the server
   Print("Starting server on port ",InpPort,"...");
   if(!StartListen(InpPort))
     {
      PrintLogs();
      Print("OnInit() - FAILED");
      return -1;
     }

Dans cette version, l'Expert Advisor ne se préoccupe pas des clients connectés. Chaque fois qu'il y a une transaction, une notification est envoyée à tous les clients, même s'il n'y en a pas. Puisque nous n'avons besoin de connaître que les transactions, nous utiliserons la fonction OnTrade() et supprimerons OnTick(). Dans cette fonction, nous examinons l'histoire récente et décidons s'il s'agit d'une affaire dont nous devons être informés ou non.

Voir mes commentaires dans le code pour mieux le comprendre :

//+------------------------------------------------------------------+
//| OnTrade() - every time when there is an activity related to      |
//|             traiding.                                            |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- find all new deals and report them to all connected clients
//--- 24 hours back.
   datetime dtStart=TimeCurrent()-60*60*24;
//--- 24 hours front (in case if you live in GMT-<hours>)
   datetime dtEnd=TimeCurrent()+60*60*24;
//--- select history from last 24 hours.
   if(HistorySelect(dtStart,dtEnd))
     {
      //--- go through all deals (from oldest to newest).
      for(int i=0;i<HistoryDealsTotal();i++)
        {
         //--- get deal ticket.
         ulong ticket=HistoryDealGetTicket(i);
         //--- if this deal is interesting for us.
         if(HistoryDealGetInteger(ticket,DEAL_ENTRY)!=DEAL_ENTRY_STATE)
           {
            //Print("Entry type ok.");
            //--- check if this deal is newer than previously reported one.
            if(HistoryDealGetInteger(ticket,DEAL_TIME)>g_dtLastDealTime)
              {
               //--- if some part of position has been closed then check if we need to enable it
               if(HistoryDealGetInteger(ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT)
                 {
                  vUpdateEnabledSymbols();
                 }
               //--- if opposite position is opened, then we need to enable disabled symbol.
               else if(HistoryDealGetInteger(ticket,DEAL_ENTRY)==DEAL_ENTRY_INOUT)
                 {
                  //--- enable this specific symbol.
                  vEnableSymbol(HistoryDealGetString(ticket,DEAL_SYMBOL));
                 }
               //--- check if symbol is enabled.
               if(bIsThisSymbolEnabled(HistoryDealGetString(ticket,DEAL_SYMBOL)))
                 {
                  //--- build deal-string and send to all connected clients
                  int cnt=SendToAll(sBuildDealString(ticket));
                  //--- technical error with server.
                  if(cnt<0)
                    {
                     Print("Failed to send new deals!");
                    }
                  //--- if sent to no one (cnt==0) or if sent to someone (cnt>0)                  
                  else
                    {
                     //--- update datetime for last sucessfully transfered deal
                     g_dtLastDealTime=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
                    }
                 }
               //--- do not notify becayse symbol is disabled.
               else
                 {
                  //--- update datetime for last deal, we will not notify about.
                  g_dtLastDealTime=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
                 }
              }
           }
        }
     }
  }

Comme vous l'avez remarqué, lorsqu'une nouvelle offre est trouvée, nous appelons la fonction BuildDealString() pour préparer les données au transfert. Toutes les données sont transférées au format texte et chaque transaction commence par « < » et se termine par « > ».

Cela nous aidera à séparer plusieurs offres car il est possible de recevoir plus d'une offre à la fois en raison du protocole TCP/IP.

//+------------------------------------------------------------------+
//| This function builds deal string                                 |
//| Examples:                                                        |
//| EURUSD;BUY;IN;0.01;1.37294                                       |
//| EURUSD;SELL;OUT;0.01;1.37310                                     |
//| EURUSD;SELL;IN;0.01;1.37320                                      |
//| EURUSD;BUY;INOUT;0.02;1.37294                                    |
//+------------------------------------------------------------------+
string sBuildDealString(ulong ticket)
  {
   string deal="";
   double volume=0;
   bool bFirstInOut=true;
//--- find deal volume.
//--- if this is INOUT then volume must contain ONLY volume of 'IN'.
   if(HistoryDealGetInteger(ticket,DEAL_ENTRY)==DEAL_ENTRY_INOUT)
     {
      if(PositionSelect(HistoryDealGetString(ticket,DEAL_SYMBOL)))
        {
         volume=PositionGetDouble(POSITION_VOLUME);
        }
      else
        {
         Print("Failed to get volume!");
        }
     }
//--- if it's 'IN' or 'OUT' deal then use it's volume as is.
   else
     {
      volume=HistoryDealGetDouble(ticket,DEAL_VOLUME);
     }
//--- build deal string(format sample: "<EURUSD;BUY;IN;0.01;1.37294>").
   int iDealEntry=(int)HistoryDealGetInteger(ticket,DEAL_ENTRY);
//--- if this is OUT deal, and there are no open positions left.
   if(iDealEntry==DEAL_ENTRY_OUT && !PositionSelect(HistoryDealGetString(ticket,DEAL_SYMBOL)))
     {
      //--- For safety reasons, we check if there is any position left with current symbol. If NO, then let's use 
      //--- new deal type - OUTALL. This will guarante that there are no open orders left on or account when all
      //--- position has been closed on 'remote' MetaTrader 5 side. This can happen due to fact, that volume is 
      //--- is mapped to new values on client side, therefor there can be some very small difference which leaves
      //--- order open with very small lot size. 
      iDealEntry=DEAL_ENTRY_OUTALL;  // My own predefined value (this value should not colide with EMUN_DEAL_ENTRY values).
     }
   StringConcatenate(deal,"<",AccountInfoInteger(ACCOUNT_LOGIN),";",
                   HistoryDealGetString(ticket,DEAL_SYMBOL),";",
                   Type2String((ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE)),";",
                   Entry2String(iDealEntry),";",DoubleToString(volume,2),";",
                      DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
                   (int)SymbolInfoInteger(HistoryDealGetString(ticket,DEAL_SYMBOL),SYMBOL_DIGITS)),">");
   Print("DEAL:",deal);
   return deal;
  }

En regardant le code, vous pourriez être surpris par le nouveau type DEAL_ENTRY - DEAL_ENTRY_OUTALL. Il est créé par moi et vous en comprendrez plus lorsque je vous expliquerai la gestion des volumes dans MetaTrader 4.

Une autre chose qui pourrait être intéressante est la fonction OnTimer(). Pendant l'initialisation, j'appelle EventSetTimer(1) pour obtenir l'appel OnTimer() toutes les secondes. À l'intérieur si cette fonction est une ligne qui imprime les informations (journaux) de la bibliothèque du serveur :

//+------------------------------------------------------------------+
//| Print logs from Server every second (if there are any)           |
//+------------------------------------------------------------------+
void OnTimer()
  {
   PrintLogs();
  }

Appelez cette fonction (PrintLogs) après chaque fonction que vous exécutez à partir de la bibliothèque du serveur, pour imprimer les informations d'état et d'erreur.

Côté serveur, vous trouverez également un paramètre d'entrée StartupType :

enum ENUM_STARTUP_TYPE
  {
   STARTUP_TYPE_CLEAR,    // CLEAR - Send every new DEAL wich appears on account.
   STARTUP_TYPE_CONTINUE  // CONTINUE - Do not send DEAL before existing POSITION has been closed.
  };
//--- input parameters
input ENUM_STARTUP_TYPE InpStartupType=STARTUP_TYPE_CONTINUE; // Startup type

Cela s'ajoute au fait que le fournisseur de signaux peut être ajouté sur un compte qui a déjà des positions ouvertes (par exemple, s'il suit un championnat) et donc les informations les concernant peuvent être trompeuses du côté du client. Par ce paramètre, vous pouvez choisir si vous souhaitez recevoir des informations sur les transactions existantes ou uniquement sur les positions nouvellement ouvertes.

C'est également important si vous faites une demande de compte pour la première fois ou si vous faites une nouvelle demande pour un compte sur lequel vous l'avez déjà exécuté et que vous venez de redémarrer votre PC, programme ou fait une modification dans votre code.


5.2. Client

Côté client, nous avons un script qui boucle dans la fonction de réception de socket pour l'infini (recv). Étant donné que cette fonction est « bloquante », le script est verrouillé pour le temps jusqu'à ce qu'il reçoive quelque chose du serveur, ce qui évite les problèmes de temps de traitement.

//--- server up and running. Start data collection and processing
   while(!IsStopped())
     {
      Print("Client: Waiting for DEAL...");
      ArrayInitialize(iBuffer,0);
      iRetVal=recv(iSocketHandle,iBuffer,ArraySize(iBuffer)<<2,0);
      if(iRetVal>0)
        {
         string sRawData=struct2str(iBuffer,iRetVal<<18);
         Print("Received("+iRetVal+"): "+sRawData);

Cela provoque un problème pour arrêter le client. Si vous cliquez sur « Supprimer le script », le script ne sera pas supprimé. Vous devez cliquer deux fois dessus, puis le script sera supprimé par délai d'attente. Cela pourrait être corrigé si un délai d'attente pour la fonction de réception pouvait être appliqué, mais puisque j'utilise un exemple déjà disponible dans Codebase, je vais laisser l'auteur original s'en occuper. 

Une fois les données reçues, nous procédons au fractionnement et à la vérification avant que la transaction ne soit traitée sur un compte réel :

         //--- split records
         string arrDeals[];
         //--- split raw data in multiple deals (in case if more than one is received).
         int iDealsReceived=Split(sRawData,"<",10,arrDeals);
         Print("Found ",iDealsReceived," deal orders.");
         //--- process each record
         //--- go through all DEALs received
         for(int j=0;j<iDealsReceived;j++) 
           {
            //--- split each record to values
            string arrValues[];
            //--- split each DEAL in to values
            int iValuesInDeal=Split(arrDeals[j],";",10,arrValues);
            //--- verify if DEAL request received in correct format (with correct count of values)
            if(iValuesInDeal==6)
              {
                 if(ProcessOrderRaw(arrValues[0],arrValues[1],arrValues[2],
                                    arrValues[3],arrValues[4],
                                         StringSubstr(arrValues[5],0,StringLen(arrValues[5])-1)))
                 {
                  Print("Processing of order done sucessfully.");
                 }
               else
                 {
                  Print("Processing of order failed:\"",arrDeals[j],"\"");
                 }
              }
            else
              {
               Print("Invalid order received:\"",arrDeals[j],"\"");
               //--- this was last one in array
               if(j==iDealsReceived-1)
                 {
                  //--- it might be incompleate beginning of next deal.
                  sLeftOver=arrDeals[j];
                 }
              }
           }
//+------------------------------------------------------------------+
//| Processing received raw data (text format)                       |
//+------------------------------------------------------------------+
bool ProcessOrderRaw(string saccount,string ssymbol,string stype,string sentry,string svolume,string sprice)
  {
//--- clearing
   saccount= Trim(saccount);
   ssymbol = Trim(ssymbol);
   stype=Trim(stype);
   sentry=Trim(sentry);
   svolume= Trim(svolume);
   sprice = Trim(sprice);
//--- validations
   if(!ValidateAccountNumber(saccount)){Print("Invalid account:",saccount);return(false);}
   if(!ValidateSymbol(ssymbol)){Print("Invalid symbol:",ssymbol);return(false);}
   if(!ValidateType(stype)){Print("Invalid type:",stype);return(false);}
   if(!ValidateEntry(sentry)){Print("Invalid entry:",sentry);return(false);}
   if(!ValidateVolume(svolume)){Print("Invalid volume:",svolume);return(false);}
   if(!ValidatePrice(sprice)){Print("Invalid price:",sprice);return(false);}
//--- convertations
   int account=StrToInteger(saccount);
   string symbol=ssymbol;
   int type=String2Type(stype);
   int entry=String2Entry(sentry);
   double volume= GetLotSize(StrToDouble(svolume),symbol);
   double price = NormalizeDouble(StrToDouble(sprice),(int)MarketInfo(ssymbol,MODE_DIGITS));
   Print("DEAL[",account,"|",symbol,"|",Type2String(type),"|",
        Entry2String(entry),"|",volume,"|",price,"]");
//--- execution
   ProcessOrder(account,symbol,type,entry,volume,price);
   return(true);
  }

Comme tout le monde n'a pas 10 000 $ sur son compte, le recalcul de la taille du Lot est effectué côté client par la fonction GetLotSize(). Une stratégie exécutée côté serveur peut également impliquer une gestion de l'argent et nous devons donc faire de même côté client.

Je vous propose le « Lot mapping » - l'utilisateur du client peut spécifier ses préférences de taille de lot (min et max), puis Client Script fera le mappage pour vous :

extern string _1 = "--- LOT MAPPING ---";
extern double  InpMinLocalLotSize  =  0.01;
extern double  InpMaxLocalLotSize  =  1.00; // Recomended bigger than
extern double  InpMinRemoteLotSize =  0.01;
extern double  InpMaxRemoteLotSize =  15.00;
//+------------------------------------------------------------------+
//| Calculate lot size                                               |
//+------------------------------------------------------------------+
double GetLotSize(string remote_lots, string symbol)
{
   double dRemoteLots = StrToDouble(remote_lots);
   double dLocalLotDifference = InpMaxLocalLotSize - InpMinLocalLotSize;
   double dRemoteLotDifference = InpMaxRemoteLotSize - InpMinRemoteLotSize;
   double dLots = dLocalLotDifference * (dRemoteLots / dRemoteLotDifference);
   double dMinLotSize = MarketInfo(symbol, MODE_MINLOT); 
   if(dLots<dMinLotSize)
      dLots=dMinLotSize;
   return (NormalizeDouble(dLots,InpVolumePrecision));
}

Le côté client prend en charge les courtiers à 4 et 5 chiffres et prend également en charge les « lots réguliers » (0,1) et les « mini-lots » (0,01). Pour cette raison, j'avais besoin de créer un nouveau type de DEAL_ENTRY - DEAL_OUTALL.

Étant donné que le côté client effectue le mappage, il peut arriver qu'un lot de petite taille ne soit pas fermé.

void ProcessOrder(int account, string symbol, int type, int entry, double volume, double price)
{
   if(entry==OP_IN)
   {
      DealIN(symbol,type,volume,price,0,0,account);
   }
   else if(entry==OP_OUT)
   {
      DealOUT(symbol, type, volume, price, 0, 0,account);
   }
   else if(entry==OP_INOUT)
   {
      DealOUT_ALL(symbol, type, account);
      DealIN(symbol,type,volume,price,0,0,account);
   }
   else if(entry==OP_OUTALL)
   {
      DealOUT_ALL(symbol, type, account);
   }
}

5.3. Positions MetaTrader 5 vs Ordres MetaTrader 4

Lors de la mise en œuvre, j'ai trouvé un autre problème - dans MetaTrader 5, il n'y a toujours qu'une seule position pour chaque symbole alors que dans MetaTrader 4, il est géré de manière totalement différente. Pour me rapprocher le plus possible de chaque nouvelle offre avec la même entrée et le même symbole, je couvre en ouvrant plusieurs ordres sur MetaTrader 4.

Chaque nouvelle transaction « IN » est un nouvel ordre et lorsqu'il y a une transaction « OUT », j'ai mis en œuvre une fonctionnalité qui effectue une clôture en 3 étapes :

  1. Passez en revue tout les ordres ouverts et fermez ceux qui correspond à la taille demandée, si aucune, alors
  2. Passez en revue tout les ordres ouverts et fermez ceux qui sont inférieurs à la taille du volume OUT demandée, s'il reste quelque chose, alors
  3. Fermer l’ordre dont la taille est supérieure à la taille demandée et ouvrir une nouvelle commande avec la taille qui ne devrait pas être fermée. Dans les cas normaux, la troisième étape ne doit jamais être effectuée. Créé à des fins de protection.
//+------------------------------------------------------------------+
//| Process DEAL ENTRY OUT                                           |
//+------------------------------------------------------------------+
void DealOUT(string symbol, int cmd, double volume, double price, double stoploss, double takeprofit, int account)
{
   int type = -1;
   int i=0;
   
   if(cmd==OP_SELL)
      type = OP_BUY;
   else if(cmd==OP_BUY)
      type = OP_SELL;  
   
   string comment = "OUT."+Type2String(cmd);
   //--- Search for orders with equal VOLUME size and with PROFIT > 0
   for(i=0;i<OrdersTotal();i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()==volume)
                  {
                     if(OrderProfit()>0)
                     {
                        if(CloseOneOrder(OrderTicket(), symbol, type, volume))
                        {
                           Print("Order with exact volume and profit>0 found and executed.");
                           return;
                        }
                     }
                  }
               }
            }
         }
      }
   }
   //--- Search for orders with equal VOLUME size and with ANY profit size
   for(i=0;i<OrdersTotal();i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()==volume)
                  {
                     if(CloseOneOrder(OrderTicket(), symbol, type, volume))
                     {
                        Print("Order with exact volume found and executed.");
                        return;
                     }
                  }
               }
            }
         }
      }
   }
   double volume_to_clear = volume;
   //--- Search for orders with smaller volume AND with PROFIT > 0
   int limit = OrdersTotal();
   for(i=0;i<limit;i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()<=volume_to_clear)
                  {
                     if(OrderProfit()>0)
                     {
                        if(CloseOneOrder(OrderTicket(), symbol, type, OrderLots()))
                        {
                           Print("Order with smaller volume and profit>0 found and executed.");
                           volume_to_clear-=OrderLots();
                           if(volume_to_clear==0)
                           {
                              Print("All necessary volume is closed.");
                              return;
                           }
                           limit = OrdersTotal();
                           i = -1; // Because it will be increased at end of cycle and will have value 0.
                        }
                     }
                  }
               }
            }
         }
      }
   }
   //--- Search for orders with smaller volume
   limit = OrdersTotal();
   for(i=0;i<limit;i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()<=volume_to_clear)
                  {
                     if(CloseOneOrder(OrderTicket(), symbol, type, OrderLots()))
                     {
                        Print("Order with smaller volume found and executed.");
                        volume_to_clear-=OrderLots();
                        if(volume_to_clear==0)
                        {
                           Print("All necessary volume is closed.");
                           return;
                        }
                        limit = OrdersTotal();
                        i = -1; // Because it will be increased at end of cycle and will have value 0.
                     }
                  }
               }
            }
         }
      }
   }
   //--- Search for orders with higher volume
   for(i=0;i<OrdersTotal();i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()>=volume_to_clear)
                  {
                     if(CloseOneOrder(OrderTicket(), symbol, type, OrderLots()))
                     {
                        Print("Order with smaller volume found and executed.");
                        volume_to_clear-=OrderLots();
                        if(volume_to_clear<0)//Closed too much
                        {
                           //Open new to compensate lose
                           DealIN(symbol,type,volume_to_clear,price,OrderStopLoss(),OrderTakeProfit(),account);
                        }
                        else if(volume_to_clear==0)
                        {
                           Print("All necessary volume is closed.");
                           return;
                        }
                     }
                  }
               }
            }
         }
      }
   }
   if(volume_to_clear!=0)
   {
      Print("Some volume left unclosed: ",volume_to_clear);
   }
}

Conclusion

Les fichiers créés et joints ici peuvent certainement être améliorés avec un meilleur protocole client-serveur, une communication plus intelligente et une meilleure exécution, mais ma tâche consistait à vérifier si c'était possible et à le construire avec une qualité acceptable, afin que chacun puisse l'utiliser pour ses besoins privés.

Cela fonctionne assez bien pour suivre vos propres stratégies et celles de tous les participants au championnat MQL5. Les performances et les possibilités offertes par MQL4 et MQL5 sont suffisamment bonnes pour être utilisées de manière professionnelle et commerciale. Je pense qu'il est possible de créer un très bon fournisseur de signaux pour tous les clients MetaTrader 4 et MetaTrader 5 en utilisant simplement votre ordinateur personnel et votre propre stratégie.

J'aimerais que les gens améliorent le code que j'ai fourni ici et reviennent avec des avis et des recommandations. J'essaierai également de répondre à vos questions au cas où vous en auriez. En parallèle, j'ai réalisé un test où je suis les participants à mon championnat préféré. Cela fait maintenant une semaine que ça marche bien. Si je rencontre des problèmes, je vous fournirai des mises à jour.

Tsaktuo

Veuillez noter qu'en appliquant les fonctionnalités et les exécutables décrits à votre compte réel, vous assumez l'entière responsabilité de toutes les pertes ou de tous les dommages qui pourraient en résulter. Ne traitez sur un compte réel UNIQUEMENT après avoir effectué de bons tests et UNIQUEMENT si vous comprenez bien les fonctionnalités fournies ici.


Traduit de l’anglais par MetaQuotes Ltd.
Article original : https://www.mql5.com/en/articles/344

Fichiers joints |
Analyse de régression multiple. Générateur et testeur de stratégie en un Analyse de régression multiple. Générateur et testeur de stratégie en un
L'article donne une description des méthodes d'utilisation de l'analyse de régression multiple pour le développement de systèmes de trading. Il démontre l'utilisation de l'analyse de régression pour l'automatisation de la recherche de stratégies. Une équation de régression générée et intégrée dans un EA sans nécessiter une grande maîtrise de la programmation est donnée à titre d'exemple.
MQL5-RPC. Appels de procédure à distance depuis MQL5 : Accès aux services Web et analyseur XML-RPC ATC pour le plaisir et le profit MQL5-RPC. Appels de procédure à distance depuis MQL5 : Accès aux services Web et analyseur XML-RPC ATC pour le plaisir et le profit
Cet article décrit l'infrastructure MQL5-RPC qui active les appels de procédure distante à partir de MQL5. Il commence par les bases de XML-RPC, la mise en œuvre de MQL5 et se poursuit par deux exemples d'utilisation réelle. Le premier exemple utilise un service Web externe et le second est un client vers un simple service d'analyse XML-RPC ATC 2011. Si vous souhaitez savoir comment mettre en œuvre et analyser différentes statistiques de l'ATC 2011 en temps réel, cet article est fait pour vous.
Quelques conseils pour les nouveaux clients Quelques conseils pour les nouveaux clients
Une sagesse proverbiale souvent attribuée à diverses personnes célèbres dit : « Celui qui ne fait pas d'erreurs ne fait jamais rien. » À moins que vous ne considériez l'oisiveté comme une erreur, cette affirmation est difficile à contredire. Mais vous pouvez toujours analyser les erreurs passées (les vôtres et celles des autres) pour minimiser le nombre de vos erreurs futures. Nous allons tenter de passer en revue les situations qui peuvent se présenter lors de l'exécution de travaux dans le service du même nom.
Création d'Expert Advisors MQL5 en quelques minutes à l'aide d'EA Tree : Partie un Création d'Expert Advisors MQL5 en quelques minutes à l'aide d'EA Tree : Partie un
EA Tree est le premier constructeur de MetaTrader MQL5 Expert Advisor par glisser-déposer. Vous pouvez créer des MQL5 complexes à l'aide d'une interface utilisateur graphique très facile à utiliser. Dans EA Tree, les Expert Advisors sont créés en connectant des cases entre elles. Les cases peuvent contenir des fonctions MQL5, des indicateurs techniques, des indicateurs personnalisés ou des valeurs. A l'aide de « l'arbre des cases », EA Tree génère le code MQL5 de l'Expert Advisor.