English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
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

MetaTrader 5Entegrasyon | 22 Aralık 2021, 14:10
215 0
investeo
investeo

Giriş

Bu makale, son haftalarda oluşturduğum MQL5-RPC çerçevesini açıklayacaktır. XML-RPC erişim temelleri, MQL5 uygulamasının açıklamasını ve iki gerçek dünya MQL5-RPC kullanım örneğini kapsar. Birincisi, harici bir forex web sitesinin web servisi üzerinde bir uzaktan prosedür çağrısı olacak ve ikincisi, Automated Trading Championship 2011'den toplanan sonuçları ayrıştırmak, analiz etmek ve sağlamak için kullanılan kendi XML-RPC sunucumuzun bir istemcisi olacak. 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.


XML-RPC temelleri

XML-RPC temelleri ile başlayalım. XML-RPC, XML Uzaktan Yordam Çağrısı anlamına gelir. Bu, harici bir yöntemi çağırmak için geçirilen parametreleri kodlamak ve kodunu çözmek için XML kullanan bir ağ protokolüdür. Veri alışverişi için aktarım mekanizması olarak HTTP protokolünü kullanır. Harici yöntemle, uzak prosedürleri ortaya çıkaran başka bir bilgisayar programını veya web hizmetini kastediyorum.

Açığa çıkan yöntem, XML-RPC protokol yığınını kullanması ve sunucuya ağ erişimi olması koşuluyla, ağa bağlı herhangi bir makineden herhangi bir bilgisayar dili tarafından çağrılabilir. Bu aynı zamanda XML-RPC'nin aynı makinede başka bir programlama dilinde yazılmış bir yöntemi çağırmak için kullanılabileceği anlamına gelir. Bu, makalenin ikinci bölümünde gösterilecektir.


XML-RPC veri modeli

XML-RPC belirtimi altı temel veri türü kullanır: int, double, boole değeri, dize, tarih saat, base64 ve iki bileşik veri türü: dizi ve yapı. Dizi herhangi bir temel öğeden oluşabilir ve yapı, ilişkisel diziler veya nesne özellikleri gibi ad-değer çiftleri sağlar.


XML-RPC'deki temel veri türleri
 Tür Değer Örnekler
int veya i42.147.483.648 ve 2.147.483.647 arasındaki 32 bit tamsayılar.<int>11<int>
<i4>12345<i4>
çift64 bit kayan noktalı sayılar <double>30.02354</double>
<double>-1.53525</double>
Boole değeridoğru (1) veya yanlış (0)<boolean>1</boolean>
<boolean>0</boolean>
dizeASCII metni, birçok uygulama Unicode'u destekler<string>Merhaba</string>
<string>MQL5</string>
dateTime.iso8601ISO8601 formatındaki tarihler: CCYYMMDDTHH:MM:SS<dateTime.iso8601>
20111125T02:20:04
</dateTime.iso8601>
<dateTime.iso8601>
20101104T17:27:30
</dateTime.iso8601>
base64RFC 2045'te tanımlandığı gibi kodlanmış ikili bilgi<base64>
TDVsbG8sIFdvdwxkIE==
</base64>


Tablo 1. XML-RPC'deki temel veri türleri

Dizi, aynı türden olması gerekmeyen temel türlerden herhangi birini tutabilir. Dizi öğesi, değer öğesinin içine yerleştirilmelidir. Veri öğesinde bir veri öğesi ve bir veya daha fazla değer öğesi içerir. Aşağıdaki örnek, dört tamsayı değerinden oluşan bir diziyi göstermektedir.

<value>
   <array>
      <data>
         <value><int>111</int></value>
         <value><int>222</int></value>
         <value><int>-3456</int></value>
         <value><int>666</int></value>
      </data>
   </array>
</value>

İkinci örnek, beş dize değerinden oluşan bir diziyi gösterir.

<value>
   <array>
      <data>
         <value><string>MQL5</string></value>
         <value><string>is </string></value>
         <value><string>a</string></value>
         <value><string>great</string></value>
         <value><string>language.</string></value>
      </data>
   </array>
</value>

Başka bir XML-RPC dizisi oluşturmak için bu iki örnekteki benzerlikleri tespit edebileceğinize inanıyorum.

Yapıların, değer öğesinin içinde bir yapı öğesi ve yapı öğesi içinde üye bölümleri vardır. Her üye kendi adından ve sahip olduğu değerden oluşur. Bu nedenle, yapılar kullanarak ilişkisel bir dizinin veya bir nesnenin üyelerinin değerlerini iletmek kolaydır.

Lütfen aşağıdaki örneğe bakın.

<value>
   <struct>
      <member>
         <name>AccountHolder</name>
         <value><string>John Doe</string></value>
      </member>
      <member>
         <name>Age</name>
         <value><int>77</int></value>
      </member>
      <member>
         <name>Equity</name>
         <value><double>1000000.0</double></value>
      </member>
   </struct>
</value>

XML-RPC veri modeli ile tanıştıktan sonra, istek ve yanıt yapılarına geçiyoruz. Bu, MQL5'de XML-RPC istemcisinin uygulanması için bir temel oluşturacaktır.


XML-RPC talep yapıları

XML-RPC isteği, mesaj başlığı ve mesaj yükünden oluşur. İleti başlığı, HTTP gönderme yöntemini (POST), XML-RPC hizmetine göreli yolu, HTTP protokol sürümünü, kullanıcı aracı adını, ana bilgisayar IP adresini, içerik türünü (metin/xml) ve bayt cinsinden içerik uzunluğunu belirtir. 

POST /xmlrpc HTTP 1.1
User-Agent: mql5-rpc/1.0
Host: 10.12.10.10
Content-Type: text/xml
Content-Length: 188

XML-RPC isteğinin yükü XML belgesidir. XML ağacının kök öğesi methodCall olarak adlandırılmalıdır. Bir methodCall, içeriği yöntem adı olarak yürütülen tek bir methodName öğesi içerir. MethodName öğesi, sıfır veya bir parametre öğesi içeriyor.

params öğesi bir veya daha fazla değer, dizi veya yapı öğesi içerir. Tüm değerler, veri türüne göre kodlanmıştır (yukarıdaki tabloya bakın). Lütfen, işleve iletilecek iki çift değerle "çarpma" yöntemi yürütme isteğini gösteren aşağıdaki örnek yüke bakın.

<?xml version="1.0"?>
<methodCall>
   <methodName>multiply</methodName>
      <params>
         <param>
            <value><double>8654.41</double></value>
         </param>
         <param>
            <value><double>7234.00</double></value>
         </param>
      </params>
</methodCall>

Başlık ve yük, girişi kabul eden sunucuya HTTP aracılığıyla gönderilir. Sunucu varsa yöntem adını ve parametre listesini kontrol eder ve istenen yöntemi yürütür. İşlemi bitirdikten sonra, istemci tarafından okunabilen XML-RPC yanıt yapısını hazırlar.

 

XML-RPC yanıt yapıları

XML-RPC Talebine benzer şekilde, XML-RPC Yanıtı bir başlık ve bir yükten oluşur. Başlık metindir ve yük XML belgesidir. İstek doğruysa başlığın ilk satırı sunucunun bulunduğunu (200 kod) bildirir ve protokol sürümünü belirtir. Başlık ayrıca, bayt cinsinden yükün uzunluğu olan Content-Type text/xml ve Content-Length içermelidir.

HTTP/1.1 200 OK
Date: Tue, 08 Nov 2011 23:00:01 GMT
Server: Unix
Connection: close
Content-Type: text/xml
Content-Length: 124

Şaşırtıcı olmayan bir şekilde yanıtın yükü de XML belgesidir. XML ağacının kök öğesi methodResponse olarak adlandırılmalıdır. methodResponse öğesi, başarı öğesinde bir paragraf veya başarısızlık durumunda bir hata öğesi içerir. Params öğesi tam olarak bir param öğesi içerir. Param öğesi tam olarak bir değer öğesi içerir.

Bir başarılı yanıt örneği aşağıda sunulmuştur: 

<?xml version="1.0"?>
<methodResponse>
   <params>
      <param>
         <value><double>62606001.94</double></value>
      </param>
   </params>
</methodResponse>

XML-RPC isteğinin işlenmesinde bir sorun olması durumunda hata yanıtı hazırlanır.

Arıza elemanı, params elemanı gibi, sadece tek bir çıkış değerine sahiptir.

<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value><string>No such method!</string></value>
   </fault>
</methodResponse>

XML-RPC hata kodlarını standartlaştırmadığından, hata mesajları uygulamaya bağlıdır.


MQL5-RPC ile tanışın

Alex Sergeev'in "İnternet Üzerinden Terminaller Arası Veri Alışverişi için WinInet.dll'yi Kullanımı" ve "MQL5'te WinInet'i Kullanma" başlıklı iki makalesine rastladım. Bölüm 2: POST İstekleri ve Dosyaları" başlıklı iki makalesine rastladım ve MetaTrader 5 için bir XML-RPC istemcisi uygulayabileceğimi fark ettim. Spesifikasyonları inceledikten sonra sıfırdan kendiminkini uyguladım. Bu devam eden bir projedir ve henüz tüm özellikleri kapsamamaktadır (yakın gelecekte base64 desteği eklenecektir), ancak bunu MetaTrader 5'ten büyük bir XML-RPC araması alt kümesi yapmak için zaten kullanabilirsiniz.


MQL5-RPC veri modeli

Benim için uygulamanın en zor kısmı MQL5 için doğru bir veri modeli bulmaktı. Çerçeve kullanıcısı için olabildiğince basit olması gerektiğine karar verdim, bu nedenle işlevselliğini içine alan birkaç sınıf oluşturdum. İlk karar, tek bir CObject* işaretçisi olarak bir istek parametresi verisi yapmaktı. Bu işaretçi, CObject sınıfından türetilen dizilere yönelik bir işaretçi dizisini tutar.

CObject dizilerini tutan standart sınıflar vardır; CARrayInt, CARrayDouble, CArrayString. Bu nedenle buna dayandım ve temel veri türlerini tamamlamak için CArrayBool, CArrayDatetime ve yapı dizisi eklemek için CArrayMqlRates uyguladım. Şimdilik sadece base64 türü eksik, ancak bu da yakın gelecekte desteklenecek. Dizi yalnızca bir öğe içeriyorsa XML'de tek bir değer öğesi olarak kapsüllenir.

CObject* dizisine farklı dizilerin nasıl ekleneceğine ve farklı türlerdeki dizilerin tamamının nasıl görüntüleneceğine dair bir örnek yazdım. Aşağıda mevcuttur. 

//+------------------------------------------------------------------+
//|                                                 ArrayObjTest.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
//---
#include <Arrays\ArrayObj.mqh>
#include <Arrays\ArrayInt.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
#include <Arrays\ArrayBool.mqh>
#include <Arrays\ArrayMqlRates.mqh>
#include <Arrays\ArrayDatetime.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CArrayObj* params = new CArrayObj;
   
   CArrayInt* arrInt = new CArrayInt;
   
   arrInt.Add(1001);
   arrInt.Add(1002);
   arrInt.Add(1003);
   arrInt.Add(1004);
   
   CArrayDouble* arrDouble = new CArrayDouble;
   
   arrDouble.Add(1001.0);
   arrDouble.Add(1002.0);
   arrDouble.Add(1003.0);
   arrDouble.Add(1004.0);
      
   CArrayString* arrString = new CArrayString;
   
   arrString.Add("s1001.0");
   arrString.Add("s1002.0");
   arrString.Add("s1003.0");
   arrString.Add("s1004.0");
   
   CArrayDatetime* arrDatetime = new CArrayDatetime;
   
   arrDatetime.Add(TimeCurrent());
   arrDatetime.Add(TimeTradeServer()+3600);
   arrDatetime.Add(TimeCurrent()+3600*24);
   arrDatetime.Add(TimeTradeServer()+3600*24*7);
   
   CArrayBool* arrBool = new CArrayBool;
   
   arrBool.Add(false);
   arrBool.Add(true);
   arrBool.Add(true);
   arrBool.Add(false);
   
   CArrayMqlRates* arrRates = new CArrayMqlRates;
   
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   int copied=CopyRates(Symbol(),0,0,4,rates);
   
   arrRates.Add(rates[0]);
   arrRates.Add(rates[1]);
   arrRates.Add(rates[2]);
   arrRates.Add(rates[3]);
   
   params.Add(arrInt);
   params.Add(arrDouble);
   params.Add(arrString);
   params.Add(arrDatetime);
   params.Add(arrBool);
   params.Add(arrRates);
   
   Print("params has " + IntegerToString(params.Total()) + " arrays.");
 
   for (int p=0; p<params.Total(); p++)
   {
      int type = params.At(p).Type();
      
      switch (type) {
         case TYPE_INT: { 
            CArrayInt *arr = params.At(p); 
            for (int i=0; i<arr.Total(); i++)
               PrintFormat("%d %d %d", p, i, arr.At(i));
            break; }
         case TYPE_DOUBLE: { 
            CArrayDouble *arr = params.At(p); 
            for (int i=0; i<arr.Total(); i++)
               PrintFormat("%d %d %f", p, i, arr.At(i));
            break; }
         case TYPE_STRING: { 
            CArrayString *arr = params.At(p); 
            for (int i=0; i<arr.Total(); i++)
               PrintFormat("%d %d %s", p, i, arr.At(i));
            break; }
         case TYPE_BOOL: { 
            CArrayBool *arr = params.At(p); 
            for (int i=0; i<arr.Total(); i++)
               if (arr.At(i) == true)
                  PrintFormat("%d %d true", p, i);
               else
                  PrintFormat("%d %d false", p, i);
            break; }
         case TYPE_DATETIME: { 
            CArrayDatetime *arr = params.At(p); 
            for (int i=0; i<arr.Total(); i++)
               PrintFormat("%d %d %s", p, i, TimeToString(arr.At(i), TIME_DATE|TIME_MINUTES));
            break; }
         case TYPE_MQLRATES: {  //  
            CArrayMqlRates *arr = params.At(p); 
            for (int i=0; i<arr.Total(); i++)
               PrintFormat("%d %d %f %f %f %f", p, i, arr.At(i).open, arr.At(i).high, arr.At(i).low, arr.At(i).close);
            break; }
      };
         
   };
   delete params;
   
  }
//+------------------------------------------------------------------+

Sonuç açık olmalıdır: 6 alt dizi vardır: tamsayı değerleri dizisi, çift değerler dizisi, dize dizisi, tarihsaat dizisi, boole değerleri dizisi ve MqlRates dizisi.

ArrayObjTest (EURUSD,H1)        23:01:54        params has 6 arrays.
ArrayObjTest (EURUSD,H1)        23:01:54        0 0 1001
ArrayObjTest (EURUSD,H1)        23:01:54        0 1 1002
ArrayObjTest (EURUSD,H1)        23:01:54        0 2 1003
ArrayObjTest (EURUSD,H1)        23:01:54        0 3 1004
ArrayObjTest (EURUSD,H1)        23:01:54        1 0 1001.000000
ArrayObjTest (EURUSD,H1)        23:01:54        1 1 1002.000000
ArrayObjTest (EURUSD,H1)        23:01:54        1 2 1003.000000
ArrayObjTest (EURUSD,H1)        23:01:54        1 3 1004.000000
ArrayObjTest (EURUSD,H1)        23:01:54        2 0 s1001.0
ArrayObjTest (EURUSD,H1)        23:01:54        2 1 s1002.0
ArrayObjTest (EURUSD,H1)        23:01:54        2 2 s1003.0
ArrayObjTest (EURUSD,H1)        23:01:54        2 3 s1004.0
ArrayObjTest (EURUSD,H1)        23:01:54        3 0 2011.11.11 23:00
ArrayObjTest (EURUSD,H1)        23:01:54        3 1 2011.11.12 00:01
ArrayObjTest (EURUSD,H1)        23:01:54        3 2 2011.11.12 23:00
ArrayObjTest (EURUSD,H1)        23:01:54        3 3 2011.11.18 23:01
ArrayObjTest (EURUSD,H1)        23:01:54        4 0 false
ArrayObjTest (EURUSD,H1)        23:01:54        4 1 true
ArrayObjTest (EURUSD,H1)        23:01:54        4 2 true
ArrayObjTest (EURUSD,H1)        23:01:54        4 3 false
ArrayObjTest (EURUSD,H1)        23:01:54        5 0 1.374980 1.374980 1.374730 1.374730
ArrayObjTest (EURUSD,H1)        23:01:54        5 1 1.375350 1.375580 1.373710 1.375030
ArrayObjTest (EURUSD,H1)        23:01:54        5 2 1.374680 1.375380 1.373660 1.375370
ArrayObjTest (EURUSD,H1)        23:01:54        5 3 1.375270 1.377530 1.374360 1.374690

Diğer veri türlerinin dizilerini nasıl uyguladığımla ilgilenebilirsiniz. CArrayBool ve CArrayDatetime'da ben sadece CarrayInt'i temel aldım ama CArrayMqlRates'de yapı referans olarak iletilmesi gerektiğinden ve TYPE_MQLRATES tanımlı olmadığından biraz farklıydı.

Aşağıda, CArrayMqlRates sınıfının kısmi kaynak kodunu bulabilirsiniz. Diğer sınıflar makalenin eki olarak mevcuttur. 

//+------------------------------------------------------------------+
//|                                                ArrayMqlRates.mqh |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//|                                              Revision 2011.03.03 |
//+------------------------------------------------------------------+
#include "Array.mqh"
//+------------------------------------------------------------------+
//| Class CArrayMqlRates.                                            |
//| Purpose: Class of dynamic array of structs                       |
//|          of MqlRates type.                                       |
//|          Derived from CArray class.                              |
//+------------------------------------------------------------------+
#define TYPE_MQLRATES 7654

class CArrayMqlRates : public CArray
  {
protected:
   MqlRates          m_data[];           // data array
public:
                     CArrayMqlRates();
                    ~CArrayMqlRates();
   //--- method of identifying the object
   virtual int       Type() const        { return(TYPE_MQLRATES); }
   //--- methods for working with files
   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
   //--- methods of managing dynamic memory
   bool              Reserve(int size);
   bool              Resize(int size);
   bool              Shutdown();
   //--- methods of filling the array
   bool              Add(MqlRates& element);
   bool              AddArray(const MqlRates &src[]);
   bool              AddArray(const CArrayMqlRates *src);
   bool              Insert(MqlRates& element,int pos);
   bool              InsertArray(const MqlRates &src[],int pos);
   bool              InsertArray(const CArrayMqlRates *src,int pos);
   bool              AssignArray(const MqlRates &src[]);
   bool              AssignArray(const CArrayMqlRates *src);
   //--- method of access to the array
   MqlRates          At(int index) const;
   //--- methods of changing
   bool              Update(int index,MqlRates& element);
   bool              Shift(int index,int shift);
   //--- methods of deleting
   bool              Delete(int index);
   bool              DeleteRange(int from,int to);
protected:
   int               MemMove(int dest,int src,int count);
  };
//+------------------------------------------------------------------+
//| Constructor CArrayMqlRates.                                      |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CArrayMqlRates::CArrayMqlRates()
  {
//--- initialize protected data
   m_data_max=ArraySize(m_data);
  }
//+------------------------------------------------------------------+
//| Destructor CArrayMqlRates.                                       |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CArrayMqlRates::~CArrayMqlRates()
  {
   if(m_data_max!=0) Shutdown();
  }
...

//+------------------------------------------------------------------+
//| Adding an element to the end of the array.                       |
//| INPUT:  element - variable to be added.                          |
//| OUTPUT: true if successful, false if not.                        |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CArrayMqlRates::Add(MqlRates& element)
  {
//--- checking/reserve elements of array
   if(!Reserve(1)) return(false);
//--- adding
   m_data[m_data_total++]=element;
   m_sort_mode=-1;
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array.    |
//| INPUT:  src - source array.                                      |
//| OUTPUT: true if successful, false if not.                        |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CArrayMqlRates::AddArray(const MqlRates &src[])
  {
   int num=ArraySize(src);
//--- checking/reserving elements of array
   if(!Reserve(num)) return(false);
//--- adding
   for(int i=0;i<num;i++) m_data[m_data_total++]=src[i];
   m_sort_mode=-1;
//---
   return(true);
  }

...

Tüm MQL5 verileri, RPC isteği olarak gönderilmeden önce XML değerlerine dönüştürülmelidir, bu nedenle bir değer alan ve onu XML dizesi olarak kodlayan bir CXMLRPCEncoder yardımcı sınıfı tasarladım.

class CXMLRPCEncoder
  {
    public:
                    CXMLRPCEncoder(){};
   string            header(string path,int contentLength);
   string            fromInt(int param);
   string            fromDouble(double param);
   string            fromBool(bool param);
   string            fromString(string param);
   string            fromDateTime(datetime param);
   string            fromMqlRates(MqlRates &param);
  };

Uygulanan yöntemlerden üçünü aşağıya yapıştırdım. Hepsi bir parametre (bool, dize, tarihsaat) alır ve XML-RPC protokolü için XML geçerli veri tipi olan dizeyi döndürür.

//+------------------------------------------------------------------+
//| fromBool                                                         |
//+------------------------------------------------------------------+
string CXMLRPCEncoder::fromBool(bool param)
  {
   CString s_bool;
   s_bool.Clear();
   s_bool.Append(VALUE_B);
   s_bool.Append(BOOL_B);
   if(param==true)
      s_bool.Append("1");
   else s_bool.Append("0");
   s_bool.Append(BOOL_E);
   s_bool.Append(VALUE_E);

   return s_bool.Str();
  }
//+------------------------------------------------------------------+
//| fromString                                                       |
//+------------------------------------------------------------------+
string CXMLRPCEncoder::fromString(string param)
  {
   CString s_string;
   s_string.Clear();
   s_string.Append(VALUE_B);
   s_string.Append(STRING_B);
   s_string.Append(param);
   s_string.Append(STRING_E);
   s_string.Append(VALUE_E);

   return s_string.Str();
  }
//+------------------------------------------------------------------+
//| fromDateTime                                                     |
//+------------------------------------------------------------------+
string CXMLRPCEncoder::fromDateTime(datetime param)
  {
   CString s_datetime;
   s_datetime.Clear();
   s_datetime.Append(VALUE_B);
   s_datetime.Append(DATETIME_B);
   CString s_iso8601;
   s_iso8601.Assign(TimeToString(param, TIME_DATE|TIME_MINUTES));
   s_iso8601.Replace(" ", "T");
   s_iso8601.Remove(":");
   s_iso8601.Remove(".");
   s_datetime.Append(s_iso8601.Str());
   s_datetime.Append(DATETIME_E);
   s_datetime.Append(VALUE_E);

   return s_datetime.Str();
  }

'Etiket başlangıcı' anlamına gelen _B son ekine ve 'etiket sonu' anlamına gelen _E son ekine sahip bazı etiketler olduğunu fark edebilirsiniz.

Uygulamayı çok daha şeffaf hale getirdiğinden, XML etiketlerinin ve başlıklarının adlarını tutan bir başlık dosyası kullanmaya karar verdim

//+------------------------------------------------------------------+
//|                                                   xmlrpctags.mqh |
//|                                      Copyright 2011, Investeo.pl |
//|                                           http://www.investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://www.investeo.pl"

#define HEADER_1a      "POST"
#define HEADER_1b      "HTTP/1.1"
#define HEADER_2       "User-Agent: MQL5RPC/1.1"
#define HEADER_3       "Host: host.com"
#define HEADER_4       "Content-Type: text/xml"
#define HEADER_5       "Content-Length: "
#define HEADER_6       "<?xml version='1.0'?>"
#define METHOD_B       "<methodCall>"
#define METHOD_E       "</methodCall>"
#define METHOD_NAME_B  "<methodName>"
#define METHOD_NAME_E  "</methodName>"
#define RESPONSE_B     "<methodResponse>"
#define RESPONSE_E     "</methodResponse>"
#define PARAMS_B       "<params>"
#define PARAMS_E       "</params>"
#define PARAM_B        "<param>"
#define PARAM_E        "</param>"
#define VALUE_B        "<value>"
#define VALUE_E        "</value>"
#define INT_B          "<int>"
#define INT_E          "</int>"
#define I4_B           "<i4>"
#define I4_E           "</i4>"
#define BOOL_B         "<boolean>"
#define BOOL_E         "</boolean>"
#define DOUBLE_B       "<double>"
#define DOUBLE_E       "</double>"
#define STRING_B       "<string>"
#define STRING_E       "</string>"
#define DATETIME_B     "<dateTime.iso8601>"
#define DATETIME_E     "</dateTime.iso8601>"
#define BASE64_B       "<base64>"
#define BASE64_E       "</base64>"
#define ARRAY_B        "<array>"
#define ARRAY_E        "</array>"
#define DATA_B         "<data>"
#define DATA_E         "</data>"
#define STRUCT_B       "<struct>"
#define STRUCT_E       "</struct>"
#define MEMBER_B       "<member>"
#define MEMBER_E       "</member>"
#define NAME_B         "<name>"
#define NAME_E         "</name>"
//+------------------------------------------------------------------+

MQL5-RPC'nin veri modelini tanımladıktan sonra, tam bir XML-RPC talebi oluşturmaya başlayabiliriz.


MQL5-RPC talebi

Daha önce belirtildiği gibi, XML-RPC isteği, bir istek başlığından ve XML yükünden oluşur. MQL5 veri dizilerinden otomatik olarak bir sorgu nesnesi oluşturan CXMLRPCQuery sınıfını tasarladım. Sınıf, verileri XML'de kapsüllemek için CXMLRPCEncoder'ı kullanır ve methodName etiketinin içine yöntem adını ekler.

class CXMLRPCQuery
  {
private:
   CString           s_query;
   void              addValueElement(bool start,bool array);
public:
                    CXMLRPCQuery() {};
                    CXMLRPCQuery(string method="",CArrayObj *param_array=NULL);
   string            toString();
  };

Sınıfın yapıcısının iki parametresi vardır: yöntem adı ve yöntemi çağırmak için CArrayObj parametresini tutan işaretçi. Tüm parametreler, önceki bölümde açıklandığı gibi XML'e sarılır ve bir sorgu başlığı eklenir. Tüm XML sorgusu, toString() yöntemi kullanılarak görüntülenebilir.

CXMLRPCQuery::CXMLRPCQuery(string method="",CArrayObj *param_array=NULL)
  {
//--- constructs a single XMLRPC Query
   this.s_query.Clear();

   CXMLRPCEncoder encoder;
   this.s_query.Append(HEADER_6);
   this.s_query.Append(METHOD_B);
   this.s_query.Append(METHOD_NAME_B);
   this.s_query.Append(method);
   this.s_query.Append(METHOD_NAME_E);
   this.s_query.Append(PARAMS_B);

   for(int i=0; i<param_array.Total(); i++)
     {
      int j=0;
      this.s_query.Append(PARAM_B);

      int type=param_array.At(i).Type();
      int elements=0;

      switch(type)
        {
         case TYPE_INT:
           {
            CArrayInt *arr=param_array.At(i);
            elements=arr.Total();
            if(elements==1) addValueElement(true,false); else addValueElement(true,true);
            for(j=0; j<elements; j++) this.s_query.Append(encoder.fromInt(arr.At(j)));
            break;
           }
         case TYPE_DOUBLE:
           {
            CArrayDouble *arr=param_array.At(i);
            elements=arr.Total();
            if(elements==1) addValueElement(true,false); else addValueElement(true,true);
            for(j=0; j<elements; j++) this.s_query.Append(encoder.fromDouble(arr.At(j)));
            break;
           }
         case TYPE_STRING:
           {
            CArrayString *arr=param_array.At(i);
            elements=arr.Total();
            if(elements==1) addValueElement(true,false); else addValueElement(true,true);
            for(j=0; j<elements; j++) this.s_query.Append(encoder.fromString(arr.At(j)));
            break;
           }
         case TYPE_BOOL:
           {
            CArrayBool *arr=param_array.At(i);
            elements=arr.Total();
            if(elements==1) addValueElement(true,false); else addValueElement(true,true);
            for(j=0; j<elements; j++) this.s_query.Append(encoder.fromBool(arr.At(j)));
            break;
           }
         case TYPE_DATETIME:
           {
            CArrayDatetime *arr=param_array.At(i);
            elements=arr.Total();
            if(elements==1) addValueElement(true,false); else addValueElement(true,true);
            for(j=0; j<elements; j++) this.s_query.Append(encoder.fromDateTime(arr.At(j)));
            break;
           }
         case TYPE_MQLRATES:
           {
            CArrayMqlRates *arr=param_array.At(i);
            elements=arr.Total();
            if(elements==1) addValueElement(true,false); else addValueElement(true,true);
            for(j=0; j<elements; j++)
              {
               MqlRates tmp=arr.At(j);
               this.s_query.Append(encoder.fromMqlRates(tmp));
              }
            break;
           }
        };

      if(elements==1) addValueElement(false,false); else addValueElement(false,true);

      this.s_query.Append(PARAM_E);
     }

   this.s_query.Append(PARAMS_E);
   this.s_query.Append(METHOD_E);
  }

Lütfen aşağıdaki sorgu testi örneğine bakın. Bu en basiti değildir, çünkü oldukça karmaşık yöntemler çağırmanın mümkün olduğunu göstermek istiyorum.

Girdi parametreleri şunlardır: çift değerler dizisi, tamsayı değerleri dizisi, string değerleri dizisi, bool değerleri dizisi, tek bir tarihsaat değeri ve MqlRates yapıları dizisi.

//+------------------------------------------------------------------+
//|                                          MQL5-RPC_query_test.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                           http://www.investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://www.investeo.pl"
#property version   "1.00"

#include <MQL5-RPC.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Arrays\ArrayInt.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
#include <Arrays\ArrayBool.mqh>
#include <Arrays\ArrayDatetime.mqh>
#include <Arrays\ArrayMqlRates.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- query test
   CArrayObj* params = new CArrayObj;
   
   CArrayDouble*   param1 = new CArrayDouble;
   CArrayInt*      param2 = new CArrayInt;
   CArrayString*   param3 = new CArrayString;
   CArrayBool*     param4 = new CArrayBool;
   CArrayDatetime* param5 = new CArrayDatetime;
   CArrayMqlRates* param6 = new CArrayMqlRates;
   
   for (int i=0; i<4; i++)
      param1.Add(20000.0 + i*100.0);
   
   params.Add(param1);
   
   for (int i=0; i<4; i++)
      param2.Add(i);
      
   params.Add(param2);
   
   param3.Add("first_string");
   param3.Add("second_string");
   param3.Add("third_string");
   
   params.Add(param3);
   
   param4.Add(false);
   param4.Add(true);
   param4.Add(false);
   
   params.Add(param4);
   
   param5.Add(TimeCurrent());
   params.Add(param5);
   
   const int nRates = 3;
   MqlRates rates[3];
   ArraySetAsSeries(rates,true);
   int copied=CopyRates(Symbol(),0,0,nRates,rates);
   if (copied==nRates) {
      param6.AddArray(rates);
      params.Add(param6);
   }
        
   CXMLRPCQuery query("sampleMethodname", params);
   
   Print(query.toString());
   
   delete params;
  }
//+------------------------------------------------------------------+

Ortaya çıkan sorgu aşağıdaki gibidir. Normalde bu tek satırlık bir dize değeridir, ancak XML-RPC bölümlerini ve tek değerleri ayırt etmeyi kolaylaştırmak için sekmeli bir metin olarak sunuyorum.

<?xml version="1.0" ?>
<methodCall>
      <methodName>sampleMethodname</methodName>
      <params>
            <param>
                  <value>
                        <array>
                              <data>
                                    <value>
                                          <double>20000.0000000000000000</double>
                                    </value>
                                    <value>
                                          <double>20100.0000000000000000</double>
                                    </value>
                                    <value>
                                          <double>20200.0000000000000000</double>
                                    </value>
                                    <value>
                                          <double>20300.0000000000000000</double>
                                    </value>
                              </data>
                        </array>
                  </value>
            </param>
            <param>
                  <value>
                        <array>
                              <data>
                                    <value>
                                          <int>0</int>
                                    </value>
                                    <value>
                                          <int>1</int>
                                    </value>
                                    <value>
                                          <int>2</int>
                                    </value>
                                    <value>
                                          <int>3</int>
                                    </value>
                              </data>
                        </array>
                  </value>
            </param>
            <param>
                  <value>
                        <array>
                              <data>
                                    <value>
                                          <string>first_string</string>
                                    </value>
                                    <value>
                                          <string>second_string</string>
                                    </value>
                                    <value>
                                          <string>third_string</string>
                                    </value>
                              </data>
                        </array>
                  </value>
            </param>
            <param>
                  <value>
                        <array>
                              <data>
                                    <value>
                                          <boolean>0</boolean>
                                    </value>
                                    <value>
                                          <boolean>1</boolean>
                                    </value>
                                    <value>
                                          <boolean>0</boolean>
                                    </value>
                              </data>
                        </array>
                  </value>
            </param>
            <param>
                  <value>
                        <dateTime.iso8601>20111111T2042</dateTime.iso8601>
                  </value>
            </param>
            <param>
                  <value>
                        <array>
                              <data>
                                    <value>
                                          <struct>
                                                <member>
                                                      <name>open</name>
                                                      <value>
                                                            <double>1.02902000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>high</name>
                                                      <value>
                                                            <double>1.03032000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>low</name>
                                                      <value>
                                                            <double>1.02842000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>close</name>
                                                      <value>
                                                            <double>1.02867000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>time</name>
                                                      <value>
                                                            <dateTime.iso8601>
                                                                  <value>
                                                                        <dateTime.iso8601>20111111T1800</dateTime.iso8601>
                                                                  </value>
                                                            </dateTime.iso8601>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>tick_volume</name>
                                                      <value>
                                                            <double>4154.00000000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>real_volume</name>
                                                      <value>
                                                            <double>0.00000000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>spread</name>
                                                      <value>
                                                            <double>30.00000000</double>
                                                      </value>
                                                </member>
                                          </struct>
                                    </value>
                                    <value>
                                          <struct>
                                                <member>
                                                      <name>open</name>
                                                      <value>
                                                            <double>1.02865000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>high</name>
                                                      <value>
                                                            <double>1.02936000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>low</name>
                                                      <value>
                                                            <double>1.02719000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>close</name>
                                                      <value>
                                                            <double>1.02755000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>time</name>
                                                      <value>
                                                            <dateTime.iso8601>
                                                                  <value>
                                                                        <dateTime.iso8601>20111111T1900</dateTime.iso8601>
                                                                  </value>
                                                            </dateTime.iso8601>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>tick_volume</name>
                                                      <value>
                                                            <double>3415.00000000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>real_volume</name>
                                                      <value>
                                                            <double>0.00000000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>spread</name>
                                                      <value>
                                                            <double>30.00000000</double>
                                                      </value>
                                                </member>
                                          </struct>
                                    </value>
                                    <value>
                                          <struct>
                                                <member>
                                                      <name>open</name>
                                                      <value>
                                                            <double>1.02760000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>high</name>
                                                      <value>
                                                            <double>1.02901000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>low</name>
                                                      <value>
                                                            <double>1.02756000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>close</name>
                                                      <value>
                                                            <double>1.02861000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>time</name>
                                                      <value>
                                                            <dateTime.iso8601>
                                                                  <value>
                                                                        <dateTime.iso8601>20111111T2000</dateTime.iso8601>
                                                                  </value>
                                                            </dateTime.iso8601>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>tick_volume</name>
                                                      <value>
                                                            <double>1845.00000000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>real_volume</name>
                                                      <value>
                                                            <double>0.00000000</double>
                                                      </value>
                                                </member>
                                                <member>
                                                      <name>spread</name>
                                                      <value>
                                                            <double>30.00000000</double>
                                                      </value>
                                                </member>
                                          </struct>
                                    </value>
                              </data>
                        </array>
                  </value>
            </param>
      </params>
</methodCall>

Bu oldukça gelişmiş bir XML ağacıdır ve protokol daha da karmaşık yöntemleri çağırmak için esnektir.


MQL5-RPC yanıtı

XML-RPC isteğinde olduğu gibi, XML-RPC yanıtı başlıklardan ve XML yükünden oluşturulur, ancak bu sefer işlem sırası tersine çevrilmelidir, yani XML yanıtı MQL5 verilerine dönüştürülmelidir. CXMLRPCResult sınıfı bu görevle başa çıkmak için tasarlanmıştır.

Sonuç, sınıf yapıcısına iletilen bir dize olarak alınabilir veya istenen yöntem yürütüldükten sonra CXMLServerProxy sınıfından otomatik olarak alınabilir. Sonucun önceden bilinmeyen yapılar içerdiği durumlarda parseXMLResponseRAW() yöntemi tüm <value> etiketlerini arayabilir ve bulunan tüm değer öğelerinin dizisini içeren CARrayObj işaretçisini döndürür.

class CXMLRPCResult
  {
private:
   CArrayObj        *m_resultsArr;

   CString           m_cstrResponse;
   CArrayString      m_params;

   bool              isValidXMLResponse();
   bool              parseXMLValuesToMQLArray(CArrayString *subArr,CString &val);
   bool              parseXMLValuesToMQLArray(CArrayDouble *subArr,CString &val);
   bool              parseXMLValuesToMQLArray(CArrayInt *subArr,CString &val);
   bool              parseXMLValuesToMQLArray(CArrayBool *subArr,CString &val);
   bool              parseXMLValuesToMQLArray(CArrayDatetime *subArr,CString &val);
   bool              parseXMLValuesToMQLArray(CArrayMqlRates *subArr,CString &val);
   bool              parseXMLResponse();

public:
                     CXMLRPCResult() {};
                    ~CXMLRPCResult();
                     CXMLRPCResult(string resultXml);
   
   CArrayObj        *getResults();
   bool              parseXMLResponseRAW();
   string            toString();
  };

Sınıfın kurucusu, yanıt XML'indeki her üstbilgiyi tarar ve sahne arkasında XML'i MQL5 verilerine dönüştürmenin zor işini yapan parseXMLValuesToMQLArray() özel yöntemini çağırır.

Paramın bir dizi mi yoksa tek bir öğe mi olduğunu tanır ve CArrayObj dizi sonucuna eklenen uygun dizileri doldurur.

bool CXMLRPCResult::parseXMLResponse()
  {
   CArrayObj *results=new CArrayObj;

   m_params.Clear();

   //--- find params and put them in m_params array
   int tagStartIdx= 0;
   int tagStopIdx = 0;
   while((tagStartIdx!=-1) && (tagStopIdx!=-1))
     {

      tagStartIdx= m_cstrResponse.Find(tagStartIdx,PARAM_B);
      tagStopIdx = m_cstrResponse.Find(tagStopIdx,PARAM_E);

      if((tagStartIdx!=-1) && (tagStopIdx!=-1))
        {
         m_params.Add(m_cstrResponse.Mid(tagStartIdx+StringLen(PARAM_B),tagStopIdx-tagStartIdx-StringLen(PARAM_B)));
         tagStartIdx++; tagStopIdx++;
        };

     };

   for(int i=0; i<m_params.Total(); i++)
     {
      CString val;
      val.Assign(m_params.At(i));

      //--- parse value tag

      val.Assign(val.Mid(StringLen(VALUE_B),val.Len()-StringLen(VALUE_B)-StringLen(VALUE_E)));

      //--- now check first tag and handle it approprietaly

      string param_type=val.Mid(0,val.Find(0,">")+1);

      if(param_type==INT_B || param_type==I4_B)
        {
         val.Assign(m_params.At(i));
         CArrayInt *subArr=new CArrayInt;
         bool isValid=parseXMLValuesToMQLArray(subArr,val);
         if(isValid==true)
            results.Add(subArr);
        }
      else if(param_type==BOOL_B)
        {
         val.Assign(m_params.At(i));
         CArrayBool *subArr=new CArrayBool;
         bool isValid=parseXMLValuesToMQLArray(subArr,val);
         if(isValid==true)
            results.Add(subArr);
        }
      else if(param_type==DOUBLE_B)
        {
         val.Assign(m_params.At(i));
         CArrayDouble *subArr=new CArrayDouble;
         bool isValid=parseXMLValuesToMQLArray(subArr,val);
         if(isValid==true)
            results.Add(subArr);
        }
      else if(param_type==STRING_B)
        {
         val.Assign(m_params.At(i));
         CArrayString *subArr=new CArrayString;
         bool isValid=parseXMLValuesToMQLArray(subArr,val);
         if(isValid==true)
            results.Add(subArr);
        }
      else if(param_type==DATETIME_B)
        {
         val.Assign(m_params.At(i));
         CArrayDatetime *subArr=new CArrayDatetime;
         bool isValid=parseXMLValuesToMQLArray(subArr,val);
         if(isValid==true)
            results.Add(subArr);
        }
      else if(param_type==ARRAY_B)
        {
         val.Assign(val.Mid(StringLen(ARRAY_B)+StringLen(DATA_B),val.Len()-StringLen(ARRAY_B)-StringLen(DATA_E)));
         //--- find first type and define array
         string array_type=val.Mid(StringLen(VALUE_B),val.Find(StringLen(VALUE_B)+1,">")-StringLen(VALUE_B)+1);

         if(array_type==INT_B || array_type==I4_B)
           {
            CArrayInt *subArr=new CArrayInt;
            bool isValid=parseXMLValuesToMQLArray(subArr,val);
            if(isValid==true)
               results.Add(subArr);
           }
         else if(array_type==BOOL_B)
           {
            CArrayBool *subArr=new CArrayBool;
            bool isValid=parseXMLValuesToMQLArray(subArr,val);
            if(isValid==true)
               results.Add(subArr);
           }
         else if(array_type==DOUBLE_B)
           {
            CArrayDouble *subArr=new CArrayDouble;
            bool isValid=parseXMLValuesToMQLArray(subArr,val);
            if(isValid==true)
               results.Add(subArr);
           }
         else if(array_type==STRING_B)
           {
            CArrayString *subArr=new CArrayString;
            bool isValid=parseXMLValuesToMQLArray(subArr,val);
            if(isValid==true)
               results.Add(subArr);

           }
         else if(array_type==DATETIME_B)
           {
            CArrayDatetime *subArr=new CArrayDatetime;
            bool isValid=parseXMLValuesToMQLArray(subArr,val);
            if(isValid==true)
               results.Add(subArr);
           }
        }
     };

   m_resultsArr=results;

   return true;
  }

Dönüştürme, aşırı yüklenmiş parseXMLValuesToMQLArray yöntemleri içinde gerçekleşir. Dize değeri, XML'den çıkarılır ve temel MQL5 değişkenlerine dönüştürülür.

Dönüştürme için kullanılan yöntemlerden üçü referans için aşağıya yapıştırılmıştır.

//+------------------------------------------------------------------+
//| parseXMLValuesToMQLArray                                         |
//+------------------------------------------------------------------+
bool CXMLRPCResult::parseXMLValuesToMQLArray(CArrayBool *subArr,CString &val)
  {
   //--- parse XML values and populate MQL array
   int tagStartIdx=0; int tagStopIdx=0;

   while((tagStartIdx!=-1) && (tagStopIdx!=-1))
     {
      tagStartIdx= val.Find(tagStartIdx,VALUE_B);
      tagStopIdx = val.Find(tagStopIdx,VALUE_E);
      if((tagStartIdx!=-1) && (tagStopIdx!=-1))
        {
         CString e;
         e.Assign(val.Mid(tagStartIdx+StringLen(VALUE_B)+StringLen(BOOL_B),
                  tagStopIdx-tagStartIdx-StringLen(VALUE_B)-StringLen(BOOL_B)-StringLen(BOOL_E)));
         if(e.Str()=="0")
            subArr.Add(false);
         if(e.Str()=="1")
            subArr.Add(true);

         tagStartIdx++; tagStopIdx++;
        };
     }
   if(subArr.Total()<1) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| parseXMLValuesToMQLArray                                         |
//+------------------------------------------------------------------+
bool CXMLRPCResult::parseXMLValuesToMQLArray(CArrayInt *subArr,CString &val)
  {
    //--- parse XML values and populate MQL array
   int tagStartIdx=0; int tagStopIdx=0;

   while((tagStartIdx!=-1) && (tagStopIdx!=-1))
     {
      tagStartIdx= val.Find(tagStartIdx,VALUE_B);
      tagStopIdx = val.Find(tagStopIdx,VALUE_E);
      if((tagStartIdx!=-1) && (tagStopIdx!=-1))
        {
         CString e;
         e.Assign(val.Mid(tagStartIdx+StringLen(VALUE_B)+StringLen(INT_B),
                  tagStopIdx-tagStartIdx-StringLen(VALUE_B)-StringLen(INT_B)-StringLen(INT_E)));
         subArr.Add((int)StringToInteger(e.Str()));
         tagStartIdx++; tagStopIdx++;
        };
     }
   if(subArr.Total()<1) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| parseXMLValuesToMQLArray                                         |
//+------------------------------------------------------------------+
bool CXMLRPCResult::parseXMLValuesToMQLArray(CArrayDatetime *subArr,CString &val)
  {
   // parse XML values and populate MQL array
   int tagStartIdx=0; int tagStopIdx=0;

   while((tagStartIdx!=-1) && (tagStopIdx!=-1))
     {
      tagStartIdx= val.Find(tagStartIdx,VALUE_B);
      tagStopIdx = val.Find(tagStopIdx,VALUE_E);
      if((tagStartIdx!=-1) && (tagStopIdx!=-1))
        {
         CString e;
         e.Assign(val.Mid(tagStartIdx+StringLen(VALUE_B)+StringLen(DATETIME_B),
                  tagStopIdx-tagStartIdx-StringLen(VALUE_B)-StringLen(DATETIME_B)-StringLen(DATETIME_E)));
         e.Replace("T"," "); e.Insert(4,"."); e.Insert(7,".");
         subArr.Add(StringToTime(e.Str()));
         tagStartIdx++; tagStopIdx++;
        };
     }
   if(subArr.Total()<1) return false;
   return true;
  }

Gördüğünüz gibi, CString sınıfında bulunan yöntemler kullanılarak XML-RPC etiketlerinden dize değerleri çıkarılır; Assign(), Mid(), Find() ve ayrıca bool değişkenlerinde olduğu gibi StringToTime(), StringToInteger(), StringToDouble() veya özel bir yöntem kullanılarak MQL5'e dönüştürülür.

Tüm değerler ayrıştırıldıktan sonra, mevcut tüm veriler toString() yöntemi kullanılarak görüntülenebilir. Değerleri ayırmak için iki nokta üst üste kullanıyorum.

string CXMLRPCResult::toString(void) 
  {
   // returns results array of arrays as a string
   CString r;

   for(int i=0; i<m_resultsArr.Total(); i++) 
     {
      int rtype=m_resultsArr.At(i).Type();
      switch(rtype) 
        {
         case(TYPE_STRING) : 
           {
            CArrayString *subArr=m_resultsArr.At(i);
            for(int j=0; j<subArr.Total(); j++) 
              {
               r.Append(subArr.At(j)+":");
              }
            break;
           };
         case(TYPE_DOUBLE) : 
           {
            CArrayDouble *subArr=m_resultsArr.At(i);
            for(int j=0; j<subArr.Total(); j++) 
              {
               r.Append(DoubleToString(NormalizeDouble(subArr.At(j),8))+":");
              }
            break;
           };
         case(TYPE_INT) : 
           {
            CArrayInt *subArr=m_resultsArr.At(i);
            for(int j=0; j<subArr.Total(); j++) 
              {
               r.Append(IntegerToString(subArr.At(j))+":");
              }
            break;
           };
         case(TYPE_BOOL) : 
           {
            CArrayBool *subArr=m_resultsArr.At(i);
            for(int j=0; j<subArr.Total(); j++) 
              {
               if(subArr.At(j)==false) r.Append("false:");
               else r.Append("true:");
              }
            break;
           };
         case(TYPE_DATETIME) : 
           {
            CArrayDatetime *subArr=m_resultsArr.At(i);
            for(int j=0; j<subArr.Total(); j++) 
              {
               r.Append(TimeToString(subArr.At(j),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+" : ");
              }
            break;
           };
        };
     }

   return r.Str();
  }

XML-RPC sonuç parametrelerinin MQL5'e nasıl dönüştürüldüğünü açıkça görmeniz için bir sonuç testi uyguladım.

//+------------------------------------------------------------------+
//|                                         MQL5-RPC_result_test.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                           http://www.investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://www.investeo.pl"
#property version   "1.00"
//---
#include <MQL5-RPC.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Arrays\ArrayInt.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
#include <Arrays\ArrayBool.mqh>
#include <Arrays\ArrayDatetime.mqh>
#include <Arrays\ArrayMqlRates.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
    string sampleXMLResult = "<?xml version='1.0'?>"
                            "<methodResponse>" +
                            "<params>" +
                            "<param>" +
                            "<value><array><data>" +
                            "<value><string>Xupypr</string></value>" +
                            "<value><string>anuta</string></value>" +
                            "<value><string>plencing</string></value>" +
                            "<value><string>aharata</string></value>" +
                            "<value><string>beast</string></value>" +
                            "<value><string>west100</string></value>" +
                            "<value><string>ias</string></value>" +
                            "<value><string>Tim</string></value>" +
                            "<value><string>gery18</string></value>" +
                            "<value><string>ronnielee</string></value>" +
                            "<value><string>investeo</string></value>" +
                            "<value><string>droslan</string></value>" +
                            "<value><string>Better</string></value>" +
                            "</data></array></value>" +
                            "</param>" + 
                            "<param>" +
                            "<value><array><data>" +
                            "<value><double>1.222</double></value>" +
                            "<value><double>0.456</double></value>" +
                            "<value><double>1000000000.10101</double></value>" +
                            "</data></array></value>" +
                            "</param>" + 
                            "<param>" +
                            "<value><array><data>" +
                            "<value><boolean>1</boolean></value>" +
                            "<value><boolean>0</boolean></value>" +
                            "<value><boolean>1</boolean></value>" +
                            "</data></array></value>" +
                            "</param>" + 
                            "<param>" +
                            "<value><array><data>" +
                            "<value><int>-1</int></value>" +
                            "<value><int>0</int></value>" +
                            "<value><int>1</int></value>" +
                            "</data></array></value>" +
                            "</param>" + 
                            "<param>" +
                            "<value><array><data>" +
                            "<value><dateTime.iso8601>20021125T02:20:04</dateTime.iso8601></value>" +
                            "<value><dateTime.iso8601>20111115T00:00:00</dateTime.iso8601></value>" +
                            "<value><dateTime.iso8601>20121221T00:00:00</dateTime.iso8601></value>" +
                            "</data></array></value>" +
                            "</param>" + 
                            "<param><value><string>Single string value</string></value></param>" +
                            "<param><value><dateTime.iso8601>20111115T00:00:00</dateTime.iso8601></value></param>" +
                            "<param><value><int>-111</int></value></param>" +
                            "<param><value><boolean>1</boolean></value></param>" +
                            "</params>" +
                            "</methodResponse>";

   CXMLRPCResult* testResult = new CXMLRPCResult(sampleXMLResult);
      
   Print(testResult.toString());

   delete testResult;
  }
//+------------------------------------------------------------------+

Sonucu aşağıda bulabilirsiniz:

MQL5-RPC__result_test (EURUSD,H1)       23:16:57        
Xupypr:anuta:plencing:aharata:beast:west100:ias:Tim:gery18:ronnielee:investeo:
droslan:Better:1.22200000:0.45600000:1000000000.10099995:true:false:true:-1:0:
1:2002.11.25 02:20:04 : 2011.11.15 00:00:00 : 2012.12.21 00:00:00 :
Single string value:2011.11.15 00:00:00 :-111:true:

Bu, XML'den dönüştürülmüş farklı MQL5 değerlerinin görüntülenen dizileri dizisidir. Gördüğünüz gibi tek bir CArrayObj işaretçisinden erişilebilir dizeler, çift değerleri, boole değerleri, tamsayı değeri, tarih saat değerleri


MQL5-RPC proxy sınıfı

CXMLRPCServer proxy, HTTP iletişimi için bir çekirdek sınıftır. HTTP işlevselliğini uygulamak için "MQL5'te WinInet'i kullanma. Bölüm 2: POST İstekleri ve Dosyaları" makalesini kullandım ve XML-RPC belirtimi ile tutarlı özel başlık eklendi.

CXMLRPCServerProxy::CXMLRPCServerProxy(string s_proxy,int timeout=0)
  {
   CString proxy;
   proxy.Assign(s_proxy);
   //--- find query path
   int sIdx = proxy.Find(0,"/");
   if (sIdx == -1)  
      m_query_path = "/";
   else  {
       m_query_path = proxy.Mid(sIdx, StringLen(s_proxy) - sIdx) + "/";
       s_proxy = proxy.Mid(0, sIdx);
    };
   //--- find query port. 80 is default
   int query_port = 80;
   int pIdx = proxy.Find(0,":");
   if (pIdx != -1) {
      query_port = (int)StringToInteger(proxy.Mid(pIdx+1, sIdx-pIdx));
      s_proxy = proxy.Mid(0, pIdx);
   };
   //Print(query_port);
   //Print(proxy.Mid(pIdx+1, sIdx-pIdx));
   if(InternetAttemptConnect(0)!=0)
     {
      this.m_connectionStatus="InternetAttemptConnect failed.";
      this.m_session=-1;
      this.m_isConnected=false;
      return;
     }
   string agent = "Mozilla";
   string empty = "";

   this.m_session=InternetOpenW(agent,OPEN_TYPE_PRECONFIG,empty,empty,0);

   if(this.m_session<=0)
     {
      this.m_connectionStatus="InternetOpenW failed.";
      this.m_session=-2;
      this.m_isConnected=true;
      return;
     }
   this.m_connection=InternetConnectW(this.m_session,s_proxy,query_port,empty,empty,SERVICE_HTTP,0,0);
   if(this.m_connection<=0)
     {
      this.m_connectionStatus="InternetConnectW failed.";
      return;
     }
   this.m_connectionStatus="Connected.";

  }

XML-RPC çağrısını tetiklemek için CXMLRPCQuery nesnesi CXMLRPCServerProxy execute() yöntemine geçirilmelidir.

Yöntemler, XML-RPC çağrısı olarak adlandırılan komut dosyasında daha fazla kullanılabilecek CXMLRPCResult nesnesine işaretçi döndürür.

CXMLRPCResult *CXMLRPCServerProxy::execute(CXMLRPCQuery &query)
  {
   
   //--- creating descriptor of the request
   string empty_string = "";
   string query_string = query.toString();
   string query_method = HEADER_1a;
   string http_version = HEADER_1b;
   
   uchar data[];
   
   StringToCharArray(query.toString(),data);
   
   int ivar=0;
   int hRequest=HttpOpenRequestW(this.m_connection,query_method,m_query_path,http_version,
                                 empty_string,0,FLAG_KEEP_CONNECTION|FLAG_RELOAD|FLAG_PRAGMA_NOCACHE,0);
   if(hRequest<=0)
     {
      Print("-Err OpenRequest");
      InternetCloseHandle(this.m_connection);
      return(new CXMLRPCResult);
     }
   //-- sending the request
   CXMLRPCEncoder encoder;
   string header=encoder.header(m_query_path,ArraySize(data));

   int aH=HttpAddRequestHeadersW(hRequest,header,StringLen(header),HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE);
   bool hSend=HttpSendRequestW(hRequest,empty_string,0,data,ArraySize(data)-1);
   if(hSend!=true)
     {
      int err=0;
      err=GetLastError();
      Print("-Err SendRequest= ",err);
     }
   string res;

   ReadPage(hRequest,res,false);
   CString out;
   out.Assign(res);
   out.Remove("\n");
   
   //--- closing all handles
   InternetCloseHandle(hRequest); InternetCloseHandle(hSend);
   CXMLRPCResult* result = new CXMLRPCResult(out.Str());
   return result;
  }


Örnek 1 - Web Hizmeti erişimi

MQL5-RPC'nin çalışan ilk örneği, harici web hizmetine yapılan bir çağrıdır. Bulduğum örnek, bir para biriminin belirli bir miktarını başka bir para birimine dönüştürmek için geçerli döviz kurunu kullanıyor. Yöntem parametrelerinin tam belirtimi çevrimiçi olarak mevcuttur.

Web hizmeti, iki dize ve bir kayan değer olmak üzere üç parametreyi kabul eden foxrate.currencyConvert yöntemini sunar:

  • para biriminden (ör: USD) = dize;
  • para birimine (ör: GBP) = dize;
  • dönüştürülecek miktar (örneğin: 100.0) = kayan nokta.

Uygulama oldukça kısadır ve sadece birkaç satır sürer.

//+------------------------------------------------------------------+
//|                              MQL5-RPC_ex1_ExternalWebService.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                           http://www.investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://www.investeo.pl"
#property version   "1.00"
//---
#include <MQL5-RPC.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //--- web service test
   CArrayObj* params = new CArrayObj;
   
   CArrayString* from   = new CArrayString;
   CArrayString* to     = new CArrayString;
   CArrayDouble* amount = new CArrayDouble;
   
   from.Add("GBP"); to.Add("USD"); amount.Add(10000.0);
   params.Add(from); params.Add(to); params.Add(amount);
   
   CXMLRPCQuery query("foxrate.currencyConvert", params);
   
   Print(query.toString());
   
   CXMLRPCServerProxy s("foxrate.org/rpc");
   CXMLRPCResult* result;
   
   result = s.execute(query);
   
   result.parseXMLResponseRAW();
   Print(result.toString());
     
   delete params;
   delete result;
  }
//+------------------------------------------------------------------+

Yöntem karmaşık yapı döndürdüğü için sonucu parseXMLResponseRAW() yöntemini kullanarak ayrıştırdım.

Bu örnekteki XML-RPC sorgusu aşağıdaki gibidir:

<?xml version="1.0" ?>
<methodCall>
      <methodName>foxrate.currencyConvert</methodName>
      <params>
            <param>
                  <value>
                        <string>GBP</string>
                  </value>
            </param>
            <param>
                  <value>
                        <string>USD</string>
                  </value>
           </param>
           <param>
                  <value>
                        <double>10000.00000000</double>
                  </value>
           </param>
      </params>
</methodCall>

Yanıt, XML'e sarılmış bir yapı olarak döndürülür.

<?xml version="1.0" ?>
<methodResponse>
      <params>
            <param>
                  <value>
                        <struct>
                              <member>
                                    <name>flerror</name>
                                    <value>
                                          <int>0</int>
                                    </value>
                              </member>
                              <member>
                                    <name>amount</name>
                                    <value>
                                          <double>15773</double>
                                    </value>
                              </member>
                              <member>
                                    <name>message</name>
                                    <value>
                                          <string>cached</string>
                                    </value>
                              </member>
                        </struct>
                  </value>
            </param>
      </params>
</methodResponse>

toString() çıktısı, sonuçta üç değerin mevcut olduğunu gösterir: 0 - hata yok, ikinci değer değişim için gereken temel para birimi miktarı ve üçüncü parametre en son döviz kurunun zamanı (önbelleğe alındı).

0:15773.00000000:cached:

Daha ilginç kullanım durumuyla devam edelim.


Örnek 2 - XML-RPC ATC 2011 Çözümleyici

Automated Trading Championship 2011'den istatistik almak istediğinizi (yaptım) ve bu bilgiyi MetaTrader 5 terminalinde kullanmak istediğinizi hayal edin. Bunu nasıl başaracağınızı bilmek istiyorsanız okumaya devam edin.

MetaQuotes Software Corp., belirli Expert Advisor'lardan gelen sinyallerin abone olmasını sağlayacak bir Sinyal takip hizmeti hazırladığından, bu hizmetin çözümleyiciyle birleşiminin, karmaşık bir analiz gerçekleştirmenin ve ATC'den yararlanmanın yeni yollarını sağlamanın çok güçlü bir yolu olacağını düşünüyorum. Aslında bu yöntemi kullanarak birkaç farklı kaynaktan veri alabilir ve buna dayalı karmaşık bir Expert Advisor yapabilirsiniz.


XML-RPC ATC 2011 Çözümleyici sunucusu

Bir ATC Çözümleyici sunucusu oluşturmanın ilk adımı, çıktı analizi hazırlamaktır. Örneğin hesapları belirli bir eşiğin üzerinde olan ve şu anda sahip oldukları pozisyonlarla ilgilenen tüm katılımcıları almak ve ilgilendiğimiz döviz çiftinin alım satım pozisyonu istatistiklerini görüntülemek istiyoruz.

Verileri almak ve ayrıştırmak için Python dilini ve BeautifulSoup kitaplığını kullandım. Python'u daha önce hiç kullanmadıysanız tarayıcınızı http:/Python.org adresine yönlendirmenizi ve bu dilin ne anlama geldiğini okumanızı şiddetle tavsiye ederim, pişman olmayacaksınız.

Elinizi çabuk bir şekilde çözümleyiciye koymak isterseniz download Python 2.7.1 installer ve setup_tools paketlerinin her ikisine de kurun. Bunun ardından Windows konsolunu çalıştırın ve dizini C:\Python27\Scripts olarak veya Python'u yüklediğiniz klasör olarak değiştirin. Ardından 'easy_install BeautifulSoup' komutunu verin. Bu işlem BeautifulSoup paketini kuracaktır:

C:\Python27\Scripts>easy_install BeautifulSoup
Searching for BeautifulSoup
Reading http://pypi.python.org/simple/BeautifulSoup/
Reading http://www.crummy.com/software/BeautifulSoup/
Reading http://www.crummy.com/software/BeautifulSoup/download/
Best match: BeautifulSoup 3.2.0
Downloading http://www.crummy.com/software/BeautifulSoup/download/3.x/BeautifulS
oup-3.2.0.tar.gz
Processing BeautifulSoup-3.2.0.tar.gz
Running BeautifulSoup-3.2.0\setup.py -q bdist_egg --dist-dir c:\users\przemek\ap
pdata\local\temp\easy_install-zc1s2v\BeautifulSoup-3.2.0\egg-dist-tmp-hnpwoo
zip_safe flag not set; analyzing archive contents...
C:\Python27\lib\site-packages\setuptools\command\bdist_egg.py:422: UnicodeWarnin
g: Unicode equal comparison failed to convert both arguments to Unicode - interp
reting them as being unequal
  symbols = dict.fromkeys(iter_symbols(code))
Adding beautifulsoup 3.2.0 to easy-install.pth file

Installed c:\python27\lib\site-packages\beautifulsoup-3.2.0-py2.7.egg
Processing dependencies for BeautifulSoup
Finished processing dependencies for BeautifulSoup

C:\Python27\Scripts>

Bundan sonra Python konsolunu çalıştırabilmeli ve "BeautifulSoup'u içe aktar" komutunu hatasız verebilmelisiniz.

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import BeautifulSoup
>>> 

Ayrıca ekli analizörü "python ContestantParser.py" komutuyla çalıştırın. Analizör web sitesini getirir, bazı anahtar kelime araması yapar ve çıktıyı konsola koyar.

import re
import urllib
from BeautifulSoup import BeautifulSoup

class ContestantParser(object):
    URL_PREFIX = 'https://championship.mql5.com/2011/en/users/'
    
    def __init__(self, name):
        print "User:", name
        url = ContestantParser.URL_PREFIX + name
        feed = urllib.urlopen(url)
        s = BeautifulSoup(feed.read())
        account = s.findAll('td', attrs={'class' : 'alignRight'})
        self.balance = float(account[0].contents[0].replace(' ', ''))
        self.equity = float(account[2].contents[0].replace(' ', ''))
        terminals = s.findAll('table', attrs={'class': 'terminal'})
        terminal = terminals[0]
        trs = terminal.findAll('tr')
        pairs = terminal.findAll('span', attrs={'class':'stateText'})
        self.stats = {}
        for i in range(len(pairs)-1):
            if len(pairs[i].string)> 6:
                break
            self.stats[str(pairs[i].string)] = str(trs[i+1].contents[7].string)

    def __str__(self):
        for k in self.stats.keys():
            print k, self.stats[k] 
        
        return "Bal " + str(self.balance) + " Equ " +  str(self.equity)
    
    def position(self, pair):
        if pair in self.stats.keys():
            return self.stats[pair]
        else:
            return "none"
        
class ContestantsFinder(object):
    URL_PREFIX = "https://championship.mql5.com/2011/en/users/index/page"
    URL_SUFFIX = "?orderby=equity&dir=down"
    
    def __init__(self, min_equity):
        self.min_equity = min_equity        
        self.user_list = []
        self.__find()
        
    def __find(self):
        isLastPage = False
        pageCnt = 1
        while isLastPage == False:
            url = ContestantsFinder.URL_PREFIX + str(pageCnt) + \
                  ContestantsFinder.URL_SUFFIX
            feed = urllib.urlopen(url)
            s = BeautifulSoup(feed.read())
            urows = s.findAll('tr', attrs={'class' : re.compile('row.*')})
            for row in urows:
                user_name = row.contents[5].a.string
                equity = float(row.contents[19].string.replace(' ',''))
                if equity <= self.min_equity:
                    isLastPage = True
                    break
                self.user_list.append(str(user_name))
                print user_name, equity
            pageCnt += 1
    
        
    def list(self):
        return self.user_list
    
    
 
if __name__ == "__main__":
        
    # find all contestants with equity larger than threshold
    contestants = ContestantsFinder(20000.0)
    print contestants.list()
    # display statistics
    print "* Statistics *"
    for contestant in contestants.list():
        u = ContestantParser(contestant)    
        print u
        print u.position('eurusd')
        print '-' * 60
    

Bu, yalnızca HTML kaynak kodunun içine bakarak ve etiketler arasındaki ilişkileri bularak sağlandı. Bu kod doğrudan Python konsolundan çalıştırılırsa yarışmacıların adlarını, bakiyelerini, özkaynaklarını ve mevcut tüm açık pozisyonları verir.

Lütfen örnek bir sorgunun çıktısını gözlemleyin:

* Statistics *
User: Tim
Bal 31459.2 Equ 31459.2
none
------------------------------------------------------------
User: enivid
eurusd sell
euraud sell
Bal 26179.98 Equ 29779.89
sell
------------------------------------------------------------
User: ias
eurjpy sell
usdchf sell
gbpusd buy
eurgbp buy
eurchf sell
audusd buy
gbpjpy sell
usdjpy buy
usdcad buy
euraud buy
Bal 15670.0 Equ 29345.66
none
------------------------------------------------------------
User: plencing
eurusd buy
Bal 30233.2 Equ 29273.2
buy
------------------------------------------------------------
User: anuta
audusd buy
usdcad sell
gbpusd buy
Bal 28329.85 Equ 28359.05
none
------------------------------------------------------------
User: gery18
Bal 27846.7 Equ 27846.7
none
------------------------------------------------------------
User: tornhill
Bal 27402.4 Equ 27402.4
none
------------------------------------------------------------
User: rikko
eurusd sell
Bal 25574.8 Equ 26852.8
sell
------------------------------------------------------------
User: west100
eurusd buy
Bal 27980.5 Equ 26255.5
buy
------------------------------------------------------------
...

İyi görünüyor, değil mi? Bu verilerle ilginç istatistikler toplayabilir ve bunları talep üzerine sağlayacak bir XML-RPC hizmeti uygulayabiliriz. MetaTrader 5 adresindeki XMLRPC istemcisi, gerektiğinde bu istatistikleri getirebilir.

Bir XMLRPC sunucusu yapmak için python'un SimpleXMLRPCserver kitaplığını kullandım. Sunucu, dış dünyaya iki yöntem sunar: listContestants ve getStats. İlki yalnızca belirli eşiğin üzerinde özsermayeye sahip yarışmacı isimlerini görüntülerken, ikincisi belirli bir döviz çiftinde kaç tanesinin açık pozisyonu olduğunu ve bu pozisyonlarda alım/satım oranının ne olduğunu gösterir.

Makaledeki sinyal hizmeti ve/veya alım satım kopyalayıcı ile birlikte, gerçekten alım satım yaptığınız kurulumun karlı olma şansının daha yüksek olduğunu doğrulamak isteyebilirsiniz.

from SimpleXMLRPCServer import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler

from PositionGrabberTest import ContestantParser, ContestantsFinder

class RequestHandler( SimpleXMLRPCRequestHandler ):
    rpc_path = ( '/RPC2', )
    
class ATC2011XMLRPCServer(SimpleXMLRPCServer):

    def __init__(self):
        SimpleXMLRPCServer.__init__( self, ("192.168.235.168", 6666), requestHandler=RequestHandler, logRequests = False)
        
        self.register_introspection_functions()
        self.register_function( self.xmlrpc_contestants, "listContestants" )        
        self.register_function( self.xmlrpc_get_position_stats, "getStats" )        
        
    
    def xmlrpc_contestants(self, min_equity):
        try:
            min_equity = float(min_equity)
        except ValueError as error:
            print error
            return []
    
        self.contestants = ContestantsFinder(min_equity)
        
        return self.contestants.list()
    
    def xmlrpc_get_position_stats(self, pair):
        total_users = len(self.contestants.list())
        holding_pair = 0
        buy_positions = 0
        
        for username in self.contestants.list():
            u = ContestantParser(username)
            position = u.position(pair)
            if position != "none":
                holding_pair += 1
                if position == "buy":
                    buy_positions += 1
        
        return [ total_users, holding_pair, buy_positions ]            
    

if __name__ == '__main__':
    server = ATC2011XMLRPCServer() 
    server.serve_forever()        

Bu sunucuya doğrudan Python konsolundan erişilebilir. Bu script dosyasını çalıştırırken, IP adresini sisteminizin kullandığı ile değiştirmeyi unutmayın.

C:\Program Files\MetaTrader 5\MQL5>python
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> from xmlrpclib import ServerProxy
>>> u = ServerProxy('http://192.168.235.168:6666')
>>> u.listContestants(20000.0)
['lf8749', 'ias', 'aharata', 'Xupypr', 'beast', 'Tim', 'tmt0086', 'yyy999', 'bob
sley', 'Diubakin', 'Pirat', 'notused', 'AAA777', 'ronnielee', 'samclider-2010',
'gery18', 'p96900', 'Integer', 'GradyLee', 'skarkalakos', 'zioliberato', 'kgo',
'enivid', 'Loky', 'Gans-deGlucker', 'zephyrrr', 'InvestProm', 'QuarkSpark', 'ld7
3', 'rho2011', 'tornhill', 'botaxuper']
>>>

Muhtemelen bunu okurken yarışmacıların sonuçları tamamen değişti ama burada neyi başarmaya çalıştığımı anlamalısınız.

"eurusd" parametresiyle getStats() yöntemini çağırmak üç sayı döndürür:

In : u.getStats("eurusd")
Out: [23, 12, 9]

Birincisi eşik üzerinde hisseye sahip yarışmacı sayısı, ikincisi EURUSD açık pozisyonuna sahip yarışmacı sayısı ve üçüncüsü uzun eurusd pozisyonuna sahip yarışmacı sayısı. Bu durumda, kazanan yarışmacıların üçte ikisi uzun EURUSD açmışlardır, bu nedenle robotunuz veya göstergeleriniz 'açık eurusd uzun' sinyali ürettiğinde bu bir onay sinyali işlevi görebilir.


MQL5-RPC ATC 2011 Çözümleyici istemcisi

MetaTrader 5'te bu verileri talep üzerine getirmek için MQL5-RPC çerçevesini kullanmanın zamanı geldi. Aslında kaynak kodunu izlerseniz kullanımı açıklayıcıdır.

//+------------------------------------------------------------------+
//|                           MQL5-RPC_ex2_ATC2011AnalyzerClient.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                           http://www.investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://www.investeo.pl"
#property version   "1.00"

#include <MQL5-RPC.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Arrays\ArrayInt.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //--- ATC 2011 analyzer test
   CXMLRPCServerProxy s("192.168.235.168:6666");
   CXMLRPCResult* result;
   
   //--- Get list of contestants
   CArrayObj* params1 = new CArrayObj;
   CArrayDouble* amount = new CArrayDouble;
   
   amount.Add(20000.0); params1.Add(amount);
   CXMLRPCQuery query("listContestants", params1);
   
   Print(query.toString());
   
   result = s.execute(query);
   Print(result.toString());
   delete result;

   //--- Get position statistics
   CArrayObj* params2 = new CArrayObj;
   CArrayString* pair = new CArrayString;
   
   pair.Add("eurusd"); params2.Add(pair);
   CXMLRPCQuery query2("getStats", params2);
   
   Print(query2.toString());
   
   result = s.execute(query2);
   
   CArrayObj* resultArray = result.getResults();
   CArrayInt* stats = resultArray.At(0);
   
   Print("Contestants = " + IntegerToString(stats.At(0)) + 
         ". EURUSD positions = " + IntegerToString(stats.At(1)) +
         ". BUY positions = " + IntegerToString(stats.At(2)));
         
   delete params1;
   delete params2;
   
   delete result;
  }
//+------------------------------------------------------------------+

Komut dosyası çağrıldığında olan budur. Sorgu XML'e sarılır:

<?xml version="1.0" ?>
<methodCall>
      <methodName>listContestants</methodName>
      <params>
            <param>
                  <value>
                        <double>20000.00000000</double>
                  </value>
            </param>
      </params>
</methodCall>

ve bir süre sonra XML-RPC sunucusundan yanıt alınır. Benim durumumda, python XML-RPC hizmetini çalıştıran ve MetaTrader 5 çalıştıran Windows VirtualBox konuk kurulumundan çağıran Linux ana bilgisayarını kullandım.

<?xml version="1.0" ?>
<methodResponse>
      <params>
            <param>
                  <value>
                        <array>
                              <data>
                                    <value>
                                          <string>lf8749</string>
                                    </value>
                                    <value>
                                          <string>ias</string>
                                    </value>
                                    <value>
                                          <string>Xupypr</string>
                                    </value>
                                    <value>
                                          <string>aharata</string>
                                    </value>
                                    <value>
                                          <string>beast</string>
                                    </value>
                                    <value>
                                          <string>Tim</string>
                                    </value>
                                    <value>
                                          <string>tmt0086</string>
                                    </value>
                                    <value>
                                          <string>yyy999</string>
                                    </value>
                                    <value>
                                          <string>bobsley</string>
                                    </value>
                                    <value>
                                          <string>Diubakin</string>
                                    </value>
                                    <value>
                                          <string>Pirat</string>
                                    </value>
                                    <value>
                                          <string>AAA777</string>
                                    </value>
                                    <value>
                                          <string>notused</string>
                                    </value>
                                    <value>
                                          <string>ronnielee</string>
                                    </value>
                                    <value>
                                          <string>samclider-2010</string>
                                    </value>
                                    <value>
                                          <string>gery18</string>
                                    </value>
                                    <value>
                                          <string>Integer</string>
                                    </value>
                                    <value>
                                          <string>GradyLee</string>
                                    </value>
                                    <value>
                                          <string>p96900</string>
                                    </value>
                                    <value>
                                          <string>skarkalakos</string>
                                    </value>
                                    <value>
                                          <string>Loky</string>
                                    </value>
                                    <value>
                                          <string>zephyrrr</string>
                                    </value>
                                    <value>
                                          <string>Medvedev</string>
                                    </value>
                                    <value>
                                          <string>Gans-deGlucker</string>
                                    </value>
                                    <value>
                                          <string>InvestProm</string>
                                    </value>
                                    <value>
                                          <string>zioliberato</string>
                                    </value>
                                    <value>
                                          <string>QuarkSpark</string>
                                    </value>
                                    <value>
                                          <string>rho2011</string>
                                    </value>
                                    <value>
                                          <string>ld73</string>
                                    </value>
                                    <value>
                                          <string>enivid</string>
                                    </value>
                                    <value>
                                          <string>botaxuper</string>
                                    </value>
                              </data>
                        </array>
                  </value>
            </param>
      </params>
</methodResponse>

toString() yöntemi kullanılarak görüntülenen ilk sorgunun sonucu aşağıdaki gibidir:

lf8749:ias:Xupypr:aharata:beast:Tim:tmt0086:yyy999:bobsley:Diubakin:Pirat:
AAA777:notused:ronnielee:samclider-2010:gery18:Integer:GradyLee:p96900:
skarkalakos:Loky:zephyrrr:Medvedev:Gans-deGlucker:InvestProm:zioliberato:
QuarkSpark:rho2011:ld73:enivid:botaxuper:

İkinci sorgu getStats() yöntemini çağırmak için kullanılır.

<?xml version="1.0" ?>
<methodCall>
      <methodName>getStats</methodName>
      <params>
            <param>
                  <value>
                        <string>eurusd</string>
                  </value>
            </param>
      </params>
</methodCall>

XML-RPC yanıtı basittir, yalnızca üç tamsayı değeri içerir.

<?xml version="1.0" ?>
<methodResponse>
      <params>
            <param>
                  <value>
                        <array>
                              <data>
                                    <value>
                                          <int>31</int>
                                    </value>
                                    <value>
                                          <int>10</int>
                                    </value>
                                    <value>
                                          <int>3</int>
                                    </value>
                              </data>
                        </array>
                  </value>
            </param>
      </params>
</methodResponse>

Bu sefer başka bir yaklaşım benimsedim ve döndürülen değerlere MQL5 değişkenleri olarak eriştim.

MQL5-RPC_ex2_ATC2011AnalyzerClient (AUDUSD,H1)  12:39:23

lf8749:ias:Xupypr:aharata:beast:Tim:tmt0086:yyy999:bobsley:Diubakin:Pirat:
AAA777:notused:ronnielee:samclider-2010:gery18:Integer:GradyLee:p96900:
skarkalakos:Loky:zephyrrr:Medvedev:Gans-deGlucker:InvestProm:zioliberato:
QuarkSpark:rho2011:ld73:enivid:botaxuper:

MQL5-RPC_ex2_ATC2011AnalyzerClient (AUDUSD,H1)  12:39:29        

Contestants = 31. EURUSD positions = 10. BUY positions = 3

Gördüğünüz gibi çıktı kolayca anlaşılabilir bir biçimde.


Sonuç

MetaTrader 5'in XML-RPC protokolünü kullanarak uzaktan prosedür çağrıları yürütmesini sağlayan yeni bir MQL5-RPC çerçevesi sundum. İlki bir web hizmetine erişim ve ikincisi Automated Trading Championship 2011 için özel bir çözümleyici olmak üzere iki kullanım örneği sunuldu. . Bu iki örnek, daha sonraki deneyler için bir temel teşkil etmelidir.

MQL5-RPC'nin birçok farklı şekilde kullanılabilen çok güçlü bir özellik olduğuna inanıyorum. Çerçeveyi Açık Kaynak yapmaya karar verdim ve onu GPL licence altında http://code.google.com/p/mql5-rpc/. adresinde yayınladım. Herhangi biri kodu kurşun geçirmez hale getirmek, yeniden düzenlemek veya hata düzeltmeleri yapmak için yardım etmek isterse, projeye katılması için kapılar açıktır. Tüm örneklerle birlikte kaynak kodu makalenin ekinde de mevcuttur.


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

Ekli dosyalar |
mql5-rpc.zip (22.18 KB)
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
Alım satım analizinin MetaTrader 5 platformunda nasıl gerçekleştirilebileceğinin, ancak MetaTrader 4 tarafından yürütülebileceğinin analizi ve tekniklerin örnekleri. Makalede size MetaTrader 5'inizde basit sinyal sağlayıcı oluşturma ve hatta MetaTrader 4 çalışırken dahi birden fazla istemciyle ona nasıl bağlanacağınız gösterilecektir. Ayrıca gerçek MetaTrader 4 hesabınızda Otomatik Alım Satım Şampiyonası katılımcılarını nasıl takip edebileceğinizi de öğreneceksiniz.
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.
Ç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.
Ya Hep Ya Hiç Forex Stratejisi Ya Hep Ya Hiç Forex Stratejisi
Bu makalenin amacı, "Ya Hep Ya Hiç" oyun prensibini uygulayan en basit alım satım stratejisini oluşturmaktır. Karlı bir Expert Advisor oluşturmak istemiyoruz; amacımız, mümkün olan en yüksek olasılıkla ilk mevduatı birkaç kez artırmaktır. ForEx'te köşeyi dönmek veya teknik analiz hakkında hiçbir şey bilmeden ve herhangi bir gösterge kullanmadan her şeyi kaybetmek mümkün mü?