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

Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 21): Yeni emir sistemi (IV)

MetaTrader 5Ticaret sistemleri | 1 Ağustos 2023, 08:53
326 0
Daniel Jose
Daniel Jose

Giriş

Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 20): Yeni emir sistemi (III) başlıklı bir önceki makalede, görsel emir sistemini elde etmek amacıyla yapılması gereken başlıca değişiklikleri inceledik. Ancak, sonraki adımlar fazla miktarda açıklama gerektiriyordu, bu yüzden makaleyi birden çok bölüme ayırmaya karar verdim. Burada ana değişiklikleri yapmayı bitireceğiz. Çok sayıda değişiklik olacaktır ve onların hepsi gereklidir. Tüm çalışma oldukça ilginç olacaktır. Ancak burada her şeyi tamamlamayacağız, çünkü sistemi gerçekten bitirmek için hala yapılacak çok şey vardır. Fakat bu makalenin sonunda dahi sistem neredeyse tüm gerekli işlevlere sahip olacaktır.

Şimdi doğrudan uygulamaya geçelim.


1.0. Uygulama

Öncelikle emir için Kapat veya İptal düğmesi ekleyelim. Düğmelerden sorumlu sınıf aşağıda gösterilmektedir.

1.0.1. C_Object_BtnBitMap sınıfı

Bu sınıf, aşağıda görebileceğiniz gibi, grafikteki bitmap düğmelerini desteklemekten sorumludur.

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Object_Base.mqh"
//+------------------------------------------------------------------+
#define def_BtnClose    "Images\\NanoEA-SIMD\\Btn_Close.bmp"
//+------------------------------------------------------------------+
#resource "\\" + def_BtnClose
//+------------------------------------------------------------------+
class C_Object_BtnBitMap : public C_Object_Base
{
        public  :
//+------------------------------------------------------------------+
		void Create(string szObjectName, string szResource1, string szResource2 = NULL)
                        {
                                C_Object_Base::Create(szObjectName, OBJ_BITMAP_LABEL);
                                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_BMPFILE, 0, "::" + szResource1);
                                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_BMPFILE, 1, "::" + (szResource2 == NULL ? szResource1 : szResource2));
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_STATE, false);
                        };
//+------------------------------------------------------------------+
                bool GetStateButton(string szObjectName) const
                        {
                                return (bool) ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_STATE);
                        }
//+------------------------------------------------------------------+
};


Bu sınıfın kodunu yazarken, konumlandırma kodunun C_Object_Base sınıfına taşınabileceğini fark ettim. Böylece, C_Object_BackGround sınıfı, artık daha alt bir sınıfa ait olacağı için bu koddan kurtulur. Bu, kodun yeniden kullanımı olarak bilinir. Bu yaklaşım daha az programlama gerektirir, performansı artırır, ancak her şeyden önce, değişiklikler daha sık kontrol edileceğinden kodu daha kararlı hale getirir.

Kapat düğmesi eklemek için aşağıdakileri yapacağız:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Object_TradeLine.mqh"
#include "C_Object_BtnBitMap.mqh"
//+------------------------------------------------------------------+
class C_ObjectsTrade
{

// ... Class code ...

}

Sonraki adım:

enum eEventType {EV_GROUND = 65, EV_LINE, EV_CLOSE};

Sonraki adım:

C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;
C_Object_BtnBitMap      m_BtnClose;


Sonraki adım:

inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it)
                        {
                                color cor1, cor2;
                                string sz0;
                                

// ... Internal function code ...

                                switch (it)
                                {
                                        case IT_TAKE:
                                        case IT_STOP:
                                                m_BackGround.Size(sz0, 92, 22);
                                                break;
                                        case IT_PENDING:
                                                m_BackGround.Size(sz0, 110, 22);
                                                break;
                                }
                                m_BtnClose.Create(MountName(ticket, it, EV_CLOSE), def_BtnClose);
                        }


Sonraki adım:

#define macroDelete(A)  {                                                               \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND));       \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE));         \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_CLOSE));        \
                        }
                                        
inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL)
                        {
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                if ((it != NULL) && (it != IT_PENDING) && (it != IT_RESULT)) macroDelete(it)
                                else
                                {
                                        macroDelete(IT_PENDING);
                                        macroDelete(IT_RESULT);
                                        macroDelete(IT_TAKE);
                                        macroDelete(IT_STOP);
                                }
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                        }
#undef macroDelete


Ve son olarak, son adım:

#define macroSetAxleY(A)        {                                               \
                m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND), y); \
                m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE), y);    \
                m_BtnClose.PositionAxleY(MountName(ticket, A, EV_CLOSE), y);    \
                                }
                                                                        
#define macroSetAxleX(A, B)     {                                               \
                m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND), B); \
                m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE), B);    \
                m_BtnClose.PositionAxleX(MountName(ticket, A, EV_CLOSE), B + 3);\
                                }
inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy)
                        {

// ... Internal code...
                                
                        }
#undef macroSetAxleX
#undef macroSetAxleY


Sistemi çalıştırdığımızda aşağıdaki sonucu elde edeceğiz:


Ancak MetaTrader 5, düğme için Uzman Danışman tarafından işlenecek bir olay oluştursa da, düğme halen çalışmamaktadır. Bu işlevi henüz uygulamadık. Biraz sonra bu konuya geri döneceğiz.


1.0.2. C_Object_Edit sınıfı

Sistem, yatırımcıyı işlem yapılan değerler hakkında bilgilendiremezse işe yaramaz. Bu amaçla C_Object_Edit sınıfına sahibiz. Sınıfın işlevselliğini artırmak için daha sonra bazı değişikliklerden geçmesi gerekecektir, ancak şimdilik onu yatırımcıyı neler olup bittiği hakkında bilgilendirecek şekilde bırakacağız. Bunu uygulamak için sınıfa birkaç kod satırı eklememiz gerekmektedir. Yeni kodu içeren ilk kod parçası:

void Create(string szObjectName, color cor, int InfoValue)
{
        C_Object_Base::Create(szObjectName, OBJ_EDIT);
        ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_FONT, "Lucida Console");
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_FONTSIZE, 10);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_ALIGN, ALIGN_CENTER);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, clrBlack);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BORDER_COLOR, clrBlack);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_READONLY, true);
        SetTextValue(szObjectName, InfoValue, cor);
}

Vurgulanan kod, değerlerin yatırımcı tarafından değiştirilmesini engeller, ancak dediğim gibi, bu gelecekte değişecektir. Bu, şu anda alakalı olmayan bazı başka değişiklikleri gerektirmektedir.

Aşağıdaki fonksiyon, metnin görüntülenmesini sağlar. Bu fonksiyonda bir ayrıntıya dikkat edin:

void SetTextValue(string szObjectName, int InfoValue, color cor = clrNONE)
{
        color clr;
        clr = (cor != clrNONE ? cor  : (InfoValue < 0 ? def_ColorNegative : def_ColoPositive));
        ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT, IntegerToString(InfoValue < 0 ? -(InfoValue) : InfoValue));
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR, clr);
}

Vurgulanan kod, girilen değere bağlı olarak metnin arka planının hangi renkte olacağını söyleyecektir. Değerin negatif mi yoksa pozitif mi olduğunu sürekli olarak tahmin etmeye çalışmak veya metinde negatif değerin olup olmadığını belirlemek için zaman harcamak istemediğimiz için bunu burada yapmamız önemlidir. İhtiyacımız olan şey, değerin negatif mi yoksa pozitif mi olduğuna bakmak ve anlamaktır. Kodun yaptığı da budur: değerin negatif mi yoksa pozitif mi olduğunu anında belirleyebiliriz. Ancak rengin önceden tanımlanmamış olması şartı vardır. Bu daha sonra da işimize yarayacaktır.

Devam edersek, bu sınıfın aşağıda gösterilen son fonksiyonuna sahibiz:

long GetTextValue(string szObjectName) const
{
        return (StringToInteger(ObjectGetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT)) * 
                                (ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR) == def_ColorNegative ? -1 : 1));
};


Değerleri sunduğumuzda, biçimlendirmeleri nedeniyle her zaman pozitif olacaklarını fark edebilirsiniz. Ancak nesnenin içeriğini kontrol ettiğimizde doğru bilgilere sahip olmamız gerekir. Bunu yapmak için vurgulanan kodu kullanıyoruz. Renk bilgisinin kullanıldığı yer burasıdır: renk, değerin negatif olduğunu gösteriyorsa, Uzman Danışmana doğru bilginin sağlanması için düzeltilecektir. Renk pozitif değeri gösteriyorsa, değer basitçe saklanacaktır.

Renk tanımları sınıfın kendisinde bulunur. Daha sonra başka renkler ayarlamak isterseniz değiştirilebilirler, ancak önceki fonksiyonun doğru çalışması adına farklı renkler kullandığınızdan emin olun, aksi takdirde Uzman Danışman belirsiz değerler alacaktır. Uzman Danışman, negatif değerleri pozitif olarak görebilir ve bu da Uzman Danışman tarafından gerçekleştirilen tüm analizde sorunlara neden olur.


1.0.3. C_Object_Label sınıfı

Bu aşamada ihtiyacımız olan son sınıftır. Eylemleri C_Object_BtnBitMap sınıfına benzediği için aslında bu sınıfı oluşturmamayı düşünüyordum. Ancak C_Object_Edit sınıfından bağımsız olarak da metin bilgisi ekleyebilmek istediğim için burada yeni bir sınıf oluşturmaya karar verdim.

Kodu çok basittir:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Object_Edit.mqh"
//+------------------------------------------------------------------+
class C_Object_Label : public C_Object_Edit
{
        public  :
//+------------------------------------------------------------------+
                void Create(string szObjectName, string Font = "Lucida Console", string szTxt = "", int FontSize = 10, color cor = clrBlack)
                        {
                                C_Object_Base::Create(szObjectName, OBJ_LABEL);
                                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_FONT, Font);
                                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT, szTxt);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_FONTSIZE, FontSize);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, cor);
                        };
//+------------------------------------------------------------------+
};


İşin geri kalanı zaten alt sınıf nesneler tarafından uygulandığından, bu sınıfta başka bir şeye ihtiyacımız yoktur.

Gördüğünüz gibi, OOP çok güçlü bir araçtır. Kodu sınıflar halinde ne kadar çok düzenlersek, birbirine benzer sınıfları programlamaya o kadar az ihtiyaç duyarız.

Ancak yapmamız gereken küçük bir değişiklik vardır. Denemeler yaparken gösterge verilerini yorumlamanın çok zor olduğunu fark ettim ve aşağıdaki gibi değiştirdim.

    

Böylece büyük değerleri görüntülemek daha kolay hale geldi. Nihai gösterge şu şekilde görünecektir: üst kısım sözleşme sayısını veya açık pozisyonun kaldıraç katsayısını, alt kısım ise pozisyonun sonucunu gösterecektir.

Bunu uygulamak için, esas olarak nesnelerin konumlandırılmasından sorumlu olan C_Object_Base sınıfını değiştirmemiz gerekmektedir. Değişiklikler aşağıdaki kodda vurgulanmıştır.

virtual void PositionAxleY(string szObjectName, int Y, int iArrow = 0)
                        {
                                int desl = (int)ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, (iArrow == 0 ? Y - (int)(desl / 2) : (iArrow == 1 ? Y : Y - desl)));
                        };


Şimdi bir sonraki adıma geçebiliriz: C_ObjectsTrade sınıfını değiştirme.


1.0.4. C_ObjectsTrade sınıfı

Şimdi tüm nesneleri çizmeyi tamamlayalım, böylece grafikte gerçekten istediğimiz sonucu elde edebileceğiz: nesneleri birbirine bağlı olan göstergelere sahip olacağız. Bunu yapmak zor değildir, adım adım analiz edeceğiz. Nasıl yapıldığını anlarsanız, sadece talimatları izleyerek istediğiniz diğer bilgileri ekleyebilirsiniz ve herhangi bir sorun çıkmayacaktır. Yapmamız gereken ilk şey, nesnelerin yanıt vermesi gereken yeni olayları tanımlamaktır. Bunlar aşağıdaki kodda vurgulanmıştır:

enum eEventType {EV_GROUND = 65, EV_LINE, EV_CLOSE, EV_EDIT, EV_VOLUME, EV_MOVE};

Şimdi nesneleri ekleyelim. Yeni nesneler aşağıdaki kodda vurgulanmıştır:

C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;
C_Object_BtnBitMap      m_BtnClose;
C_Object_Edit           m_EditInfo,
                        m_InfoVol;
C_Object_Label          m_BtnMove;


Devamında, nesneleri oluşturuyoruz ve ekranda nasıl görüneceklerini belirliyoruz. Lütfen nesnelerin görünmeleri gerektiği sırayla oluşturulması gerektiğini unutmayın: önce arka plan nesnesi, ardından arka plan üzerine yerleştirilecek sıradaki nesne ve tüm nesneler oluşturulana kadar bu şekilde devam eder. Yanlış sırada yaparsanız nesnelerden biri gizlenebilir, bu kodda konumunu değiştirmeniz yeterlidir. Kod şu şekildedir:

inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it)
{
        color cor1, cor2, cor3;
        string sz0;
        int infoValue;
                                
        switch (it)
        {
                case IT_TAKE    :
                        infoValue = m_BaseFinance.FinanceTake;
                        cor1 = clrForestGreen;
                        cor2 = clrDarkGreen;
                        cor3 = clrNONE;
                        break;
                case IT_STOP    :
                        infoValue = - m_BaseFinance.FinanceStop;
                        cor1 = clrFireBrick;
                        cor2 = clrMaroon;
                        cor3 = clrNONE;
                        break;
                case IT_PENDING:
                        infoValue = m_BaseFinance.Leverange;
                        cor1 = clrCornflowerBlue;
                        cor2 = clrDarkGoldenrod;
                        cor3 = clrLightBlue;
                        break;
                case IT_RESULT  :
                default:
                        infoValue = m_BaseFinance.Leverange;
                        cor1 = clrDarkBlue;
                        cor2 = clrDarkBlue;
                        cor3 = clrSilver;
                        break;
                }                               
                m_TradeLine.Create(MountName(ticket, it, EV_LINE), cor2);
                if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE));
                m_BackGround.Create(sz0 = MountName(ticket, it, EV_GROUND), cor1);
                switch (it)
                {
                        case IT_TAKE:
                        case IT_STOP:
                        case IT_PENDING:
                                m_BackGround.Size(sz0, 92, 22);
                                break;
                        case IT_RESULT:
                                m_BackGround.Size(sz0, 84, 34);
                                break;
                }
                m_BtnClose.Create(MountName(ticket, it, EV_CLOSE), def_BtnClose);
                m_EditInfo.Create(sz0 = MountName(ticket, it, EV_EDIT), cor3, infoValue);
                m_EditInfo.Size(sz0, 60, 14);
                if (it != IT_RESULT) m_BtnMove.Create(MountName(ticket, it, EV_MOVE), "Wingdings", "u", 17, cor2);
                else
                {
                        m_InfoVol.Create(sz0 = MountName(ticket, it, EV_VOLUME), clrNONE, infoValue);
                        m_InfoVol.Size(sz0, 60, 14);
                }
}

Vurgulanan tüm satırlar, önceki makalede sunulan son versiyondan bu yana koda yapılan eklemelerdir. Şimdi aşağıdaki fonksiyonun kodunu yazabiliriz:

#define macroDelete(A)  {                                                                       \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND));               \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE));                 \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_CLOSE));                \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_EDIT));                 \
                if (A != IT_RESULT)                                                             \
                        ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_MOVE));         \
                else                                                                            \
                        ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_VOLUME));       \
                        }
                                        
inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL)
                        {
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                if ((it != NULL) && (it != IT_PENDING) && (it != IT_RESULT)) macroDelete(it)
                                else
                                {
                                        macroDelete(IT_PENDING);
                                        macroDelete(IT_RESULT);
                                        macroDelete(IT_TAKE);
                                        macroDelete(IT_STOP);
                                }
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                        }
#undef macroDelete


Makro kullanmanın bize ne kadar yardımcı olduğuna dikkat edin: göstergedeki tüm nesneleri silebilmek için yalnızca vurgulanan kısımları eklemek gerekti. Şimdi 4 göstergede 6 nesne kullanıyoruz. Bu farklı bir şekilde uygulanmış olsaydı, çok fazla çalışma gerektirecekti ve dolayısıyla hata olasılığı yüksek olacaktı. Konumlandırma fonksiyonunu tamamlayalım:

#define macroSetAxleY(A)        {                                                                       \
                m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND), y);                         \
                m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE), y);                            \
                m_BtnClose.PositionAxleY(MountName(ticket, A, EV_CLOSE), y);                            \
                m_EditInfo.PositionAxleY(MountName(ticket, A, EV_EDIT), y, (A == IT_RESULT ? -1 : 0));  \
                if (A != IT_RESULT)                                                                     \
                        m_BtnMove.PositionAxleY(MountName(ticket, A, EV_MOVE), y);                      \
                else                                                                                    \
                        m_InfoVol.PositionAxleY(MountName(ticket, A, EV_VOLUME), y, 1);                 \
                                }
                                                                        
#define macroSetAxleX(A, B)     {                                                               \
                m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND), B);                 \
                m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE), B);                    \
                m_BtnClose.PositionAxleX(MountName(ticket, A, EV_CLOSE), B + 3);                \
                m_EditInfo.PositionAxleX(MountName(ticket, A, EV_EDIT), B + 21);                \
                if (A != IT_RESULT)                                                             \
                        m_BtnMove.PositionAxleX(MountName(ticket, A, EV_MOVE), B + 80);         \
                else                                                                            \
                        m_InfoVol.PositionAxleX(MountName(ticket, A, EV_VOLUME), B + 21);       \
                                }
                                                                                
inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy)
                        {
                                double ad;
                                int x, y;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                macroSetAxleY(it);
                                macroSetAxleX(it, m_PositionMinimalAlxeX);
                                if (Leverange == 0) return;
                                if (it == IT_PENDING)
                                {
                                        ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal());
                                        ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad))), x, y);
                                        macroSetAxleY(IT_TAKE);
                                        macroSetAxleX(IT_TAKE, m_PositionMinimalAlxeX + 110);
                                        ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad)), x, y);
                                        macroSetAxleY(IT_STOP);
                                        macroSetAxleX(IT_STOP, m_PositionMinimalAlxeX + 220);
                                }
                        }
#undef macroSetAxleX
#undef macroSetAxleY


Yine çok az kod eklendi. Buna rağmen fonksiyon, makro kullanımı sayesinde öğeleri doğru bir şekilde konumlandırdığı için tüm öğelerle çalışabilir. Bu aşamada Uzman Danışmanı derledikten sonra şu sonucu elde ederiz:


Her şey güzel görünse de, kontroller hala çalışmıyor: nesnelerin her biri için olayları uygulamalıyız. Olaylar olmadan arayüz neredeyse işe yaramaz, çünkü aslında yapacağı tek şey orijinal olarak kullanılan çizgileri değiştirmek olacaktır.


2.0. Sorunlarla başa çıkma

Basit olsaydı, herkes yapabilirdi. Ancak her zaman çözmemiz gereken sorunlarımız vardır ve bu, geliştirme sürecinin bir parçasıdır. Nasıl çözüleceğini göstermek yerine sadece çözümü sunabilirim. Ancak bu makalelerin sizi sorunların üstesinde gelmeye ve gerçekten programlamayı öğrenmeye motive etmesini istiyorum.


2.0.1. Grafik güncellendiğinde ayarlama yapma gereği

Bu yaşadığımız ilk sorundur. Nesnelerin konumlarının grafik güncellemesiyle birlikte güncellenmemesinden kaynaklanmaktadır. Bunu anlamak için aşağıdaki animasyona bir göz atalım:

Bunun gibi şeyler çıldırtıcı olabilir ama çözümü çok basittir: MetaTrader 5'in kendisi, grafiğin güncellenmesi gerektiğini bildiren bir olay oluşturur. Öyleyse yapmamız gereken, olayı yakalamak ve emir sistemimizi güncellemektir.

CHARTEVENT_CHART_CHANGE olayı çağrıldığında yakalama yapılmalıdır. Uzman Danışman kodunda bulunan UpdatePosition fonksiyonunu kullanırsanız güncelleme daha kolaydır. Tek yapmamız gereken kodumuza sadece bir satır eklemektir. Bu, aşağıda gösterildiği gibi C_OrderView sınıfında gerçekleştirilir:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{

// ... Code ....
                                
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        SetPositionMinimalAxleX();
                        UpdatePosition();
                        break;

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

Bu basit çözümün bir sorunu vardır: varlık için çok sayıda emir mevcutsa, süreç biraz zaman alabilir ve bu da Uzman Danışmanın güncellemede takılıp kalmasına ve diğer görevlere geçememesine neden olabilir. Süreci hızlandıran daha karmaşık başka çözümler de vardır. Ama bu çözüm bu sistem için oldukça yeterlidir. Sonuç aşağıdaki gibidir:


Her şey iyi gibi görünüyor, değil mi? Ama burada bir hata vardır. Sistemi test edene kadar bunu fark etmek zordur. Ancak sistemde gerçekten de düzeltildikten sonra bile hiçbir yere gitmeyen bir kusur vardır.


2.0.2. Uzman Danışman, lütfen öğeleri otomatik olarak seçmeyi bırak

Yukarıdaki animasyona bakarsanız, biz seçmediğimiz halde durma çizgisinin seçili olduğunu fark edeceksiniz. Uzman Danışman, grafikte her değişiklik yaptığınızda bunu yapacaktır. Göstergenin oluşturulması sırasındaki ayarlara bağlı olarak, Uzman Danışman Kârı Al veya pozisyon çizgisini seçebilir ve bu, grafiği her hareket ettirdiğinizde gerçekleşecektir.

Neler olup bittiğini anlamaya çalışırken çıldırabilirsiniz, ancak çözüm öncekinden daha da basittir: aynı koda bir satır ekleyelim ve böylece Uzman Danışman otomatik olarak çizgi seçmeyi durduracaktır. Düzeltme aşağıdaki kodda vurgulanmıştır:

inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it)
{
        color cor1, cor2, cor3;
        string sz0;
        double infoValue;

// ... Internal code...

        Select(NULL);
}

Bu tür şeyler geliştirme sürecinde her zaman olacaktır. Fark edilmesi kolay ve bazen de ilk başta fark edemediğimiz diğer daha az belirgin olan hataları kastediyorum. Her zaman olabilirler. Dolayısıyla, programlamayı öğrenmek süreklilik arz eden bir süreçtir. Bazen bu sorunları kendiniz çözebilirsiniz ve sonrasında da aynı sorunu çözmelerine yardımcı olmak amacıyla herkesle paylaşabilirsiniz. Siz de aynı şekilde diğer programcıların sunduğu sorunlar ve çözümler üzerinde çalışabilirsiniz. Ben şahsen böyle çok pratik yapıyorum çünkü geçmişten beri programlamayı bu şekilde öğrendim. Bunu yaparak siz de programlama becerinizi geliştirebilirsiniz. Genel olarak, bir kaynak kodu edinmek, onun üzerinde değişiklikler yapmak ve sonuçları gözlemlemek öğrenmenin bir parçasıdır. Bunu işlevsel bir kodla yapmalısınız, böylece nasıl oluşturulduğunu anlamak daha kolay olacaktır. Her programcının belirli bir sorunu nasıl çözmeyi başardığını anlayarak çok şey öğrenebileceğimiz için bu bize çok fayda sağlayacaktır.

Şimdi bir sonraki önemli konuya geçelim.


3.0. Olay işleme

Pozisyon sonucunu raporlayacak bir sistem kuracağız. Bu, ticaret arayüzündeki ilgili alanın yerini alacaktır, ancak ben ticaret arayüzünü olduğu gibi bırakacağım çünkü pozisyonların toplam sonucunu göstermektedir. Hesap hedging türündeyse, değer emir sisteminde belirtilenden farklı olacaktır (biri yerel değer, diğeri de toplam değer olacaktır). Diğer hesap türlerinde böyle bir fark olmayacaktır, bu nedenle dilerseniz sonuç sistemini ticaret arayüzünden kaldırabilirsiniz.

3.0.1. Pozisyon sonucunu görüntüleme

Kodu ilk kez görenler, bilgiyi nerede arayacaklarını bilemeyerek kaybolabilir ve ardından da orijinal kodun halihazırda yaptığı bir şeyi yapmak için başka kodlar yazabilir. Ve genellikle birçok soruna yol açan şey de budur: orijinal kodun içermediği hatalar oluşturabilecek ekstra kod yazmak. Bu, yalnızca gerçekten gerekli olduğunda programlama yapmamız gerektiğini belirten “yeniden kullan” kuralına da uyumlu değildir. Dolayısıyla, MetaTrader 5'in ve Uzman Danışmanın nasıl çalıştığını bilerek, ticaret arayüzünde sunulan sonucun nerede üretildiğini bulabiliriz. Çünkü pozisyonların sonucu sunuluyorsa, aradığımız yer orasıdır ve onu kullanmalıyız. Aşağıdaki koda dikkat edin:

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


Ardından, vurgulanan noktaya geçelim. Kaynak kodu aşağıda gösterilmektedir:

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


Peki, şu düşünülebilir: ama hangi nesnelere başvurmam gerektiğini bilmenin bir yolu yoksa, verileri oradan nasıl alıp göstergeye uygulayacağım? Nesneler herhangi bir kriter ya da özen olmaksızın üstünkörü bir şekilde oluşturulmuş gibi görünebilir... Ancak bu doğru değildir. Eğer bu şekilde düşünüyorsanız ve bir çözüm aklınıza gelmiyorsa, MetaTrader 5 platformunun gerçekte nasıl çalıştığı hakkında daha fazla bilgi edinmeniz sizin için yararlı olacaktır. Oluşturulan nesnelere atıfta bulunan herhangi bir liste, dizi veya yapı oluşturmadığım doğrudur, ancak bunu bir amaç için yaptım. Çünkü işe yaradığını biliyorum. Size bunun gerçekten işe yaradığını göstereceğim: grafikte yer alacak nesneleri depolamak için herhangi bir yapı kullanmadan belirli bir nesneye başvurabiliriz. Asıl ihtiyaç olan, nesne adının doğru bir şekilde modellenmesidir. Bu kadar.

Soru: Nesne adları nasıl modellendi?

Cevap aşağıdaki gibidir:

1 - Başlığın sırası Emir sisteminde kullanılan nesneyi diğer tüm nesnelerden ayırır
2 - Sınırlayıcı karakter Diğer bazı bilgilerin takip edeceğini ifade eder
3 - Tür Kârı Al ve Zararı Durdur arasında ayrım yapar
4 - Sınırlayıcı karakter 2. madde ile aynıdır
5 - Emir veya pozisyon fişi Emir fişini kaydeder - OCO emrinin göstergelerini birbirine bağlar ve emirler arasında ayrım yapar
6 - Sınırlayıcı karakter 2. madde ile aynıdır
7 - Olay Aynı göstergedeki nesneler arasında ayrım yapar

Yani, modelleme her şeydir. Programlama hakkında yeterli bilgisi olmayan kişiler tekrar eden bir şey yarattığımızı düşünebilir, ancak aslında benzersiz bir şey yaratıyoruz - nesnelerin her biri benzersizdir ve basit bir kuralla onlara başvurulabilir. Bu kural aşağıdaki kod tarafından oluşturulur:

inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev)
                        {
                                return StringFormat("%s%c%c%c%d%c%c", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)ev);
                        }


Bu kod aracılığıyla hangi emir fişine, hangi göstergeye ve hangi olaya erişmek istediğimizi söylersek, o nesnenin adını elde ederiz. Bu şekilde nesnenin niteliklerini manipüle edebiliriz. Bunun ilk adım olduğunu biliyoruz ve şimdi bir karar daha vermemiz gerekiyor: bu manipülasyonu, kodda kaosa neden olmadan ve böylece onu bir Frankenstein'a dönüştürmeden güvenli bir şekilde nasıl yapabiliriz?

Şimdi onu yapalım. C_ObjectsTrade sınıfına gidip aşağıdaki kodu ekleyelim:

inline void SetResult(ulong ticket, double dVolume, double dResult)
                        {
                                m_InfoVol.SetTextValue(MountName(ticket, IT_RESULT, EV_VOLUME), (dVolume / Terminal.GetVolumeMinimal()), def_ColorVolumeResult);
                                m_EditInfo.SetTextValue(MountName(ticket, IT_RESULT, EV_EDIT), dResult);
                        }


Şimdi C_Router sınıfına geçelim ve vurgulanan kodu ekleyelim:

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

Bu sorunlardan birini çözmektedir. Ama hala çözülmemiş başka sorunlarımız vardır.


3.0.2. Bekleyen emrin hacminin belirtilmesi

Şimdi bekleyen emrin hacminin belirtilmesiyle ilişkili sorununu çözelim. Bunu yapmak için C_ObjectsTrade sınıfında yeni bir fonksiyon oluşturmamız gerekmektedir.

inline void SetVolumePendent(ulong ticket, double dVolume)
                        {
                                m_EditInfo.SetTextValue(MountName(ticket, IT_PENDING, EV_EDIT), dVolume / Terminal.GetVolumeMinimal(), def_ColorVolumeEdit);
                        }


Sonrasında, C_Router sınıfından UpdatePosition fonksiyonunu kullanacağız ve böylece güncelleme sorunsuz bir şekilde gerçekleşecektir.

void UpdatePosition(int iAdjust = -1)
{

// ... Internal code ....

        for (int i0 = o; i0 >= 0; i0--) if ((ul = OrderGetTicket(i0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol())
        {
                price = OrderGetDouble(ORDER_PRICE_OPEN);
                take = OrderGetDouble(ORDER_TP);
                stop = OrderGetDouble(ORDER_SL);
                bTest = CheckLimits(price);
                vol = OrderGetDouble(ORDER_VOLUME_CURRENT);

// ... Internal code...

                CreateIndicatorTrade(ul, price, IT_PENDING);
                SetVolumePendent(ul, vol);
                CreateIndicatorTrade(ul, take, IT_TAKE);
                CreateIndicatorTrade(ul, stop, IT_STOP);
        }
};


Böylece sorun çözülmüş olur. Şimdi, Kârı Al ve Zararı Durdur değerlerinin sorununu çözmemiz gerekiyor, çünkü bu değerler emri grafiğe yerleştirdikten sonra doğru değerlere karşılık gelmemektedir.


3.0.3. Göstergenin Kapat düğmesinin tıklanma olayı

Bir emri veya eşiklerinden birini kaldırmanın tek güvenli yolu, göstergelerin her birinin köşesinde bulunan Kapat düğmesidir. Ancak burada yanlış uygulanmış bir olay vardır. Bunu düzeltelim.

Tıklama olayı aslında C_OrderView sınıfında uygulanmalıdır. Eski sistemi vurgulanan kodla değiştiriyoruz:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   	ticket;
        double  	price, pp, pt, ps;
        eIndicatorTrade it;
        eEventType 	ev;
                                
        switch (id)
        {

// ... Internal code...
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetInfosOrder(sparam, ticket, price, it, ev))
                        {
                                switch (ev)
                                {
                                        case EV_CLOSE:
                                                if (OrderSelect(ticket)) switch (it)
                                                {
                                                        case IT_PENDING:
                                                                RemoveOrderPendent(ticket);
                                                                break;
                                                        case IT_TAKE:
                                                                ModifyOrderPendent(ticket, OrderGetDouble(ORDER_PRICE_OPEN), 0, OrderGetDouble(ORDER_SL));
                                                                break;
                                                        case IT_STOP:
                                                                ModifyOrderPendent(ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), 0);
                                                                break;
                                                }
                                                if (PositionSelectByTicket(ticket)) switch (it)
                                                {
                                                        case IT_RESULT:
                                                                ClosePosition(ticket);
                                                                break;
                                                        case IT_TAKE:
                                                                ModifyPosition(ticket, 0, PositionGetDouble(POSITION_SL));
                                                                break;
                                                        case IT_STOP:
                                                                ModifyPosition(ticket, PositionGetDouble(POSITION_TP), 0);
                                                                break;
                                                }
                                                break;

// ... Rest of the code...


Bu sınıfa eklenecek bir şey daha vardır. Yatırımcı yanlışlıkla pozisyon verilerini bildiren bir nesneyi silerse ne olur? Bunu önlemek adına sisteme aşağıdaki kodu ekleyeceğiz:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong           ticket;
        double          price, pp, pt, ps;
        eIndicatorTrade it;
        eEventType      ev;
                                
        switch (id)
        {
                case CHART_EVENT_OBJECT_DELETE:
                case CHARTEVENT_CHART_CHANGE:
                        SetPositionMinimalAxleX();
                        UpdatePosition();
                        break;

// ... Rest of the code...

Böylece, yatırımcı silmemesi gereken bir şeyi silerse, Uzman Danışman silinen göstergeyi veya nesneyi hızlı bir şekilde geri getirecektir.

Aşağıdaki video, sistemin şu anda nasıl çalıştığını göstermektedir. Makalede ele almadığımız, açıklama gerektirmeyecek kadar bazı küçük değişiklikler de mevcuttur.




Sonuç

Sistem tamamlanmış gibi görünse ve onunla ticaret yapmak isteseniz de, henüz tamamlanmadığı konusunda sizi uyarmalıyım. Bu makalenin amacı, çok daha pratik ve kullanımı kolay bir emir sistemine sahip olmak için bir şeylerin nasıl eklenebileceğini ve değiştirilebileceğini göstermektir. Ancak yine de pozisyonları hareket ettirmekten sorumlu sistemden yoksundur ve Uzman Danışmanı yol gösterici, pratik ve kullanımı sezgisel yapacak olan da budur. Ama bunu bir sonraki makaleye bırakacağız.

Ekte, mevcut geliştirme aşamasındaki sistem yer almaktadır.


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

Ekli dosyalar |
Bull’s Power göstergesine dayalı bir ticaret sistemi nasıl geliştirilir? Bull’s Power göstergesine dayalı bir ticaret sistemi nasıl geliştirilir?
En popüler teknik göstergelere dayalı ticaret sistemlerinin nasıl geliştirileceğine ilişkin serimizin yeni makalesine hoş geldiniz. Bu sefer Bull’s Power teknik göstergesini inceleyeceğiz.
Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 20): Yeni emir sistemi (III) Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 20): Yeni emir sistemi (III)
Yeni emir sistemini uygulamaya devam ediyoruz. Böyle bir sistemin oluşturulması, MQL5'e iyi hakim olmanın yanı sıra MetaTrader 5 platformunun gerçekte nasıl çalıştığını ve hangi kaynakları sağladığını anlamayı gerektirir.
VIDYA göstergesine dayalı bir ticaret sistemi nasıl geliştirilir? VIDYA göstergesine dayalı bir ticaret sistemi nasıl geliştirilir?
En popüler teknik göstergelere dayalı ticaret sistemlerinin nasıl geliştirileceğine ilişkin serimizin yeni makalesine hoş geldiniz. Bu makalede, Variable Index Dynamic Average (VIDYA) teknik göstergesini konuk edeceğiz.
Popülasyon optimizasyon algoritmaları: Yapay arı kolonisi (Artificial Bee Colony, ABC) Popülasyon optimizasyon algoritmaları: Yapay arı kolonisi (Artificial Bee Colony, ABC)
Bu makalede, yapay arı kolonisi algoritmasını inceleyeceğiz ve bilgi birikimimizi fonksiyon uzaylarıyla çalışmanın yeni ilkeleriyle destekleyeceğiz. Ayrıca algoritmanın klasik versiyonuna yorumumuzu katarak değiştirilmiş bir versiyonunu uygulayacağız.