
MQL5'te Nesne Yaratma ve Yok Etme
Bu makale ne hakkındadır?
MQL5 programları, Nesne Yönelimli Programlama (OOP) kavramlarıyla yazılmıştır ve bu, yalnızca özel kitaplıklar oluşturmak için yeni olanaklar açmakla kalmaz, aynı zamanda diğer geliştiricilerin eksiksiz ve test edilmiş sınıflarını kullanabilmenizi de sağlar. MetaTrader 5 Müşteri Terminali'nin içerdiği Standart Kitaplıkta, binlerce yöntemi içeren yüzlerce sınıf vardır.
OOP'nin tüm avantajlarından yararlanmak için MQL5 programlarında nesne oluşturma ve silme ile ilgili bazı ayrıntıları netleştirmemiz gerekir. Nesne Oluşturma ve Silme, Belgeler bölümünde kısaca açıklanmıştır ve bu makale bu konuyu örneklerle açıklayacaktır. Global Değişkenlerin Başlatılması ve Sonlandırılması
Global değişkenlerin başlatılması, MQL5 programı çalıştırıldıktan hemen sonra ve herhangi bir fonksiyon çağrısından önce yapılır. Başlatma sırasında, ilk değerler basit tiplerin değişkenlerine atanır ve bunlar içinde bildirilmişse nesnelerin yapıcısı çağrılır.
Örnek olarak, CObjectA ve CObjectB olmak üzere iki sınıf tanımlayalım. Her sınıfın basit Print() fonksiyonunu içeren bir yapıcısı ve yıkıcısı vardır. Bu sınıf tiplerinin değişkenlerini global olarak tanımlayalım ve betiği çalıştıralım.
//+------------------------------------------------------------------+ //| GlobalVar_TestScript.mq5 | //| http://www.mql5.com | //+------------------------------------------------------------------+ class CObjectA { public: CObjectA(){Print(__FUNCTION__," Constructor");} ~CObjectA(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CObjectB { public: CObjectB(){Print(__FUNCTION__," Constructor");} ~CObjectB(){Print(__FUNCTION__," Destructor");} }; //--- declaring the objects globally CObjectA first; CObjectB second; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Print(__FUNCTION__); }
Betik sonucu, Uzman günlüğünde gösterilir:
GlobalVar_TestScript (EURUSD,H1) 13:05:07 CObjectA::ObjectA Constructor
GlobalVar_TestScript (EURUSD,H1) 13:05:07 CObjectB::ObjectB Constructor
GlobalVar_TestScript (EURUSD,H1) 13:05:07 OnStart
GlobalVar_TestScript (EURUSD,H1) 13:05:07 CObjectB::~ObjectB Destructor
GlobalVar_TestScript (EURUSD,H1) 13:05:07 CObjectA::~ObjectA Destructor
Günlükten açıkça anlaşılacağı üzere, başlatma sırası GlobalVar_TestScript.mq5 betiğindeki değişkenlerin bildirim sırasına uygundur ve sonlandırma işlemi MQL5 programı kullanıma alınmadan önce ters sırada yapılmıştır.
Yerel Değişkenlerin Başlatılması ve Sonlandırılması
Yerel değişkenler bildirildikleri program blokunun bitiminde, bildirim sıralarının tersine göre sonlandırılır. Program bloğu, anahtar operatörünün, döngü operatörlerinin (while ve do-while için), fonksiyon gövdesinin veya if-else operatörünün bir parçası olabilen bileşik bir operatördür.
Yerel değişkenler, yalnızca programda kullanılıyorlarsa başlatılır. Bir değişken bildirilmişse, ancak bildirildiği kod bloğu yürütülmemişse, bu değişken oluşturulmaz ve bu nedenle başlatılmaz.
Bunu göstermek için CObjectA ve CObjectB sınıflarımıza geri dönelim ve yeni CObjectС sınıfını oluşturalım. Sınıflar hala global olarak bildirilir, ancak bu sınıfların değişkenleri artık OnStart() fonksiyonunda yerel olarak bildirilir.
Şimdi CObjectA sınıfının değişkenini fonksiyonun ilk satırında açık bir şekilde bildirelim, ancak CObjectB ve CObjectС sınıflarının nesneleri ayrı bloklarda bildirilecek ve bu, yürütme girdi değişkeninin değerine bağlı olarak yürütülecektir. MetaEditor'da MQL5 programlarının girdi değişkenleri kahverengi ile vurgulanmıştır.
//+------------------------------------------------------------------+ //| LocalVar_TestScript.mq5 | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property script_show_inputs //--- input parameters input bool execute=false; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CObjectA { public: CObjectA(){Print(__FUNCTION__," Constructor");} ~CObjectA(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CObjectB { public: CObjectB(){Print(__FUNCTION__," Constructor");} ~CObjectB(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CObjectC { public: CObjectC(){Print(__FUNCTION__," Constructor");} ~CObjectC(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- CObjectA objA; //--- this block will NOT be executed if execute==false if(execute) { CObjectB objB; } //--- this block WILL be executed if execute==false if(!execute) { CObjectC objC; } } //+------------------------------------------------------------------+
Sonuç:
LocalVar_TestScript (GBPUSD,H1) 18:29:00 CObjectA::CObjectA Constructor
LocalVar_TestScript (GBPUSD,H1) 18:29:00 CObjectC::CObjectC Constructor
LocalVar_TestScript (GBPUSD,H1) 18:29:00 CObjectC::~CObjectC Destructor
LocalVar_TestScript (GBPUSD,H1) 18:29:00 CObjectA::~CObjectA Destructor
Her zaman CObjectA sınıfının nesnesi, yürütme girdi parametresinin değeri ne olursa olsun,ilk önce otomatik olarak başlatılacaktır. Ardından, objB veya objC nesnesi otomatik olarak başlatılır. Bu, yürütme girdi parametresinin değerine göre hangi bloğun yürütüldüğüne bağlıdır. Varsayılan olarak, bu parametre yanlış değerine sahiptir ve bu durumda objA değişkeninin başlatılmasından sonra objC değişkeninin başlatılması gelir. Bu, yapıcı ve yıkıcının yürütmesinde açıkça görülür.
Ancak, başlatma sırası ne olursa olsun (yürütme parametresinden bağımsız olarak), karmaşık türdeki değişkenlerin sonlandırma işlemi, başlatma sırasının tersinde yapılır. Bu, hem yerel hem de otomatik olarak oluşturulan global sınıf nesneleri için geçerlidir. Bu durumda aralarında bir fark yoktur.
Dinamik Olarak Oluşturulan Nesnelerin Başlatılması ve Sonlandırılması
MQL5'te bileşik nesneler otomatik olarak başlatılır, ancak nesne oluşturma sürecini elle kontrol etmek istiyorsanız nesne işaretçilerini kullanmalısınız. Bir sınıfın nesne işaretçisi olarak bildirilen bir değişken, nesnenin kendisini içermez ve bu nesnenin otomatik olarak başlatılması söz konusu değildir.
İşaretçiler yerel ve/veya genel olarak bildirilebilir ve aynı zamanda devralınan tipin NULL ile boş değeri başlatılabilirler. Nesne oluşturma, yalnızca nesne işaretçisine yeni operatör uygulandığında yapılır ve nesne işaretçisi bildirimine bağlı değildir.
Dinamik olarak oluşturulan nesneler, silme operatörü kullanılarak silinir, bu yüzden bunu ele almalıyız. Örnek olarak, global olarak iki değişken tanımlayalım: Biri CObjectA tipinden ve biri de CObjectB tipinden olsun; bir diğeri de nesne işaretçisi ile CObjectC tipinden başka bir değişken olsun.
//+------------------------------------------------------------------+ //| GlobalVar_TestScript_2.mq5 | //| http://www.mql5.com | //+------------------------------------------------------------------+ class CObjectA { public: CObjectA(){Print(__FUNCTION__," Constructor");} ~CObjectA(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CObjectB { public: CObjectB(){Print(__FUNCTION__," Constructor");} ~CObjectB(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CObjectC { public: CObjectC(){Print(__FUNCTION__," Constructor");} ~CObjectC(){Print(__FUNCTION__," Destructor");} }; CObjectC *pObjectC; CObjectA first; CObjectB second; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- pObjectC=new CObjectC; Print(__FUNCTION__); delete(pObjectC); } //+------------------------------------------------------------------+
Dinamik olarak oluşturulan nesne işaretçisi pObjectC, birinci ve ikinci statik değişkenlerden önce bildirilmiş olsa da, bu nesne yalnızca yeni operatör tarafından oluşturulduğunda başlatılır. Bu örnekte, yeni operatör OnStart() fonksiyonunun içindedir.
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 CObjectA::CObjectA Constructor
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 CObjectB::CObjectB Constructor
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 CObjectC::CObjectC Constructor
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 OnStart
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 CObjectC::~CObjectC Destructor
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 CObjectB::~CObjectB Destructor
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 CObjectA::~CObjectA Destructor
OnStart() fonksiyonundaki program yürütmesi operatöre ulaştığında
pObjectC=new CObjectC;
nesne başlatılır ve bu nesnenin yapıcısı çağrılır. Sonra program bu dizgiyi yürütür
Print(__FUNCTION__);
bu, Journal'da aşağıdaki metni verir:
GlobalVar_TestScript_2 (EURUSD,H1) 15:03:21 OnStart
ardından dinamik olarak oluşturulan nesne, silme operatörü çağrılarak silinir:
delete(pObjectC);
Yani nesneler yeni operatör tarafından oluşturulurken dinamik olarak başlatılır ve silme operatörü tarafından silinir.
Zorunlu durum: object_pointer=new Class_Name ifadesi kullanılarak oluşturulan tüm nesneler, her zaman delete(object_pointer) operatörü kullanılarak silinmelidir. Dinamik olarak oluşturulan nesne (başlatıldığı bloğun sonundan sonra) herhangi bir nedenle silme operatörü kullanılarak silinmediyse, Uzman günlüğünde ilgili bir mesaj gösterilecektir.
Dinamik Olarak Oluşturulmuş Nesneleri Silme
Daha önce bahsedildiği gibi, dinamik olarak oluşturulan her nesne yeni operatör kullanılarak başlatılır ve her zaman silme operatörü kullanılarak silinmelidir. Ancak, yeni operatörün bir nesne oluşturduğunu ve bir işaretçiyi o nesneye döndürdüğünü unutmayın. Oluşturulan nesnenin kendisi, nesne işaretçisini içeren değişkende değildir. Birkaç işaretçi bildirebilir ve bunları aynı nesne işaretçisine atayabilirsiniz.
//+------------------------------------------------------------------+ //| LocalVar_TestScript_1.mq5 | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property link "http://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| simple class | //+------------------------------------------------------------------+ class CItem { public: CItem(){Print(__FUNCTION__," Constructor");} ~CItem(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- declaring the first object pointer array CItem* array1[5]; //--- declaring the first object pointer array CItem* array2[5]; //--- filling arrays in the loop for(int i=0;i<5;i++) { //--- creating a pointer for the first array using new operator array1[i]=new CItem; //--- creating a pointer for the second array via copy from the first array array2[i]=array1[i]; } // We "forgot" to delete objects before exiting the function. See "Experts" tab. } //+------------------------------------------------------------------+
Çıktı, birkaç silinmemiş nesne kaldığını söylüyor. Ancak, yeni operatör yalnızca 5 nesne oluşturduğu için, düşündüğünüz gibi, 10 yerine yalnızca 5 silinmemiş nesne olacaktır.
(GBPUSD,H1) 12:14:04 CItem::CItem Constructor
(GBPUSD,H1) 12:14:04 CItem::CItem Constructor
(GBPUSD,H1) 12:14:04 CItem::CItem Constructor
(GBPUSD,H1) 12:14:04 CItem::CItem Constructor
(GBPUSD,H1) 12:14:04 CItem::CItem Constructor
(GBPUSD,H1) 12:14:04 5 silinmemiş nesne kaldı
Dinamik olarak oluşturulan nesne için yıkıcı çağrılmasa bile (nesne, silme operatörü kullanılarak silinmez), bellek yine de temizlenecektir. Ancak "Uzmanlar" günlüğünde nesnenin silinmediği söyleniyor. Bu, uygun olmayan nesne yönetimini tespit etmenize ve hatayı düzeltmenize yardımcı olabilir.
Bir sonraki örnekte, iki işaretçi dizisinin her birindeki işaretçileri silmeye çalışalım: – array1 ve array2.
//+------------------------------------------------------------------+ //| LocalVar_TestScript_2.mq5 | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property link "http://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| simple class | //+------------------------------------------------------------------+ class CItem { public: CItem(){Print(__FUNCTION__," Constructor");} ~CItem(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- declaring the first object pointer array CItem* array1[5]; //--- declaring the second object pointer array CItem* array2[5]; //--- filling arrays in the loop for(int i=0;i<5;i++) { //--- creating a pointer for the first array using new operator array1[i]=new CItem; //--- creating a pointer for the second array via copy from the first array array2[i]=array1[i]; } //--- deleting object using pointers of second array for(int i=0;i<5;i++) delete(array2[i]); //--- let's try to delete objects using pointers of first array for(int i=0;i<5;i++) delete(array2[i]); // in Experts tab there are messages about trying to delete invalid pointer } //+------------------------------------------------------------------+
Uzmanlar sekmesindeki sonuç artık farklı.
(GBPUSD,H1) 15:02:48 CItem::CItem Constructor
(GBPUSD,H1) 15:02:48 CItem::CItem Constructor
(GBPUSD,H1) 15:02:48 CItem::CItem Constructor
(GBPUSD,H1) 15:02:48 CItem::CItem Constructor
(GBPUSD,H1) 15:02:48 CItem::CItem Constructor
(GBPUSD,H1) 15:02:48 CItem::~CItem Destructor
(GBPUSD,H1) 15:02:48 CItem::~CItem Destructor
(GBPUSD,H1) 15:02:48 CItem::~CItem Destructor
(GBPUSD,H1) 15:02:48 CItem::~CItem Destructor
(GBPUSD,H1) 15:02:48 CItem::~CItem Destructor
(GBPUSD,H1) 15:02:48 geçersiz işaretçiyi sil
(GBPUSD,H1) 15:02:48 geçersiz işaretçiyi sil
(GBPUSD,H1) 15:02:48 geçersiz işaretçiyi sil
(GBPUSD,H1) 15:02:48 geçersiz işaretçiyi sil
(GBPUSD,H1) 15:02:48 geçersiz işaretçiyi sil
CItem tarafından oluşturulan nesneler ilk for() döngüsünde başarıyla silinmiştir ancak ikinci döngüde var olmayan nesneleri silmeye yönelik başka girişimler, geçersiz işaretçilere dair bazı mesajlara neden olmuştur. Dinamik olarak oluşturulan nesne bir kez silinmeli ve herhangi bir nesne işaretçisi kullanılmadan önce CheckPointer() fonksiyonu ile kontrol edilmelidir.
CheckPointer() fonksiyonu ile işaretçi denetimi
CheckPointer(), işaretçileri kontrol etmek için kullanılır ve işaretçi tipini tanımlamaya yarar. Dinamik olarak oluşturulmuş nesnelerle çalışırken iki olası durum vardır:
- yürütme bloğunun sonunda silmeyi geri alma
- zaten silinmiş nesneyi silme girişimi
Nesnelerin karşılıklı ilişkilerini gösteren başka bir örneğe bakalım. İki sınıf oluşturalım: Birinci sınıf CItemArray, başka bir CItem sınıfının işaretçi dizisini içerir.
//+------------------------------------------------------------------+ //| LocalVar_TestScript_3.mq5 | //| Copyright 2009, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property link "http://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| simple class | //+------------------------------------------------------------------+ class CItem { public: CItem(){Print(__FUNCTION__," Constructor");} ~CItem(){Print(__FUNCTION__," Destructor");} }; //+------------------------------------------------------------------+ //| class, containing pointer array of CItem class | //+------------------------------------------------------------------+ class CItemArray { private: CItem *m_array[]; public: CItemArray(){Print(__FUNCTION__," Constructor");} ~CItemArray(){Print(__FUNCTION__," Destructor");Destroy();} void SetArray(CItem &array[]); protected: void Destroy(); }; //+------------------------------------------------------------------+ //| filling pointers array | //+------------------------------------------------------------------+ CItemArray::SetArray(CItem &array[]) { int size=ArraySize(array); ArrayResize(m_array,size); for(int i=0;i<size;i++)m_array[i]=GetPointer(array[i]); } //+------------------------------------------------------------------+ //| releasing | //+------------------------------------------------------------------+ CItemArray::Destroy(void) { for(int i=0;i<ArraySize(m_array);i++) { if(CheckPointer(m_array[i])!=POINTER_INVALID) { if(CheckPointer(m_array[i])==POINTER_DYNAMIC) delete(m_array[i]); } else Print("Invalid pointer to delete"); } }
Sınıflar bizzat herhangi bir hata içermez, ancak kullanımları sürprizlere açık olabilir. Betiğin ilk varyantı:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- CItemArray items_array; CItem array[5]; items_array.SetArray(array); }
Bu betik varyantını çalıştırmak aşağıdaki mesajları görüntüler:
(GBPUSD,H1) 16:06:17 CItemArray::CItemArray Constructor
(GBPUSD,H1) 16:06:17 CItem::CItem Constructor
(GBPUSD,H1) 16:06:17 CItem::CItem Constructor
(GBPUSD,H1) 16:06:17 CItem::CItem Constructor
(GBPUSD,H1) 16:06:17 CItem::CItem Constructor
(GBPUSD,H1) 16:06:17 CItem::CItem Constructor
(GBPUSD,H1) 16:06:17 CItem::~CItem Destructor
(GBPUSD,H1) 16:06:17 CItem::~CItem Destructor
(GBPUSD,H1) 16:06:17 CItem::~CItem Destructor
(GBPUSD,H1) 16:06:17 CItem::~CItem Destructor
(GBPUSD,H1) 16:06:17 CItem::~CItem Destructor
(GBPUSD,H1) 16:06:17 CItemArray::~CItemArray Destructor
(GBPUSD,H1) 16:06:17 Silinmek için geçersiz işaretçi
(GBPUSD,H1) 16:06:17 Silinmek için geçersiz işaretçi
(GBPUSD,H1) 16:06:17 Silinmek için geçersiz işaretçi
(GBPUSD,H1) 16:06:17 Silinmek için geçersiz işaretçi
CItemArray sınıf değişkeninin bildirimi önce geldiğinden, ilk olarak o başlatılır ve sınıf yıkıcı çağrılır. Ardından, CItem class nesne işaretçilerini içeren array[5] bildirilir. Bu yüzden her nesnenin başlatılmasıyla ilgili beş mesaj görürüz.
Bu basit betiğin son satırında, array[5] dizisindeki işaretçiler, items_array adlı dahili nesne işaretçileri dizisine kopyalanır (Bkz. 'LocalVar_TestScript_4.mq5').
items_array.SetArray(array);
Betik, şimdilik yürütmeyi durdurur ve otomatik olarak oluşturulan nesneler otomatik olarak silinir. Silinecek ilk nesne, en son başlatılan nesnedir. Bu, array[5] işaretçiler dizisidir. CItem sınıf yıkıcısının çağrılmasıyla ilgili beş Günlük kaydı bunu doğrulamaktadır. Ardından, array[5] değişkeninden hemen önce başlatılması sebebiyle, items_array nesnesi için yıkıcının çağrılmasıyla ilgili mesaj gelir.
Ancak, CArrayItem sınıf yıkıcısı, silme operatörü üzerinden m_array[] içindeki işaretçiler aracılığıyla CItem nesnelerini silmeye çalışan korumalı Destroy() fonksiyonunu çağırır. Önce işaretçi kontrol edilir ve geçersizse nesneler silinmez ve "Silmek için geçersiz işaretçi" mesajı görüntülenir.
Günlükte bu türden 5 kayıt vardır, yani m_array[] dizisindeki tüm işaretçiler geçersizdir. Bunun nedeni, bu işaretçilerin nesnelerinin array[] dizisinin sonlandırılması sırasında zaten sonlandırılmış olmasıdır.
Şimdi Item_array ve items_array[] değişkenlerinin bildirimlerini değiştirerek betiğimizde ince ayar yapalım.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- CItem array[5]; CItemArray items_array; items_array.SetArray(array); }
Düzeltilmiş betik hata üretmez. En son bildirildiği için, ilk olarak items_array değişkeni sonlandırıldı. Sonlandırma işlemi sırasında ~CItemArray() sınıfı yıkıcısı çağrıldı, bu da Destroy() fonksiyonunu çağırdı.
Bu bildirim sırasında, items_array, array[5] dizisinden önce silinir. item_array yıkıcısından çağrılan Destroy() fonksiyonunda, işaretçi nesneleri hala mevcuttur, dolayısıyla hiçbir hata oluşmaz.
Dinamik olarak oluşturulan nesnelerin doğru silinmesi GetPointer() fonksiyonu örneğinde de görülebilir. Bu örnekte, nesnelerin silinmesinin doğru sırasını sağlamak için Destroy() fonksiyonu açıkça çağrılır.
Sonuç
Gördüğünüz gibi, nesnelerin oluşturulması ve silinmesi basit bir şekilde yapılır. Bu makaledeki tüm örnekleri gözden geçirerek otomatik ve dinamik olarak oluşturulan nesneler arasında kendi karşılıklı ilişki varyantlarınızı oluşturabilirsiniz.
Nesnelerin doğru silinmesi için her zaman sınıflarınızı kontrol etmeli ve yıkıcılarınızı doğru şekilde tasarlamalısınız, böylece geçersiz işaretçilere erişirken hata oluşmaz. Yeni operatör kullanılarak dinamik olarak oluşturulan nesneleri kullanırsanız, bu nesneleri silme operatörünü kullanarak doğru şekilde silmeniz gerektiğini unutmayın.
Bu makaleden, yalnızca MQL5'te nesne oluşturma ve silme sırasını öğrendiniz. Nesne işaretçileriyle güvenli bir çalışma düzenleme konusu ise bu makalenin kapsamını aşmaktadır.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/28





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