English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
preview
Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 14): Hacim profili ekleme (II)

Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 14): Hacim profili ekleme (II)

MetaTrader 5Ticaret sistemleri | 21 Mart 2023, 08:47
352 0
Daniel Jose
Daniel Jose

Giriş

Uzman Danışmanımız halihazırda ticaret sırasında bize yardımcı olacak çeşitli özelliklere, kaynaklara sahiptir - onları önceki makalelerimizde ekledik. Ancak her şeye rağmen Uzman Danışmanımızın görselleştirme ve yeniden boyutlandırmayla ilgili olarak bazı ufak sorunları vardır. Bu sorunlar ticaret açısından bir engel oluşturmasa da bazı noktalarda ekranda yığılmaya neden olabiliyorlar ve dolayısıyla ekranı yenilemek zorunda bırakabiliyorlar. Ayrıca, bize değerli bilgiler sağlayabilecek bazı şeyler de şu anda Uzman Danışmanımızda eksiktir. Spesifik olabilirler, ancak yine de gereklidirler.

Öyleyse işe koyulalım ve bu yeni iyileştirmeleri uygulamaya başlayalım. Bu makalenin size bilgilerin sunumu konusunda yeni fikirler ve yöntemler sağlayacağını düşünüyorum. Aynı zamanda projelerinizdeki küçük kusurları düzeltmeniz sırasında da size yardımcı olacaktır.


Hacim profilinde yeni özelliğin planlanması ve uygulanması

1. Planlama

Ticaretle ilgili merak uyandıran bir konu vardır. Piyasanın belirli fiyat bölgelerinde uzun süre biriktiğini (akümüle olduğunu) ve devamında alış ya da satış tarafındaki durma seviyeleri tetiklendiğinde fiyatta hızlı bir hareket meydana geldiğini sıklıkla görüyoruz. Bu hareket, “Zaman ve ticaret (I)” ve “Zaman ve ticaret (II)” makalelerinde sunduğum zaman ve ticaret sisteminden görülebilir. Bu makalelerde, gerçekleştirilen emirlerin akışını okumak ve analiz etmek için alternatif bir grafik sistemin nasıl oluşturulacağını inceledik. Daha yakından bakarsanız, bazı anlarda fiyatın o anda ayrılmak istemediği birikim (accumulation) bölgesine tekrar geri dönme eğiliminde olduğunu fark edeceksiniz. Ancak şu anda hacim profili göstergemizde, fiyatın son zamanlarda ne kadar süredir bu belirli bölgede kaldığını söylememiz zordur. Bu göstergeyi, "Hacim profili ekleme (I)" makalesinde uygulamıştık. Bu göstergeyle, aşağıdaki görüntüde gösterilen nesnenin değerini ayarlayarak analizin başlangıç ​​noktasını değiştirerek nispeten yakın zamanda gerçekleşmiş hareketleri analiz edebilme olanağına sahibiz:

Ancak bu aslında pratik değildir, çünkü biz ana zaman dilimine bağlıyız, yani 60 dakikalık zaman diliminde grafiğimiz varsa, bu zaman diliminin altındaki fiyat hareketlerini analiz edemeyiz. Analiz noktasını ayarlayabilmemiz için daha düşük bir zaman dilimine geçmememiz gerekecektir. Vadeli işlem sözleşmeleri ticareti söz konusu olduğunda, çoğu yatırımcı genellikle 5, 10 veya 30 dakika gibi daha düşük zaman dilimlerini kullanmaktadır, dolayısıyla analizin başlangıç noktasını ayarlamak bir sorun gibi gözükmüyor. Ancak, az önce de belirttiğim gibi, bazen fiyat istediğinden değil durma seviyeleri tetiklendiğinden birikim bölgesinden çıkar ve sonrasında da çoğunlukla 5 dakikadan daha kısa süre içerisinde tekrar birikim bölgesine geri döner. Bu gibi durumlarda, grafikte uzun bir üst veya alt gölgeye sahip bir mum görürüz. Bu fiyat hareketi bize, meydana gelmiş olan durumun bir piyasa yoklaması olduğunu söyler. Böyle bir fiyat hareketi, aşağıda oklarla gösterilen mumlarda görülebilir:

   

Alıcıların yoklama hareketi veya satış durma seviyelerinin tetiklenmesi

 

Satıcıların yoklama hareketi veya alış durma seviyelerinin tetiklenmesi

Bu tür hareketler sıklıkla meydana gelir. Fiyat aralıklarının her birinde meydana gelen hacmin analiz edilmesi, piyasanın yoklama mı gerçekleştirdiğini yoksa trendin gerçekten terse mi döndüğünü anlamamıza olanak sağladığı için çok önemlidir. Ancak bunu daha önce sunduğumuz hacim profili göstergesini kullanarak düzgün ve hızlı bir şekilde yapmak imkansızdır.

Göstergenin nesne sınıfında küçük bir değişiklik yaparak neler olup bittiğine dair daha net bir fikre sahip olabiliriz. Böylece belirli bir süre içerisinde gerçekleşmiş olan ticaretin izini elde edeceğiz.


2. Uygulama

Yapmamız gereken ilk şey, 60, 45, 30, 19, 7 veya 1 dakika olacak şekilde ne kadar izleme süresi ayarlamak istediğimizi belirlemektir. Ancak bu değerlerden bağımsız olarak, izleme sisteminin gerçekten yararlı olabilmesi adına olabildiğince bölünebilir değerleri kullanmanızı tavsiye ederim. Pratik nedenlerden dolayı 30 dakikalık izleme süresi kullanarak uygulayacağız. Onu aşağıdaki kod satırında tanımlıyoruz:

#define def_MaxTrailMinutes     30

Ama neden 30 dakika? Aslında izleme dakikada bir yapılacak ancak maksimum izleme süresi 30 dakika olacaktır. Yani, her zaman 30 dakikalık hacim izine sahip olacağız. Örneğin, izleme 31. dakikaya geçtiğinde, ilk dakikanın izi artık görüntülenmeyecektir. Bunu nasıl uygulayacağız? Bunun için aşağıda gösterilen yakalama sistemini kullanıyoruz:

inline void SetMatrix(MqlTick &tick)
{
        int pos;
                                
        if ((tick.last == 0) || ((tick.flags & (TICK_FLAG_BUY | TICK_FLAG_SELL)) == (TICK_FLAG_BUY | TICK_FLAG_SELL))) return;
        pos = (int) ((tick.last - m_Infos.FirstPrice) / Terminal.GetPointPerTick()) * 2;
        pos = (pos >= 0 ? pos : (pos * -1) - 1);
        if ((tick.flags & TICK_FLAG_BUY) == TICK_FLAG_BUY) m_InfoAllVaP[pos].nVolBuy += tick.volume; else
        if ((tick.flags & TICK_FLAG_SELL) == TICK_FLAG_SELL) m_InfoAllVaP[pos].nVolSell += tick.volume;
        m_InfoAllVaP[pos].nVolDif = (long)(m_InfoAllVaP[pos].nVolBuy - m_InfoAllVaP[pos].nVolSell);
        m_InfoAllVaP[pos].nVolTotal = m_InfoAllVaP[pos].nVolBuy + m_InfoAllVaP[pos].nVolSell;
        m_Infos.MaxVolume = (m_Infos.MaxVolume > m_InfoAllVaP[pos].nVolTotal ? m_Infos.MaxVolume : m_InfoAllVaP[pos].nVolTotal);
        m_Infos.CountInfos = (m_Infos.CountInfos == 0 ? 1 : (m_Infos.CountInfos > pos ? m_Infos.CountInfos : pos));
        m_Infos.Momentum = macroGetMin(tick.time);
        m_Infos.Momentum = (m_Infos.Momentum > (def_MaxTrailMinutes - 1) ? m_Infos.Momentum - def_MaxTrailMinutes : m_Infos.Momentum);
        if (m_Infos.memMomentum != m_Infos.Momentum)
        {
                for (int c0 = 0; c0 <= m_Infos.CountInfos; c0++) m_TrailG30[m_Infos.Momentum].nVolume[c0] = 0;
                m_Infos.memMomentum = m_Infos.Momentum;
        }
        m_TrailG30[m_Infos.Momentum].nVolume[pos] += tick.volume;
}

Vurgulanan satırlar, nesne sınıfının kaynak koduna eklendi - bu satırlarla hacim izini yakaladık. Aşağıdaki satırlar, izlemenin beklendiği gibi gerçekleştirileceğini garanti eder:

m_Infos.Momentum = macroGetMin(tick.time);
m_Infos.Momentum = (m_Infos.Momentum > (def_MaxTrailMinutes - 1) ? m_Infos.Momentum - def_MaxTrailMinutes : m_Infos.Momentum);

Böylece, iz yakalama sistemimizi oluşturduk. Şimdi yeni bir karar vermemiz gerekiyor. İzin dakikada bir kaydedildiğine dikkat edin. Dolayısıyla her fiyat aralığındaki hacmi 1 dakikalık yenilemelerle görebileceğimiz şekilde grafikte sunabiliriz. Aşağıdaki görüntüye benzer bir şey yapmayı düşünebiliriz:

 

Daha açık tonlar daha yeni hacimleri temsil eder, ki bu iyi bir fikir olabilir.

Ancak, iyi bir fikir gibi görünse de, hacim düşük olduğunda veya hareket çok hızlı olduğunda, o an için anlamlı bir hacim olsa bile, bu anlamlı hacim o ana kadar gerçekleşmiş maksimum hacim ayarlanarak çizileceği için izde aslında görünmeyebilir. Bu sorunu çözmek için biraz farklı bir çizim yapmak isteyebiliriz, şu şekilde:

Her renk, hacim izindeki belirli bir aralığı temsil eder.

Bu, hacimdeki çok dar bantların analiz edilmesine yardımcı olur ve böylece ilk durumda görülebilecek sorunu bir nebze çözer. Ancak yine de, hacim başka bir noktadaki toplam hacim kadar anlamlı olmadığında ortaya çıkacak ayarlama problemini yaşamaya devam edeceğiz. Ayrıca, çok aktif ticaret zamanları sırasında analizin kafa karıştırıcı olmaması adına renkleri her aralık için çok dikkatli seçmeliyiz.

Dolayısıyla ben burada daha basit bir model kullanacağım. Siz bu model üzerinde yukarıdaki gibi farklı aralıklardaki hareketleri analiz edebilmek amacıyla ayarlamalar yapabilirsiniz. Ancak bunu yaparken yukarıda belirttiğimiz sorunları aklınızda bulundurmalısınız. Böylece iz görüntüsü aşağıda gösterildiği gibi olacaktır:

Neler olup bittiğini iyi bir şekilde anlayabilmemiz için hem zaman ve ticaret sistemimize hem de fiyat hareketine bakabileceğimiz temiz bir ize sahibiz.

Hacim görüntüsünü değiştirmek için düzenlenmesi gereken tek fonksiyon aşağıda gösterilmektedir:

void Redraw(void)
{
        uint            x, y, y1, p;
        double  reason = (double) (m_Infos.MaxVolume > m_WidthMax ? (m_WidthMax / (m_Infos.MaxVolume * 1.0)) : 1.0);
        double  desl = Terminal.GetPointPerTick() / 2.0;
        ulong           uValue;
                                
        Erase();
        p = m_WidthMax - 8;
        for (int c0 = 0; c0 <= m_Infos.CountInfos; c0++)
        {
                if (m_InfoAllVaP[c0].nVolTotal == 0) continue;
                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, m_Infos.FirstPrice + (Terminal.GetPointPerTick() * (((c0 & 1) == 1 ? -(c0 + 1) : c0) / 2)) + desl, x, y);
                y1 = y + Terminal.GetHeightBar();
                FillRectangle(p + 2, y, p + 8, y1, macroColorRGBA(m_InfoAllVaP[c0].nVolDif > 0 ? m_Infos.ColorBuy : m_Infos.ColorSell, m_Infos.Transparency));
                FillRectangle((int)(p - (m_InfoAllVaP[c0].nVolTotal * reason)), y, p, y1, macroColorRGBA(m_Infos.ColorBars, m_Infos.Transparency));
                uValue = 0;
                for (int c1 = 0; c1 < def_MaxTrailMinutes; c1++) uValue += m_TrailG30[c1].nVolume[c0];
                FillRectangle((int) (p - (uValue * reason)), y, p, y1, macroColorRGBA(clrRoyalBlue, m_Infos.Transparency));
        }
        C_Canvas::Update();
};

Daha net olmak gerekirse, yalnızca vurgulanan kodun değiştirilmesi gerekmektedir. İstediğiniz sonucu elde edene kadar onda ayarlamalar yapabilirsiniz. Yukarıda vurgulanan bu kod parçasının dışında sınıftaki başka hiçbir şeyin değiştirilmesi gerekmez. Programı derleyip grafikte çalıştırdığımızda aşağıdakine benzer bir görüntüye sahip olacağız:



Render problemini çözme

Kodun aslında herhangi bir sorunu olmasa da, grafik yeniden boyutlandırılırken bir aksaklık meydana gelmektedir: ekranı kaplayan bir grafik başka bir boyuta yeniden boyutlandırıldığında ve ardından tekrar ekranı kaplar boyuta getirildiğinde, bazı nesneler kaybolabiliyor, beklendiği gibi davranmayabiliyor ya da yanlış yerlerde konumlanabiliyor. Problem aşağıdaki koddadır - bu kodu önceki makalelerde kullanmıştık.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        ChartRedraw();
}

Çok basit bir değişiklik yapılması gerekiyor ama "Ben bir şey göremiyorum, kod doğru" diye düşünebilirsiniz. İlk bakışta ben de yanlış bir şey görmüyordum, ancak bazı ekstra özellikler eklerken, tam olarak yukarıda açıklanan aksaklığın meydana geldiğini fark ettim. Bu sorunu çözmek için yukarıdaki kodu aşağıdaki kodla değiştirmeliyiz:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        ChartRedraw();
}

Aptalca gelebilir ama nedenini anlamak için fonksiyon kodunun tamamına ve vurgulanan kısma bakmamız gerekiyor. Artık sistem düzeldiğine göre bir sonraki adıma geçebiliriz.


Ekstra özellikler ekleme

Şimdi ekleyeceğimiz özellik çok basittir ve çoğu kişi onu uygulamak için pek bir neden görmeyebilir, ancak onu uygulamak, emirlerin yerleştirilmesi, taşınması vb. dahil olmak üzere emirlerle çalışırken veya sadece hacim profilini takip ederken bize çok yardımcı olacaktır.

Yapılacak ilk şey, fiyat çizgisinin değişme kodunun parçası olduğu sınıfta değişiklik yapmaktır. Bu kod C_OrderView sınıfından çıkıp C_Terminal sınıfına girecektir. Kod, yeni sınıfın değişkenleriyle çalışmaya başlayacağı için bazı küçük değişikliklere de uğraması gerekmektedir. Yeni kod aşağıda görünmektedir:

double AdjustPrice(const double arg)
{
        double v0, v1;
                                
        if(m_Infos.TypeSymbol == OTHER) return arg;
        v0 = (m_Infos.TypeSymbol == WDO ? round(arg * 10.0) : round(arg));
        v1 = fmod(round(v0), 5.0);
        v0 -= ((v1 != 0) || (v1 != 5) ? v1 : 0);
        return (m_Infos.TypeSymbol == WDO ? v0 / 10.0 : v0);
};

Bunu yaptıktan sonra, yeni bir Uzman Danışman sınıfı oluşturabiliriz. Bu C_Mouse sınıfı olacaktır, kısaca açıklarsak bu nesne sınıfı fare olaylarından sorumlu olacaktır, o halde geliştirmenin bu noktada nasıl ilerleyeceğini görelim. Ancak öncesinde Uzman Danışmanımızın aşağıda gösterilen mevcut sınıf yapısına bakalım:

Ekleyeceğimiz özelliklerin hayata geçebilmesi için yeni bir yapıya ihtiyacımız vardır.

Öyleyse, yukarıdaki yapı göz önünde bulundurulduğunda, aşağıda görülebilen değişkenlerin bildiriminden başlayarak C_Mouse nesne sınıfının kodunu inceleyelim:

class C_Mouse
{
        private  :
                struct st00
                {
                color   cor01,
                        cor02,
                        cor03;
                string  szNameObjH,
                        szNameObjV,
                        szNameObjT,
                        szNameObjI,
                        szNameObjB;
                }m_Infos;
                struct st01
                {
                        int      X,
                                 Y;
                        datetime dt;
                        double   price;
                        uint     ButtonsStatus;
                }Position;

Geliştirmenin bu aşamasında ihtiyaç duyulan birkaç değişken olduğunu görüyoruz, dikkate değer bir sonraki kısım aşağıda görülmektedir:

~C_Mouse()
{
// ... Internal code ...
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, true);
}

Bu kod, artı imleci CHART_CROSSHAIR_TOOL'u geri yükler ve fare olaylarının grafik tarafından kullanılmasını devre dışı bırakır; bu, MT5'in artık bu tür olayları grafiğe gönderme konusunda endişelenmesine gerek kalmadığı, çünkü onların platformun kendisi tarafından işleneceği anlamına gelir.

Ayrıca fareyi kontrol edeceğimiz zaman kullanılan iki fonksiyonumuz vardır:

inline void Show(void)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, m_Infos.cor01);
}
//+------------------------------------------------------------------+
inline void Hide(void)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, clrNONE);
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, 0, 0);
}

İşin ilginç yanı fare aslında ekrandan kaybolmamaktadır, sadece oluşturduğumuz nesneler ekrandan kaybolmaktadır ve fareyi “açtığımızda” sadece fiyat çizgisi görünecektir. Bu kulağa tuhaf gelebilir, ancak Uzman Danışmanın bazı belirli noktalarında kullanımları vardır. Bu noktalardan biri, C_OrderView nesne sınıfında bulunan aşağıdaki kodda vurgulanan kısımdır:

inline void MoveTo(uint Key)
{
        static double local = 0;
        int w = 0;
        datetime dt;
        bool bEClick, bKeyBuy, bKeySell;
        double take = 0, stop = 0, price;
                                
        bEClick  = (Key & 0x01) == 0x01;    //Left click
        bKeyBuy  = (Key & 0x04) == 0x04;    //SHIFT pressed
        bKeySell = (Key & 0x08) == 0x08;    //CTRL pressed
        Mouse.GetPositionDP(dt, price);
        if (bKeyBuy != bKeySell) Mouse.Hide(); else Mouse.Show();
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLinePrice, 0, 0, price = (bKeyBuy != bKeySell ? price : 0));
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLineTake, 0, 0, take = price + (m_Infos.TakeProfit * (bKeyBuy ? 1 : -1)));
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLineStop, 0, 0, stop = price + (m_Infos.StopLoss * (bKeyBuy ? -1 : 1)));
        if((bEClick) && (bKeyBuy != bKeySell) && (local == 0)) CreateOrderPendent(bKeyBuy, m_Infos.Volume, local = price, take, stop, m_Infos.IsDayTrade); else local = 0;
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLinePrice, OBJPROP_COLOR, (bKeyBuy != bKeySell ? m_Infos.cPrice : clrNONE));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineTake, OBJPROP_COLOR, (take > 0 ? m_Infos.cTake : clrNONE));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineStop, OBJPROP_COLOR, (stop > 0 ? m_Infos.cStop : clrNONE));
};

Vurgulanan kısmın hemen üzerindeki satıra dikkat edin:

Mouse.GetPositionDP(dt, price);

Bu satır, farenin konumunun değerini yakalayacaktır. Bu değerleri raporlayacak kod aşağıdadır:

inline void GetPositionDP(datetime &dt, double &price)
{
        dt = Position.dt;
        price = Position.price;
}

Ancak hepsi bu kadar değildir. Bazı durumlarda, ekran konumu açısından grafiğin kartezyen koordinatlarına ihtiyacımız vardır. Bu değerlerin elde edilmesi aşağıdaki başka bir fonksiyonla yapılır:

inline void GetPositionXY(int &X, int &Y)
{
        X = Position.X;
        Y = Position.Y;
}

C_OrderView sınıfına dönersek, yine dikkati hak eden ilginç bir noktamız daha vardır:

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(Mouse.GetButtonStatus());
                        break;

// ... The rest of the code ...

}

MoveTo fonksiyonuna az önce yukarıda değinmiştik. Aynı zamanda C_OrderView sınıfının bir parçasıdır. Ancak daha da önemlisi Mouse.GetButtonsStatus fonksiyonudur. Bu fonksiyon, fare olaylarıyla ilişkili düğmelerin ve tuşların durumunu geri döndürür.

Bu Mouse.GetButtonStatus fonksiyonu aşağıda gösterilmektedir:

inline uint GetButtonStatus(void) const
{
        return Position.ButtonsStatus;
}

Son fare olayından bu yana kaydedilen değerleri içeren değişkeni geri döndüren tek bir satırdan oluşur. Şimdi bu değeri kaydeden koda gelelim. Ama öncesinde, fare başlatma koduna bakalım, çünkü Uzman Danışmana fareyi başlatmak istediğimizi ve bu andan itibaren fareyle ilgili çeşitli şeylerle onun ilgilenmekten sorumlu olacağını söylemeliyiz. Bunu yerine getirecek kod aşağıdadır:

// ... Other things ....

input group "Mouse"
input color     user50 = clrBlack;      //Price line
input color     user51 = clrDarkGreen;  //Positive move
input color     user52 = clrMaroon;     //Negative move
//+------------------------------------------------------------------+

// ... General information ...

//+------------------------------------------------------------------+
int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);

// ... The rest of the code ...

Koddan da görüldüğü gibi sistemin kullanacağı üç renk tanımlamamız gerekiyor. Bu renkler, verilerin grafikte net ve görünür olması açısından uygun bir şekilde seçilmelidir. Bu konunun biraz daha iyi bir şekilde anlaşılabilmesi adına Mouse.Init koduna bir göz atalım:

void Init(color c1, color c2, color c3)
{
        m_Infos.cor01 = c1;
        m_Infos.cor02 = c2;
        m_Infos.cor03 = c3;
        if (m_Infos.szNameObjH != NULL) return;
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, true);
        ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, false);
        m_Infos.szNameObjH = "H" + (string)MathRand();
        m_Infos.szNameObjV = "V" + (string)MathRand();
        m_Infos.szNameObjT = "T" + (string)MathRand();
        m_Infos.szNameObjB = "B" + (string)MathRand();
        m_Infos.szNameObjI = "I" + (string)MathRand();
//---
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjH, OBJ_HLINE, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjV, OBJ_VLINE, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjT, OBJ_TREND, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjB, OBJ_BITMAP, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjI, OBJ_TEXT, 0, 0, 0);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TOOLTIP, "\n");
//---
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_WIDTH, 2);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_BMPFILE, "::" + def_Fillet);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONT, "Lucida Console");
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONTSIZE, 10);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_BACK, false);
        Hide();
        Show();
}

Bu kodun çok özel bir şeyi yok - burada sadece sınıfın kullanması için bazı nesneler oluşturuyoruz. Ancak vurgulanan kısım biraz kafa karıştırıcı olabilir, çünkü onu sınıfta ararsanız, bildirildiği herhangi bir yer bulamazsınız. Bunun nedeni, aslında onun diğer kaynaklarla birlikte Uzman Danışmanın dosyasının kodunda bildirilmiş olmasıdır. Gelecekte tüm bunları bir dosyada gruplandıracağım ama şimdilik bu şekilde kalacak. Uzman Danışmanın koduna bakarsanız aşağıdaki satırları bulacaksınız:

#define def_Resource    "Resources\\SubSupport.ex5"
#define def_Fillet      "Resources\\Fillet.bmp"
//+------------------------------------------------------------------+
#resource def_Resource
#resource def_Fillet

Fare başlatma kodunda vurgulanan kaynak burada bildirilmektedir.

Şimdi aşağıdaki kod parçasının çağrıldığı, bu sınıftaki son noktamıza geldik:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        ChartRedraw();
}

Ardından, Uzman Danışmanın kodundaki vurgulanan satır aşağıdaki kodu çağırır:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        int     w = 0;
        uint    key;
        static int b1 = 0;
        static double memPrice = 0;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Position.X = (int)lparam;
                        Position.Y = (int)dparam;
                        ChartXYToTimePrice(Terminal.Get_ID(), Position.X, Position.Y, w, Position.dt, Position.price);
                        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjH, 0, 0, Position.price = Terminal.AdjustPrice(Position.price));
                        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjV, 0, Position.dt, 0);
                        key = (uint) sparam;
                        if ((key & 0x10) == 0x10)
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, m_Infos.cor01);
                                b1 = 1;
                        }
                        if (((key & 0x01) == 0x01) && (b1 == 1))
                        {
                                ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, false);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, m_Infos.cor01);
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 0, Position.dt, memPrice = Position.price);
                                b1 = 2;
                        }
                        if (((key & 0x01) == 0x01) && (b1 == 2))
                        {
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 1, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
                                ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TEXT, StringFormat("%.2f ", Position.price - memPrice));
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjI, 0, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
                        }
                        if (((key & 0x01) != 0x01) && (b1 == 2))
                        {
                                b1 = 0;
                                ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, true);
                                Hide();
                                Show();
                        }
                        Position.ButtonsStatus = (b1 == 0 ? key : 0);
                        break;
        }
}

Lütfen yukarıdaki kodun tam olarak uygulama kodu olmadığını unutmayın. Bu kod, şu anki geliştirme aşamasına kadar olan Uzman Danışmanın yalnızca ana görevlerini destekleyecek ve çözecektir. Bunu anlamak için, fare başlatma kodundaki bir şeye dikkat edelim:

ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, false);

Bu satır, farenin orta düğmesine tıkladığımızda artı imlecin görüntülenmesini engeller. Ama artı imlecin oluşturulmasını neden engelliyoruz? Aşağıdaki görüntüye bir göz atalım:

Grafik üzerinde analiz yapmaya çalıştığımızda çok fazla hassasiyete sahip olmadığımızı görüyoruz. Bazı durumlarda analiz yapabilmek için belirli bir düzeyde hassasiyete sahip olmamız gereklidir. Ancak MetaTrader 5'teki artı imleci bu durumlar için yeterli değildir. Yeni bir sisteme başvurmalıyız ve bu nedenle MetaTrader 5'in Uzman Danışman çalışırken artı imleci oluşturmasını engellemeliyiz. Bunun yerine, analizde kullanmak üzere kendi artı imlecimizi oluşturacağız. Onu bizim için daha alakalı olan verileri ve değerleri gösterecek şekilde ayarlayabiliriz. Bu, Uzman Danışman çalışırken veri modelleme sisteminin kullanılmasının sonucunu gösteren aşağıdaki görüntüden görülebilir:


Gördüğünüz gibi, artı imlecinde belirtilen değerler tam hareket değerlerine karşılık gelmektedir. Ayrıca görsel olarak da ifade sağlanmaktadır: değer pozitifse yeşile, negatifse kırmızıya dönmektedir. Kalın bir çizgi oluşmaktadır ve başlangıç ve bitiş noktaları kolayca görülebilir durumdadır. Ancak daha önce de belirttiğim gibi sistem henüz tamamlanmış değildir. İsterseniz ve ihtiyacınız olursa, artı imlecinde ileri iyileştirmeler de yapabilirsiniz. C_Mouse sınıfının yeni bir sürümü çıkana kadar bu sürümü geliştirebilir ve ihtiyaç duyduğunuz daha fazla veriyi göstermesini sağlayabilirsiniz. Fakat bunu yapabilmek için öncelikle her şeyin nasıl çalıştığını anlamamız gerekir, bu amaçla şimdi C_Mouse sınıfının mesaj koduna daha yakından bakalım. 


C_Mouse sınıfının DispathMessage kodunu anlama

Kod, fare konumu değişkenlerinin değerlerini yakalayıp ayarlayarak başlar:

Position.X = (int)lparam;
Position.Y = (int)dparam;
ChartXYToTimePrice(Terminal.Get_ID(), Position.X, Position.Y, w, Position.dt, Position.price);

Konum değerleri MetaTrader 5 platformu tarafından bildirilir, ancak değerler aslında işletim sisteminden gelir ve ekran koordinatları yani X ve Y cinsindedir. Ancak onları grafik koordinatlarına dönüştürmemiz gerekiyor. Bunun için MQL5'te bulunan ve hayatımızı büyük ölçüde kolaylaştıran ChartXYToTimePrice fonksiyonunu kullanacağız.

Bunu yaptıktan sonra, fiyat ve zaman çizgilerini hareket ettiririz.

ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjH, 0, 0, Position.price = Terminal.AdjustPrice(Position.price));
ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjV, 0, Position.dt, 0);

Zaman çizgisi başlangıçta bize görünmez, bu nedenle onu başlangıçta grafikte göremeyiz. Farenin durumunu yakalıyoruz:

key = (uint) sparam;

Şu ana kadar her şey iyi gidiyor. Şimdi şunu yapalım: orta tuşa basılı durumda olup olmadığını kontrol edelim. Eğer öyleyse, zaman çizgisi grafikte görünür hale gelmelidir. Bu, aşağıdaki kodda uygulanmaktadır:

if ((key & 0x10) == 0x10)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, m_Infos.cor01);
        b1 = 1;
}

Bu amaçla, bu olayı depolamak için static türünde değişken kullanıyoruz, böylece bundan sonra Uzman Danışman tarafından başka hiçbir olay kabul edilmeyecek ve işlenmeyecektir. Grafik üzerinde yapmak istediğimiz analizle ilgilenecektir. Aslında grafik üzerinde yapacağımız analiz sadece farenin sol tuşuna bastığımızda başlamaktadır, yani MetaTrader 5 platformunun tüm kullanıcıları tarafından grafik analizleri için çoktandır bilinen aynı başlatma modunu kullanıyorum. Analize başlamanın yeni bir yolunu öğrenmek zorunda kalmamız sistemden vazgeçmemize yol açabileceğinden, en uygun olan yol budur. Uzman Danışman aşağıdaki kodla bu sol tıklamayı bekler:

if (((key & 0x01) == 0x01) && (b1 == 1))
{
        ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, false);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, m_Infos.cor01);
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 0, Position.dt, memPrice = Position.price);
        b1 = 2;
}

Tıklama meydana geldiğinde, grafik hareket sistemi kilitlenir. Ardından, analiz noktalarını gösteren bir trend çizgisi oluşturuyoruz. Sonrasında sistem, b1'deki yeni değerden görülebilen bir sonraki adıma geçer. Burası, daha alakalı olduğunu düşündüğümüz daha fazla bilgi ekleyebileceğimiz kısımdır. Buradaki amacım sadece sistemi göstermektedir, dolayısıyla siz istediğinizi ekleyebilirsiniz. Bu, aşağıda gösterildiği gibi yapılır:

if (((key & 0x01) == 0x01) && (b1 == 2))
{
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 1, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TEXT, StringFormat("%.2f ", Position.price - memPrice));
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjI, 0, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
}

Vurgulanan satıra dikkat edin, çünkü grafik ekranında görünen değer burada hesaplanır ve görüntülenir. Buraya istediğiniz şekilde farklı yararlı bilgiler daha ekleyebilirsiniz. Sol tuşa basıldığında, bu bölümün çalışması verilerin hesaplanıp görüntülenmesine neden olur. Yani bu, MetaTrader 5'teki varsayılanla aynı davranıştır, ancak değerler sizin istek ve ihtiyaçlarınıza göre ayarlanacak ve modellenecektir.

Şimdi aşağıda gösterilen bir sınama daha yapmamız gerekiyor.

if (((key & 0x01) != 0x01) && (b1 == 2))
{
        b1 = 0;
        ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, true);
        Hide();
        Show();
}

Sol fare düğmesini bıraktıktan sonra grafik serbest kalır ve sürüklenebilir duruma geçer ve analizi oluşturmak için kullanılan tüm öğeler gizlenir, yalnızca fiyat çizgisi tekrar görünür hale gelir. Ve son olarak, aşağıda gösterilen son kod parçasına sahibiz:

Position.ButtonsStatus = (b1 == 0 ? key : 0);

Analiz çağrılmadığında, fare düğmesi durumu saklanır, böylece Uzman Danışmanın başka bir yerinde kullanılabilir, ancak analiz çağrıldığında, durum verisi olarak NULL değeri kullanılır, bu şekilde emir oluşturmak veya pozisyon değiştirmek mümkün olmayacaktır.

Aşağıdaki videodan izin nasıl çalıştığını, hacmin nasıl ayarlandığını görebilirsiniz. Onu nasıl doğru bir şekilde kullanacağınızı öğrenirseniz size çok yardımcı olacaktır. Zaman ve ticaretle birlikte hacim profili, piyasadaki en gelişmiş ticaret yöntemlerinden biri olan gürültü analizine dayalı iyi bir ikili araç seti oluşturdular.




MetaQuotes Ltd tarafından Portekizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/pt/articles/10419

Ekli dosyalar |
EA_-_Mouse.zip (5986.31 KB)
Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 15): İnternetteki verilere erişme (I) Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 15): İnternetteki verilere erişme (I)
MetaTrader 5’te internetteki verilere nasıl erişilir? İnternet üzerinde devasa miktarda bilginin yer aldığı çok sayıda web sitesi vardır. Bilinmesi gereken, nereye bakılacağı ve bu bilgilerin en iyi şekilde nasıl kullanılacağıdır.
Veri Bilimi ve Makine Öğrenimi (Bölüm 05): Karar Ağaçları Veri Bilimi ve Makine Öğrenimi (Bölüm 05): Karar Ağaçları
Karar ağaçları, insanların düşünme şeklini taklit ederek verileri sınıflandırır. Bu makalede, karar ağaçlarını nasıl oluşturacağımızı ve onları verileri sınıflandırmak ve öngörmek için nasıl kullanacağımızı göreceğiz. Karar ağacı algoritmasının temel amacı, heterojen verilerden homojen veya homojene yakın verileri ayırmaktır.
Ichimoku göstergesine dayalı bir ticaret sistemi nasıl geliştirilir? Ichimoku göstergesine dayalı bir ticaret sistemi nasıl geliştirilir?
Bu makalede de en popüler teknik göstergelere dayalı ticaret sistemlerini nasıl oluşturacağımızı öğrendiğimiz serimize devam ediyoruz. Bu sefer Ichimoku göstergesi hakkında konuşacağız ve ona dayalı bir ticaret sistemi geliştireceğiz.
Veri Bilimi ve Makine Öğrenimi (Bölüm 04): Borsa Çöküşünü Öngörme Veri Bilimi ve Makine Öğrenimi (Bölüm 04): Borsa Çöküşünü Öngörme
Bu makalede, ABD ekonomisinin temel analizine dayalı olarak borsa çöküşünü öngörmek için lojistik modelimizi kullanmaya çalışacağız. Değerlendirmemizi Netflix ve Apple hisse senetleri üzerinde yapacağız ve 2019 ve 2020’deki borsa çöküşlerindeki verileri kullanacağız. Bakalım lojistik modelimiz kasvetli piyasa koşullarında nasıl performans gösterecek.