English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Utilizzo di MetaTrader 5 come Fornitore di segnali per MetaTrader 4

Utilizzo di MetaTrader 5 come Fornitore di segnali per MetaTrader 4

MetaTrader 5Esempi | 11 gennaio 2022, 15:21
73 0
Karlis Balcers
Karlis Balcers

Introduzione

Ci sono state molteplici ragioni per cui ho scelto di scrivere questo articolo e di indagare se è fattibile.

Innanzitutto, MetaTrader 5 è disponibile da molto tempo, ma tutti stiamo ancora aspettando che i nostri broker preferiti ci permettano di fare trading in tempo reale. Alcuni hanno fatto strategie utilizzando MQL5 e hanno una buona performance e vogliono adesso eseguirle su conti reali. Ad altri, forse, piace come è organizzato il trading e vogliono fare trading manualmente, ma usando MetaTrader 5 invece di MetaTrader 4.

Secondo motivo, durante l’ Automated Trading Championship tutti hanno pensato di seguire i leader nei propri conti reali. Alcuni hanno creato il proprio modo di seguire le negoziazioni, ma alcuni stanno ancora cercando di capire come farlo, come ottenere risultati il più vicino possibile ai trader in campionato, come applicare la stessa gestione del denaro.

In terzo luogo, alcune persone hanno buone strategie e vogliono fornire i loro segnali di trading non solo a se stessi, ma anche ai loro amici o ad altri. Hanno bisogno della possibilità di accettare più connessioni senza perdere prestazioni e distribuire segnali in tempo reale.

Queste sono le domande che mi sono sempre rimaste in mente e cercherò di trovare una soluzione che copra queste esigenze.


1. Come seguire le attività del campionato MQL5?

Ultimamente ho trovato più articoli nella MQL5.community al mio livello di conoscenza e ho pensato che avrei potuto costruirlo. Ti dirò anche che ho usato l'applicazione che seguiva le attività nella homepage del campionato e stava negoziando nel mio conto reale (fortunatamente, con profitto). Il problema era che i dati vengono aggiornati ogni 5 minuti e puoi perdere il momento giusto per aprire e chiudere.

Dal forum campionato ho capito che ci sono altre persone che stanno facendo la stessa cosa, ma non è efficace e dà un enorme traffico per la homepage del campionato. Agli organizzatori potrebbe non piacere. Quindi, c'è una soluzione? Ho esaminato tutte le soluzioni e mi è piaciuta la possibilità di accedere al conto di ogni partecipante in modalità "investitore" (trading disabilitato) tramite MetaTrader 5.

Possiamo usarlo per ricevere informazioni su ogni attività di trading in tempo reale e per trasferirle in tempo reale? Per trovarlo, ho creato un Expert Advisor e ho provato a eseguirlo su un account che aveva solo accesso alla modalità "investitore". Per mia sorpresa, è stato possibile allegarlo e, inoltre, è stato possibile ottenere informazioni su posizioni, ordini e operazioni - quelli in cui ci sono porte aperte a una possibile soluzione!


2. Cosa seguire: posizioni, ordini o operazioni?

Se stiamo per trasferire informazioni da MetaTrader 5 a MetaTrader 4, allora dobbiamo prendere in considerazione tutti i tipi di ordine che sono possibili su MetaTrader 4. Inoltre, quando seguiamo, vogliamo sapere di ogni azione eseguita nel conto relativo al trading, quindi "Posizioni" non ci fornirà informazioni complete a meno che non confrontiamo lo stato di "Posizioni" su ogni tick o secondo.

Pertanto, sarebbe meglio seguire "Ordini" o "Operazioni".

Ho iniziato a guardare gli ordini:

Ordini

Mi è piaciuto il fatto che vengano eseguiti prima delle "Operazioni" e contengono anche informazioni sugli ordini in sospeso (limite), ma mancano di una cosa importante rispetto alle "Operazioni" - tipo di voce (ENUM_DEAL_ENTRY):

Operazioni

DEAL_ENTRY_TYPE aiuta a capire cosa è successo nel conto dei trader mentre gli "Ordini" richiedono il calcolo in parallelo. La cosa migliore sarebbe unire "Operazioni" con "Ordini", quindi potremmo avere ordini in sospeso e anche seguire ogni azione nel conto di trading. Poiché i movimenti dei prezzi differiscono tra le diverse società di broker, gli ordini in sospeso potrebbero effettivamente portare a errori e risultati errati.

Nel caso in cui seguiamo solo le "Operazioni", eseguiremo comunque gli ordini in sospeso, ma con un piccolo ritardo (fino alla connessione di rete). Tra velocità (ordini in sospeso) e le prestazioni (operazioni) ho scelto di andare per prestazioni ("operazioni").


3. Come fornire i "segnali"?

Ci sono stati diversi articoli e discussioni su come comunicare e trasferire dati da MetaTrader 5 ad altre applicazioni e computer. Dal momento che voglio che altri client siano in grado di connettersi a noi e molto probabilmente si troveranno su altri computer, scelgo la connessione socket TCP.

Poiché MQL5 non consente di farlo con le funzioni API, è necessario utilizzare una libreria esterna. Ci sono più articoli sul coinvolgimento della libreria "WinInet.dll" (ad es. "Using WinInet.dll for Data Exchange between Terminals via the Internet" e altri), ma nessuno di essi soddisfa davvero le nostre esigenze.

Dato che ho un po 'di familiarità con C#, ho deciso di creare la mia libreria. Per questo, ho usato l'articolo "Exposing C# code to MQL5 using unmanaged exports" per aiutarmi con problemi di compatibilità. Ho creato un server con interfaccia molto semplice e possibilità di accettare fino a 500 client contemporaneamente (richiede .NET framework 3.5 o successivo nel tuo PC. Già installato nella maggior parte dei computer. "Microsoft .NET Framework 3.5").

#import "SocketServer.dll"    // Library created on C# (created by using information available on https://www.mql5.com/it/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

Il server stesso è in esecuzione in background su thread separati e non bloccherà o rallenterà il lavoro di MetaTrader 5 o della tua strategia, indipendentemente dal numero di client collegati.

Codice sorgente 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);
            }
        }

Per saperne di più su Asynchronous Server Sockets in C# ti consiglio di leggere Microsoft MSDN o alcuni articoli che Google.


4. Come raccogliere i "segnali"?

Su MetaTrader 4 vorremmo ricevere informazioni tutto il tempo e non solo quando viene generato un nuovo segno di spunta, quindi creiamo "Script" invece dell’Expert Advisor. Inoltre, dobbiamo essere in grado di aprire la connessione socket con il nostro fornitore di segnali - MetaTrader 5.

Per questo ho scelto di ricevere aiuto dalla codebase MQL4: "https://www.mql5.com/it/code/9296". Lì ho trovato abbastanza buono includere il file (WinSock.mqh) che consente di lavorare con i socket in modo molto semplice. Anche alcune persone si sono lamentate della stabilità, l'ho trovata abbastanza buona per il mio scopo e non ho riscontrato problemi durante i miei test.

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


5. Elaborazione dei dati

Ora abbiamo il nostro concetto e tutto ciò che dobbiamo fare è assicurarci che le operazioni vengano elaborate e trasferite una per una a tutti i client in un formato che possano comprendere ed eseguire.

5.1. Lato server

Come abbiamo chiarito, sarà l’Expert Advisor, ma non si preoccupa della valuta su cui è stato aggiunto.

Durante l'avvio inizierà anche l'ascolto del thread che sarà in attesa di connessioni in entrata:

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;
     }

In questa versione, l’Expert Advisor non si preoccuperà dei client connessi. Ogni volta che c'è un’operazione, invierà una notifica a tutti i client, anche se non ce ne sono. Dal momento che abbiamo bisogno di sapere solo delle negoziazioni, useremo la funzione OnTrade () e rimuoveremo OnTick (). In questa funzione guardiamo alla cronologia più recente e decidiamo se questo è un’operazione di cui dobbiamo dare informazioni o meno.

Vedi i miei commenti nel codice per capirlo meglio:

//+------------------------------------------------------------------+
//| 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);
                 }
              }
           }
        }
     }
  }

Come hai notato, quando viene trovato una nuova operazione, chiamiamo la funzione BuildDealString() per preparare i dati per il trasferimento. Tutti i dati vengono trasferiti in formato testo e ogni accordo inizia con "<" e termina con ">".

Questo ci aiuterà a separare più operazioni poiché è possibile ricevere più di un accordo alla volta a causa del protocollo 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;
  }

Quando guardi il codice, potresti essere sorpreso dal nuovo tipo di DEAL_ENTRY- DEAL_ENTRY_OUTALL. È stato creato da me e ne capirai di più quando spiegherò la gestione del volume nel lato MetaTrader 4.

Un'altra cosa che potrebbe essere interessante è la funzione OnTimer(). Durante l'inizializzazione chiamo EventSetTimer(1) per ottenere una chiamata OnTimer() ogni secondo. All'interno se quella funzione è una riga che stampa le informazioni (log) dalla libreria del server:

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

Chiama questa funzione (PrintLogs) dopo ogni funzione eseguita dalla libreria del server per stampare informazioni sullo stato e sull'errore.

Nel lato server troverai anche un parametro di input 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

Questo si aggiunge al fatto che il fornitore di segnali può essere aggiunto sul conto che ha già posizioni aperte (ad esempio se segue il campionato) e quindi le informazioni su di loro potrebbero essere fuorvianti sul lato client. Con questo parametro è possibile scegliere, se si desidera ricevere informazioni da operazioni esistenti o solo da posizioni appena aperte.

È anche importante se si applica all'account per la prima volta o si riapplica per l'account su cui è stato eseguito in precedenza e si è appena riavviato il PC, il programma o apportato una modifica al codice.


5.2. Client

Sul lato client abbiamo uno script che è in loop nella funzione di ricezione socket per infinito (recv). Poiché questa funzione è in "blocco", lo script viene bloccato per il tempo fino a quando qualcosa non viene ricevuto dal server. Quindi non preoccuparti del tempo del processore.

//--- 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);

Ciò causa un problema per arrestare il client. Quando si farà clic su "Rimuovi script", lo script non verrà rimosso. È necessario cliccarlo due volte e quindi lo script verrà rimosso per time-out. Questo si potrebbe risolvere se il time-out per la funzione di ricezione potesse essere applicato, ma poiché sto usando un esempio già disponibile in Codebase, lo lascerò per l'autore originale. 

Una volta ricevuti i dati, eseguiamo la suddivisione e la verifica prima che l'affare venga elaborato in un conto reale:

         //--- 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);
  }

Poiché, non tutti hanno 10 000 $ sul proprio account, il ricalcolo della dimensione del lotto viene eseguito sul lato client dalla funzione GetLotSize(). La strategia in esecuzione sul lato server può anche implicare la gestione del denaro e quindi dobbiamo fare lo stesso sul lato client.

Ti offro la "Mappatura del lotto" - l'utente del client può specificare le sue preferenze di dimensione del lotto (min e max) e quindi il client Script eseguirà la mappatura per te:

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));
}

Il lato client supporta broker a 4 e 5 cifre e ha anche il supporto "regular-lot" (0.1) e "mini-lot" (0.01). Per questo motivo avevo bisogno di creare nuovi DEAL_ENTRY tipo - DEAL_OUTALL.

Poiché il lato client sta eseguendo la mappatura, ci può essere una situazione in cui le piccole dimensioni del lotto non vengono chiuse.

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. Posizioni MetaTrader 5 vs Ordini MetaTrader 4

Durante l'implementazione, ho trovato un altro problema: su MetaTrader 5 c'è sempre una sola posizione per ogni simbolo, mentre su MetaTrader 4 è gestito in modo totalmente diverso. Per avvicinarmi il più possibile, a ogni nuova operazione con la stessa voce e simbolo copro aprendo più ordini sul lato MetaTrader 4.

Ogni nuova offerta "IN" è un nuovo ordine e, quando c'è un accordo "OUT", implemento una funzionalità che esegue la chiusura in 3 passaggi:

  1. Passa attraverso tutti gli ordini aperti e chiudi quello che corrisponde alla dimensione richiesta. Se non ve ne sono, allora
  2. passa attraverso tutti gli ordini aperti e chiudi quelli che sono più piccoli della dimensione del volume OUT richiesta. Se è ancora rimasto qualcosa,
  3. chiudi l'ordine la cui dimensione è più grande della dimensione richiesta e apri un nuovo ordine con la dimensione che deve essere lasciata non chiusa. In casi normali, il terzo passaggio non dovrebbe mai essere eseguito. Creato per scopi di protezione.
//+------------------------------------------------------------------+
//| 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);
   }
}

Conclusione

I file realizzati e allegati qui possono sicuramente essere migliorati con un migliore protocollo client server, una comunicazione più intelligente e una migliore esecuzione, ma il mio compito era verificare se fosse possibile costruirlo con una qualità accettabile, in modo che tutti potessero usarlo per le loro esigenze private.

Funziona abbastanza bene da seguire le proprie strategie per tutti i partecipanti al campionato MQL5. Le prestazioni e le possibilità fornite da MQL4 e MQL5 sono abbastanza buone da portarlo anche in modo professionale e commerciale. Credo che sia possibile creare un ottimo fornitore di segnali per tutti i client MetaTrader 4 e MetaTrader 5 semplicemente usando il proprio computer privato e la propria strategia.

Mi piacerebbe vedere le persone migliorare il codice che ho fornito qui e ritornare con opinioni e raccomandazioni. Cercherò anche di rispondere alle tue domande nel caso in cui ne avessi. Parallelamente, sto eseguendo test in cui seguo i miei partecipanti al campionato preferiti. Ora sta funzionando bene da una settimana. Se troverò problemi, ti fornirò aggiornamenti.

Tsaktuo

Da notare che, applicando le funzionalità e gli eseguibili descritti al proprio account reale, l'utente si assume la piena responsabilità per tutte le perdite o i danni che potrebbero essere causati da esso. Fai trading su conto reale SOLO dopo un buon test e SOLO con una buona comprensione della funzionalità che viene fornita qui.


Tradotto dall’inglese da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/en/articles/344

File allegati |
Analisi di regressione multipla. Generatore di strategie e tester tutto in uno Analisi di regressione multipla. Generatore di strategie e tester tutto in uno
L'articolo fornisce una descrizione delle modalità di utilizzo dell'analisi di regressione multipla per lo sviluppo di sistemi di trading. Dimostra l'uso dell'analisi di regressione per l'automazione della ricerca strategica. Come esempio viene fornita un'equazione di regressione generata e integrata in un EA senza richiedere un'elevata competenza nella programmazione.
MQL5-RPC. Chiamate di procedura remota da MQL5: Accesso al servizio web e analizzatore ATC XML-RPC per divertimento e profitto MQL5-RPC. Chiamate di procedura remota da MQL5: Accesso al servizio web e analizzatore ATC XML-RPC per divertimento e profitto
Questo articolo descrive il framework MQL5-RPC che abilita le chiamate di procedura remota da MQL5. Inizia con le basi XML-RPC, l'implementazione MQL5 e continua con due esempi di utilizzo reali. Il primo esempio utilizza un servizio web esterno e il secondo è un client per il semplice servizio XML-RPC ATC 2011 Analyzer. Se sei interessato a come implementare e analizzare diverse statistiche da ATC 2011 in tempo reale, questo articolo fa per te.
Alcuni suggerimenti per i clienti alle prime armi Alcuni suggerimenti per i clienti alle prime armi
Un saggio proverbio spesso attribuito a vari personaggi famosi dice: "Chi non sbaglia non farà mai nulla." A meno che tu non consideri l'ozio stesso un errore, questa affermazione è difficile da discutere. Ma puoi sempre analizzare gli errori passati (tuoi e degli altri) per ridurre al minimo il numero dei tuoi errori futuri. Cercheremo di esaminare le possibili situazioni che si verificano durante l'esecuzione di lavori nel servizio con lo stesso nome.
Creazione di Expert Advisor MQL5 in pochi minuti utilizzando EA Tree: Prima parte Creazione di Expert Advisor MQL5 in pochi minuti utilizzando EA Tree: Prima parte
EA Tree è il primo generatore di Expert Advisor MetaTrader MQL5 drag and drop. È possibile creare MQL5 complessi utilizzando un'interfaccia utente grafica molto facile da usare. In EA Tree, gli Expert Advisor vengono creati collegando i riquadri insieme. Le caselle possono contenere funzioni MQL5, indicatori tecnici, indicatori personalizzati o valori. Utilizzando l'"albero dei riquadri", EA Tree genera il codice MQL5 dell'Expert Advisor.