![MQL5 Cookbook: ОСО Talimatları](https://c.mql5.com/i/articles/overlay.png)
MQL5 Cookbook: ОСО Talimatları
Giriş
Bu makale, OCO gibi bu tür bir talimat çifti ile ilgilenmeye odaklanmaktadır. Bu mekanizma, MetaTrader 5 ile rekabet eden bazı alım satım terminallerinde uygulanmaktadır. OCO talimatlarının işlenmesi için bir panele sahip bir EA oluşturma örneğinde iki amaç güdüyorum. Bir yandan Standart Kitaplığın özelliklerini açıklamak, diğer yandan bir yatırımcının araç setini genişletmek istiyorum.
1. OCO Talimatlarının Özü
OCO talimatları (biri diğer talimatı iptal eder) bekleyen iki talimatı temsil eder.
Karşılıklı iptal işleviyle bağlanırlar: ilki tetiklenirse ikincisi kaldırılmalıdır ve bunun tersi de geçerlidir.
Şek. 1 Çift OCO talimatı
Şekil 1, basit bir talimat karşılıklı bağımlılık şemasını göstermektedir. Temel bir tanımı yansıtır: Her iki talimat talimat var olduğu sürece bir çift vardır. Mantık açısından çiftin herhangi bir [bir] talimatı, çiftin varlığı için gerekli ancak yeterli olmayan bir koşuldur.
Bazı kaynaklar, çiftin bir limit talimatı ve bir durdur talimatı olması gerektiğini, ayrıca talimatların tek yönlü (al veya sat) olması gerektiğini söylüyor. Bana göre bu tür kısıtlamalar, esnek alım satım stratejilerinin oluşturulmasına yardımcı olamaz. Çiftte çeşitli OCO talimatlarının analiz edilmesini ve en önemlisi bu çifti programlamaya çalışmamızı öneriyorum.
2. Programlama Talimat Çifti
Benim düşünceme göre, ООP araç seti, OCO talimatları üstündeki kontrol ile bağlantılı programlama görevleri için mümkün olan en uygunudur.
Aşağıdaki bölümler amacımıza hizmet edecek yeni veri türlerine ayrılmıştır. CiOcoObject sınıfı önce gelir.
2.1. CiOcoObject Sınıfı
Bu nedenle, birbirine bağlı iki talimatın kontrolünden sorumlu bir yazılım nesnesi bulmamız gerekiyor.
Geleneksel olarak, CObject abstract sınıfı temelinde yeni bir nesne oluşturalım.
Bu yeni sınıf aşağıdaki gibi görünebilir:
//+------------------------------------------------------------------+ //| Class CiOcoObject | //| Purpose: a class for OCO orders | //+------------------------------------------------------------------+ class CiOcoObject : public CObject { //--- === Data members === --- private: //--- tickets of pair ulong m_order_tickets[2]; //--- initialization flag bool m_is_init; //--- id uint m_id; //--- === Methods === --- public: //--- constructor/destructor void CiOcoObject(void){m_is_init=false;}; void ~CiOcoObject(void){}; //--- copy constructor void CiOcoObject(const CiOcoObject &_src_oco); //--- assignment operator void operator=(const CiOcoObject &_src_oco); //--- initialization/deinitialization bool Init(const SOrderProperties &_orders[],const uint _bunch_cnt=1); bool Deinit(void); //--- get id uint Id(void) const {return m_id;}; private: //--- types of orders ENUM_ORDER_TYPE BaseOrderType(const ENUM_ORDER_TYPE _ord_type); ENUM_BASE_PENDING_TYPE PendingType(const ENUM_PENDING_ORDER_TYPE _pend_type); //--- set id void Id(const uint _id){m_id=_id;}; };
Her bir OCO talimatının çiftinin kendi tanımlayıcısı olacaktır. Değeri, rastgele sayı üreteci aracılığıyla ayarlanır (CRandom sınıfı nesnesi).
Çift başlatma ve başlatmadan kaldırma yöntemleri, arayüz bağlamında endişe vericidir. İlki çifti yaratır (başlatır), ikincisi onu kaldırır (başlangıçtan çıkarır).
CiOcoObject::Init() yöntemi, argüman olarak SOrderProperties türündeki yapıların dizisini kabul eder. Bu tür yapı, çiftteki düzenin özelliklerini temsil eder, yani OCO talimatı.
2.2 SOrderProperties Yapısı
Yukarıda bahsedilen yapının alanlarını ele alalım.
//+------------------------------------------------------------------+ //| Order properties structure | //+------------------------------------------------------------------+ struct SOrderProperties { double volume; // order volume string symbol; // symbol ENUM_PENDING_ORDER_TYPE order_type; // order type uint price_offset; // offset for execution price, points uint limit_offset; // offset for limit price, points uint sl; // stop loss, points uint tp; // take profit, points ENUM_ORDER_TYPE_TIME type_time; // expiration type datetime expiration; // expiration string comment; // comment }
Bu nedenle, başlatma yönteminin çalışması için iki öğeden oluşan yapılar dizisini önceden doldurmalıyız. Basitçe ifade edersek programa hangi talimatları vereceğini açıklamamız gerekiyor.
Yapıda ENUM_PENDING_ORDER_TYPE türünün numaralandırması kullanılıyor:
//+------------------------------------------------------------------+ //| Pending order type | //+------------------------------------------------------------------+ enum ENUM_PENDING_ORDER_TYPE { PENDING_ORDER_TYPE_BUY_LIMIT=2, // Buy Limit PENDING_ORDER_TYPE_SELL_LIMIT=3, // Sell Limit PENDING_ORDER_TYPE_BUY_STOP=4, // Buy Stop PENDING_ORDER_TYPE_SELL_STOP=5, // Sell Stop PENDING_ORDER_TYPE_BUY_STOP_LIMIT=6, // Buy Stop Limit PENDING_ORDER_TYPE_SELL_STOP_LIMIT=7, // Sell Stop Limit };
Genel konuşursak standart ENUM _ORDER_TYPE numaralandırmayla aynı görünür ancak yalnızca bekleyen talimatların veya daha doğrusu bu tür talimatların türlerinin seçilmesine izin verir.
Giriş parametrelerinde ilgili talimat türünü seçerken hatalardan korur (Şekil 2).
Şek. 2. Kullanılabilir talimat türlerinin açılır listesini içeren "Tür" alanı
Bununla birlikte, ENUM _ORDER_TYPE standart numaralandırmayı kullanırsak yalnızca bekleyen talimatlarla ilgilendiğimiz için gerekli olmayan bir piyasa talimatı türü (ORDER_TYPE_BUY veya ORDER_TYPE_SELL) belirleyebiliriz.
2.3. Çift başlatılıyor
Yukarıda belirtildiği gibi, talimat çifti başlatma işleminde CiOcoObject::Init() yöntemi devreye girer.
Aslında, talimat çiftini kendisi yerleştirir ve yeni çift oluşumunun başarısını veya başarısızlığını kaydeder. Alım satım işlemlerini kendisi yaptığı için bunun aktif bir yöntem olduğunu söylemeliyim. Pasif yöntem de oluşturabiliriz. Bağımsız olarak yerleştirilmiş, halihazırda aktif bekleyen talimat çiftine bağlanacaktır.
Tüm yöntemin kodunu vermeyeceğim. Ancak, tüm fiyatları (açılış, durdur, kar, limit) hesaplamanın önemli olduğunu belirtmek isterim, bu nedenle CTrade::OrderOpen() alım satım sınıfı yöntemi bir alım satım talimatı gerçekleştirebilir. Bu amaçla iki şeyi dikkate almalıyız: talimat yönü (alış veya satış) ve bir talimat uygulama fiyatının cari bir fiyata (yukarı veya aşağı) göre pozisyonu.
Bu yöntem birkaç özel yöntemi çağırır: BaseOrderType() ve PendingType(). Birincisi talimat yönünü, ikincisi ise bekleyen talimat türünü belirler.
Talimat verilirse bileti m_order_tickets[] dizisine kaydedilir.
Bu yöntemi test etmek için basit bir Init_OCO.mq5 script dosyası kullandım.
#property script_show_inputs //--- #include "CiOcoObject.mqh" //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ sinput string Info_order1="+===--Order 1--====+"; // +===--Order 1--====+ input ENUM_PENDING_ORDER_TYPE InpOrder1Type=PENDING_ORDER_TYPE_SELL_LIMIT; // Type input double InpOrder1Volume=0.02; // Volume input uint InpOrder1PriceOffset=125; // Offset for execution price, points input uint InpOrder1LimitOffset=50; // Offset for limit price, points input uint InpOrder1SL=250; // Stop loss, points input uint InpOrder1TP=455; // Profit, points input string InpOrder1Comment="OCO Order 1"; // Comment //--- sinput string Info_order2="+===--Order 2--====+"; // +===--Order 2--====+ input ENUM_PENDING_ORDER_TYPE InpOrder2Type=PENDING_ORDER_TYPE_SELL_STOP; // Type input double InpOrder2Volume=0.04; // Volume input uint InpOrder2PriceOffset=125; // Offset for execution price, points input uint InpOrder2LimitOffset=50; // Offset for limit price, points input uint InpOrder2SL=275; // Stop loss, points input uint InpOrder2TP=300; // Profit, points input string InpOrder2Comment="OCO Order 2"; // Comment //--- globals CiOcoObject myOco; SOrderProperties gOrdersProps[2]; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- property of the 1st order gOrdersProps[0].order_type=InpOrder1Type; gOrdersProps[0].volume=InpOrder1Volume; gOrdersProps[0].price_offset=InpOrder1PriceOffset; gOrdersProps[0].limit_offset=InpOrder1LimitOffset; gOrdersProps[0].sl=InpOrder1SL; gOrdersProps[0].tp=InpOrder1TP; gOrdersProps[0].comment=InpOrder1Comment; //--- property of the 2nd order gOrdersProps[1].order_type=InpOrder2Type; gOrdersProps[1].volume=InpOrder2Volume; gOrdersProps[1].price_offset=InpOrder2PriceOffset; gOrdersProps[1].limit_offset=InpOrder2LimitOffset; gOrdersProps[1].sl=InpOrder2SL; gOrdersProps[1].tp=InpOrder2TP; gOrdersProps[1].comment=InpOrder2Comment; //--- initialization of pair if(myOco.Init(gOrdersProps)) PrintFormat("Id of new OCO pair: %I32u",myOco.Id()); else Print("Error when placing OCO pair!"); }
Burada çiftin gelecekteki talimatlarının çeşitli özelliklerini ayarlayabilirsiniz. MetaTrader 5, altı farklı bekleyen talimat türüne sahiptir.
Bu bağlamda, çiftlerin 15 varyant (kombinasyonu) olabilir (çiftte farklı talimatı olması şartıyla).
C(k,N) = C(2,6) = 15
Tüm varyantlar script dosyası yardımıyla test edilmiştir. Satın Al Durdur - Satın Al Durdur Sınırı çifti için bir örnek vereceğim.
Talimat türleri script dosyası parametrelerinde belirtilmelidir (Şekil 3).
Şek. 3. "Satın Al Durdur Sınırı" talimatı ile "Satın Al Durdur" talimatı çifti
"Expert'ler" kaydında aşağıdaki bilgiler görünecektir:
QO 0 17:17:41.020 Init_OCO (GBPUSD.e,M15) Code of request result: 10009 JD 0 17:17:41.036 Init_OCO (GBPUSD.e,M15) New order ticket: 24190813 QL 0 17:17:41.286 Init_OCO (GBPUSD.e,M15) Code of request result: 10009 JH 0 17:17:41.286 Init_OCO (GBPUSD.e,M15) New order ticket: 24190814 MM 0 17:17:41.379 Init_OCO (GBPUSD.e,M15) Id of new OCO pair: 3782950319
Ancak, döngüye başvurmadan script dosyası yardımıyla OCO talimatlarıyla en üst düzeyde çalışamayız.
2.4. Çiftin Başlatılmaması
Bu yöntem, talimat çifti üzerindeki kontrolden sorumludur. Herhangi bir talimat aktif talimatlar listesinden çıktığında çift "ölecektir".
Bu yöntemin EA kodunun OnTrade() veya OnTradeTransaction() tanıtıcılarına yerleştirilmesi gerektiğini düşünüyorum. Bu şekilde, EA herhangi bir çift talimatının aktivasyonunu gecikmeden işleyebilecektir.
//+------------------------------------------------------------------+ //| Deinitialization of pair | //+------------------------------------------------------------------+ bool CiOcoObject::Deinit(void) { //--- if pair is initialized if(this.m_is_init) { //--- check your orders for(int ord_idx=0;ord_idx<ArraySize(this.m_order_tickets);ord_idx++) { //--- current pair order ulong curr_ord_ticket=this.m_order_tickets[ord_idx]; //--- another pair order int other_ord_idx=!ord_idx; ulong other_ord_ticket=this.m_order_tickets[other_ord_idx]; //--- COrderInfo order_obj; //--- if there is no current order if(!order_obj.Select(curr_ord_ticket)) { PrintFormat("Order #%d is not found in active orders list.",curr_ord_ticket); //--- attempt to delete another order if(order_obj.Select(other_ord_ticket)) { CTrade trade_obj; //--- if(trade_obj.OrderDelete(other_ord_ticket)) return true; } } } } //--- return false; }
Bir ayrıntıdan bahsetmek istiyorum. Çift başlatma bayrağı, sınıf yönteminin gövdesinde kontrol edilir. Bayrak temizlenmişse talimatları kontrol etme girişimi yapılmayacaktır. Bu yaklaşım, henüz başka bir talimat verilmemişken bir aktif talimatın silinmesini önler.
Birkaç talimatın verildiği script dosyasına işlevsellik ekleyelim. Bu amaçla, Control_OCO_EA.mq5 test EA'sını oluşturacağız.
Genel konuşursak EA script dosyasından yalnızca kodundaki Trade() olay işleme bloğu ile farklılık gösterecektir:
//+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade() { //--- OCO pair deinitialization if(myOco.Deinit()) { Print("No more order pair!"); //--- clear pair CiOcoObject new_oco; myOco=new_oco; } }
Video, MetaTrader 5 terminalindeki her iki programın çalışmasını gösterir.
Ancak her iki test programının da zayıf yönleri vardır.
İlk program (script dosyası) yalnızca çifti aktif olarak oluşturabilir ancak daha sonra üzerindeki kontrolünü kaybeder.
İkinci program (Expert Advisor) çifti kontrol etse de, ilkinin oluşturulmasından sonra tekrar tekrar başka çiftler oluşturamaz. OCO talimat programını (script dosyası) tam özellikli hale getirmek için araç setini talimat verme fırsatı ile genişletmemiz gerekiyor. Bunu bir sonraki bölümde yapacağız.
3. EA'yı Kontrol Etme
Çiftli talimatların yerleştirilmesi ve parametrelerinin ayarlanması için grafik üzerinde OCO Talimat Yönetim Paneli oluşturalım.
Kontrol eden EA'nın bir parçası olacaktır (Şekil 4). Kaynak kodu Panel_OCO_EA.mq5 içinde bulunur.
Şek. 4. OCO talimatlarını oluşturma paneli: başlangıç durumu
Gelecekteki bir talimatın türünü seçmeli ve OCO talimat çiftini yerleştirmek için alanları doldurmalıyız.
Ardından panel üzerindeki tek buton üstündeki etiket değişecektir (metin özelliği, Şekil 5).
Şek. 5. OCO talimatlarını oluşturma paneli: yeni çift
Panelimizi oluşturmak için aşağıdaki Standart Kitaplık sınıfları kullanıldı:
- CAppDialog ana uygulama iletişim kutusudur;
- CPanel dikdörtgen bir etikettir;
- CLabel bir metin etiketidir;
- CComboBox açılır liste içeren bir alandır;
- CEdit bir giriş alanıdır;
- CButton bir butondur.
Tabii ki, üst sınıf yöntemleri otomatik olarak çağrıldı.
Şimdi koda geçiyoruz. Standart Kitaplık'nin gösterge panelleri ve diyaloglar oluşturmaya ayrılmış bölümünün oldukça büyük olduğu belirtilmelidir.
Örneğin bir açılır liste kapatma olayını yakalamak istiyorsanız, çağrı yığınını derinlemesine araştırmanız gerekecektir (Şekil 6).
Şek. 6. Çağrı Yığını
Bir geliştirici, belirli olaylar için %MQL5\\Include\\Controls\\Defines.mqh dosyasında makroları ve bir gösterimi ayarlar.
OCO çiftini oluşturmak için ON_OCO özel olayını oluşturdum.
#define ON_OCO (101) // OCO pair creation event
Gelecekteki talimatların parametreleri doldurulur ve çift OnChartEvent() tanıtıcı gövdesinde oluşturulur.
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- handling all chart events by main dialog myDialog.ChartEvent(id,lparam,dparam,sparam); //--- drop-down list handling if(id==CHARTEVENT_CUSTOM+ON_CHANGE) { //--- if it is Panel list if(!StringCompare(StringSubstr(sparam,0,7),"myCombo")) { static ENUM_PENDING_ORDER_TYPE prev_vals[2]; //--- list index int combo_idx=(int)StringToInteger(StringSubstr(sparam,7,1))-1; ENUM_PENDING_ORDER_TYPE curr_val=(ENUM_PENDING_ORDER_TYPE)(myCombos[combo_idx].Value()+2); //--- remember order type change if(prev_vals[combo_idx]!=curr_val) { prev_vals[combo_idx]=curr_val; gOrdersProps[combo_idx].order_type=curr_val; } } } //--- handling input fields else if(id==CHARTEVENT_OBJECT_ENDEDIT) { //--- if it is Panel's input field if(!StringCompare(StringSubstr(sparam,0,6),"myEdit")) { //--- find object for(int idx=0;idx<ArraySize(myEdits);idx++) { string curr_edit_obj_name=myEdits[idx].Name(); long curr_edit_obj_id=myEdits[idx].Id(); //--- if names coincide if(!StringCompare(sparam,curr_edit_obj_name)) { //--- get current value of field double value=StringToDouble(myEdits[idx].Text()); //--- define gOrdersProps[] array index int order_num=(idx<gEditsHalfLen)?0:1; //--- define gOrdersProps structure field number int jdx=idx; if(order_num) jdx=idx-gEditsHalfLen; //--- fill up gOrdersProps structure field switch(jdx) { case 0: // volume { gOrdersProps[order_num].volume=value; break; } case 1: // execution { gOrdersProps[order_num].price_offset=(uint)value; break; } case 2: // limit { gOrdersProps[order_num].limit_offset=(uint)value; break; } case 3: // stop { gOrdersProps[order_num].sl=(uint)value; break; } case 4: // profit { gOrdersProps[order_num].tp=(uint)value; break; } } } } //--- OCO pair creation flag bool is_to_fire_oco=true; //--- check structure filling for(int idx=0;idx<ArraySize(gOrdersProps);idx++) { //--- if order type is set if(gOrdersProps[idx].order_type!=WRONG_VALUE) //--- if volume is set if(gOrdersProps[idx].volume!=WRONG_VALUE) //--- if offset for execution price is set if(gOrdersProps[idx].price_offset!=(uint)WRONG_VALUE) //--- if offset for limit price is set if(gOrdersProps[idx].limit_offset!=(uint)WRONG_VALUE) //--- if stop loss is set if(gOrdersProps[idx].sl!=(uint)WRONG_VALUE) //--- if take profit is set if(gOrdersProps[idx].tp!=(uint)WRONG_VALUE) continue; //--- clear OCO pair creation flag is_to_fire_oco=false; break; } //--- create OCO pair? if(is_to_fire_oco) { //--- complete comment fields for(int ord_idx=0;ord_idx<ArraySize(gOrdersProps);ord_idx++) gOrdersProps[ord_idx].comment=StringFormat("OCO Order %d",ord_idx+1); //--- change button properties myButton.Text("New pair"); myButton.Color(clrDarkBlue); myButton.ColorBackground(clrLightBlue); //--- respond to user actions myButton.Enable(); } } } //--- handling click on button else if(id==CHARTEVENT_OBJECT_CLICK) { //--- if it is OCO pair creation button if(!StringCompare(StringSubstr(sparam,0,6),"myFire")) //--- if to respond to user actions if(myButton.IsEnabled()) { //--- generate OCO pair creation event EventChartCustom(0,ON_OCO,0,0.0,"OCO_fire"); Print("Command to create new bunch has been received."); } } //--- handling new pair initialization command else if(id==CHARTEVENT_CUSTOM+ON_OCO) { //--- OCO pair initialization if(gOco.Init(gOrdersProps,gOcoList.Total()+1)) { PrintFormat("Id of new OCO pair: %I32u",gOco.Id()); //--- copy pair CiOcoObject *ptr_new_oco=new CiOcoObject(gOco); if(CheckPointer(ptr_new_oco)==POINTER_DYNAMIC) { //--- add to list int node_idx=gOcoList.Add(ptr_new_oco); if(node_idx>-1) PrintFormat("Total number of bunch: %d",gOcoList.Total()); else PrintFormat("Error when adding OCO pair %I32u to list!",gOco.Id()); } } else Print("OCO-orders placing error!"); //--- clear properties Reset(); } }
Tanıtıcı kodu küçük değil. Birkaç blok üzerinde durmak istiyorum.
Tüm grafik olaylarının ilk işlenmesi ana iletişim kutusuna verilir.
Sonraki, çeşitli olayların ele alındığı bloklardır:
- Bir talimat türünün tanımlanması için açılır listelerin değiştirilmesi;
- Talimatların özelliklerini doldurmak için giriş alanlarını düzenleme;
- ON_OCO olay oluşturma için düğmeye tıklayın;
- ON_OCO olay yanıtı: talimat çifti oluşturma.
EA, panelin alanlarının doldurulmasının doğruluğunu doğrulamaz. Bu yüzden değerleri kendimiz kontrol etmeliyiz, aksi takdirde EA OCO talimatları yerleştirme hatası gösterecektir.
OnTrade() tanıtıcı gövdesinde çifti kaldırma ve kalan talimatı kapatma gerekliliği kontrol edilir.
Sonuç
Belirli görevlerin yerine getirilmesi için kullanılabilecek Standart Kitaplık sınıflarının zenginliklerini göstermeye çalıştım.
Özellikle, OCO talimatlarının işlenmesiyle ilgili bir sorunla uğraşıyorduk. OCO talimatlarının işlenmesi için Panelli EA kodunun, daha karmaşık talimat çiftlerinin oluşturulması açısından bir başlangıç noktası olacağını umuyorum.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/1582
![MetaTrader 5'te RSS Beslemelerini Görüntülemek için Etkileşimli Bir Uygulama Oluşturma](https://c.mql5.com/2/17/RSS_Feed_MetaTrader5__1.png)
![Teknik Analiz ve Piyasa Tahmini Yöntemleri Üzerine](https://c.mql5.com/2/17/982_30.gif)
![Fiyat Yönüne ve Hareket Hızına Dayalı Alım Satım Fikirleri](https://c.mql5.com/2/18/zbm4cy.png)
![Yatırımcıların Altın Kuralı](https://c.mql5.com/2/12/1021_34.png)
![MQL5 - MetaTrader 5 müşteri terminalinde yerleşik ticaret stratejileri dili](https://c.mql5.com/i/registerlandings/logo-2.png)
- Ücretsiz ticaret 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