English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında iletişim kurmak için DLL içermeyen bir çözüm

Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında iletişim kurmak için DLL içermeyen bir çözüm

MetaTrader 5Örnekler | 15 Aralık 2021, 10:14
84 0
investeo
investeo

Giriş

MetaTrader 5 terminalleri arasındaki olası iletişim yollarını bir müddet merak ettim. Amacım, tick göstergesini kullanmak ve terminallerden birinde farklı fiyat teklifi sağlayıcılardan gelen tickleri görüntülemekti.

Doğal çözüm, sabit sürücüde ayrı dosyalar kullanmaktı. Bir terminal dosyaya veri yazar ve diğeri onu okurdu. Bu yöntem, tek tek mesaj göndermek için uygun olsa da, akışlı fiyat teklifleri için en etkili yöntem gibi görünmüyor.

Daha sonra, WCF hizmetlerini kullanarak tekliflerin .NET uygulamalarına nasıl aktarılacağına dair Alexander'ın güzel bir makalesine rastladım ve bitirmek üzereyken karşıma Sergeev tarafından yazılmış başka bir makale çıktı.

Her iki makale de ihtiyaçlarımı hemen hemen karşılıyordu ancak biri Sunucu, diğeri İstemci olarak işlev gören farklı terminaller tarafından kullanılabilecek DLL içermeyen bir çözüm aradım. İnternette arama yaparken Adlandırılmış Kanalların iletişim için kullanılabileceğini öneren bir not buldum ve kanalları kullanarak İşlemler Arası İletişim için MSDN spesifikasyonunu iyice okudum

Adlandırılmış Kanalların aynı bilgisayar üzerinden veya intranet üzerinden farklı bilgisayarlar üzerinden iletişimi desteklediğini keşfettim ve bu yaklaşımı tercih etmeye karar verdim.

Bu makalede, Adlandırılmış Kanallar iletişimi tanıtılmakta ve CNamedPipes sınıfını tasarlama süreci açıklanmaktadır. Ayrıca, MetaTrader 5 terminalleri ve genel sistem verimi arasındaki tick göstergesi akışının test edilmesini de içermektedir.

1. Adlandırılmış Kanalları Kullanarak İşlemler Arası İletişim

Tipik bir kanal düşündüğümüzde, medyayı iletmek için kullanılan bir tür silindir hayal ederiz. Bu aynı zamanda bir işletim sistemindeki işlemler arası iletişim araçlarından biri için kullanılan bir terimdir. Bizim durumumuzda, veri alışverişi yapan MetaTrader 5 terminalleri gibi iki süreci birbirine bağlayan bir kanal hayal edebilirsiniz. 

Kanallar anonim veya adlandırılmış olabilir. Aralarındaki iki temel fark vardır: İlki, anonim kanalların bir ağ üzerinden kullanılamaması ve ikincisi iki sürecin ilişkilendirilmesi gerektiğidir. Yani, bir süreç üst ve diğeri alt olmalıdır. Adlandırılmış kanallar bu sınırlamaya sahip değildir.

Kanalları kullanarak iletişim kurmak için, bir sunucu işlemi bilinen bir ada sahip bir kanal kurmalıdır. Kanal adı bir dizedir ve \\servername\pipe\pipename biçiminde olmalıdır. Kanallar aynı bilgisayarda kullanılıyorsa sunucu adı atlanabilir ve bunun yerine bir nokta konulabilir: \\.\pipe\pipename.

Bir kanala bağlanmaya çalışan istemcinin adını bilmesi gerekir. Terminalleri ayırt etmek için \\.\pipe\mt[account_number] ad kuralını kullanıyorum, ancak adlandırma kuralı isteğe bağlı olarak değiştirilebilir.

2. CNamedPipes sınıfını uygulama

Adlandırılmış bir kanal oluşturma ve ona bağlanmanın düşük seviyeli mekanizmasına ilişkin kısa bir açıklamayla başlayacağım. Windows işletim sistemlerinde kanalları işleyen tüm işlevler kernel32.dll kitaplığı aracılığıyla kullanılabilir. Sunucu tarafında adlandırılmış bir kanal örneği oluşturma işlevi şu şekildedir: CreateNamedPipe().

Kanal oluşturulduktan sonra, sunucu bir istemcinin bağlanmasını beklemek için ConnectNamedPipe() işlevini çağırır. Bağlantı başarılı olursa ConnectNamedPipe() işlevi sıfır olmayan bir tamsayı döndürür. Yine de, istemcinin CreateNamedPipe() işlevi çağrıldıktan sonra ve ConnectNamedPipe() işlevi çağrılmadan önce başarıyla bağlanması mümkündür. Bu durumda, ConnectNamedPipe() işlevi sıfır döndürür ve GetLastError() işlevi 535 (0X217) hatasını döndürür : ERROR_PIPE_CONNECTED.

Bir kanala yazma ve kanaldan okuma, dosya erişimiyle aynı işlevlerle sağlanır:

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

Adlandırılmış kanallar hakkında bilgi edindikten sonra, alttaki düşük düzeyli talimatları gizlemek için CNamedPipes sınıfını tasarladım.

Artık CNamedPipes.mqh dosyasını terminalin uygun (/include) klasörüne koymak ve kaynak koduna dahil etmek ve bir CNamedPipe nesnesi bildirmek yeterlidir.

Tasarladığım sınıf, adlandırılmış kanalları işlemek için birkaç temel yöntem sunar:

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

Ek gereksinimlere göre sınıf daha da genişletilebilir.

Create() yöntemi, belirli bir ada sahip bir kanal oluşturmaya çalışır. Terminaller arasındaki bağlantıyı basitleştirmek için 'hesap' giriş parametresi, kanalı kullanacak bir istemcinin hesap numarasıdır.

Hesap adı girilmezse yöntem, mevcut terminalin hesap numarası ile bir kanal açmaya çalışır. Create() işlevi, kanal başarıyla oluşturulduysa true değerini döndürür.

//+------------------------------------------------------------------+
/// 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() yöntemi, bir istemcinin bir kanala bağlanmasını bekler. İstemci bir kanala başarıyla bağlanırsa true değerini döndürür.

//+------------------------------------------------------------------+
/// 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() yöntemi, sunucunun bağlantısını bir kanaldan keser.

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

Open() yöntemi bir istemci tarafından kullanılmalıdır; önceden oluşturulmuş bir kanalı açmaya çalışır. Kanal açma işlemi başarılı olursa true değerini döndürür.  Oluşturulan kanala herhangi bir nedenle 5 saniyelik zaman aşımı içinde bağlanamazsa veya kanal açılma işlemi başarısız olursa false değerini döndürür.

//+------------------------------------------------------------------+
/// 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() yöntemi, kanal tanıtıcısını kapatır.

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

Sonraki altı yöntem, kanallar üzerinden okumak ve yazmak için kullanılır. İlk iki çift, dizeleri Unicode ve ANSI biçimlerinde işler, her ikisi de terminaller arasında komut veya mesaj göndermek için kullanılabilir.

MQL5'teki dize değişkeni, Unicode içeren bir nesne olarak depolanır; bu nedenle doğal yol Unicode yöntemlerini sağlamaktı, ancak MQL5 UnicodeToANSI yöntemlerini sağladığı için ANSI dize iletişimini de uyguladım. Son iki yöntem, MqlTick nesnesinin adlandırılmış bir kanal aracılığıyla gönderilmesini ve alınmasını işler. 

WriteUnicode() yöntemi, Unicode karakterlerden oluşan mesajı yazar. Her karakter iki bayttan oluştuğu için, bir kanala bir dizi ushort olarak gönderir.

//+------------------------------------------------------------------+
/// 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() yöntemi, ushort dizisini alır ve bir dize nesnesini döndürür.

//+------------------------------------------------------------------+
/// 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() yöntemi, ANSI uchar dizisini bir kanala yazar.

//+------------------------------------------------------------------+
/// 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() yöntemi, bir kanaldan uchar dizisini okur ve bir dize nesnesini döndürür.

//+------------------------------------------------------------------+
/// 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() yöntemi, bir kanala tek bir MqlTick nesnesi yazar.

//+------------------------------------------------------------------+
/// 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() yöntemi, bir kanaldan tek bir MqlTick nesnesini okur. Bir kanal boşsa 0 döndürür, boş değilse bir dizi MqlTick nesnesi baytını döndürmelidir.

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

Adlandırılmış kanalları işlemenin temel yöntemleri bilindiğine göre, iki MQL programıyla başlayabiliriz: Fiyat teklifi almak için basit bir script dosyası ve fiyat teklifi göndermek için bir gösterge.

3. Fiyat Teklifi Almak için Sunucu Script Dosyası

Örnek sunucu, adlandırılmış kanalı başlatır ve bir istemcinin bağlanmasını bekler. İstemci bağlantısını kestikten sonra, o istemci tarafından toplamda kaç tick alındığını gösterir ve yeni bir istemcinin bağlanmasını bekler. İstemci bağlantısı kesilirse ve sunucu genel bir 'gvar0' değişkeni bulursa, çıkar. 'gvar0' değişkeni yoksa, bir grafiğe sağ tıklanarak ve Expert Listesi seçeneği seçilerek sunucu manuel olarak durdurulabilir.

//+------------------------------------------------------------------+
//|                                              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. Fiyat Teklifi Göndermek İçin Basit Gösterge

Fiyat teklifi gönderme göstergesi, OnInit() yöntemi içinde bir kanal açar ve OnCalculate() yöntemi her tetiklendiğinde tek bir MqlTick gönderir:
//+------------------------------------------------------------------+
//|                                        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. Tek İstemci Terminalinde Birden Çok Sağlayıcıdan Tick Göstergelerini Çalıştırma

Gelen fiyat tekliflerini ayrı tick göstergelerinde görüntülemek istediğim için durum daha da karmaşık bir hal aldı. Bunu, EventChartCustom() yöntemini tetikleyerek, gelen tickleri tick göstergesine yayınlayan kanal sunucusunu uygulayarak başardım.

Alış ve satış fiyat teklifleri, noktalı virgülle bölünmüş tek bir dize olarak gönderilir, ör. '1.20223;120225'. Uygun gösterge, OnChartEvent() içindeki özel bir olayı işler ve bir tick grafiği görüntüler. 

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

Tickleri görüntülemek için MQLmagazine içine yerleştirilmiş tick göstergesini seçtim fakat OnCalculate() yönteminin yerine OnChartEvent() içinde işleme uyguladım ve koşullu talimatlar ekledim. Yalnızca dparam parametresi kanal numarasına eşitse ve olay kimliği CHARTEVENT_CUSTOM+6666'ya eşitse, işleme için bir fiyat teklifi kabul edilir:

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

Aşağıdaki ekran görüntüsünde üç tick göstergesi vardır.

Bunlardan ikisi kanallardan alınan tickleri göstermekte olup ticklerin kaybolmadığını kontrol etmek için, kanalları kullanmayan üçüncü bir gösterge çalıştırıldı.  

Farklı terminallerden gelen verileri içeren tick göstergesi

Şek. 1 Adlandırılmış bir kanal aracılığıyla alınan fiyat teklifleri

Göstergeleri nasıl çalıştırdığıma ilişkin yorumları içeren ekran görüntüsünü ekte bulabilirsiniz:

Şek. 2 Gösterge kurulumunu açıklayan ekran kaydı

6. Sistem Verimliliğini Test Etme

Kanallar paylaşılan belleği kullandığı için iletişim çok hızlıdır. İki MetaTrader 5 terminali arasında art arda 100 000 ve 1 000 000 tick gönderme testi yaptım. Gönderen script dosyası WriteTick() işlevini kullanır ve GetTickCount() kullanarak zaman aralığını ölçer:

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

Sunucu, gelen fiyat tekliflerini okur. Zaman aralığı, ilk gelen fiyat teklifinden istemci bağlantısı kesilene kadar ölçülür:

//+------------------------------------------------------------------+
//|                                          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 örnek çalışmasının sonuçları şu şekildedir:

Çalışma
Fiyat Teklifleri
Gönderme süresi [ms]
Alma süresi [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

Tablo 1 Verimlilik hızı ölçümleri

1 000 000 fiyat teklifi göndermenin ortalama hızı, 2,0GHz T4200 CPU ve 3GB RAM ile Windows Vista çalıştıran bir dizüstü bilgisayarda yaklaşık 170 000 tick/saniye idi.

Sonuç

Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında bir iletişim yöntemi sundum. Yöntemin, terminaller arasında gerçek zamanlı fiyat teklifleri göndermek için yeterli olduğu ortaya çıktı.

CNamedPipes sınıfı, örneğin iki bağımsız hesapta riskten korunmayı mümkün kılmak için ek gereksinimlere göre daha da genişletilebilir. CNamedPipe sınıfı kaynak kodunu chm biçiminde belgelerle ve makaleyi yazmak için uyguladığım diğer kaynak kodlarını ekte bulabilirsiniz.

MetaQuotes Ltd tarafından İngilizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/en/articles/115

MQL5 Nesne Yönelimli Programlama Yaklaşımını Kullanarak Expert Advisor Yazma MQL5 Nesne Yönelimli Programlama Yaklaşımını Kullanarak Expert Advisor Yazma
Bu makalede, "Yeni Başlayanlar için MQL5'te Expert Advisor yazmak için Adım Adım Kılavuz" adlı makalede yaptığımız şeyi yapmak için (Basit bir Expert Advisor oluşturma) nesne yönelimli yaklaşıma odaklanılmaktadır. Birçok kişi bunun zor olduğunu düşünüyor, ancak bu makaleyi okumayı bitirdiğinizde, nesne yönelimi temelinde kendi Expert Advisor'ınızı yazabileceğinizi garanti ediyorum.
Google Chart API ile Grafik Oluşturmak için Kitaplık Google Chart API ile Grafik Oluşturmak için Kitaplık
Çeşitli diyagram türlerinin oluşturulması, piyasa durumuna ilişkin analizlerin ve bir alım satım sisteminin test edilmesinin önemli bir parçasıdır. Sıklıkla, güzel görünümlü bir diyagram oluşturmak için, veri çıktısını bir dosyada düzenlemek ve ardından MS Excel gibi uygulamalarda kullanmak gerekir. Bu, çok elverişli değildir ve bizi verileri dinamik olarak güncelleme özelliğinden mahrum eder. Google Charts API, sunucuya özel bir istek göndererek çevrimiçi modlarda grafikler oluşturmak için araçlar sağladı. Bu makalede, böyle bir istek oluşturma ve Google sunucusundan grafik alma sürecini otomatikleştirmeye çalışıyoruz.
Belirtilen Sihirli Sayıya Göre Toplam Pozisyon Hacmini Hesaplamak İçin Optimum Yöntem Belirtilen Sihirli Sayıya Göre Toplam Pozisyon Hacmini Hesaplamak İçin Optimum Yöntem
Bu makalede, belirtilen sembol ve sihirli sayının toplam pozisyon hacminin hesaplanması sorunu ele alınmaktadır. Önerilen yöntem, yatırım geçmişinin yalnızca gerekli olan minimum bölümünü ister, toplam pozisyonun sıfıra eşit olduğu en yakın zamanı bulur ve son yatırımlarla hesaplamaları gerçekleştirir. Ayrıca istemci terminalinin genel değişkenleriyle çalışmak da dikkate alınır.
ORDER_MAGIC'in Tek Enstrüman Üzerinde Farklı Expert Advisor'larla Alım Satım Yapmak İçin Kullanımı ORDER_MAGIC'in Tek Enstrüman Üzerinde Farklı Expert Advisor'larla Alım Satım Yapmak İçin Kullanımı
Bu makalede, sihirli tanımlama ve farklı Expert Advisor'ların otomatik alım satım işlemlerinin bölünmesi, birleştirilmesi ve senkronizasyonu kullanılarak bilgi kodlama sorunları ele alınmıştır. Söz konusu makale hem yeni başlayanlar hem de daha deneyimli yatırımcılar için ilginç olacaktır; zira Expert Advisor'ların karmaşık senkronizasyon sistemlerinin ve çeşitli stratejilerin uygulanmasında faydalı olabilecek sanal pozisyonlar sorununa değinilmiştir.