Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 11): Çapraz emir sistemi

Daniel Jose | 28 Kasım, 2022

Giriş

Yatırımcıların hayatını çok zorlaştıran bir varlık türü vardır - vadeliler. Peki neden hayatı zorlaştırıyorlar? Çünkü vadelilerin sona erme zamanları vardır, bu zamanda vadelinin mevcut sözleşmesi sona erer, yeni bir sözleşme oluşturulur ve bu yeni sözleşme işlem görmeye başlar. Esasen, vadelinin mevcut sözleşmesi sona erdiğinde, üzerinde gerçekleştirdiğimiz tüm analizleri bitirmemiz, her şeyi bir şablon olarak kaydetmemiz ve analize devam etmek adına bu şablonu yeni sözleşmeye aktarmamız gerekir. Bu, bu tür varlıkların ticaretini yapan herkes için yaygın bir durumdur, ancak vadelilerin de bir fiyat geçmişi vardır ve bu geçmişi kullanarak onları süregiden bir şekilde analiz edebiliriz.

Profesyonel yatırımcılar belirli geçmiş bilgileri analiz etmeyi severler, bu tür varlıklar için ise böyle bir durum ikinci bir grafik gerektirir. Ancak uygun araçlar kullanılırsa ikinci bir grafiğe gerek kalmaz. İşte bu araçlardan biri çapraz emir sistemidir.


Planlama

Bu serinin ilk makalesinde bu emirlerden bahsetmiştik ancak uygulamaya geçmemiştik. O makalede başka şeylere odaklanmıştık, MetaTrader 5 platformunda çalışabilecek eksiksiz bir sistem oluşturmak amacıyla bir giriş yapmıştık. Bu makalede ise bu işlevselliğin nasıl uygulanacağını inceleyeceğiz.

Böyle bir işlevselliği oluşturmanın arkasındaki nedenini daha iyi anlamak adına aşağıdaki iki görüntüye bir göz atalım:

           

Soldaki görüntü tipik bir vadeli sözleşmesidir, grafikten de görülebileceği gibi birkaç gün önce başlamış olan WDO’dur (Mini Dollar). Sağdaki grafik ise aynı sözleşmeye ek olarak süresi dolmuş sözleşmelerin fiyatlarını da içermektedir, dolayısıyla sağdaki grafik bir fiyat geçmişi grafiğidir. Bu nenenle, sağdaki grafik, ilgili vadeliyi analiz etmek konusunda çok daha uygun ve kullanışlıdır. Ancak ticaret işlemleri gerçekleştirmek istediğimizde karşımıza bir sorun ortaya çıkmaktadır. Bu sorun aşağıda gösterilmektedir:

          

İşlem gören sembolün ticaret arayüzünde belirtildiğine dikkat edin. Fiyat geçmişi grafiği kullanılırken bile ticaret arayüzü emrin hangi sembol için gönderileceğini bilir - bunu görüntülerde gösterilen araç kutusundan görebilirsiniz. Sorun şu ki, soldaki görüntüde, grafikte, mevcut sözleşme için oluşturulmuş emri görebiliyoruz, ancak sağdaki görüntüde, emir yalnızca araç kutusunda görülebilirken, grafikte hiçbir şey görünmemektedir.

Bunun sadece bir görüntüleme sorunu olduğunu düşünebilirsiniz, ancak hayır, mesele daha karmaşıktır. Bu makalede ele alacağımız konu işte budur.

Önemli! Burada analiz için fiyat geçmişini kullanabilmek adına finansal enstrümanın adlandırma kuralının nasıl oluşturulacağını göreceğiz. Bizim durumumuzda, bu kural Brezilya borsasında (B3) işlem gören WDO (Mini Dollar), WIN (Mini Indeks), DOL (Dollar) ve IND (Indeks) vadelileri için olacaktır. Ancak doğru anlayış, ilgili kuralın dünyanın herhangi bir borsasındaki herhangi bir vadeliye uyarlanmasına olanak tanıyacaktır.

Sistemin kendisi şu ya da bu varlıkla sınırlı değildir, her şey kodun doğru kısımlarını uyarlamakla ilgilidir. Bu doğru bir şekilde yapılırsa, vadeli sözleşmesinin süresinin sona ermek üzere olup olmadığı ve bir sonraki sözleşmenin ne olacağı konusunda endişelenmemizi ortadan kaldıracak bir Uzman Danışmana sahip olacağız - Uzman Danışman bunu bizim için, sözleşmeyi gerektiği anda doğru olanla değiştirerek yapacaktır.


Kuralı anlama

WDO (Mini Dollar), WIN (Mini Indeks), DOL (Dollar) ve IND (Indeks) vadelileri, sözleşme özellikleri açısından özel bir adlandırma kuralına tabidir. İlk olarak, sözleşmenin sona erme zamanını nasıl öğreneceğimizi görelim:


Vurgulanan alanlara dikkat edin: mavi alan, sözleşmenin sona erme zamanının özetini (gününü) gösterir, kırmızı alan ise sözleşmenin sona ereceği ve artık işlem görmeyeceği tam zamanı gösterir. Bunu bilmek çok önemlidir.

Sözleşmenin sona erme zamanı sözleşmenin kendisinde belirtilirken, görülebileceği gibi, aynı şey sözleşmenin adı için geçerli değildir. Sözleşmenin adı, piyasada kullanılan bir katı kural olan ilgili adlandırma kuralına göre türetilebilir. Ele aldığımız vadeliler söz konusu olduğunda, aşağıdakiler geçerlidir:

İlk üç harf sözleşmenin türünü gösterir:

Kod Sözleşme
WIN Mini Ibovespa vadelisi 
IND Ibovespa vadelisi
WDO Mini dolar vadelisi
DOL Dolar vadelisi

Bu kodun ardından sözleşmenin sona erme ayını gösteren bir harf gelir:

Sona erme ayı WDO ve DOL için harf WIN ve IND için harf 
Ocak F
 
Şubat  G  G
Mart  H  
Nisan  J  J
Mayıs  K  
Haziran  M  M
Temmuz  N  
Ağustos  Q  Q
Eylül  U  
Ekim  V  V
Kasım  X  
Aralık  Z  Z

Ve son olarak, sözleşmenin sona erme yılını temsil eden iki rakam yer alır. Örneğin, Nisan 2022'de sona eren bir dolar vadeli sözleşmesi DOLJ22 olarak gösterilir. Bu, mayıs ayının başına kadar işlem görebilecek sözleşmedir. Mayıs ayı başladığında ilgili sözleşme sona erecektir. Bu kural WIN ve IND için biraz farklılık gösterir, sözleşme belirtilen ayın 15'ine en yakın çarşamba günü sona erer. Dolayısıyla, kural biraz karmaşıktır, ancak Uzman Danışman bunu başarılı bir şekilde yönetebilecek ve her zaman doğru sözleşmeyi sağlayacaktır.


Uygulama

Uzman Danışmanımız adlandırma kuralını alma konusunda gerekli noktalara halihazırda sahiptir. Yalnızca emir gönderme sistemiyle ilgili birkaç düzenleme yapmamız gerekecek. O zaman hadi başlayalım. Öncelikle, aşağıdaki kodu C_Terminal nesne sınıfına ekleyelim:

void CurrentSymbol(void)
{
        MqlDateTime mdt1;
        string sz0, sz1;
        datetime dt = TimeLocal();
            
        sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);                           
        if ((sz0 != "WDO") && (sz0 != "DOL") && (sz0 != "WIN") && (sz0 != "IND")) return;
        sz1 = ((sz0 == "WDO") || (sz0 == "DOL") ? "FGHJKMNQUVXZ" : "GJMQVZ");
        TimeToStruct(TimeLocal(), mdt1);
        for (int i0 = 0, i1 = mdt1.year - 2000;;)
        {
                m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1);
                if (i0 < StringLen(sz1)) i0++; else
                {
                        i0 = 0;
                        i1++;
                }
                if (macroGetDate(dt) < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_EXPIRATION_TIME))) break;
        }
}

Bu kod, varlık adını oluşturmak için yukarıda bahsettiğimiz adlandırma kuralını kullanır. Her zaman mevcut sözleşmeyi kullandığımızdan emin olmak için, vurgulanan satırda gösterilen kontrolü uygulayacağız ve sonucunda Uzman Danışman oluşturulan adı kullanacaktır. Başka vadelilerle çalışmak istiyorsanız tek yapmanız gereken, varlık adının doğru bir şekilde oluşturulması adına kodu uyarlamaktır.

Şimdi emir detaylarının yer aldığı kısma geçelim. Sistemi eğer şu anda mevcut geliştirme safhasında kullanırsak, aşağıdaki davranışı görürüz:


Başka bir deyişle, çoktan çapraz emir moduna sahibiz, ancak henüz tam olarak hazır değildir - yerleştirilen emir grafik üzerinde gösterilmemektedir. Bu pek çok kişinin sandığı kadar yapılması zor bir şey değildir, tek yapmamız gereken yerleştirilen emirleri yatay çizgiler şeklinde belirtmektir. Ancak hepsi bu kadar değildir, yerleştirilen emirler hareket ettirilirken de sorunsuz şekilde grafik üzerinde görüntülenmelidir. Dolayısıyla, çapraz emirleri kullandığımızda, MetaTrader 5’in bize sağladığı bazı şeyleri kaybederiz, bu nedenle emir sisteminin güvenli ve istikrarlı bir şekilde çalışabilmesi için bu eksik mantığı tekrar uygulamamız gerekiyor. Aksi takdirde, çapraz emirleri kullanırken sorunlar yaşayabiliriz.

Evet, bu açıdan bakıldığında işler çok da basit görünmüyor. MetaTrader platformunun orijinal olarak sunduğu tüm mantığı yeniden oluşturmamız gerekeceğinden, aslında o kadar da basit değildir. Bu nedenle, yapılacak ilk şey dahili MetaTrader sistemini unutmaktır - çapraz emir sistemini kullanmaya başladığımız andan itibaren bizi desteklemeyecektir.

Şu andan itibaren, emirlerin manipülasyonunu emir fişleri üzerinden gerçekleştireceğiz. Ancak bunun bazı olumsuz sonuçları vardır. En sinir bozucu olanlarından biri, yatırımcının grafiğe kaç emir yerleştirdiğini bilemememizdir. Emir sayısının sınırlandırılması da yatırımcı için kesinlikle hoş olmayacaktır. Dolayısıyla, yatırımcının sistemi MetaTrader’ın sunduğu desteği kullanıyormuş gibi kullanabilmesini sağlamak adına bir şeyler yapmamız gerekiyor.


C_HLineTrade sınıfı

Bu sorunu çözmek için yeni bir sınıf oluşturacağız: C_HLineTrade sınıfı. Bu sınıf, MetaTrader’ın yerleştirilen emirlerin grafik üzerinde görüntülenmesini sağlayan sisteminin yerini alacaktır. Öyleyse, sınıfın bildirilmesiyle başlayalım:

class C_HLineTrade
{
#define def_NameHLineTrade "*HLTSMD*"
        protected:
                enum eHLineTrade {HL_PRICE, HL_STOP, HL_TAKE};
        private :
                color   m_corPrice,
                        m_corStop,
                        m_corTake;
                string  m_SelectObj;

Burada birkaç şeyin tanımlandığına dikkat edin - bunlar kod boyunca sıkça kullanılacaktır. Bu yüzden lütfen sonraki değişiklikler konusunda çok dikkatli olun - çok sayıda değişiklik olacaktır. Şimdi sınıfın yapıcısını ve yıkıcısını bildiriyoruz:
C_HLineTrade() : m_SelectObj("")
{
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false);
        RemoveAllsLines();
};
//+------------------------------------------------------------------+  
~C_HLineTrade()
{
        RemoveAllsLines();
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, true);
};

Yapıcı, orijinal çizgilerin görüntülenmesini engeller, yıkıcı ise onları grafiğe yeniden koyar. Her ikisinin de aşağıdaki gibi ortak bir fonksiyonu vardır:

void RemoveAllsLines(void)
{
        string sz0;
        int i0 = StringLen(def_NameHLineTrade);
                                
        for (int c0 = ObjectsTotal(Terminal.Get_ID(), -1, -1); c0 >= 0; c0--)
        {
                sz0 = ObjectName(Terminal.Get_ID(), c0, -1, -1);
                if (StringSubstr(sz0, 0, i0) == def_NameHLineTrade) ObjectDelete(Terminal.Get_ID(), sz0);
        }
}

Vurgulanan satır, nesnenin (yatay çizginin) sınıf tarafından kullanılan nesnelerden biri olup olmadığını kontrol eder. Eğer öyleyse, nesneyi siler. Kaç tane nesnemiz olduğunu bilmediğimize dikkat edin, ancak sistem, sınıf tarafından oluşturulan nesnelerin hepsini temizlemek adına nesneleri tek tek kontrol edecektir. Sınıftaki bir sonraki öne çıkan fonksiyon aşağıda görülmektedir:

inline void SetLineOrder(ulong ticket, double price, eHLineTrade hl, bool select)
{
        string sz0 = def_NameHLineTrade + (string)hl + (string)ticket, sz1;
                                
        if (price <= 0)
        {
                ObjectDelete(Terminal.Get_ID(), sz0);
                return;
        }
        if (!ObjectGetString(Terminal.Get_ID(), sz0, OBJPROP_TOOLTIP, 0, sz1))
        {
                ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0);
                ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_COLOR, (hl == HL_PRICE ? m_corPrice : (hl == HL_STOP ? m_corStop : m_corTake)));
                ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_WIDTH, 1);
                ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_STYLE, STYLE_DASHDOT);
                ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTABLE, select);
                ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTED, false);
                ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_BACK, true);
                ObjectSetString(Terminal.Get_ID(), sz0, OBJPROP_TOOLTIP, (string)ticket + " "+StringSubstr(EnumToString(hl), 3, 10));
        }
        ObjectSetDouble(Terminal.Get_ID(), sz0, OBJPROP_PRICE, price);
}
Bu fonksiyon için, çağrı anında nesne var olup olmadığı önemli değildir. Çizginin oluşturulmasını ve doğru yerde yerleştirilmesini sağlar. Oluşturulan çizgi, orijinal olarak MetaTrader'da kullanılan çizginin yerini alır.

Amacımız çizgilerin güzel görünmesinden ziyade işlevsel olmasını sağlamaktır. Dolayısıyla çizgiler oluşturulduklarında seçili olmazlar - isterseniz bu davranışı değiştirebilirsiniz. Çizgileri konumlandırmak için MetaTrader 5 mesajlaşma sistemini kullanıyoruz. Bu yüzden onları hareket ettirebilmek için bunu açıkça belirtmemiz gerekecektir. Hangi çizginin ayarlandığını belirtmek için başka bir fonksiyonumuz vardır:

inline void Select(const string &sparam)
{
        int i0 = StringLen(def_NameHLineTrade);
                                
        if (m_SelectObj != "") ObjectSetInteger(Terminal.Get_ID(), m_SelectObj, OBJPROP_SELECTED, false);
        m_SelectObj = "";
        if (StringSubstr(sparam, 0, i0) == def_NameHLineTrade)
        {
                if (ObjectGetInteger(Terminal.Get_ID(), sparam, OBJPROP_SELECTABLE))
                {
                        ObjectSetInteger(Terminal.Get_ID(), sparam, OBJPROP_SELECTED, true);
                        m_SelectObj = sparam;
                };
        }
}
Bu fonksiyon çizgi seçimini gerçekleştirir. Başka bir çizgi seçildiğinde, öncekinin seçimini iptal eder. Bu oldukça basittir. Fonksiyon sadece sınıf tarafından işlenen çizgileri manipüle edecektir. Bu sınıfın üzerinde durulmayı hak eden son fonksiyonu da aşağıda yer almaktadır:
bool GetNewInfosOrder(const string &sparam, ulong &ticket, double &price, eHLineTrade &hl)
{
        int i0 = StringLen(def_NameHLineTrade);
                                
        if (StringSubstr(sparam, 0, i0) == def_NameHLineTrade)
        {
                hl = (eHLineTrade) StringToInteger(StringSubstr(sparam, i0, 1));
                ticket = (ulong)StringToInteger(StringSubstr(sparam, i0 + 1, StringLen(sparam)));
                price = ObjectGetDouble(Terminal.Get_ID(), sparam, OBJPROP_PRICE);
                return true;
        }
        return false;
}
Bu fonksiyon belki de bu sınıftaki en önemli fonksiyondur: grafikte kaç çizgi olduğunu bilmediğimiz için, kullanıcının hangi çizgiyi manipüle ettiğini bilmemiz gerekiyor. Bu fonksiyon da tam olarak bunu yapar - sisteme kullanıcı tarafından manipüle edilen çizginin verilerinin ne olduğunu söyler.

Pekala, bu, yapmamız gerekenlerin yalnızca küçük bir kısmıydı. Sistem hâlâ tam olarak işlevsel olmaktan uzaktır. O halde bir sonraki adıma geçelim - emir yönlendirmeden sorumlu olan C_Router sınıfının fonksiyonlarını ekleyip düzenleyeceğiz. Bu sınıf, C_HLineTrade sınıfında oluşturduğumuz işlevselliği devralacaktır. Bunu aşağıdaki koddan görebiliriz:

#include "C_HLineTrade.mqh"
//+------------------------------------------------------------------+
class C_Router : public C_HLineTrade



Yeni C_Router sınıfı

Orijinal C_Router sınıfında yalnızca bir açık pozisyona sahip olma sınırlaması vardı. Bu sınırlamayı kaldıracağız, bu amaçla C_Router sınıfında önemli değişiklikler yapacağız.

İlk değişiklik, sınıfın güncelleme fonksiyonundadır:

void UpdatePosition(void)
{
        static int memPositions = 0, memOrder = 0;
        ulong ul;
        int p, o;
                                
        p = PositionsTotal() - 1;
        o = OrdersTotal() - 1;
        if ((memPositions != p) || (memOrder != o))
        {
                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                RemoveAllsLines();
                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                memOrder = o;
                memPositions = p;
        };
        for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                ul = PositionGetInteger(POSITION_TICKET);
                SetLineOrder(ul, PositionGetDouble(POSITION_PRICE_OPEN), HL_PRICE, false);
                SetLineOrder(ul, PositionGetDouble(POSITION_TP), HL_TAKE, true);
                SetLineOrder(ul, PositionGetDouble(POSITION_SL), HL_STOP, true);
        }
        for (int i0 = o; i0 >= 0; i0--) if ((ul = OrderGetTicket(i0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol())
        {
                SetLineOrder(ul, OrderGetDouble(ORDER_PRICE_OPEN), HL_PRICE, true);
                SetLineOrder(ul, OrderGetDouble(ORDER_TP), HL_TAKE, true);
                SetLineOrder(ul, OrderGetDouble(ORDER_SL), HL_STOP, true);
        }
};

Öncesinde bu fonksiyon sadece tek bir açık pozisyondan verileri topluyordu. Artık fonksiyon tüm pozisyonları ve emirleri grafikte görüntüleyecektir. Bu kesinlikle MetaTrader tarafından sunulan sistemin yerine geçecektir. Ciddi bir konu olduğu için bu fonksiyonun çok iyi anlaşılması gerekiyor çünkü başarısız olursa tüm çapraz emir sistemini etkileyecektir. Dolayısıyla, bu sistem bir gerçek hesapta kullanılmadan önce bir demo hesapta test edilmelidir. Bu tür sistemler, her şeyin olması gerektiği gibi çalıştığından tam anlamıyla emin olana kadar uygun şekilde tekrar ve tekrar test edilmelidir. Sistemimizin çalışma şekli MetaTrader’ın çalışma şeklinden biraz farklı olduğundan sistemimizi gerektiği gibi yapılandırmamız gerekiyor.

Vurgulanan satırlara bakın ve dürüstçe cevap verin: Gerçekte ne yaptıklarına dair bir fikriniz var mı? Bu iki satırın orada bulunmasının nedeni, bu makalenin bir sonraki bölümünde, C_OrderView sınıfından bahsederken anlaşılacaktır. Bu iki satır olmadan kod çok kararsızdır ve garip bir şekilde çalışır. Fonksiyonun geri kalanıysa oldukça basittir - C_HLineTrade nesne sınıfı aracılığıyla çizgilerin her birini oluşturur. Seçilemeyen sadece bir çizgi vardır. Bu, aşağıdaki kod parçasında gösterildiği gibi kolayca belirtilir:

SetLineOrder(ul, PositionGetDouble(POSITION_PRICE_OPEN), HL_PRICE, false);

Sistem çok basit ve anlaşılır hale geldi. Fonksiyon, OnTrade'deki bir olay sırasında Uzman Danışman tarafından çağrılır:

C_TemplateChart Chart;

// ... Expert Advisor code ...

void OnTrade()
{
        Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, Chart.UpdateRoof(), C_Chart_IDE::szMsgIDE[C_Chart_IDE::eROOF_DIARY]);
        Chart.UpdatePosition();
}

// ... The rest of the Expert Advisor code ...

Vurgulanan kod, ekrandaki emir sisteminin güncellenmesini sağlayacaktır. Bunun için C_TemplateChart sınıfını kullandığımıza dikkat edin - bunun nedeni sistemin sınıf yapısının değişmiş olmasıdır. Yeni yapı aşağıda gösterilmektedir:

Bu yapı, Uzman Danışmanda yönlü bir mesaj akışı sağlar. Mesaj akışının belirli bir sınıfa nasıl ulaştığı konusunda şüpheye düştüğümüzde, bu sınıf kalıtım grafiğine bakabiliriz. public olan tek sınıf C_Terminal nesne sınıfıdır, diğer tüm sınıflar ise aralarındaki kalıtımla işlenir ve sistemde hiçbir değişken public değildir.

Sistem artık yalnızca tek bir pozisyonu analiz etmediği için dikkat edilmesi gereken bir şey daha vardır: Ticaret işlemlerin sonucu nasıl elde edilecek? Peki bu neden önemli? Yalnızca tek bir açık pozisyon olduğunda, sistem her şeyi kolayca anlayabilmektedir. Ancak açık pozisyonların sayısı artınca, tüm pozisyonlarda neler olup bittiğinin anlaşılması gerekli hale gelir. İşte bu bilgiyi sağlayan fonksiyon:

void OnTick()
{
        Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, Chart.CheckPosition(), C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT]);
}

Pek bir değişiklik görünmüyor. Vurgulanan fonksiyonun koduna bir göz atalım:

inline double CheckPosition(void)
{
        double Res = 0, last, sl;
        ulong ticket;
                        
        last = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_LAST);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                ticket = PositionGetInteger(POSITION_TICKET);
                Res += PositionGetDouble(POSITION_PROFIT);
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (last < sl) ClosePosition(ticket);
                }else
                {
                        if ((last > sl) && (sl > 0)) ClosePosition(ticket);
                }
        }
        return Res;
};

Fonksiyonun vurgulanmış üç kısmı bulunmaktadır: sarı kısım açık pozisyonların sonucu hakkında bilgi verir, yeşil kısımlar ise yüksek volatilite nedeniyle zararı durdur seviyesinin kaçırılması durumuna dair pozisyonu kontrol eder, ki böyle bir durum varlığında pozisyon mümkün olan en kısa sürede kapatılmalıdır. Dolayısıyla bu fonksiyon, varlıkta bulunan tüm pozisyonların sonucunu geri döndürür.

Bu fonksiyonlara ek olarak, çapraz emir modelini kullanırken sistemin çalışmaya devam etmesine yardımcı olan başka fonksiyonlar da vardır. Üzerinde konuşulmayı hak eden ikisi aşağıda görülmektedir:

bool ModifyOrderPendent(const ulong Ticket, const double Price, const double Take, const double Stop, const bool DayTrade = true)
{
        if (Ticket == 0) return false;
        ZeroMemory(TradeRequest);
        ZeroMemory(TradeResult);
        TradeRequest.action     = TRADE_ACTION_MODIFY;
        TradeRequest.order      = Ticket;
        TradeRequest.price      = NormalizeDouble(Price, Terminal.GetDigits());
        TradeRequest.sl         = NormalizeDouble(Stop, Terminal.GetDigits());
        TradeRequest.tp         = NormalizeDouble(Take, Terminal.GetDigits());
        TradeRequest.type_time  = (DayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
        TradeRequest.expiration = 0;
        return OrderSend(TradeRequest, TradeResult);
};
//+------------------------------------------------------------------+
bool ModifyPosition(const ulong Ticket, const double Take, const double Stop)
{
        ZeroMemory(TradeRequest);
        ZeroMemory(TradeResult);
        if (!PositionSelectByTicket(Ticket)) return false;
        TradeRequest.action     = TRADE_ACTION_SLTP;
        TradeRequest.position   = Ticket;
        TradeRequest.symbol     = PositionGetString(POSITION_SYMBOL);
        TradeRequest.tp         = NormalizeDouble(Take, Terminal.GetDigits());
        TradeRequest.sl         = NormalizeDouble(Stop, Terminal.GetDigits());
        return OrderSend(TradeRequest, TradeResult);
};
Birincisi açık emri, ikinci ise açık pozisyonu değiştirir. Aynı gibi görünseler de, değillerdir. Sistemin aynı derecede önemli olan bir başka fonksiyonu daha vardır:
bool RemoveOrderPendent(ulong Ticket)
{
        ZeroMemory(TradeRequest);
        ZeroMemory(TradeResult);
        TradeRequest.action     = TRADE_ACTION_REMOVE;
        TradeRequest.order      = Ticket;       
        return OrderSend(TradeRequest, TradeResult);
};

Bu son fonksiyonla C_Router sınıfını kapatıyoruz. Böylece, MetaTrader tarafından sunulan işlevselliğin yerine geçen temel bir sistem uyguladık - çapraz emir modeli kullandığımızdan dolayı artık MetaTrader’ın orijinal desteğine güvenemeyiz. Ancak sistem henüz tamamlanmadı. Sistemin tamamen çalışır hale gelmesi için bir şey daha eklememiz gerekiyor. Sistemimizin şu anki durumuna bir göz atalım, eğer bir emir mevcutsa, aşağıdaki gibi görünecektir:



Yukarıdaki görüntüye yakından bakalım. Araç kutusu, açık emri ve hangi varlık için açıldığını göstermektedir. İşlem gören varlık da ticaret arayüzünde gösterilmektedir. Bu varlığın araç kutusunda belirtilen varlıkla aynı olduğuna dikkat edin. Şimdi, grafikte görüntülenen varlığı kontrol edelim. Bu, grafik penceresi başlığından kontrol edilebilir. Farklı olduğunu görüyoruz - grafik fiyat geçmişi grafiğidir, yani artık dahili MetaTrader sistemini değil, bu makalede açıkladığımız çapraz emir sistemini kullanıyoruz. Sistemimizde emrin nerede olduğunu görüntüleyen işlevselliğe sahibiz, ancak bu yeterli değildir. Emrin hareket ettirilmesine ilişkin olayla ilgili olarak bir şeye daha ihtiyacımız vardır. Bu başka bir sınıfta uygulanacaktır.


C_OrderView sınıfında yeni işlevsellik

C_OrderView nesne sınıfı birçok şey yapabilse de henüz pozisyonların veya emirlerin verilerini işleyememektedir. Ancak, ona bir mesajlaşma sistemi eklediğimizde, onu kullanmak için daha fazla olanağa sahip oluruz. Şimdilik sınıfa yapacağımız tek ekleme bu. Fonksiyon aşağıda tam olarak gösterilmektedir:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong           ticket;
        double          price, pp, pt, ps;
        eHLineTrade     hl;
        
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        MoveTo((int)lparam, (int)dparam, (uint)sparam);
                        break;
                case CHARTEVENT_OBJECT_DELETE:
                        if (GetNewInfosOrder(sparam, ticket, price, hl))
                        {
                                if (OrderSelect(ticket))
                                {
                                        switch (hl)
                                        {
                                                case HL_PRICE:
                                                        RemoveOrderPendent(ticket);
                                                        break;
                                                case HL_STOP:
                                                        ModifyOrderPendent(ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), 0);
                                                        break;
                                                case HL_TAKE:
                                                        ModifyOrderPendent(ticket, OrderGetDouble(ORDER_PRICE_OPEN), 0, OrderGetDouble(ORDER_SL));
                                                        break;
                                        }
                                }else if (PositionSelectByTicket(ticket))
                                {
                                        switch (hl)
                                        {
                                                case HL_PRICE:
                                                        ClosePosition(ticket);
                                                        break;
                                                case HL_STOP:
                                                        ModifyPosition(ticket, OrderGetDouble(ORDER_TP), 0);
                                                        break;
                                                case HL_TAKE:
                                                        ModifyPosition(ticket, 0, OrderGetDouble(ORDER_SL));
                                                        break;
                                        }
                                }
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:
                        C_HLineTrade::Select(sparam);
                        break;
                case CHARTEVENT_OBJECT_DRAG:
                        if (GetNewInfosOrder(sparam, ticket, price, hl))
                        {
                                price = AdjustPrice(price);
                                if (OrderSelect(ticket)) switch(hl)
                                {
                                        case HL_PRICE:
                                                pp = price - OrderGetDouble(ORDER_PRICE_OPEN);
                                                pt = OrderGetDouble(ORDER_TP);
                                                ps = OrderGetDouble(ORDER_SL);
                                                if (!ModifyOrderPendent(ticket, price, (pt > 0 ? pt + pp : 0), (ps > 0 ? ps + pp : 0))) UpdatePosition();
                                                break;
                                        case HL_STOP:
                                                if (!ModifyOrderPendent(ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), price)) UpdatePosition();
                                                break;
                                        case HL_TAKE:
                                                if (!ModifyOrderPendent(ticket, OrderGetDouble(ORDER_PRICE_OPEN), price, OrderGetDouble(ORDER_SL))) UpdatePosition();
                                                break;
                                }
                                if (PositionSelectByTicket(ticket)) switch (hl)
                                {
                                        case HL_PRICE:
                                                UpdatePosition();
                                                break;
                                        case HL_STOP:
                                                ModifyPosition(ticket, PositionGetDouble(POSITION_TP), price);
                                                break;
                                        case HL_TAKE:
                                                ModifyPosition(ticket, price, PositionGetDouble(POSITION_SL));
                                                break;
                                }
                        };
                break;
        }
}

Bu kod çapraz emir sistemini tamamlamaktadır. Böylece, çapraz emir sistemi öncesi mümkün olan şeylerin neredeyse aynısını yapabilme yeteneğine kavuşuyoruz. Genel olarak, bu fonksiyonda herhangi bir anormallik görünmüyor. Ancak çok yaygın olmayan bir olay türüne sahiptir - CHARTEVENT_OBJECT_DELETE. Grafikten bir çizgi silindiğinde, bu, grafiğe ve emir sistemine yansıtılacaktır, dolayısıyla grafikten çizgiler silerken dikkatli olunmalıdır. Ancak Uzman Danışmanı grafikten kaldırırken endişelenmemize gerek yoktur, çünkü aşağıdaki animasyonda gösterildiği gibi emirler bozulmadan kalacaktır:


Ancak, Uzman Danışman grafik üzerindeyse, grafikten çizgileri, özellikle de nesneler listesinde gizli olanları silerken çok dikkatli olmalıyız. Çapraz emir sistemi tarafından oluşturulan çizgileri sildiğimizde emir sisteminde neler olduğunu aşağıdan görebiliriz.

Şimdi ise çizgileri sürüklediğimizde emre ne olduğunu görelim. Şunu unutmayın: sürüklenen çizgi seçili olmalıdır, seçili değilse, hareket ettirmek mümkün olmayacaktır. Fiyat değişikliği, çizgi grafikte serbest bırakıldığında gerçekleşecektir, hareket sırasında fiyat eski şekilde kalacaktır.


Çizginin seçili olup olmadığını anlamak zor geliyorsa seçim kodunda değişiklik yapalım. Değişiklikler aşağıda vurgulanmaktadır:

inline void Select(const string &sparam)
{
        int i0 = StringLen(def_NameHLineTrade);
                
        if (m_SelectObj != "")
        {
                ObjectSetInteger(Terminal.Get_ID(), m_SelectObj, OBJPROP_SELECTED, false);
                ObjectSetInteger(Terminal.Get_ID(), m_SelectObj, OBJPROP_WIDTH, 1);
        }
        m_SelectObj = "";
        if (StringSubstr(sparam, 0, i0) == def_NameHLineTrade)
        {
                if (ObjectGetInteger(Terminal.Get_ID(), sparam, OBJPROP_SELECTABLE))
                {
                        ObjectSetInteger(Terminal.Get_ID(), sparam, OBJPROP_SELECTED, true);
                        ObjectSetInteger(Terminal.Get_ID(), sparam, OBJPROP_WIDTH, 2);
                        m_SelectObj = sparam;
                };
        }
}

Bu kod değişikliğinin sonucu aşağıdaki animasyondan görülebilir.


Sonuç

Bu makalede size MetaTrader'da çapraz emir sisteminin nasıl oluşturulacağını gösterdim. Umarım bu sistem, bu bilgiyi kullanacak olan herkese faydalı olur. Lütfen şunu unutmayın: Bu sistemle bir gerçek hesapta ticaret yapmaya başlamadan önce, onu birçok farklı piyasa senaryosunda mümkün olduğunca kapsamlı bir şekilde test etmelisiniz. Çünkü bu sistem MetaTrader platformunda uygulanmasına rağmen hata işleme anlamında platform tarafından neredeyse hiçbir desteği yoktur, bu nedenle tesadüfen meydana gelirse büyük kayıplar yaşamamak için hızlı hareket etmeniz gerekecektir. Sistemi farklı senaryolarda test ederek sorunların nereden kaynaklandığını öğrenebilirsiniz: bilgisayarınızın işleyebileceği maksimum emir sayısı, analiz sistemi için izin verilen maksimum spread, açık emirler için izin verilen maksimum volatilite seviyesi gibi. Çünkü açık emirlerin ve analiz edilecek bilgilerin sayısı ne kadar fazlaysa, kötü bir şey olma olasılığı o kadar yüksektir. Emirlerin her biri sisteme gelen her tikte analiz edildiğinde, aynı anda birçok emir varlığında sorunlar meydana gelebilir.

Bir demo hesapta mümkün olduğu kadar çok senaryoda test etmeden önce bu sisteme güvenmemenizi tavsiye ederim. Kod mükemmel görünse de herhangi bir hata analizine sahip değildir.

Uzman Danışmanın kodunu makaleye ekledim. Bir sonraki makalede görüşünceye kadar hoşça kalın.