English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Usar MetaTrader 5 como um provedor de sinal para o MetaTrader 4

Usar MetaTrader 5 como um provedor de sinal para o MetaTrader 4

MetaTrader 5Exemplos | 10 março 2014, 15:48
3 052 0
Karlis Balcers
Karlis Balcers

Introdução

Tem havido várias razões para mim, do porque escolhi escrever este artigo e investigar se o mesmo é factível.

Primeiro, o MetaTrader 5 estava fora e disponível por um longo tempo, mas todos nós ainda estamos esperando por nossos corretores favoritos para nos permitir negociar em tempo real. Alguns fizeram estratégias usando MQL5 e tiveram um bom desempenho, querendo executá-los em contas reais agora. Outros, talvez, gostam de como a negociação é organizada e querem negociar manualmente, mas usando MetaTrader 5, ao invés do MetaTrader 4.

Segunda razão, durante o Automated Trading Championship todos ficaram pensando em acompanhar líderes em suas próprias contas reais. Alguns criaram sua própria maneira de acompanhar as negociações, mas alguns ainda estão procurando como fazer isso, como obter resultados o mais próximo possível aos comerciantes no campeonato e como aplicar a mesma gestão de dinheiro.

Em terceiro lugar, algumas pessoas têm boas estratégias e querem oferecer seus sinais de negociação, não só para eles mesmos, mas também para os seus amigos ou outros. Eles precisam de possibilidade para aceitar múltiplas conexões sem perder o desempenho e distribuir sinais de tempo real.

Estas são as questões as quais perduram em minha mente e eu tentarei encontrar uma solução a qual cobriria esses requisitos.


1. Como acompanhar as atividades do campeonato em MQL5?

Ultimamente tenho encontrado vários artigos na MQL5.community que estavam em meu nível de conhecimento e me fizeram pensar que eu poderia construí-lo. Também irei lhe dizer que tenho usado a aplicação que estava acompanhando as atividades na página inicial do campeonato e foi negociada em minha conta real (felizmente, com fins lucrativos). O problema foi que - os dados são atualizados a cada 5 minutos e você pode perder o momento certo para abrir e fechar.

A partir do fórum do campeonato eu entendi que existem outras pessoas que estão fazendo a mesma coisa, não sendo eficaz e também proporcionando um enorme tráfego na página inicial do campeonato, e os organizadores podem não gostar. Então, há uma solução? Considerei todas as soluções e gostei da possibilidade de acessar a conta de cada participante no modo 'investidor' (negociação desativada) através do MetaTrader 5.

Podemos usá-lo para receber as informações de todas as atividades de negociação em tempo real e transferi-los em tempo real? Para encontrá-lo, eu criei o Expert Advisor e tentei executá-lo na conta que tivesse apenas o acesso do modo 'investidor'. Para minha surpresa, foi possível anexá-lo e também obter informações sobre as posições, pedidos e ofertas - aquelas onde abre-se uma porta para uma possível solução!


2. O que acompanhar - Posições, pedidos ou negócios?

Se estamos prestes a transferir informações do MetaTrader 5 para o MetaTrader 4, então temos que levar em consideração todos os tipos de pedidos que são possíveis no MetaTrader 4. Além disso, quando nós acompanhamos queremos saber sobre cada ação realizada na conta relacionada à negociação, portanto, as "posições" não nos darão a informação completa, a menos que comparemos o status das "posições" em cada marcação ou segundo.

Portanto, seria melhor acompanhar "pedidos" ou "negócios".

Comecei a olhar para os pedidos:

Pedidos

Eu gostei pois eles são executados antes do 'Negócio' e também contêm informações sobre pedidos (limite) pendentes, mas lhe falta uma coisa importante em relação aos 'negócios' - tipo de entrada (ENUM_DEAL_ENTRY):

Negócios

O DEAL_ENTRY_TYPE ajuda a entender o que aconteceu na conta dos negociadores enquanto os "pedidos" requerem cálculo paralelo. O melhor seria unir os "negócios" com os "pedidos", aí então poderíamos ter pedidos pendentes e também acompanhar toda a ação na conta comercial. Uma vez que os movimentos de preços diferem entre as diferentes companhias corretoras, então os pedidos pendentes realmente poderiam levar a erros e resultados incorretos.

No caso, se nós acompanharmos apenas os "Negócios", ainda vamos executar os pedidos pendentes, mas com um pequeno atraso (até a conexão de rede). Entre a velocidade (pedidos pendentes) e o desempenho (negócios) eu escolhi ir para o desempenho ('Negócios').


3. Como fornecer 'sinais'?

Houve diversos artigos e discussões de como se comunicar e transferir dados a partir do MetaTrader 5 para outras aplicações e computadores. Uma vez que quero que outros clientes sejam capazes de conectar-se a nós, e eles provavelmente estarão localizados em outros computadores, então eu escolhi a conexão de tomada TCP.

Visto que a MQL5 não permite fazê-lo com funções de API, então precisamos usar a biblioteca externa. Existem vários artigos sobre o envolvimento da biblioteca "WinInet.dll" (por exemplo, "usar WinInet.dll para troca de dados entre os terminais via Internet" e outros), mas nenhum deles realmente satisfaz nossas necessidades.

Posto que sou um pouco familiarizado com C#, então eu decidi criar minha própria biblioteca. Para isso, usei o artigo "Expondo o código C# à MQL5 usando exportações não gerenciadas" para me ajudar com problemas de compatibilidade. Eu criei o servidor com uma interface muito simples e possibilidade de aceitar até 500 clientes ao mesmo tempo (requer. quadro NET de 3.5 ou posterior em seu PC. Já instalado na maioria dos computadores. "Microsoft .NET Framework 3.5").

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

O próprio servidor está sendo executado em segundo plano nos segmentos separados e não bloqueará ou retardar o trabalho de MetaTrader 5 ou sua estratégia, não importando quantos clientes serão conectados.

Código-fonte 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);
            }
        }

Para saber mais sobre tomadas de servidores assíncronos em C # eu recomendo que você leia Microsoft MSDN ou alguns artigos que você pode encontrar com o Google.


4. Como coletar 'sinais'?

No MetaTrader 4 gostaríamos de receber informações o tempo todo e não só quando a nova marca é gerada, portanto, criamos o 'Script' para ele, em vez do Expert Advisor. Além disso, precisamos ser capaz de abrir conexão de tomada com nosso provedor de sinais - MetaTrader 5.

Por isso eu optei por receber a ajuda da base do código MQL4: "https://www.mql5.com/pt/code/9296". Achei um arquivo incluso muito bom (WinSock.mqh), que permite trabalhar com tomadas de maneira muito simples. Mesmo algumas pessoas tendo se queixado sobre a estabilidade, eu achei bom o suficiente para o meu propósito e não tive quaisquer problemas durante o meu teste.

#include <winsock.mqh>  // Downloaded from MQ4 homepage
                        // DOWNLOAD:  https://www.mql5.com/pt/code/9296
                        // ARTICLE:   https://www.mql5.com/en/code/download/9296


5. Processamento de dado

Agora temos o nosso conceito e tudo o que precisamos fazer é certificarmo-nos de que os negócios são processados e transferidos um a um, para todos os clientes no formato que eles possam compreender e executar.

5.1. Lado do servidor

Como esclarecemos, ele será um Expert Advisor mas não se preocupando com a moeda em qual foi adicionado.

Durante o start-up ele também começará processos que estarão esperando por conexões de entrada:

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

Nesta versão, o Expert Advisor não se preocupará com os clientes conectados. Toda vez que há um negócio - ele enviará uma notificação para todos os clientes, ainda que não haja nenhum. Posto que precisamos saber apenas sobre negócios então vamos usar a função OnTrade() e remover OnTick(). Nesta função olhamos para o histórico mais recente e decidimos se este é um negócio que precisamos ter conhecimento ou não.

Veja meus comentários no código para entender melhor:

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

Como você percebeu, quando há novo negócio encontrado, chamamos a função BuildDealString() para preparar os dados para transferência. Todos os dados são transferidos em formato de texto e cada negócio começa com ''.

Isso nos ajudará a separar vários negócios, uma vez que é possível receber mais de um negócio no momento devido ao protocolo 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;
  }

Ao olhar para o código, você pode ser surpreendido sobre o novo tipo de DEAL_ENTRY - DEAL_ENTRY_OUTALL. Ele é criado por mim e você entenderá mais sobre ele quando eu explicar sobre a manipulação do volume no lado do MetaTrader 4.

Mais uma coisa que pode ser interessante é a função OnTimer(). Durante a inicialização eu chamo o EventSetTimer(1) para obter a chamada OnTimer() a cada segundo. Interiormente, se essa função é uma linha que imprime informações (registros) da biblioteca do servidor:

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

Chame essa função de (PrintLogs) depois de cada função que você executa a partir da biblioteca do servidor, para imprimir informações de status e erros.

No lado do servidor, você também vai encontrar um parâmetro de entrada 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

Este é adicionado ao fato de que o provedor de sinais pode ser adicionado em conta que já teve posições abertas (por exemplo, o seguinte campeonato) e, portanto, informações sobre eles podem ser enganosas no lado do cliente. Por esse parâmetro, você pode escolher, se você quer receber informações de negócios já existentes ou só a partir de posições recém abertas.

Também é importante se você aplicar à conta pela primeira vez ou reaplicar à conta em que você tenha executado antes e você acabou de reiniciar o PC, programa ou fez uma modificação em seu código.


5.2. Cliente

No lado do cliente, temos o script que é um ciclo na função de recepção da tomada para a extensão ilimitada de espaço (receber). Visto que esta é uma função de 'bloqueio', então o script está bloqueado até que algo seja recebido de um servidor, por isso não se preocupe com o tempo do processador.

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

Isto causa um problema para parar o cliente. Quando você clicar em "Remover script", o script não será removido. Você precisa clicar duas vezes e, em seguida, o roteiro será removido pelo tempo limite. Isso poderia ser corrigido se o tempo limite para receber a função pudesse ser aplicado, mas desde que eu estou usando a amostra já disponível na base do código então vou deixá-lo para o autor original.

Depois que os dados são recebidos fazemos a divisão e verificação antes do negócio ser processado em conta real:

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

Uma vez que nem todos têm 10.000 $ em sua conta, então o recálculo do tamanho do lote é feito no lado do cliente pela função GetLotSize(). A estratégia em execução no lado do servidor também pode implicar gestão do dinheiro e, portanto, precisamos fazer o mesmo no lado do cliente.

Eu ofereço-lhe "mapeamento de lote" - o usuário do cliente pode especificar suas preferências de tamanho de lote (min e max) e então o script do cliente fará o mapeamento para você:

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

O lado do cliente suporta 4 e 5 corretores de dígitos e também tem suporte de "lote regular" (0.1) e "mini lote" (0.01). Por esta razão, eu precisava criar um novo tipo de DEAL_ENTRY - DEAL_OUTALL.

Visto que o lado do cliente está fazendo o mapeamento, pode haver alguma situação em que o tamanho pequeno do lote parte aberto.

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. Posições de MetaTrader 5 versus pedido de MetaTrader 4

Durante a implementação, eu encontrei um outro problema - no MetaTrader 5, há sempre apenas uma posição para cada símbolo, enquanto no MetaTrader 4 isto é tratado de maneira totalmente diferente. Para chegar tão perto quanto possível, a cada novo negócio, com a mesma entrada e símbolo, eu cubro abrindo vários pedidos no lado do MetaTrader 4.

Cada novo negócio 'IN' é um novo pedido e quando há um 'OUT', eu implementei a funcionalidade que realiza três etapas de encerramento:

  1. Passar por todos os pedidos em aberto e fechar aquele que corresponde ao tamanho solicitado, se nenhum, então; 
  2. Passar por todos os pedidos em aberto e fechar aqueles que são menores que o tamanho do volume OUT solicitado, se algo ainda resta, então;
  3. Fechar o pedido em que o tamanho é maior do que o tamanho solicitado e o novo pedido aberto com o tamanho que deve ser deixado aberto. Em casos normais, a terceira etapa nunca deve ser realizada. Criado para fins de proteção.
//+------------------------------------------------------------------+
//| 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);
   }
}

Conclusão

Os arquivos feitos e anexados aqui podem definitivamente serem melhorados com um protocolo de servidor do cliente melhor, comunicação mais inteligente e melhor execução, mas a minha tarefa foi verificar se é possível, e para construí-lo com qualidade aceitável, para que todos pudessem usá-lo para as suas necessidades particulares.

Ele funciona bem o suficiente para seguir suas próprias estratégias e estratégias para todos os participantes no MQL5 Championship. O desempenho e as possibilidades que são fornecidos por MQL4 e MQL5 são bons o suficiente para até mesmo levá-lo de forma profissional e comercial. Eu acredito que é possível fazer um provedor de sinal muito bom para todos os clientes de MetaTrader 4 e MetaTrader 5 apenas usando seu computador pessoal e sua própria estratégia.

Eu gostaria de ver as pessoas melhorarem o código que eu forneci aqui e voltarem com pareceres e recomendações. Eu também tentarei responder suas perguntas caso você tenha alguma. Paralelamente, eu estou executando o teste onde eu acompanho meus participantes do campeonato favoritos. Agora ele tem executado bem durante uma semana. Se eu encontrar algum problema, então eu vou lhe fornecer atualizações.

Tsaktuo

Por favor perceba que através da aplicação de funcionalidade e executáveis​ descritas na sua conta real, você assume total responsabilidade por todas as perdas ou danos que possam ser causados ​por ele. Negociar em conta real SOMENTE após bons testes e SOMENTE com um bom entendimento sobre a funcionalidade que está disponível aqui.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/344

Arquivos anexados |
Análise de Regressão Múltipla. Gerador de Estratégia e Tester in One Análise de Regressão Múltipla. Gerador de Estratégia e Tester in One
O artigo fornece uma descrição dos modos de uso da análise de regressão múltipla para desenvolvimento dos sistemas de negócio. Ele demonstra o uso da análise de regressão para automação da busca de estratégia. é dado neste exemplo uma equação de regressão gerada e integrada em um EA sem necessitar alta proficiência em programação.
Como se tornar um provedor de sinais para MetaTrader 4 e MetaTrader 5 Como se tornar um provedor de sinais para MetaTrader 4 e MetaTrader 5
Você deseja oferecer os seus sinais de negociação e obter lucros? Registre-se como vendedor no website MQL5.com e configure a sua conta de negociação para oferecer os seus sinais aos negociadores.
Algumas dicas para clientes iniciantes Algumas dicas para clientes iniciantes
Um provérbio que é geralmente atribuído a diversas pessoas famosas diz: "Aquele que não comete erros nunca faz nada." A menos que você considere a própria inatividade um erro, é difícil argumentar contra esta afirmação. Mas você sempre pode analisar os erros anteriores (os seus e os dos outros) para minimizar o número dos seus erros futuros. Tentaremos analisar possíveis situações que surgem ao executar empregos no serviço de mesmo nome.
MQL5-RPC. Chamadas de procedimento remoto de MQL5: Acesso de serviço da Web e analisador XML-RPC ATC para diversão e lucro MQL5-RPC. Chamadas de procedimento remoto de MQL5: Acesso de serviço da Web e analisador XML-RPC ATC para diversão e lucro
Este artigo descreve o framework MQL5-RPC que possibilita Chamadas de procedimento remoto do MQL5. Ele começa com o básico do XML-RPC, implementação do MQL5 e segue dois exemplos de utilização real. O primeiro exemplo é usando um serviço web externo e o segundo é um cliente para simples serviço XML-RPC ATC 2011 Analyzer. Se você está interessado em como implementar e analisar estatísticas diferentes do ATC 2011 em tempo real, este artigo é para você.