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

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

MetaTrader 5Ticaret | 10 Ekim 2023, 10:53
479 0
Daniel Jose
Daniel Jose

Giriş

Bir önceki Sıfırdan bir ticaret Uzman Danışmanı Geliştirme (Bölüm 22): Yeni emir sistemi (V) başlıklı makalede, bekleyen emirleri ve pozisyon eşik seviyelerini taşımak için bir sistem geliştirdik. Bu yöntem daha güvenli olsa da (çünkü işlem sunucusunda bulunanları yansıtır), seviyeleri hızlı bir şekilde hareket ettirmenin en iyi yolu değildir.

Sorun şu ki, fareyi kullanarak bir şeyi her değiştirdiğimizde, bu olay sunucuya gönderilir ve ardından bir yanıt beklememiz gerekir. Bu, olayın her tikte gönderilmesiyle ilişkilidir, yani bir seferde bir seviyeyi birkaç tik hareket ettirmemiz gerekirse, tüm ara değerlerden geçmemiz gerekir ve bu da tüm süreci çok yavaşlatır. İşte bu makalede, sistemi daha akıcı hale getirmek ve böylece seviyeleri çok daha hızlı değiştirebilmek için kodda yapılması gereken değişiklikleri ele alacağız.


1.0. Planlama

Değişiklikleri uygulamak için çok basit bir şey yapmamız gerekiyor: sunucuyu tüm değişiklikler hakkında bilgilendirmemek, sadece gerekli değişiklik hakkında bilgilendirmek. Sadece bunu yapmak bile her şeyin yolunda gitmesini sağlayacaktır, ancak her şeyin tam olarak yaptığımız gibi olduğundan kesinlikle emin olamayacağız.

Şimdi kodu nerelerde değiştirmemiz gerektiğini görelim. Aşağıda gösterilen ayrı bir fonksiyon kullanıyoruz:

#define macroGetPrice(A) StringToDouble(ObjectGetString(Terminal.Get_ID(), MountName(ticket, A, EV_LINE), OBJPROP_TOOLTIP))
                void MoveSelection(double price, uint keys)
                        {
                                static string memStr = NULL;
                                static ulong ticket = 0;
                                static eIndicatorTrade it;
                                eEventType ev;
                                double tp, sl, pr;
                                bool isPending;
                                
                                string sz0 = m_TradeLine.GetObjectSelected();
                                
                                if (sz0 != NULL)
                                {
                                        if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev);
                                        isPending = OrderSelect(ticket);
                                        switch (it)
                                        {
                                                case IT_TAKE:
                                                        if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP));
                                                        else ModifyPosition(ticket, price, macroGetPrice(IT_STOP));
                                                        break;
                                                case IT_STOP:
                                                        if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price);
                                                        else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price);
                                                        break;
                                                case IT_PENDING:
                                                        pr = macroGetPrice(IT_PENDING);
                                                        tp = macroGetPrice(IT_TAKE);
                                                        sl = macroGetPrice(IT_STOP);
                                                        ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr));
                                                        break;
                                        }
                                };
                        }
#undef macroGetPrice

Ancak fonksiyon içerisindeki değişikliklerin yanı sıra, fare olaylarıyla ilgili bazı değişiklikleri de uygulamamız gerekecektir. İlk olarak buna odaklanacağız.

Vurgulanan kısımların, kullanılacak yeni pozisyonları temsil etmek için başka bir şeyle değiştirilmesi gerekir. Ancak tüm değişiklikler kullanıcı için anlaşılabilir olmalıdır.

Tüm kod yapısında büyük değişiklikler yapmadan her şeyi anlaşılması kolay ve aynı zamanda işlevsel hale getirmenin etkili bir yolunu buldum. Bu, ihtiyaç duyulana kadar görünmeyecek bir hayalet gösterge oluşturmaktır. Neyse ki, MetaTrader 5 bunu yapmak için çok basit bir yol sunmaktadır. Dolayısıyla, bu makaleyi anlamak, bu serinin önceki makalelerindeki materyallere zaten aşina olanlar için çok kolay olacaktır.


2.0. Uygulama

Hayalet göstergeyi basitçe gerçek göstergeyle birlikte oluşturacağız. Hayalet gösterge, bir önceki makalede gösterdiğimiz şekilde fiyatı değiştireceğimiz zamana kadar gerçek göstergenin tam gölgesi olacaktır. Gerçek gösterge hareket ettikçe hayalet gösterge grafikte görünecektir. Bu, neler olup bittiğini kolayca karşılaştırmanızı ve değişiklik yapıp yapmamanız gerektiğini anlamanızı sağlayacaktır.


2.0.1. Hayalet gösterge oluşturma

Tüm değişiklikler C_IndicatorTradeView sınıfı içerisinde uygulanır. Üç yeni yönerge tanımlayarak başlayalım:

#define def_IndicatorGhost      "G"
#define def_IndicatorReal       "R"
#define def_IndicatorGhostColor clrDimGray

Amacımız MetaTrader 5'in bizim için çalışmasını sağlamaktır. Kural şudur: önce bir hayalet gösterge oluştururuz, sonra da bir gerçek gösterge oluştururuz. Böylece MetaTrader 5, hayalet göstergenin gerçekten görmemiz gereken ana kadar görünmemesini sağlayacaktır. Bu MetaTrader 5'in kendisi tarafından yapıldığından, programlamada çok tasarruf edeceğiz.

Önemli bir ayrıntı: hayaletin rengini değiştirmek istiyorsanız, yukarıda vurgulanan parçada belirtilen rengi değiştirmeniz yeterlidir.

Bir sonraki adım benzersiz adların oluşturulmasını sağlayan fonksiyonu değiştirmektir.

inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev, bool isGhost = false)
{
        return StringFormat("%s%c%c%c%llu%c%c%c%s", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)(isGhost ? ev + 32 : ev), def_SeparatorInfo, (isGhost ? def_IndicatorGhost : def_IndicatorReal));
}

Vurgulanan parçalar bir önceki versiyona göre eklenmiş veya değiştirilmiştir. MetaTrader 5'in benzersiz adlar oluşturmasını sağlayabiliriz. Yani, bu konuda endişelenmemize gerek yoktur. Olaylara değerler eklediğime dikkat edin. Bunu hayaletin olayları almasını ve kontrolü ele geçirmeye çalışmasını önlemek için yaptım.

Bir sonraki adım nettir- göstergenin kendisini oluşturmalıyız:

inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it)
                        {
                                color cor1, cor2, cor3;
                                string sz0, sz1;
                                
                                switch (it)
                                {
                                        case IT_TAKE    :
                                                cor1 = clrForestGreen;
                                                cor2 = clrDarkGreen;
                                                cor3 = clrNONE;
                                                break;
                                        case IT_STOP    :
                                                cor1 = clrFireBrick;
                                                cor2 = clrMaroon;
                                                cor3 = clrNONE;
                                                break;
                                        case IT_PENDING:
                                                cor1 = clrCornflowerBlue;
                                                cor2 = clrDarkGoldenrod;
                                                cor3 = def_ColorVolumeEdit;
                                                break;
                                        case IT_RESULT  :
                                        default:
                                                cor1 = clrDarkBlue;
                                                cor2 = clrDarkBlue;
                                                cor3 = def_ColorVolumeResult;
                                                break;
                                }
                                m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE, true), def_IndicatorGhostColor);
                                m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE), cor2);
                                if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE));
                                if (it != IT_RESULT) m_BackGround.Create(ticket, sz0 = MountName(ticket, it, EV_GROUND, true), def_IndicatorGhostColor);
                                m_BackGround.Create(ticket, sz1 = MountName(ticket, it, EV_GROUND), cor1);
                                switch (it)
                                {
                                        case IT_TAKE:
                                        case IT_STOP:
                                        case IT_PENDING:
                                                m_BackGround.Size(sz0, 92, 22);
                                                m_BackGround.Size(sz1, 92, 22);
                                                break;
                                        case IT_RESULT:
                                                m_BackGround.Size(sz1, 84, 34);
                                                break;
                                }
                                m_BtnClose.Create(ticket, MountName(ticket, it, EV_CLOSE), def_BtnClose);
                                m_EditInfo1.Create(ticket, sz0 = MountName(ticket, it, EV_EDIT, true), def_IndicatorGhostColor, 0.0);
                                m_EditInfo1.Create(ticket, sz1 = MountName(ticket, it, EV_EDIT), cor3, 0.0);
                                m_EditInfo1.Size(sz0, 60, 14);
                                m_EditInfo1.Size(sz1, 60, 14);
                                if (it != IT_RESULT)
                                {
                                        m_BtnMove.Create(ticket, sz0 = MountName(ticket, it, EV_MOVE, true), "Wingdings", "u", 17, def_IndicatorGhostColor);
                                        m_BtnMove.Create(ticket, sz1 = MountName(ticket, it, EV_MOVE), "Wingdings", "u", 17, cor2);
                                        m_BtnMove.Size(sz1, 21, 21);
                                }else
                                {
                                        m_EditInfo2.Create(ticket, sz1 = MountName(ticket, it, EV_PROFIT), clrNONE, 0.0);
                                        m_EditInfo2.Size(sz1, 60, 14);
                                }
                        }

Vurgulanan tüm satırlar hayaleti oluşturur. Bir şey garip gelebilir: neden tüm unsurları yeniden üretmiyoruz? Aslında hayalet, gerçek göstergenin tam bir kopyası değildir, sadece gölgesidir. Bu nedenle, tüm unsurları yeniden oluşturmamıza gerek yoktur. Gerçek gösterge, ne olması gerektiğini tanımlayacak olan göstergedir; hayalet ise yalnızca işlem sunucusunun ne göreceğine dair bir referans görevi görür.

Şimdi MetaTrader 5'in gerçekten çok çalışacağı, daha detaylı bir çalışma gerektiren kısım geliyor. Nesneleri doğru yerlere yerleştirmenin çok fazla çalışma gerektireceğini düşünebilirsiniz, ancak kaynak kodda gerçekte neyin değiştiğine bakalım.

#define macroSetAxleY(A, B)     {                                                                       \
                m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND, B), y);                              \
                m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE, B), y);                                 \
                m_BtnClose.PositionAxleY(MountName(ticket, A, EV_CLOSE, B), y);                                 \
                m_EditInfo1.PositionAxleY(MountName(ticket, A, EV_EDIT, B), y, (A == IT_RESULT ? -1 : 0));      \
                m_BtnMove.PositionAxleY(MountName(ticket, A, EV_MOVE, B), (A == IT_RESULT ? 9999 : y));         \
                m_EditInfo2.PositionAxleY(MountName(ticket, A, EV_PROFIT, B), (A == IT_RESULT ? y : 9999), 1);  \
                                }
                                                                        
#define macroSetAxleX(A, B, C)  {                                                       \
                m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND, C), B);      \
                m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE, C), B);         \
                m_BtnClose.PositionAxleX(MountName(ticket, A, EV_CLOSE, C), B + 3);     \
                m_EditInfo1.PositionAxleX(MountName(ticket, A, EV_EDIT, C), B + 21);    \
                m_BtnMove.PositionAxleX(MountName(ticket, A, EV_MOVE, C), B + 80);      \
                m_EditInfo2.PositionAxleX(MountName(ticket, A, EV_PROFIT, C), B + 21);  \
                                }                                                                               
//+------------------------------------------------------------------+
inline void ReDrawAllsIndicator(void)
                        {
                                int             max = ObjectsTotal(Terminal.Get_ID(), -1, -1);
                                ulong           ticket;
                                double          price;
                                eIndicatorTrade it;
                                eEventType      ev;
                                
                                for (int c0 = 0; c0 <= max; c0++) if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, -1), ticket, price, it, ev))
                                        PositionAxlePrice(ticket, it, price);
                        }
//+------------------------------------------------------------------+
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                ObjectSetString(Terminal.Get_ID(), MountName(ticket, it, EV_LINE), OBJPROP_TOOLTIP, DoubleToString(price));
                                macroSetAxleY(it, true);
                                macroSetAxleY(it, false);
                                switch (it)
                                {
                                        case IT_TAKE: desl = 110; break;
                                        case IT_STOP: desl = 220; break;
                                        default: desl = 0;
                                }
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), true);
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), false);
                        }
#undef macroSetAxleX
#undef macroSetAxleY

Hepsi bu kadar mı? Sadece makroları mı değiştireceğiz? Evet, tüm kodu yeniden oluşturmaya gerek yoktur, onu sadece düzenleriz. Gerçekten yapmamız gereken tek şey MetaTrader 5'e manipüle ettiğimiz nesnenin adını söylemektir, MetaTrader 5 gerisini bizim için halledecektir. Bunun için fonksiyonlar oluşturmamıza gerek yoktur. Vurgulanan parçaları eklememiz yeterlidir.

Burada değiştirilecek başka bir fonksiyon aşağıda gösterilmektedir:

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;
                                
        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        value0 = value0 / Terminal.GetVolumeMinimal();
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit);
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor);
                        break;
        }
}

Burada vurgulanan kısımları ekledik. Böylece hayalet oluşturuldu. Şu anda, gerçek göstergede neler olduğunu doğru bir şekilde yansıtmaktadır. O kadar iyi yansıtmaktadır ki düzgün çalışması için birkaç şey daha uygulamamız gerekecektir. Kod yanlış değildir, ancak hayalet gerçek göstergeyle çok yakından ilişkilidir, ki aslında bundan kaçınmalıyız.

Burada değiştirilecek son fonksiyon aşağıda gösterilmektedir:

inline void RemoveIndicator(ulong ticket, eIndicatorTrade it = IT_NULL)
                        {
#define macroDestroy(A, B)      {                                                                               \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND, B));                            \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE, B));                              \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_CLOSE, B));                             \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_EDIT, B));                              \
                if (A != IT_RESULT)     ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_MOVE, B));      \
                else ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_PROFIT, B));                       \
                                }

                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                if ((it == IT_NULL) || (it == IT_PENDING) || (it == IT_RESULT))
                                {
                                        macroDestroy(IT_RESULT, true);
                                        macroDestroy(IT_RESULT, false);
                                        macroDestroy(IT_PENDING, true);
                                        macroDestroy(IT_PENDING, false);
                                        macroDestroy(IT_TAKE, true);
                                        macroDestroy(IT_TAKE, false);
                                        macroDestroy(IT_STOP, true);
                                        macroDestroy(IT_STOP, false);
                                } else
                                {
                                        macroDestroy(it, true);
                                        macroDestroy(it, false);
                                }
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
#undef macroDestroy
                        }

Vurgulanan parçalar değişti, burada özel bir şey yoktur.


2.0.2. Hayaleti gerçekten ayırma

Önceki bölümde yapılan değişiklikler bir hayalet oluşturur. Ancak bir sorunumuz vardır - bu hayalet, gerçek nesneye çok yakındır. Bu, ilk bakışta uygulanması çok zor bir şey gibi görünebilir. Ancak koda baktığımızda, zaten bir çözümümüz olduğunu görüyoruz. Ancak bu çözüm yanlış yerde bulunmaktadır. Çözümün bulunduğu yeri değiştirmemiz ve bunu sınıf genelinde daha görünür hale getirmemiz gerekiyor.

Çözüm aşağıdaki kodda gösterilmektedir:

                void DispatchMessage(int id, long lparam, double dparam, string sparam)
                        {
                                ulong   ticket;
                                double  price, tp, sl;
                                bool            isBuy,
                                                        bKeyBuy,
                                                        bKeySell,
                                                        bEClick;
                                long            info;
                                datetime        dt;
                                uint            mKeys;
                                eIndicatorTrade         it;
                                eEventType                      ev;
                                
                                static bool bMounting = false, bIsDT = false, bIsMove = false;
                                static double leverange = 0, valueTp = 0, valueSl = 0, memLocal = 0;
                                
                                switch (id)
                                {
                                        case CHARTEVENT_MOUSE_MOVE:

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

Çözüm, vurgulanan kod parçasıdır. Ama bu nasıl mümkün olabilir? Bir bekleyen emir yerleştirirken, sistemin verileri oluşturmayı ve manipüle etmeyi yönettiğini unutmayın, böylece sonunda grafikte emrin nereye yerleştirileceğini ifade eden göstergelerin bir temsiline sahip oluruz. Bu, aşağıdaki kod kullanılarak yapılır.

case CHARTEVENT_MOUSE_MOVE:
        Mouse.GetPositionDP(dt, price);
        mKeys   = Mouse.GetButtonStatus();
        bEClick  = (mKeys & 0x01) == 0x01;    //left mouse click
        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT pressed
        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
        if (bKeyBuy != bKeySell)
        {
                if (!bMounting)
                {
                        Mouse.Hide();
                        bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl);
                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange);
                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange);
                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                        bMounting = true;
                }
                tp = price + (bKeyBuy ? valueTp : (-valueTp));
                sl = price + (bKeyBuy ? (-valueSl) : valueSl);
                UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy);
                if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT);
                }else if (bMounting)
                {
                        UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false);
                        Mouse.Show();
                        memLocal = 0;
                        bMounting = false;

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

Alış için SHIFT tuşuna veya satış için CTRL tuşuna basmanıza bağlı olarak, sistem oluşturulmakta olan bekleyen emrin bir temsilini oluşturacaktır. Doğrudan grafik üzerinde görüntülenecektir. Kârı Al veya Zararı Durdur olarak kullanılacak değerler ticaret arayüzünde yakalanır, ardından temsili seviyeyi emri yerleştirmek istediğiniz seviyeye taşırsınız ve sonrasında farenin sol düğmesine tıklayarak sisteme oraya bir emir yerleştirilmesi gerektiğini söylersiniz. Fare tekrar hareket eder etmez, emri temsil etmek için kullanılan gösterge kaldırılır ve emir göstergesi bırakılır.

Şimdiye kadar özel bir şey yoktu. Ancak koda bakarsanız, aşağıdakileri göreceksiniz:

// ... CHARTEVENT_MOUSE_MOVE code....

        }else if ((!bMounting) && (bKeyBuy == bKeySell))
        {
                if (bEClick)
                {
                        bIsMove = false;
                        m_TradeLine.SpotLight();
                }
                MoveSelection(price, mKeys);
        }
break;

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

Böylece, bir bekleyen emir oluşturmadığımızda ve tuşlar serbest bırakıldığında, farenin fiyat konumunu seçilebilecek bir göstergeye göndeririz. Bu, vurgulanan satırda yapılır. Sol tıkladığımız anda, bu aktarımı sonlandırırız, göstergenin seçimi kaldırılır ve şu anda yerel olan değişkenin (bIsMove) durumu değiştirilir. Ancak bunu değiştireceğiz. Bu değişkenin durumu yalnızca DispatchMessage fonksiyonu içerisindeki başka bir olay tarafından değiştirilmektedir. Bu olay aşağıda görünmektedir:

// ... Code ....

        case EV_MOVE:
                if (bIsMove)
                {
                        m_TradeLine.SpotLight();
                        bIsMove = false;
                }else
                {
                        m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                        bIsMove = true;
                }
        break;

Bu kod bIsMove değişkeninin durumunu değiştirecektir ve aynı zamanda da seçili olup olmadığını ifade etmek için göstergeyi değiştirecektir.

Dolayısıyla, bu değişkeni sınıf boyunca görünür hale getirirsek, hayaleti gerçek göstergeden ayırabiliriz ve bu sayede yalnızca gerçek ya da yalnızca hayaleti manipüle etmek mümkün olacaktır - bu seçim ilgilendiğiniz şeye bağlıdır. Biz burada gerçek göstergeyi değiştireceğiz, hayalet ise işlem sunucusunun ne gördüğünü gösterecektir.

Bu şekilde, kodla fazla uğraşmak zorunda kalmayız, sadece birkaç ayrıntıyı ayarlarız. Sol tıklama yapıldıktan ve nesne taşındıktan sonra, bekleyen emri veya eşik seviyesini değiştirmek için bir emir gönderilecektir.

Bunun pratikte nasıl yapıldığını görelim. İlk olarak, bir private değişken oluşturalım.

bool    m_bIsMovingSelect;

Bu, yukarıda açıkladığım şeyi yansıtmaktadır. Ancak başlatılması gerekir.

C_IndicatorTradeView() : m_bIsMovingSelect(false) {}

Şimdi, DispatchMessage'a gidiyoruz ve bIsMove yerine onu kullanıyoruz.

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

// ... Internal code...

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:

// ... Internal code...

                                }else if ((!bMounting) && (bKeyBuy == bKeySell))
                                {
                                        if (bEClick)
                                        {
                                                m_bIsMovingSelect = false;
                                                m_TradeLine.SpotLight();
                                        }
                                        MoveSelection(price, mKeys);
                                }
                                break;

// ... Internal code...

                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {

// ... Internal code....

                                case EV_MOVE:
                                        if (m_bIsMovingSelect)
                                        {
                                                m_TradeLine.SpotLight();
                                                m_bIsMovingSelect = false;
                                        }else
                                        {
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                                m_bIsMovingSelect = true;
                                        }
                                        break;
                        }
                        break;
                }
}

Değişiklikler vurgulanmıştır. Böylece artık tüm sınıf göstergeyle ne zaman çalışıp çalışmadığımızı bilecektir. Bu şekilde hayaleti gerçek göstergeden ayırabilir ve daha doğru bir sunum elde edebiliriz.


2.0.3. Sadece ilgili olanları taşıma

Önceki iki konuda, hayalet göstergeye sahip olmak için sistem oluşturduk ve bazı düzeltmeler yaptık. Şimdi bileşenleri taşımamız gerekiyor ve bunun için bazı çalışmalar yapmamız gerekecek. İlk adım, fonksiyonlar arasında aşırı çağrılardan kaçınmak adına ihtiyaç duyduğumuz çeşitli bilgileri depolamak için bir yapı oluşturmaktır. Yapı aşağıda gösterilmektedir:

struct st00
{
        eIndicatorTrade it;
        bool            bIsMovingSelect,
                        bIsBuy;
        ulong           ticket;
        double          vol,
                        pr,
                        tp,
                        sl;
}m_InfoSelection;

Vurgulanan kısım daha önce kodda mevcuttu, ancak şimdi yapının bir parçasıdır. Merak etmeyin. İlerledikçe, yapının neden bu unsurlara sahip olduğu daha açık hale gelecektir.

Burada, değiştirilen ve açıklama gerektiren fonksiyonları inceleyeceğiz. Bunlardan ilki SetTextValue'dur.

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;

        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        value0 = value0 / Terminal.GetVolumeMinimal();
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit);
                        if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor);
                        break;
        }
}

Bir şeyi taşırken, hayaletin yeni veriyi takip etmesini istemeyiz, sabit kalmasını ve göstergenin öncesinde nerede olduğunu göstermesini isteriz. Bu, vurgulanan parçalar eklenerek kolayca yapılabilir. Böylece, gerçek gösterge serbestçe hareket ederken hayalet gösterge hareketsiz kalır. Hayalet göstergede hareketin kendisine değil, sunucuda bulunan değerlerin ifadesine sahip olacağız.

Sırada aşağıda gösterilen hareket kodu vardır:

void MoveSelection(double price)
{
        double tp, sl;
                                
        if (!m_InfoSelection.bIsMovingSelect) return;
        switch (m_InfoSelection.it)
        {
                case IT_TAKE:
                        UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        break;
                case IT_STOP:
                        UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        break;
                case IT_PENDING:
                        tp = (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr);
                        sl = (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr);
                        UpdateInfosIndicators(0, m_InfoSelection.ticket, price, tp, sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        break;
        }
}

Lütfen vurgulanan kısma dikkat edin: sistemin verileri doğru bir şekilde göstermesi için hareketi ayarlar. Garip görünebilir, ancak UpdateINfosIndicators fonksiyonu başka bir düzeltmeye sahiptir. Eğer bunu şimdi yapmazsak, daha sonra sorun yaşayacağız. Diğer fonksiyonlar oldukça basittir ve açıklama gerektirmez.

void SetPriceSelection(double price)
{
        bool isPending;
        if (!m_InfoSelection.bIsMovingSelect) return;
        isPending = OrderSelect(m_InfoSelection.ticket);
        m_InfoSelection.bIsMovingSelect = false;
        m_TradeLine.SpotLight();
        switch (m_InfoSelection.it)
        {
                case IT_TAKE:
                        if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl);
                        else ModifyPosition(m_InfoSelection.ticket, price, m_InfoSelection.sl);
                        break;
                case IT_STOP:
                        if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price);
                        else ModifyPosition(m_InfoSelection.ticket, m_InfoSelection.tp, price);
                        break;
                case IT_PENDING:
                        ModifyOrderPendent(m_InfoSelection.ticket, price, (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr), (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr));
                        break;
        }
}

Yukarıdaki fonksiyon sunucuyu neler olduğu ve yeni verinin ne olduğu hakkında bilgilendirecektir. Lütfen vurgulanan satırlarda göstergenin seçilmesi gerektiğini unutmayın, aksi takdirde sunucuya istek yapılmayacaktır.

Göze çarpan son fonksiyon aşağıda gösterilmektedir:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;
        double  price;
        bool    bKeyBuy,
                bKeySell,
                bEClick;
        datetime dt;
        uint    mKeys;
        char    cRet;
        eIndicatorTrade         it;
        eEventType              ev;
                                
        static bool bMounting = false, bIsDT = false;
        static double valueTp = 0, valueSl = 0, memLocal = 0;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse click
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //Pressed SHIFT
                        bKeySell = (mKeys & 0x08) == 0x08;    //Pressed CTRL
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        Mouse.Hide();
                                        bIsDT = Chart.GetBaseFinance(m_InfoSelection.vol, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_InfoSelection.vol);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_InfoSelection.vol);
                                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                                        m_InfoSelection.it = IT_PENDING;
                                        m_InfoSelection.ticket = def_IndicatorTicket0;
                                        m_InfoSelection.bIsMovingSelect = true;
                                        m_InfoSelection.pr = price;
                                        bMounting = true;
                                }
                                m_InfoSelection.tp = m_InfoSelection.pr + (bKeyBuy ? valueTp : (-valueTp));
                                m_InfoSelection.sl = m_InfoSelection.pr + (bKeyBuy ? (-valueSl) : valueSl);
                                m_InfoSelection.bIsBuy = bKeyBuy;
                                MoveSelection(price);
                                if ((bEClick) && (memLocal == 0))
                                {
                                        MoveSelection(0);
                                        m_InfoSelection.bIsMovingSelect = false;
                                        CreateOrderPendent(m_InfoSelection.vol, bKeyBuy, memLocal = price,  price + m_InfoSelection.tp - m_InfoSelection.pr, price + m_InfoSelection.sl - m_InfoSelection.pr, bIsDT);
                                }
                        }else if (bMounting)
                        {
                                MoveSelection(0);
                                m_InfoSelection.bIsMovingSelect = false;
                                Mouse.Show();
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell))
                        {
                                if (bEClick) SetPriceSelection(price); else MoveSelection(price);
                        }
                        break;
                case CHARTEVENT_OBJECT_DELETE:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev))
                        {
                                CreateIndicatorTrade(ticket, it);
                                GetInfosTradeServer(ticket);
                                m_InfoSelection.bIsMovingSelect = false;
                                UpdateInfosIndicators(0, ticket, m_InfoSelection.pr, m_InfoSelection.tp, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        }
                        break;
                case CHARTEVENT_CHART_CHANGE:
                        ChartSetInteger(ChartID(), CHART_SHOW_OBJECT_DESCR, false);
                        ReDrawAllsIndicator();
                        break;
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {
                                case EV_CLOSE:
                                        if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)
                                        {
                                                case IT_PENDING:
                                                case IT_RESULT:
                                                        if (cRet < 0) RemoveOrderPendent(ticket); else ClosePosition(ticket);
                                                        break;
                                                case IT_TAKE:
                                                case IT_STOP:
                                                        m_InfoSelection.bIsMovingSelect = true;
                                                        SetPriceSelection(0);
                                                        break;
                                        }
                                        break;
                                case EV_MOVE:
                                        if (m_InfoSelection.bIsMovingSelect)
                                        {
                                                m_TradeLine.SpotLight();
                                                m_InfoSelection.bIsMovingSelect = false;
                                        }else
                                        {
                                                m_InfoSelection.ticket = ticket;
                                                m_InfoSelection.it = it;
                                                if (m_InfoSelection.bIsMovingSelect = (GetInfosTradeServer(ticket) != 0))
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                        }
                                        break;
                        }
                        break;
        }
}

Bu fonksiyonla ilgili dikkat edilmesi gereken birkaç şey vardır, ancak önceki versiyondan ne kadar farklı hale geldiğine dikkat edin: burada çok daha fazla kodu yeniden kullanıyoruz. Böylece, başka bir yerde kodda bir hata varsa, bunu hızlı bir şekilde fark edebilir ve düzeltebiliriz. Öncesinde, bu fonksiyonun kodu bazı düzeltmeler ve diğer kısımlardaki kusurlarla birlikte biraz dengesizdi. Ana noktalardan biri, bir bekleyen emir yerleştirirken, hareket eden göstergeler için ayrı bir sistem olmasıydı, ancak şimdi tek bir sisteme sahibiz (grafikte emir yerleştirmek için kullanılanla aynı). Nesneleri taşımak için de kullanılır, yani artık aynı CHARTEVENT_MOUSE_MOVE olayı hem bekleyen emir yerleştirmek hem de nesneleri taşımak için kullanılır. Küçük bir detay gibi görünebilir, ancak koddaki herhangi bir değişikliği görünür hale getirir ve bir sorunumuz varsa, fare olayını kullandığımız sürece ortaya çıkacaktır.


Sonuç

Yapılan değişikliklerle ilgili daha net bir fikir edinmek için lütfen aşağıdaki videoyu izleyin. Uzman Danışmanı emir sistemi açısından tamamen eksiksiz hale getirmek için şimdi sadece birkaç ayrıntıya daha ihtiyacımız olduğunu göreceksiniz.


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

Ekli dosyalar |
MQL5'te ONNX modelleri nasıl kullanılır? MQL5'te ONNX modelleri nasıl kullanılır?
Açık sinir ağı santrali (Open Neural Network eXchange, ONNX), makine öğrenimi modellerini temsil etmek için oluşturulmuş açık bir formattır. Bu makalede, finansal zaman serilerini öngörmek için bir CNN-LSTM modelinin nasıl oluşturulacağını ele alacağız. Ayrıca oluşturulan ONNX modelinin bir MQL5 Uzman Danışmanında nasıl kullanılacağını da göstereceğiz.
Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 22): Yeni emir sistemi (V) Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 22): Yeni emir sistemi (V)
Bugün yeni emir sistemini geliştirmeye devam edeceğiz. Yeni bir sistemi uygulamak o kadar da kolay değildir, zira sık sık süreci büyük ölçüde zorlaştıran sorunlarla karşılaşırız. Bu sorunlar ortaya çıktığında durmalı ve ilerlediğimiz yönü yeniden analiz etmeliyiz.
MetaTrader VPS'i ilk kez başlatma: Adım adım talimatlar MetaTrader VPS'i ilk kez başlatma: Adım adım talimatlar
Ticaret robotları veya sinyal abonelikleri kullanan herkes er ya da geç işlem platformları için güvenilir bir 7/24 sunucu kiralama ihtiyacı duyar. Çeşitli nedenlerden dolayı MetaTrader VPS kullanmanızı öneririz. MQL5.community hesabınız üzerinden hizmet için rahatça ödeme yapabilir ve aboneliği yönetebilirsiniz.
Awesome Oscillator göstergesine dayalı bir ticaret sistemi nasıl geliştirilir? Awesome Oscillator göstergesine dayalı bir ticaret sistemi nasıl geliştirilir?
Serimizin bu yeni makalesinde, ticaretimizde faydalı olabilecek yeni bir teknik araç hakkında bilgi edineceğiz. Bu, Awesome Oscillator (AO) göstergesidir. Bu göstergeyi kullanarak bir ticaret sisteminin nasıl tasarlayacağını öğreneceğiz.