MQL5 Programlama Temelleri: Terminalin Global Değişkenleri
Giriş
MQL4/5 ortamında ilginç bir enstrüman vardır - istemci terminalinin global değişkenleri. Tüm terminal programları için paylaşılan bir veri depolama alanı oluşturmaya olanak tanır. Üstelik bu alanın ömrü terminalin kapatılmasıyla bitmez. Bu makale, terminal global değişkenlerinin ne olduğu konusunda net bir fikir edinilmesi için Nesneye Yönelik Programlama araçlarının kullanılmasını önerir.
Ayrıca aksi belirtilmedikçe makalede, istemci terminalinin global değişkenleri "global değişkenler" olarak adlandırılacaktır.
1. Global Değişkenler, İşlevler
Bir programcının bakış açısına göre global bir değişken, alım satım işleminin çalışan tüm terminal programları için mevcut olan adlandırılmış bir bellek alanıdır. Acemi programcılar, aynı anda çalışan birkaç terminal varsa, her birinin global değişkenler için kendi bağımsız bellek alanına sahip olacağını unutmamalıdır. Üst üste çakışmayacaklardır.
Dil geliştiriciler Belgeler'de global değişkenlerle çalışmak için kullanılan 11 işlev olduğunu belirtirler.
Teori, MQL4 ders kitabının "GlobalVariables" bölümünde bulunabilir.
Sonraki bölümlerde, belirlenmiş görevlerin uygulanması için Nesne yönelimli programlama enstrümanlarını kullanacağım.
2. Sınıf CGlobalVar
Nesne yönelimli programlama fikirlerinin rehberliğinde, bir global değişkenin nesnesinden doğrudan sorumlu olacak CGlobalVar sınıfını oluşturalım.
//+------------------------------------------------------------------+ //| Class CGlobalVar | //+------------------------------------------------------------------+ class CGlobalVar : public CObject { //--- === Data members === --- private: string m_name; double m_value; //--- datetime m_create_time; datetime m_last_time; //--- flag for temporary var bool m_is_temp; //--- === Methods === --- public: //--- constructor/destructor void CGlobalVar(void); void CGlobalVar(const string _var_name,const double _var_val, const datetime _create_time); void ~CGlobalVar(void){}; //--- create/delete bool Create(const string _var_name,const double _var_val=0.0, const bool _is_temp=false); bool Delete(void); //--- exist bool IsGlobalVar(const string _var_name,bool _to_print=false); //--- set methods bool Value(const double _var_val); bool ValueOnCondition(const double _var_new_val,const double _var_check_val); //--- get methods string Name(void) const; datetime CreateTime(void) const; datetime LastTime(void); template<typename T> T GetValue(T _type) const; bool IsTemporary(void) const; //--- private: string FormName(const string _base_name,const bool _is_temp=false); };
Sınıf neleri içermelidir? Minimum nitelik listesi için aşağıdaki özellikleri seçerdim:
- bir değişkenin adı;
- bir değişkenin değeri;
- oluşturma zamanı;
- son arama zamanı;
- geçici bir değişkenin özelliği.
Yöntemlere gelince, aşağıdaki gibi görünürler:
- oluşturma;
- silme;
- mevcut olup olmadığını kontrol edin;
- yeni bir değer ayarlama;
- koşul tarafından yeni bir değer ayarlama;
- bir isim alma;
- bir değer alma;
- geçici bir değişken bayrağı alınıyor.
CGlobalVar::GetValue yönteminden ayrıca bahsedilmelidir. Bir şablon yöntemidir. Kullanıcının bağımsız değişken olarak ayarladığı değişken değeri için veri türünü döndürür.
Buradaki sorun, MQL'deki bir işlevin yalnızca parametrelerle yazılabilmesidir. Bu nedenle sahte bir parametrenin eklenmesi gereklidir.
CGlobalVar türündeki nesnelerle çalışacağımız Globals_test1.mq5 test script dosyasını oluşturalım.
#include "CGlobalVar.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVar gVar1; //--- create a temporary global var if(gVar1.Create("Gvar1",3.123456789101235,true)) { Print("\n---=== A new global var ===---"); PrintFormat("Name: \"%s\"",gVar1.Name()); PrintFormat("Is temporary: %d",gVar1.IsTemporary()); //--- Get the value //--- double type double d=0.0; double dRes=gVar1.GetValue(d); PrintFormat("Double value: %0.15f",dRes); //--- float type float f=0.0; float fRes=gVar1.GetValue(f); PrintFormat("Float value: %0.7f",fRes); //--- string type string s=NULL; string sRes=gVar1.GetValue(s); PrintFormat("String value: %s",sRes); //--- Set a new value double new_val=3.191; if(gVar1.Value(new_val)) PrintFormat("New value is set: %f",new_val); //--- Set a new value on condition new_val=3.18; if(gVar1.ValueOnCondition(3.18,3.191)) PrintFormat("New value on conditionis set: %f",new_val); } }
Global bir değişken şu şekilde oluşturulur:
gVar1.Create("Gvar1",3.123456789101235,true)
İlk bağımsız değişken gelecekteki değişken adının ("Gvar1") temel bileşenidir, ikinci bağımsız değişken (3.1234567891011235) değeridir ve üçüncü bağımsız değişken, değişkenin geçici (true) olacağını gösteren özelliktir.
Değişken adı, temel bileşene programın adı ve türü eklenerek oluşturulur.
Benim durumumda bu:
- Gvar1 - temel bileşendir;
- prog_Globals_test1 - değişkenin oluşturulduğu programdır (adı Globals_test1'dir);
- program türü - scr (script).
F3'e basıldığında, aşağıdaki giriş MetaTrader 5 penceresindeki global değişkenler listesinde görünmelidir:
Şek.1. Test_temp_var1_prog_Globals_test1_scr değişkeninin değeri 3,18'e eşittir
Aşağıda, başlatılmasında ve başarılı bir şekilde uygulanmasındaki girdiler "Experts" günlüğünde basılacaktır:
KP 0 10:20:20.736 Globals_test1 (AUDUSD.e,H1) ---=== A new global var ===--- EH 0 10:20:21.095 Globals_test1 (AUDUSD.e,H1) Name: "Gvar1_temp_prog_Globals_test1_scr" LF 0 10:20:21.876 Globals_test1 (AUDUSD.e,H1) Is temporary: 1 MO 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) Double value: 3.123456789101235 KG 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) Float value: 3.1234567 OP 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) String value: 3.123456789101235 RH 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) New value is set: 3.191000 DJ 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) New value on conditionis set: 3.180000
Günlükte, değişken değerinin farklı veri türleri basılır.
MetaTrader 5 terminali yeniden başlatılırsa, Gvar1_temp_prog_Globals_test1_scr değişkeni global değişkenler listesinden kaybolur. Bu değişken geçici olduğu ve terminal açıkken yaşadığı için gerçekleşir.
MQL4/5'te, global değişken hakkında veri alındığında, değişkenin geçici olup olmadığını anlamanın bir yolu yoktur. Belki de geçici bir değişkeni tanımlamanın en kolay yolu, değişkenin adına bir anahtar eklemektir. Mesela, değişken adındaki "temp" soneki olabilir. Ancak global değişkenin adının oluşturulmasını kontrol etme zorunluluğu, özellikle bu gibi değişkenler CGlobalVar sınıfını kullanmayan başka programlar tarafından oluşturuluyorsa, bu yaklaşımın belirgin bir dezavantajıdır.
Bir noktada, kaç global değişken ne kadar hızda oluşturulabilir bilmek istedim.
Önceki script dosyasını biraz değiştirdim ve Globals_test2.mq5 olarak adlandırdım. Farklı sayıda çalıştırma ile başlatıldı. Her çalıştırmadan sonra değişkenleri silmek için terminali yeniden başlattım.
#property script_show_inputs //--- #include "CGlobalVar.mqh" input uint InpCnt=10000; // Number of variables //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- start value uint start=GetTickCount(); //--- for(uint idx=0;idx<InpCnt;idx++) { CGlobalVar gVar; //--- Create a temporary global var if(!gVar.Create("Test_var"+IntegerToString(idx+1),idx+0.15,true)) Alert("Error creating a global variable!"); } //--- finish value uint time=GetTickCount()-start; //--- to print PrintFormat("Creation of %d global variables took %d ms",InpCnt,time); }
İşte sonuç (Şekil 2).
Şek.2. Geçici global değişkenler oluşturmak için harcanan zaman
Tam global değişkenler için olan benzer bir test sonucu Şekil 3'te sunulmuştur. Oluşturulmaları çok uzun sürmez.
Bunun nedeni, bu değişkenlerin Profiles klasöründeki diskte bulunan gvariables.dat dosyasına kaydedilmesidir.
Şek.3. Tam global değişkenler oluşturmak için harcanan zaman
Bu kadar çok global değişken yaratmaya gerek olduğunu düşünmüyorum. Bu değerlendirmeyi sırf meraktan yaptım.
Bir sonraki pasajda bir dizi global değişkenle çalışacağız.
3. CGlobalVarList Sınıfı
Çalışmayı global değişkenlerle düzenlemek için CGlobalVarList türündeki global değişkenlerin liste sınıfını oluşturacağız. Bu liste türü, standart liste sınıfı CList'nin alt öğesinden gelir.
Sınıf bildirimi şu şekilde sunulabilir:
//+------------------------------------------------------------------+ //| Class CGlobalVarList | //+------------------------------------------------------------------+ class CGlobalVarList : public CList { //--- === Data members === --- private: ENUM_GVARS_TYPE m_gvars_type; //--- === Methods === --- public: //--- constructor/destructor void CGlobalVarList(void); void ~CGlobalVarList(void){}; //--- load/unload bool LoadCurrentGlobals(void); bool KillCurrentGlobals(void); //--- working with files virtual bool Save(const int _file_ha); virtual bool Load(const int _file_ha); //--- service void Print(const int _digs); void SetGvarType(const ENUM_GVARS_TYPE _gvar_type); //--- private: bool CheckGlobalVar(const string _var_name); };
Geçerli global değişkenlerle bağlantılı nesneler CGlobalVarList türünde bir listeye dahil edilecekse, CGlobalVarList::LoadCurrentGlobals yöntemi kullanılır.
//+------------------------------------------------------------------+ //| Load current global vars | //+------------------------------------------------------------------+ bool CGlobalVarList::LoadCurrentGlobals(void) { ENUM_GVARS_TYPE curr_gvar_type=this.m_gvars_type; int gvars_cnt=GlobalVariablesTotal(); //--- for(int idx=0;idx<gvars_cnt;idx++) { string gvar_name=GlobalVariableName(idx); if(this.CheckGlobalVar(gvar_name)) continue; //--- gvar properties double gvar_val=GlobalVariableGet(gvar_name); datetime gvar_time=GlobalVariableTime(gvar_name); CGlobalVar *ptr_gvar=new CGlobalVar(gvar_name,gvar_val,gvar_time); //--- control gvar type if(CheckPointer(ptr_gvar)==POINTER_DYNAMIC) { if(curr_gvar_type>GVARS_TYPE_ALL) { bool is_temp=ptr_gvar.IsTemporary(); //--- only full-fledged if(curr_gvar_type==GVARS_TYPE_FULL) {if(is_temp)continue;} //--- only temporary else if(curr_gvar_type==GVARS_TYPE_TEMP) {if(!is_temp)continue;} } //--- try to add if(this.Add(ptr_gvar)>-1) continue; } //--- return false; } //--- return true; }
Bu yöntem, mevcut tüm global değişkenleri okur ve bunları listeye dahil eder.
m_gvars_type özniteliği, dahil edilen global değişkenin türünü kontrol eder. Bu ENUM_GVARS_TYPE türünün bir numaralandırması mıdır:
//+------------------------------------------------------------------+ //| Enumeration for gvars type | //+------------------------------------------------------------------+ enum ENUM_GVARS_TYPE { GVARS_TYPE_ALL=-1, // all global GVARS_TYPE_FULL=0, // only full GVARS_TYPE_TEMP=1, // only temporary };
CGlobalVarList listesinin başlatılmasından önce, Şekil 4'te sunulduğu gibi bir dizi global değişken olduğunu varsayalım.
Şek.4. Yaklaşık global değişken kümesi
Bu kümenin liste tarafından doğru bir şekilde işlenip işlenmediğini kontrol edeceğiz. Böyle bir kontrolü gerçekleştirmek için Globals_test3.mq5 test script dosyası oluşturulacaktır.
#include "CGlobalVarList.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; gvarList.LoadCurrentGlobals(); PrintFormat("Number of variables in the set: %d",gvarList.Total()); }
Yeni global değişkenler (sarı renkle vurgulanmıştır) olmaması gerektiği halde script dosyasının başlatılmasından sonra ortaya çıkmıştır (Şekil 5).
Şek.5. Yeni global değişkenler kümesi
Bir dize şu şekilde basılmıştır:
2014.10.21 11:35:00.839 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 10
Bunun nedeni, CGlobalVarList::LoadCurrentGlobals yönteminin beyannamesinde CGlobalVar::Create yöntemine başvurunun olmasıdır.
Bu, dizede yeni bir global değişken oluşturulduğu anlamına gelir:
if(ptr_gvar.Create(gvar_name,gvar_val))
Ek olarak, yeni değişkenler ortaya çıktıkça global değişkenlerin endeksleri de değişmektedir. Karışıklığa neden olan budur.
CGlobalVar::Create yöntemini daha az aktif olanla değiştirmenizi tavsiye ederim. Listedeki değişkenin dikkate alınabilmesi için CGlobalVar sınıfına parametreli bir kurucu eklenmelidir.
Modifikasyondan sonra CGlobalVarList::LoadCurrentGlobals yöntemi şöyle görünür:
//+------------------------------------------------------------------+ //| Load current global vars | //+------------------------------------------------------------------+ bool CGlobalVarList::LoadCurrentGlobals(void) { int gvars_cnt=GlobalVariablesTotal(); //--- for(int idx=0;idx<gvars_cnt;idx++) { string gvar_name=GlobalVariableName(idx); double gvar_val=GlobalVariableGet(gvar_name); datetime gvar_time=GlobalVariableTime(gvar_name); CGlobalVar *ptr_gvar=new CGlobalVar(gvar_name,gvar_val,gvar_time); if(CheckPointer(ptr_gvar)==POINTER_DYNAMIC) if(this.Add(ptr_gvar)>-1) continue; //--- return false; } //--- return true; }
Script dosyası yöntem değiştirildikten sonra düzgün çalışır. Aşağıdaki kayıt yazdırılır:
2014.10.21 11:38:04.424 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 6
Ardından, bir listeyi silmeye ve yazdırmaya izin veren özellikler ekleyeceğiz.
Şimdi Globals_test3.mq5 script dosyası şöyle görünür:
//--- #include "CGlobalVarList.mqh" //--- input ENUM_GVARS_TYPE InpGvarType=GVARS_TYPE_FULL; // Set gvar type //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; //--- delete gvars gvarList.SetGvarType(InpGvarType); //--- load current gvars gvarList.LoadCurrentGlobals(); Print("Print the list before deletion."); gvarList.Print(10); //--- delete gvars if(gvarList.KillCurrentGlobals()) { Print("Print the screen after deletion."); gvarList.Print(10); } }
10 farklı global değişken oluşturarak görevi karmaşıklaştıracağız (Şekil 6).
Şek.6. Çeşitli global değişkenler
gvarList listemize yalnızca tam değişkenler dahil edilecektir. Sonra silinecekler.
"Expert" günlüğü aşağıdakileri içerecektir:
MG 0 11:05:01.113 Globals_test3 (AUDUSD.e,H1) Print the list before deletion. KL 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) OI 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) ---===Local list===--- QS 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Global variable type: GVARS_TYPE_FULL RI 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Total number of global variables: 10 EG 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Number of global variables in current list: 5 RN 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #1, name - gVar10_prog_test1_scr, value - 16.6400000000 KP 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #2, name - gVar2_prog_test1_scr, value - 4.6400000000 GR 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #3, name - gVar4_prog_test1_scr, value - 7.6400000000 RD 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #4, name - gVar6_prog_test1_scr, value - 10.6400000000 LJ 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #5, name - gVar8_prog_test1_scr, value - 13.6400000000 EH 0 11:06:18.675 Globals_test3 (AUDUSD.e,H1) Print the list after deletion. FS 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) JJ 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) ---===Local list===--- HN 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Global variable type: GVARS_TYPE_FULL KH 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Total number of global variables: 5 QP 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Number of global variables in the current list: 0
Yalnızca tam global değişkenleri içeren liste doğru bir şekilde oluşturuldu.
Daha sonra temizlendi ve terminalde sadece 5 geçici değişken kaldı (Şekil 7).
Şek.7. Geçici global değişkenler
Amaçlanan görev yerine getirildi.
CGlobalVarList sınıfında dosyaya veri kaydetme ve dosyadan veri indirme yöntemleri de uygulandı.
4. Pratik Uygulama
Bilindiği gibi MQL4/5 özel bir programlama dilidir. Alım satım stratejilerini programlamak için oluşturulmuştur. Bu nedenle, dilin herhangi bir aracı belirli bir alım satım fikrini resmileştirme olarak düşünülmelidir.
Expert Advisor'ları MQL5 platformunda global değişkenlerle ilişkilendirmek için yeterince örnek vardır. Bugün, gereken programın uygulanması üzerinde etkili olan duruma yakından bakmayı öneriyorum.
"Globals_test_EA" alım satım robotunun modül yaklaşımına dayalı bir kodunun olduğunu varsayalım:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { Main(); }
burada ana modül şu şekilde görünür:
//+------------------------------------------------------------------+ //| Main module | //+------------------------------------------------------------------+ void Main(void) { //--- set flags for all modules for(int idx=0;idx<GVARS_LIST_SIZE;idx++) SetFlag(idx,false); //--- Check the trade possibility and connectivity //--- permission to trade if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) //--- connection to the trading server if(TerminalInfoInteger(TERMINAL_CONNECTED)) //--- permission to trade for the launched EA if(MQLInfoInteger(MQL_TRADE_ALLOWED)) { //--- 1) opening module Open(); //--- 2) closing module Close(); //--- 3) Trailing Stop module Trail(); } }
Yani ana modül 3 bileşen içerir:
- açılış modülü;
- kapanış modülü;
- Takip Eden Durdurma modülü.
Şimdi program yürütme aşamalarını kontrol eden global değişkenler yaratmamız gerekiyor.
Modül şekillerinde üç aşama vardır. Her aşama için iki kontrol noktası kullanılır. İlk kontrol noktası modül çalışmasının başlangıcını kontrol eder ve ikincisi modül çalışmasının sonunu kontrol eder.
Global değişkenler şekillerinde kontrol noktaları uygulanır.
Bu nedenle, aşağıdaki adlara sahip altı global değişkene ihtiyacımız vardır:
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
Tüm modüllerin bayrakları, Main() işlevinin başına ayarlanır ve her modülde temizlenir. Sadece "kendi" bayraklarından bahsettiğimizi söylemeye gerek yoktur. Örneğin, Open() modülüne bakalım:
//+------------------------------------------------------------------+ //| Open module | //+------------------------------------------------------------------+ void Open(void) { Comment(curr_module+__FUNCTION__); //--- if(!IsStopped()) { //--- clear the module start flag SetFlag(0,true); //--- assume that the module operates for approximately 1.25 s { Sleep(1250); } //--- clear the module finish flag SetFlag(1,true); } }
Modül yürütülürken, programın çizelge penceresindeki Open() bloğunda çalıştığına dair bir açıklama görünür.
Ardından program kapanmaya zorlanmazsa, kontrol ilgili bayrağı ayarlama/temizleme işlevine geçer. Herhangi bir kontrol noktasında bayrağın silinmemesi durumunda, modülün işi bitirmediği kabul edilir.
Global değişkenlerle modül çalışması izleme aşamaları formasyonu Şekil 8'de sunulmaktadır.
Şek. 8. Bayrak dizisi işleme formasyonu
Örneğin, Expert Advisor "Globals_test_EA" tabloya eklenir ve normal şekilde çalışır.
Expert Advisor'ı grafikten sildiğimde, günlükte aşağıdaki girdi ortaya çıkmıştır:
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
Bu nedenle Expert Advisor'ın sonlandırılması Open(). modülünde gerçekleşti.
F3'e basarak global değişkenler listesini açın (Şek.9).
Şek. 9. Expert Advisor "Globals_test_EA" için global değişkenler
Listeye bakıldığında sadece Open() modül çalışmasının başlangıcından sorumlu olan bayrak sıfırlanmıştır.
Açık pozisyonlar, bunların kapanması ve korunması ile bağlantılı komut yürütme hatasında hatalar potansiyel olarak tespit edilebilir gibi görünmektedir.
Aynı grafikteki robotun yeniden başlatılmasından sonra, günlükte aşağıdaki bilgiler görüntülenecektir:
RQ 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Open_finish>> CL 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Close_start>> DH 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Close_finish>> ES 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Trail_start>> RS 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Trail_finish>>
Program aşamalarının hatası hakkında bu şekilde uyarı alırız. Bu başka bir soruya yol açmaktadır. Bu aşamalar başarısız olursa ne yapılabilir? Bu farklı bir hikayedir.
Sonuç
Bu makalede, terminal global değişkenleriyle çalışmayı kolaylaştıran nesneler oluşturma konusunda, MQL5 dilinin nesne yönelimli imkanlarını gösterdim.
Program aşamalarının uygulanmasında, global değişkenlerin kontrol noktaları olarak kullanıldığı bir durum bir örnek teşkil etmiştir.
Her zaman olduğu gibi, yorum, öneri ve yapıcı eleştirilere açığız.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/1210
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz