English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
Named Pipes를 사용하여 MetaTrader 5 터미널 간 통신을 위한 DLL없는 솔루션

Named Pipes를 사용하여 MetaTrader 5 터미널 간 통신을 위한 DLL없는 솔루션

MetaTrader 5 | 5 7월 2021, 15:57
69 0
investeo
investeo

소개

MetaTrader 5 단말기 간의 가능한 통신 방법에 대해 잠시 궁금했습니다. 내 목표는 틱 인디케이터를 사용하고 터미널 중 하나에서 다른 견적 공급자의 틱을 표시하는 것이 었습니다.

자연스러운 해결책은 하드 드라이브에 별도의 파일을 사용하는 것이 었습니다. 한 터미널은 파일에 데이터를 쓰고 다른 터미널은 데이터를 읽습니다. 이 방법은 단일 메시지 전송과 관련이 있지만 따옴표를 스트리밍하는데 가장 효과적인 방법은 아닙니다.

그런 다음 Alexander가 WCF 서비스를 사용하여 .NET 응용 프로그램으로 따옴표를 내보내는 방법에 대한 좋은 글을 우연히 보게 되었습니다. 끝내려고 할 때 Sergeev가 쓴 또 다른 이 나타났죠.

두 글 모두 제가 필요로 하는 내용과 비슷했지만 저는 하나는 서버 역할을 하고 다른 하나는 클라이언트 역할을 하는 다른 터미널에서 사용할 수있는 DLL이 없는 솔루션을 찾고 있었습니다. 웹을 검색하는 동안 통신에 Named Pipes를 사용할 수 있다는 메모를 발견하고 파이프를 사용한 프로세스 간 통신에 대한 MSDN 사양을 자세히 읽었습니다.

Named Pipes가 동일한 컴퓨터 또는 인트라넷을 통한 다른 컴퓨터를 통한 통신을 지원한다는 것을 발견했습니다. 이 방법을 사용하기로 결정했습니다.

이 글에서는 Named Pipes 통신을 소개하고 CNamedPipes 클래스를 디자인하는 프로세스를 설명합니다. 또한 MetaTrader 5 터미널과 전체 시스템 처리량 간의 틱 인디케이터 스트리밍 테스트도 포함됩니다.

1. 명명된 파이프를 사용한 프로세스 간 통신

전형적인 파이프를 생각할 때 우리는 매체를 전달하는 데 사용되는 일종의 실린더를 상상합니다. 이것은 또한 운영 체제에서 프로세스 간 통신 수단 중 하나에 사용되는 용어입니다. 우리의 경우 데이터를 교환하는 MetaTrader 5 터미널과 같이 두 개의 프로세스를 연결하는 파이프를 상상할 수 있습니다. 

파이프는 익명이거나 명명될 수 있습니다. 주요 차이점은 두 가지입니다. 첫 번째는 익명 파이프를 네트워크에서 사용할 수 없다는 것이고 두 번째는 두 프로세스가 관련되어야 한다는 것입니다. 즉, 한 프로세스는 상위 프로세스이고 다른 프로세스는 하위 프로세스여야 합니다. 명명된 파이프에는 이 제한이 없습니다.

파이프를 사용하여 통신하려면 서버 프로세스가 알려진 이름으로 파이프를 설정해야 합니다. 파이프 이름은 문자열이며 \\servername\pipe\pipename 형식이어야 합니다. 파이프가 같은 컴퓨터에서 사용되는 경우 서버 이름을 생략하고 대신 점을 넣을 수 있습니다. \\.\pipe\pipename.

파이프에 연결하려는 클라이언트는 해당 이름을 알아야 합니다. 터미널을 구별하기 위해 \\.\pipe\mt[account_number]라는 이름 규칙을 사용하고 있지만 이름 규칙은 임의로 변경할 수 있습니다.

2. CNamedPipes 클래스 구현

명명된 파이프를 만들고 연결하는 저수준 메커니즘에 대한 간단한 설명부터 시작하겠습니다. Windows 운영 체제에서 파이프를 처리하는 모든 함수는 kernel32.dll 라이브러리를 통해 사용할 수 있습니다. 서버 측에서 명명 된 파이프를 인스턴스화하는 함수는 CreateNamedPipe()입니다.

파이프가 생성된 후 서버는 ConnectNamedPipe() 함수를 호출하여 클라이언트가 연결될 때까지 기다립니다. 연결이 성공하면 ConnectNamedPipe()는 0이 아닌 정수를 반환합니다. 그러나 클라이언트가 CreateNamedPipe() 를 호출 한 후 ConnectNamedPipe()가 호출되기 전에 성공적으로 연결되었을 수 있습니다. 이 경우 ConnectNamedPipe()는 0을 반환하고 GetLastError()는 오류 535 (0X217): ERROR_PIPE_CONNECTED를 반환합니다.

파이프 쓰기 및 읽기는 파일 액세스와 동일한 함수으로 수행됩니다.

BOOL WINAPI ReadFile(
  __in         HANDLE hFile,
  __out        LPVOID lpBuffer,
  __in         DWORD nNumberOfBytesToRead,
  __out_opt    LPDWORD lpNumberOfBytesRead,
  __inout_opt  LPOVERLAPPED lpOverlapped
);
BOOL WINAPI WriteFile(
  __in         HANDLE hFile,
  __in         LPCVOID lpBuffer,
  __in         DWORD nNumberOfBytesToWrite,
  __out_opt    LPDWORD lpNumberOfBytesWritten,
  __inout_opt  LPOVERLAPPED lpOverlapped
);

명명된 파이프에 대해 배운 후 기본 저수준 지침을 숨기기 위해 CNamedPipes 클래스를 설계했습니다.

이제 CNamedPipes.mqh 파일을 터미널의 적절한 (/include) 폴더에 넣고 이를 소스 코드에 포함시키고 CNamedPipe 객체를 선언하는 것으로 충분합니다.

내가 디자인한 클래스는 명명된 파이프를 처리하는 몇 가지 기본 메소드를 제공합니다.

Create(), Connect(), Disconnect(), Open(), Close(), WriteUnicode(), ReadUnicode(), WriteANSI(), ReadANSI(), WriteTick(), ReadTick()

추가 요구 사항에 따라 클래스를 추가로 확장 할 수 있습니다.

Create () 메소드는 주어진 이름으로 파이프를 만들려고 합니다. 터미널 간의 연결을 단순화하기 위해 입력 매개 변수 'account'는 파이프를 사용할 클라이언트의 계정 번호입니다.

계정 이름을 입력하지 않으면 메소드는 현재 터미널의 계정 번호로 파이프를 열려고 합니다. Create() 함수는 파이프가 성공적으로 생성된 경우 true를 반환합니다.

//+------------------------------------------------------------------+
/// Create() : try to create a new instance of Named Pipe
/// \param account - source terminal account number  
/// \return true - if created, false otherwise                                                                |
//+------------------------------------------------------------------+
bool CNamedPipe::Create(int account=0)
  {
   if(account==0)
      pipeNumber=IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN));
   else
      pipeNumber=IntegerToString(account);

   string fullPipeName=pipeNamePrefix+pipeNumber;

   hPipe=CreateNamedPipeW(fullPipeName,
                          (int)GENERIC_READ|GENERIC_WRITE|(ENUM_PIPE_ACCESS)PIPE_ACCESS_DUPLEX,
                          (ENUM_PIPE_MODE)PIPE_TYPE_RW_BYTE,PIPE_UNLIMITED_INSTANCES,
                          BufferSize*sizeof(ushort),BufferSize*sizeof(ushort),0,NULL);

   if(hPipe==INVALID_HANDLE_VALUE) return false;
   else
      return true;

  }

Connect() 메소드는 클라이언트가 파이프에 연결할 때까지 기다립니다. 클라이언트가 성공적으로 파이프에 연결되면 true를 반환합니다.

//+------------------------------------------------------------------+
/// Connect() : wait for a client to connect to a pipe   
/// \return true - if connected, false otherwise.
//+------------------------------------------------------------------+
bool CNamedPipe::Connect(void)
  {
   if(ConnectNamedPipe(hPipe,NULL)==false)
      return(kernel32::GetLastError()==ERROR_PIPE_CONNECTED);
   else return true;
  }

Disconnect() 메소드는 파이프에서 서버 연결을 끊습니다.

//+------------------------------------------------------------------+
/// Disconnect(): disconnect from a pipe
/// \return true - if disconnected, false otherwise    
//+------------------------------------------------------------------+
bool CNamedPipe::Disconnect(void)
  {
   return DisconnectNamedPipe(hPipe);
  }

Open() 메소드는 클라이언트에서 사용해야 하며 이전에 만든 파이프를 열려고 합니다. 파이프가 성공적으로 열리면 true를 반환합니다. 어떤 이유로든 5 초 이내에 생성된 파이프에 연결할 수 없거나 파이프를 열지 못하면 false를 반환합니다.

//+------------------------------------------------------------------+
/// Open() : try to open previously created pipe
/// \param account - source terminal account number
/// \return true - if successfull, false otherwise.
//+------------------------------------------------------------------+
bool CNamedPipe::Open(int account=0)
  {
   if(account==0)
      pipeName=IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN));
   else
      pipeName=IntegerToString(account);

   string fullPipeName=pipeNamePrefix+pipeName;

   if(hPipe==INVALID_HANDLE_VALUE)
     {
      if(WaitNamedPipeW(fullPipeName,5000)==0)
        {
         Print("Pipe "+fullPipeName+" not available...");
         return false;
        }

      hPipe=CreateFileW(fullPipeName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
      if(hPipe==INVALID_HANDLE_VALUE)
        {
         Print("Pipe open failed");
         return false;
        }

     }
   return true;
  }

Close() 메소드는 파이프 핸들을 닫습니다.

//+------------------------------------------------------------------+
/// Close() : close pipe handle
/// \return 0 if successfull, non-zero otherwise  
//+------------------------------------------------------------------+
int CNamedPipe::Close(void)
  {
   return CloseHandle(hPipe);
  }

다음 여섯 가지 방법은 파이프를 통해 읽고 쓰는 데 사용됩니다. 처음 두 쌍은 유니 코드 및 ANSI 형식의 문자열을 처리하며 둘 다 터미널간에 명령 또는 메시지를 보내는 데 사용할 수 있습니다.

MQL5의 문자열 변수는 유니 코드를 포함하는 객체로 저장되므로 자연스러운 방법은 유니 코드 메소드를 제공하는 것이었지만 MQL5는 UnicodeToANSI 메소드를 제공하므로 ANSI 문자열 통신도 구현했습니다. 마지막 두 메소드는 명명된 파이프를 통해 MqlTick 개체를 보내고 받는 것을 처리합니다. 

WriteUnicode() 메소드는 유니코드 문자로 구성된 메시지를 작성합니다. 모든 문자는 2 바이트로 구성되므로 ushort 배열로 파이프에 보냅니다.

//+------------------------------------------------------------------+
/// WriteUnicode() : write Unicode string to a pipe
/// \param message - string to send
/// \return number of bytes written to a pipe     
//+------------------------------------------------------------------+
int CNamedPipe::WriteUnicode(string message)
  {
   int ushortsToWrite, bytesWritten;
   ushort UNICODEarray[];
   ushortsToWrite = StringToShortArray(message, UNICODEarray);
   WriteFile(hPipe,ushortsToWrite,sizeof(int),bytesWritten,0);
   WriteFile(hPipe,UNICODEarray,ushortsToWrite*sizeof(ushort),bytesWritten,0);
   return bytesWritten;
  }

ReadUnicode() 메소드는 ushorts 배열을 수신하고 문자열 객체를 반환합니다.

//+------------------------------------------------------------------+
/// ReadUnicode(): read unicode string from a pipe
/// \return unicode string (MQL5 string)
//+------------------------------------------------------------------+
string CNamedPipe::ReadUnicode(void)
  {
   string ret;
   ushort UNICODEarray[STR_SIZE*sizeof(uint)];
   int bytesRead, ushortsToRead;
 
   ReadFile(hPipe,ushortsToRead,sizeof(int),bytesRead,0);
   ReadFile(hPipe,UNICODEarray,ushortsToRead*sizeof(ushort),bytesRead,0);
   if(bytesRead!=0)
      ret = ShortArrayToString(UNICODEarray);
   
   return ret;
  }

WriteANSI() 메소드는 ANSI uchar 배열을 파이프에 씁니다.

//+------------------------------------------------------------------+
/// WriteANSI() : write ANSI string to a pipe
/// \param message - string to send
/// \return number of bytes written to a pipe                                                                  |
//+------------------------------------------------------------------+
int CNamedPipe::WriteANSI(string message)
  {
   int bytesToWrite, bytesWritten;
   uchar ANSIarray[];
   bytesToWrite = StringToCharArray(message, ANSIarray);
   WriteFile(hPipe,bytesToWrite,sizeof(int),bytesWritten,0);
   WriteFile(hPipe,ANSIarray,bytesToWrite,bytesWritten,0);
   return bytesWritten;
  }

ReadANSI() 메소드는 파이프에서 uchar 배열을 읽고 문자열 객체를 반환합니다.

//+------------------------------------------------------------------+
/// ReadANSI(): read ANSI string from a pipe
/// \return unicode string (MQL5 string)
//+------------------------------------------------------------------+
string CNamedPipe::ReadANSI(void)
  {
   string ret;
   uchar ANSIarray[STR_SIZE];
   int bytesRead, bytesToRead;
 
   ReadFile(hPipe,bytesToRead,sizeof(int),bytesRead,0);
   ReadFile(hPipe,ANSIarray,bytesToRead,bytesRead,0);
   if(bytesRead!=0)
      ret = CharArrayToString(ANSIarray);
   
   return ret;
  }

WriteTick() 메소드는 단일 MqlTick 개체를 파이프에 씁니다.

//+------------------------------------------------------------------+
/// WriteTick() : write MqlTick to a pipe
/// \param MqlTick to send
/// \return true if tick was written correctly, false otherwise
//+------------------------------------------------------------------+
int CNamedPipe::WriteTick(MqlTick &outgoing)
  {
   int bytesWritten;

   WriteFile(hPipe,outgoing,MQLTICK_SIZE,bytesWritten,0);

   return bytesWritten;
  }

ReadTick() 메소드는 파이프에서 단일 MqlTick 객체를 읽습니다. 파이프가 비어 있으면 0을 반환하고 그렇지 않으면 MqlTick 객체의 바이트 수를 반환해야 합니다.

//+------------------------------------------------------------------+
/// ReadTick() : read MqlTick from a pipe
/// \return true if tick was read correctly, false otherwise
//+------------------------------------------------------------------+
int CNamedPipe::ReadTick(MqlTick &incoming)
  {
   int bytesRead;

   ReadFile(hPipe,incoming,MQLTICK_SIZE,bytesRead,NULL);

   return bytesRead;
  }
//+------------------------------------------------------------------+

명명된 파이프를 처리하는 기본 방법이 알려져 있기 때문에 두 개의 MQL 프로그램으로 시작할 수 있습니다. 따옴표를 수신하는 간단한 스크립트와 따옴표를 보내는 인디케이터입니다.

3. 견적 수신을 위한 서버 스크립트

예제 서버는 명명된 파이프를 시작하고 클라이언트가 연결될 때까지 기다립니다. 클라이언트 연결을 끊은 후 해당 클라이언트가 수신한 총 틱 수를 표시하고 새 클라이언트가 연결될 때까지 기다립니다. 클라이언트 연결이 끊어지고 서버가 전역 변수 'gvar0'을 찾으면 종료됩니다. 'gvar0' 변수가 없으면 차트를 마우스 오른쪽 버튼으로 클릭하고 전문가 목록 옵션을 선택하여 서버를 수동으로 중지할 수 있습니다.

//+------------------------------------------------------------------+
//|                                              NamedPipeServer.mq5 |
//|                                      Copyright 2010, Investeo.pl |
//|                                                http:/Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Investeo.pl"
#property link      "http:/Investeo.pl"
#property version   "1.00"

#include <CNamedPipes.mqh>

CNamedPipe pipe;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   bool tickReceived;
   int i=0;

   if(pipe.Create()==true)
      while (GlobalVariableCheck("gvar0")==false)
        {
         Print("Waiting for client to connect.");
         if (pipe.Connect()==true)
            Print("Pipe connected");
         while(true)
           {
            do
              {
               tickReceived=pipe.ReadTick();

               if(tickReceived==false)
                 {
                  if(GetError()==ERROR_BROKEN_PIPE)
                    {
                     Print("Client disconnected from pipe "+pipe.Name());
                     pipe.Disconnect();
                     break;
                    }
                 } else i++;
                  Print(IntegerToString(i) + "ticks received.");
              } while(tickReceived==true);
            if (i>0) 
            {
               Print(IntegerToString(i) + "ticks received.");
               i=0;
            };
            if(GlobalVariableCheck("gvar0")==true || (GetError()==ERROR_BROKEN_PIPE)) break;
           }

        }

 pipe.Close(); 
  }

4. 견적 보내기를 위한 간단한 인디케이터

따옴표 전송 인디케이터는 OnInit() 메소드 내부에서 파이프를 열고 OnCalculate() 메소드가 트리거 될 때마다 단일 MqlTick을 보냅니다.
//+------------------------------------------------------------------+
//|                                        SendTickPipeIndicator.mq5 |
//|                                      Copyright 2010, Investeo.pl |
//|                                                http:/Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Investeo.pl"
#property link      "http:/Investeo.pl"
#property version   "1.00"
#property indicator_chart_window

#include <CNamedPipes.mqh>

CNamedPipe pipe;
int ctx;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
 
   while (!pipe.Open(AccountInfoInteger(ACCOUNT_LOGIN)))
   {
      Print("Pipe not created, retrying in 5 seconds...");
      if (GlobalVariableCheck("gvar1")==true) break;
   }
   
   ctx = 0;
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
   ctx++;
   MqlTick outgoing;
   SymbolInfoTick(Symbol(), outgoing);
   pipe.WriteTick(outgoing);
   Print(IntegerToString(ctx)+" tick send to server by SendTickPipeClick.");
   return(rates_total);
  }
//+------------------------------------------------------------------+

5. 단일 클라이언트 터미널에서 여러 공급자의 눈금 인디케이터 실행

들어오는 따옴표를 별도의 눈금 인디케이터에 표시하고 싶었 기 때문에 상황이 더 복잡해졌습니다. EventChartCustom() 메소드를 트리거하여 들어오는 틱을 틱 인디케이터에 브로드 캐스트하는 파이프 서버를 구현하여 이를 달성했습니다.

입찰 및 요청 견적은 세미콜론으로 나눈 단일 문자열로 전송됩니다. '1.20223;120225'입니다. 적절한 인디케이터는 OnChartEvent() 내부의 사용자 지정 이벤트를 처리하고 눈금 차트를 표시합니다. 

//+------------------------------------------------------------------+
//|                                   NamedPipeServerBroadcaster.mq5 |
//|                                      Copyright 2010, Investeo.pl |
//|                                                http:/Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Investeo.pl"
#property link      "http:/Investeo.pl"
#property version   "1.00"
#property script_show_inputs
#include <CNamedPipes.mqh>

input int account = 0;

CNamedPipe pipe;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   bool tickReceived;
   int i=0;

   if(pipe.Create(account)==true)
      while(GlobalVariableCheck("gvar0")==false)
        {
         if(pipe.Connect()==true)
            Print("Pipe connected");
            i=0;
         while(true)
           {
            do
              {
               tickReceived=pipe.ReadTick();
               if(tickReceived==false)
                 {
                  if(kernel32::GetLastError()==ERROR_BROKEN_PIPE)
                    {
                     Print("Client disconnected from pipe "+pipe.GetPipeName());
                     pipe.Disconnect();
                     break;
                    }
                  } else  {
                   i++; Print(IntegerToString(i)+" ticks received BY server.");
                  string bidask=DoubleToString(pipe.incoming.bid)+";"+DoubleToString(pipe.incoming.ask);
                  long currChart=ChartFirst(); int chart=0;
                  while(chart<100) 
                    {
                     EventChartCustom(currChart,6666,0,(double)account,bidask);
                     currChart=ChartNext(currChart); 
                     if(currChart==0) break;         // Reached the end of the charts list
                     chart++;
                    }
                     if(GlobalVariableCheck("gvar0")==true || (kernel32::GetLastError()==ERROR_BROKEN_PIPE)) break;
              
                 }
              }
            while(tickReceived==true);
            if(i>0)
              {
               Print(IntegerToString(i)+"ticks received.");
               i=0;
              };
            if(GlobalVariableCheck("gvar0")==true || (kernel32::GetLastError()==ERROR_BROKEN_PIPE)) break;
            Sleep(100);
           }

        }


  pipe.Close(); 
  }

눈금을 표시하기 위해 MQLmagazine에 배치 된 눈금 인디케이터를 선택했지만 OnCalculate() 메소드 대신 OnChartEvent () 내부에서 처리를 구현하고 조건부 지침을 추가했습니다. dparam 매개 변수가 파이프 번호와 같고 이벤트 ID가 CHARTEVENT_CUSTOM+6666과 같은 경우에만 견적을 처리할 수 ​​있습니다.

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
  if (dparam==(double)incomingPipe)
   if(id>CHARTEVENT_CUSTOM)
     {
      if(id==CHARTEVENT_CUSTOM+6666)
        {
        // Process incoming tick
        }
     } else
        {
         // Handle the user event 
        }
  }

아래 스크린 샷에는 세 개의 눈금 인디케이터가 있습니다.

그중 2 개는 파이프를 통해 받은 틱을 표시하고 파이프를 사용하지 않는 세 번째 인디케이터는 틱이 손실되지 않았는지 확인하기 위해 실행되었습니다.  

다른 터미널의 데이터가 있는 눈금 인디케이터

그림 1 명명된 파이프를 통해 받은 견적

인디케이터를 실행하는 방법에 대한 설명이 포함 첨부된 스크린 캐스트를 찾으십시오.

그림 2 인디케이터 설정을 설명하는 스크린 캐스트

6. 시스템 처리량 테스트

파이프는 공유 메모리를 사용하기 때문에 통신이 매우 빠릅니다. 저는 두 개의 MetaTrader 5 터미널 사이에 100 000 및 1 000 000 틱을 연속으로 보내는 테스트를 수행했습니다. 보내는 스크립트는 WriteTick() 함수를 사용하고 GetTickCount()를 사용하여 시간 범위를 측정합니다.

   Print("Sending...");
   uint start = GetTickCount();
   for (int i=0;i<100000;i++)
      pipe.WriteTick(outgoing);
   uint stop = GetTickCount();
   Print("Sending took" + IntegerToString(stop-start) + " [ms]");
   pipe.Close();

서버는 들어오는 따옴표를 읽습니다. 시간 범위는 첫 번째 수신 견적부터 고객이 연결을 끊을 때까지 측정됩니다.

//+------------------------------------------------------------------+
//|                                          SpeedTestPipeServer.mq5 |
//|                                      Copyright 2010, Investeo.pl |
//|                                                http:/Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Investeo.pl"
#property link      "http:/Investeo.pl"
#property version   "1.00"

#property script_show_inputs
#include <CNamedPipes.mqh>

input int account=0;
bool tickReceived;
uint start,stop;

CNamedPipe pipe;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int i=0;
   if(pipe.Create(account)==true)
      if(pipe.Connect()==true)
         Print("Pipe connected");

   do
     {
      tickReceived=pipe.ReadTick();
      if(i==0) start=GetTickCount();
      if(tickReceived==false)
        {
         if(kernel32::GetLastError()==ERROR_BROKEN_PIPE)
           {
            Print("Client disconnected from pipe "+pipe.GetPipeName());
            pipe.Disconnect();
            break;
           }
        }
      else i++;
     }
   while(tickReceived==true);
   stop=GetTickCount();

   if(i>0)
     {
      Print(IntegerToString(i)+" ticks received.");
      i=0;
     };
   
   pipe.Close();
   Print("Server: receiving took "+IntegerToString(stop-start)+" [ms]");

  }
//+------------------------------------------------------------------+

10 번의 샘플 실행 결과는 다음과 같습니다.

운영
인용 부호
전송 시간 [ms]
수신 시간 [ms]
1
 100000
 624
624
2 100000 702 702
3 100000 687 687
4 100000 592 608
5 100000 624 624
6 1000000 5616 5616
7 1000000 5788 5788
8 1000000 5928 5913
9
 1000000 5772 5756
10
 1000000 5710 5710

표 1 처리 속도 측정

2.0GHz T4200 CPU 및 3GB RAM이 장착된 Windows Vista를 실행하는 랩톱에서 1,000,000 개 견적을 보내는 평균 속도는 약 170000 틱/초였습니다.

결론

Names Pipes를 사용하여 MetaTrader 5 터미널 간의 통신 방법을 제시했습니다. 이 방법은 터미널간에 실시간 견적을 보내기에 충분하다는 것이 밝혀졌습니다.

CNamedPipes 클래스는 추가 요구 사항에 따라 추가로 확장 할 수 있습니다 (예: 두 개의 독립적인 계정에 대한 헤징 가능). chm 형식의 문서와 함께 첨부 된 CNamedPipe 클래스 소스 코드와 글 작성을 위해 구현 한 기타 소스 코드를 찾으십시오.

MetaQuotes 소프트웨어 사를 통해 영어가 번역됨
원본 기고글: https://www.mql5.com/en/articles/115

MQL5 객체 지향 프로그래밍 접근 방식을 사용한 Expert Advisor 작성하기 MQL5 객체 지향 프로그래밍 접근 방식을 사용한 Expert Advisor 작성하기
이 글은 "초보자를 위한 MQL5에서 Expert Advisor를 작성하기 위한 단계별 가이드" 글에서 수행 한 작업에 대한 객체 지향 접근 방식에 초점을 맞추고 있습니다. 대부분의 사람들은 이것이 어렵다고 생각하지만, 이 글을 다 읽고 나면 객체 지향 기반의 Expert Advisor를 직접 작성할 수 있을 것임을 확신하고 싶습니다.
Google Chart API를 통해 차트를 구성하는 라이브러리 Google Chart API를 통해 차트를 구성하는 라이브러리
다양한 유형의 다이어그램 구성은 시장 상황 분석 및 거래 시스템 테스트의 필수 부분입니다. 종종 멋진 다이어그램을 구성하려면 데이터 출력을 파일로 구성해야 하며 그 후에 MS Excel과 같은 응용 프로그램에서 사용됩니다. 이건 그닥 편리하지 않고 데이터를 동적으로 업데이트하는 함수를 뺏어가기까지 합니다. Google Charts API는 서버에 특별한 요청을 보내 온라인 모드에서 차트를 만드는 수단을 제공했습니다. 이 글에서는 이러한 요청을 작성하고 Google 서버에서 차트를 가져 오는 프로세스를 자동화하려고 합니다.
특정 매직 넘버에 의한 총 포지션 볼륨 계산을 위한 최적 방법 특정 매직 넘버에 의한 총 포지션 볼륨 계산을 위한 최적 방법
이 글에서는 지정된 기호와 매직 넘버의 총 포지션 볼륨 계산 문제를 고려합니다. 제안 된 방법은 거래 내역에서 필요한 최소한의 부분만 요청하고 총 포지션이 0 일 때 가장 가까운 시간을 찾아 최근 거래로 계산을 수행합니다. 클라이언트 터미널의 전역 변수 작업도 고려됩니다.
ORDER_MAGIC을 사용하여 단일 상품에서 여러 Expert Advisors와의 거래 ORDER_MAGIC을 사용하여 단일 상품에서 여러 Expert Advisors와의 거래
이 글에서는 매직 식별을 사용한 정보 코딩 문제와 다양한 Expert Advisors의 자동 거래의 분할, 조립 및 동기화에 대해 설명합니다. 이 글은 Expert Advisors와 다양한 전략의 복잡한 동기화 시스템을 구현하는 데 유용할 수 있는 가상 포지션 문제를 다루기 때문에 초보자는 물론 숙련된 거래자에게도 흥미로울 것입니다.