English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MetaTrader 5'i MetaTrader 4 için Sinyal Sağlayıcı Olarak Kullanma

MetaTrader 5'i MetaTrader 4 için Sinyal Sağlayıcı Olarak Kullanma

MetaTrader 5Örnekler | 22 Aralık 2021, 14:12
186 0
Karlis Balcers
Karlis Balcers

Giriş

Bu makaleyi yazmayı ve başarılabilir olup olmadığını araştırmayı seçmemin birçok nedeni var.

İlk olarak, MetaTrader 5 uzun süredir piyasada değildi, ancak hepimiz hala favori brokerlerimizin gerçekte alım satım işlemi yapmamıza izin vermesini bekliyoruz. Bazıları MQL5 kullanarak stratejiler oluşturmuş ve bunlar iyi bir performansa sahip; şimdi bunları gerçek hesaplarda çalıştırmak istiyorlar. Diğerleri, alım satım işleminin organize edilme şeklini seviyor ve manuel olarak alım satım yapmak istiyor, ancak MetaTrader 4 yerine MetaTrader 5'i kullanıyor.

İkinci neden, Otomatik Alım Satım Şampiyonası sırasında herkes kendi gerçek hesaplarında liderleri takip etmeyi düşünüyordu. Bazıları alım satım işlemlerini takip etmek için kendi yollarını oluşturdu, ancak bazıları bunun hala nasıl yapılacağını, şampiyonada yatırımcılara mümkün olduğunca yakın sonuçların nasıl alınacağını, aynı para yönetimini nasıl uygulayacağını araştırıyor.

Üçüncüsü, bazı kişilerin iyi stratejileri vardır ve alım satım sinyallerini yalnızca kendilerine değil, arkadaşlarına veya başkalarına da sağlamak isterler. Performans kaybı olmadan birden fazla bağlantıyı kabul etme ve sinyalleri gerçek zamanlı olarak dağıtma olanağına ihtiyaçları vardır.

Bunlar hep aklımda olan sorular ve bu gereksinimleri karşılayacak bir çözüm bulmaya çalışacağım.


1. MQL5 şampiyonası etkinlikleri nasıl takip edilir?

Son zamanlarda MQL5.community'de bilgi düzeyime uygun birden fazla makale buldum ve bunlar, onu oluşturabileceğimi düşündürdü. Ayrıca şampiyona ana sayfasındaki etkinlikleri takip eden ve gerçek hesabımda alım satım işlemi yapan (neyse ki karlı) bir uygulama kullandığımı da söyleyeceğim. Sorun şuydu; veriler her 5 dakikada bir güncellenir ve doğru açılıp kapanma anını kaçırabilirsiniz.

şampiyona forumundan aynı şeyi yapan başka kişiler olduğunu fark ettim; bu hem etkili değil, hem de şampiyona ana sayfasında büyük bir trafiğe neden olur ve organizatörler bundan hoşlanmayabilir. Peki, bunun bir çözümü var mı? Tüm çözümlere baktım ve MetaTrader 5 aracılığıyla her katılımcının hesabına 'yatırımcı' (alım satım devre dışı) modunda erişme olasılığı hoşuma gitti.

Bunu, her alım satım etkinliğinin bilgilerini gerçek zamanlı olarak almak ve gerçek zamanlı olarak aktarmak için kullanabilir miyiz? Bunu öğrenmek için Expert Advisor'ı oluşturdum ve onu yalnızca 'yatırımcı' modu erişimi olan hesapta çalıştırmayı denedim. Sürprizim şu; onu eklemek mümkündü ve ayrıca, olası çözümlerin kapılarının bulunduğu Pozisyonlar, Talimatlar ve Yatırımlar hakkında bilgi almak mümkün oldu!


2. Neyi Takip Etmek Gerekiyor? - Pozisyonlar, Talimatlar veya Yatırımlar?

MetaTrader 5'ten MetaTrader 4'e bilgi aktarmak üzereysek, MetaTrader 4'te mümkün olan tüm talimat türlerini göz önünde bulundurmamız gerekir. Ayrıca takip ettiğimizde, alım satımla ilgili hesapta gerçekleştirilen her işlemi bilmek isteriz; bu nedenle her tick veya saniyede 'Pozisyonlar' durumunu karşılaştırmadıkça 'Pozisyonlar' bize tam bilgi vermez.

Dolayısıyla, 'Talimatlar' veya 'Yatırımlar'ı takip etmek daha iyi olacaktır.

Talimatlara bakmaya başladım:

Talimatlar

'Yatırım'dan önce yürütülmeleri ve ayrıca bekleyen (limit) talimatlar hakkında bilgi içermeleri hoşuma gitti, ancak 'Yatırımlar' ile karşılaştırıldığında önemli bir eksikleri var - giriş türü (ENUM_DEAL_ENTRY):

Yatırımlar

DEAL_ENTRY_TYPE, yatırımcıların hesabında neler olduğunu anlamaya yardımcı olurken, 'Talimatlar' paralel olarak hesaplama gerektirir. Yapılacak en iyi şey 'Yatırımlar' ile 'Talimatlar'ı birleştirmek olacaktır; o zaman bekleyen talimatlarımız olabilir ve ayrıca alım satım hesabındaki her işlemi takip edebiliriz. Farklı aracı şirketler arasında fiyat hareketleri farklılık gösterdiği için, bekleyen talimatlar aslında hatalara ve yanlış sonuçlara yol açabilir.

Yalnızca 'Yatırımlar'ı takip edersek, bekleyen talimatları yürütmeye devam edeceğiz, ancak küçük bir gecikmeyle (ağ bağlantısına bağlı olarak). Hız(bekleyen talimatlar) ve performans(yatırımlar) arasında performansa ('Yatırımlar') yönelmeyi tercih ettim.


3. 'Sinyaller' nasıl sağlanır?

MetaTrader 5'ten diğer uygulamalara ve bilgisayarlara nasıl iletişim kurulacağı ve verilerin bunlara nasıl aktarılacağı konusunda farklı makaleler ve tartışmalar vardır. Diğer istemcilerin bize bağlanabilmesini istediğim ve büyük olasılıkla başka bilgisayarlarda bulunacakları için, TCP yuvası bağlantısını seçiyorum.

MQL5, API işlevleriyle bunun yapılmasına izin vermediği için harici kitaplık kullanmamız gerekiyor. "WinInet.dll" kitaplığının dahil edilmesiyle ilgili çok sayıda makale var (örn. "İnternet Üzerinden Terminaller Arasında Veri Alışverişi için WinInet.dll'i Kullanmak" ve diğerleri) ancak hiçbiri gerçekten ihtiyaçlarımızı karşılamıyor.

C#'a biraz aşina olduğum için kendi kitaplığımı oluşturmaya karar verdim. Bunun için, uyumluluk sorunları konusunda bana yardımcı olması için "Yönetilmeyen dışa aktarmaları kullanarak C# kodunu MQL5'e gösterme" makalesinden yararlandım. Çok basit bir arayüze ve aynı anda 500'e kadar istemciyi kabul etme imkanına sahip bir sunucu oluşturdum (bilgisayarınızda .NET framework 3.5 veya üzeri olması gerekir. Bu, çoğu bilgisayarda zaten yüklü. "Microsoft .NET Framework 3.5").

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

Sunucunun kendisi arka planda ayrı ileti dizileri üzerinde çalışıyor ve kaç istemci bağlanırsa bağlansın MetaTrader 5'in veya stratejinizin çalışmasını engellemeyecek veya yavaşlatmayacak.

C# kaynak kodu: 

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

C#'da Zaman Uyumsuz Sunucu Yuvaları hakkında daha fazla bilgi edinmek için Microsoft MSDN makalesini veya Google üzerinden bulabileceğiniz makalelerden bazılarını okumanızı tavsiye ederim.


4. 'Sinyaller' nasıl toplanır?

MetaTrader 4'te, yalnızca yeni tick oluşturulduğunda değil, her zaman bilgi almak istiyoruz; bu nedenle Expert Advisor yerine bunun için 'Script Dosyası' oluşturuyoruz. Ayrıca, sinyal sağlayıcımız ile yuva bağlantısını açabilmemiz gerekiyor - MetaTrader 5.

Bunun için MQL4 kod tabanından yardım almayı seçtim: "https://www.mql5.com/tr/code/9296". Orada oldukça iyi bir içerme dosyası buldum (WinSock.mqh); bu da yuvalarla çok basit bir şekilde çalışmayı sağlıyor. Bazı kişiler tutarlılığı konusunda şikayetçi olsalar dahi, bunu amacım için yeterince iyi buldum ve testlerim sırasında herhangi bir sorunla karşılaşmadım.

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


5. Veri işleme

Artık bir kavramımız var ve tek yapmamız gereken yatırımların işlenip tek tek tüm istemcilere anlayabilecekleri ve yürütebilecekleri bir biçimde aktarıldığından emin olmak.

5.1. Sunucu tarafı

Açıklığa kavuşturduğumuz gibi, bu, Expert Advisor olacak ancak eklendiği para birimiyle ilgilenmiyor.

Başlatma sırasında, gelen bağlantıları bekleyecek olan ileti dizisini dinlemeye de başlayacaktır:

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

Bu sürümde, Expert Advisor bağlı istemcilerle ilgilenmeyecektir. Her alım satım işlemi olduğunda - hiç istemci olmasa dahi tüm istemcilere bildirim gönderecektir. Yalnızca alım satım işlemleri hakkında bilgi sahibi olmamız gerektiği için, OnTrade() işlevini kullanacağız ve OnTick() öğesini kaldıracağız. Bu işlevde, en son geçmişe bakarız ve bunun hakkında bilgi vermemiz gereken bir yatırım olup olmadığına karar veririz.

Bunu daha iyi anlamak için koddaki açıklamalarıma bakın:

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

Fark ettiğiniz gibi, yeni bir yatırım bulunduğunda, verileri aktarıma hazırlamak için BuildDealString() işlevini çağırırız. Tüm veriler metin biçiminde aktarılır ve her yatırım '<' ile başlar ve '>' ile biter.

Bu, TCP/IP protokolü nedeniyle o anda birden fazla yatırım almak mümkün olduğu için, birden fazla yatırımı ayırmamıza yardımcı olacaktır.

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

Kodlara bakarken, yeni DEAL_ENTRY türü - DEAL_ENTRY_OUTALL hakkında şaşkınlık yaşabilirsiniz. Bunu, ben oluşturdum ve MetaTrader 4 tarafında hacim işlemeyi anlatacağım zaman bunu daha iyi anlayacaksınız.

İlginç olabilecek bir şey daha OnTimer() işlevidir. Başlatma sırasında her saniye OnTimer() çağrısını almak için EventSetTimer(1)'ı çağırıyorum. İçinde bu işlev, sunucu kitaplığından bilgileri (günlükleri) yazdıran bir satırsa:

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

Durum ve hata bilgilerini yazdırmak için sunucu kitaplığından yürüttüğünüz her işlevden sonra bu işlevi (PrintLogs) çağırın.

Sunucu tarafında ayrıca bir StartupType giriş parametresi bulacaksınız:

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

Bu, sinyal sağlayıcının halihazırda açılmış pozisyonları (örn. şampiyonayı takip ediyorsa) olan hesaba eklenebileceği ve bu nedenle bunlarla ilgili bilgilerin istemci tarafında yanıltıcı olabileceği gerçeğine eklenmiştir. Bu parametre ile mevcut alım satım işlemlerinden mi yoksa yalnızca yeni açılan pozisyonlardan mı bilgi almak istediğinizi seçebilirsiniz.

Bu, hesaba ilk kez başvurmanız veya daha önce çalıştırdığınız hesap için yeniden başvurmanız durumunda ve bilgisayarınızı, programınızı yeniden başlatmanız veya kodunuzda bir değişiklik yapmanız halinde de önemlidir.


5.2. İstemci

İstemci tarafında, sonsuzluk (recv) için yuva alma işlevinde döngü yapan bir script dosyasına sahibiz. Bu işlev 'engelleme'de olduğu için, script dosyası sunucudan bir şey alınana kadar kilitlenir; bu nedenle işlemci süresi hakkında endişelenmeyin.

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

Bu, istemciyi durdurmak için bir soruna neden olur. "Script Dosyasını Kaldır"a tıkladığınızda, script dosyası kaldırılmayacaktır. Buna iki kez tıklamanız gerekiyor, ardından script dosyası zaman aşımına uğrayarak kaldırılacaktır. Alma işlevi için zaman aşımı uygulanabilirse bu, düzeltilebilir, ancak Kod Tabanı'nda halihazırda mevcut olan örneği kullandığım için bunu orijinal yazara bırakacağım. 

Veriler alındıktan sonra, yatırım gerçek hesapta işlenmeden önce bölme ve doğrulama yaparız:

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

Herkesin hesabında 10.000 USD olmadığı için, Lot boyutunun yeniden hesaplanması GetLotSize() işleviyle istemci tarafında yapılır. Sunucu tarafında çalışan strateji aynı zamanda para yönetimi de gerektirebilir ve bu nedenle aynısını istemci tarafında da yapmamız gerekiyor.

Size "Lot eşleme" sunuyorum - İstemci kullanıcısı lot boyutu tercihlerini (min. ve maks.) belirleyebilir ve ardından İstemci Script Dosyası sizin için eşlemeyi yapacaktır:

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

İstemci tarafı 4 ve 5 haneli aracıları destekler ve ayrıca 'sıradan-lot' (0,1) ve 'mini-lot' (0,01) desteğine sahiptir. Bu nedenle, yeni DEAL_ENTRY türü - DEAL_OUTALL oluşturmam gerekiyordu.

İstemci tarafı eşleme yaptığı için, küçük lot boyutu kapatılmadığında bazı durumlar olabilir.

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. MetaTrader 5 Pozisyonları ve MetaTrader 4 Talimatları

Uygulama sırasında başka bir sorun saptadım - MetaTrader 5'te her sembol için her zaman yalnızca bir pozisyon varken bu, MetaTrader 4'te tamamen farklı bir şekilde işleniyor. Aynı giriş ve sembole sahip her yeni yatırıma mümkün olduğunca yaklaşmak için MetaTrader 4 tarafında birden fazla talimat açarak ele alıyorum.

Her yeni 'IN' yatırımı yeni bir talimattır ve bir 'OUT' yatırımı olduğunda 3 adımlı kapanış gerçekleştiren bir işlevsellik uyguladım:

  1. Tüm açık talimatları gözden geçirin ve istenen boyutla eşleşeni kapatın, yoksa tüm
  2. açık talimatları gözden geçirin ve istenen OUT hacim boyutundan daha küçük olanları kapatın, hala bir şey kaldıysa, boyutu
  3. istenen boyuttan daha büyük olan talimatı kapatın ve boyutu kapatılmaması gereken yeni talimat açın. Normal durumlarda, üçüncü adım asla gerçekleştirilmemelidir. Koruma amaçlı oluşturulmuştur.
//+------------------------------------------------------------------+
//| 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);
   }
}

Sonuç

Burada oluşturulan ve eklenen dosyalar daha iyi istemci sunucu protokolü, daha akıllı iletişim ve daha iyi yürütme ile kesinlikle geliştirilebilir, ancak benim görevim bunun mümkün olup olmadığını doğrulamak ve herkesin özel ihtiyaçları için kullanabilmesi için onu kabul edilebilir kalitede oluşturmaktı.

Bu, MQL5 Şampiyonası'ndaki tüm katılımcıların stratejilerini ve kendi stratejilerinizi takip etmek için yeterince iyi çalışıyor. MQL4 ve MQL5'in sunduğu performans ve olanaklar, onu profesyonel ve ticari açıdan ele alacak kadar dahi iyidir. Yalnızca özel bilgisayarınızı ve kendi stratejinizi kullanarak tüm MetaTrader 4 ve MetaTrader 5 istemcileri için çok iyi bir sinyal sağlayıcı yapmanın mümkün olduğuna inanıyorum.

Burada sağladığım kodu geliştiren kişileri görmek ve görüş ve önerilerle geri dönmelerini istiyorum. Varsa sorularınızı da yanıtlamaya çalışacağım. Paralel olarak, en sevdiğim şampiyona katılımcılarını takip ettiğim bir test yapıyorum. Bu, bir haftadır iyi gidiyor. Herhangi bir sorun bulursam, size güncellemeleri temin edeceğim.

Tsaktuo

Açıklanan işlevleri ve yürütülebilir dosyaları gerçek hesabınıza uygulayarak, bunun neden olabileceği kayıp veya zararların tamamına ilişkin tüm sorumluluğu üstlendiğinizi lütfen unutmayın. YALNIZCA iyi testlerden sonra ve YALNIZCA burada sağlanan işlevsellik hakkında iyi bir kavrayışa sahip olduktan sonra gerçek hesapta alım satım işlemi yapın.


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

Ekli dosyalar |
Çoklu Regresyon Analizi. Strateji Oluşturucu ve Test Cihazı Bir Arada Çoklu Regresyon Analizi. Strateji Oluşturucu ve Test Cihazı Bir Arada
Makalede, alım satım sistemlerinin geliştirilmesi için çoklu regresyon analizinin kullanım yollarına ilişkin bir açıklama verilmiştir. Ayrıca, strateji arama otomasyonu için regresyon analizinin kullanımı gösterilmiştir. Programlamada yüksek yeterlilik gerektirmeden oluşturulan ve bir EA'ya entegre edilen bir regresyon denklemi örnek olarak verilmiştir.
MQL5-RPC. MQL5'ten Uzaktan Prosedür Çağrıları: Eğlence ve Kar için Web Hizmeti Erişimi ve XML-RPC ATC Çözümleyici MQL5-RPC. MQL5'ten Uzaktan Prosedür Çağrıları: Eğlence ve Kar için Web Hizmeti Erişimi ve XML-RPC ATC Çözümleyici
Bu makale, MQL5'ten Uzaktan Yordam Çağrılarını etkinleştiren MQL5-RPC çerçevesini açıklar. XML-RPC temelleri, MQL5 uygulaması ile başlar ve iki gerçek kullanım örneği ile devam eder. İlk örnek harici web servisini kullanmaktır ve ikincisi basit XML-RPC ATC 2011 Çözümleyici servisinin istemcisidir. ATC 2011'den farklı istatistikleri gerçek zamanlı olarak nasıl uygulayacağınızı ve analiz edeceğinizi merak ediyorsanız bu makale tam size göre.
Yeni Müşteriler İçin Birkaç İpucu Yeni Müşteriler İçin Birkaç İpucu
Genellikle çeşitli ünlü kişilere atfedilen meşhur bir söz vardır: "Hata yapmıyorsan, hiçbir şey yapmıyorsun demektir." Aylaklığın kendisinin bir hata olduğunu düşünmedikçe, bu ifadeye itiraz etmesi zor. Ancak gelecekteki hatalarınızın sayısını en aza indirmek için her zaman geçmişteki hatalarınızı (kendinizin ve başkalarının) analiz edebilirsiniz. Aynı ad hizmetinde işler yürütülürken ortaya çıkabilecek olası durumları gözden geçirmeye çalışacağız.
EA Ağacını kullanarak dakikalar içinde MQL5 Expert Advisor'lar oluşturma: Bölüm Bir EA Ağacını kullanarak dakikalar içinde MQL5 Expert Advisor'lar oluşturma: Bölüm Bir
EA Ağacı, ilk sürükle ve bırak MetaTrader MQL5 Expert Advisor oluşturucusudur. Kullanımı çok kolay bir grafik kullanıcı arayüzünü kullanarak karmaşık MQL5 oluşturabilirsiniz. EA Ağacı'nda, Expert Advisor'lar kutular birbirine bağlanarak oluşturulur. Kutular MQL5 işlevleri, teknik göstergeler, özel göstergeler veya değerler içerebilir. EA Ağacı, "kutu ağacını" kullanarak Expert Advisor'ın MQL5 kodunu oluşturur.