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

Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 25): Sistemin sağlamlığını artırma (II)

MetaTrader 5Örnekler | 15 Ocak 2024, 11:41
192 0
Daniel Jose
Daniel Jose

Giriş

Sistemin sağlamlığını artırma (I) başlıklı bir önceki makalede, sistemi daha istikrarlı ve güvenli hale getirmek için Uzman Danışmanın bazı bölümlerinin nasıl değiştirilebileceğini görmüştük.

Orada yaptıklarımız, bu makalede yapacaklarımıza sadece bir giriş niteliğindeydi. Bildiğiniz, planladığınız veya dilediğiniz her şeyi unutun. Buradaki en zor şey, işleri birbirinden ayırabilmektir. Bu serinin başlangıcından bu yana Uzman Danışman neredeyse sürekli olarak evrim geçiriyor: bazı şeyler ekliyor, değiştiriyor ve hatta kaldırıyoruz. Bu kez, yapmakta olduğumuz şeyin en uç noktalarına kadar gideceğiz.

Göründüğünün aksine, büyük bir sorun vardır: iyi tasarlanmış bir Uzman Danışman, içerisinde herhangi bir gösterge içermez. Sadece belirtilen emirlere uyulmasını gözlemleyecek ve sağlayacaktır. Mükemmel Uzman Danışman aslında sadece fiyatın ne yaptığına dair gerçek bir içgörü sağlayan bir sihirbazdır. Göstergelere bakmaz, yalnızca grafikteki pozisyonlara veya emirlere bakar.

Saçmaladığımı ve neden bahsettiğimi bilmediğimi düşünebilirsiniz. Peki MetaTrader 5'in neden farklı şeyler için farklı sınıflar sağladığını hiç düşündünüz mü? Platformda neden tek bir blokta değil de ayrı ayrı göstergeler, hizmetler, komut dosyaları ve Uzman Danışmanlar var? Yani...

Mesele de bu. Eğer işler birbirinden ayrılmışsa, bunun nedeni tam da ayrı ayrı daha iyi çalışılabilmeleridir.

Göstergeler, hangi türden olursa olsun, genel bir amaç için kullanılır. Göstergelerin tasarımının genel performansa zarar vermeyecek şekilde iyi düşünülmüş olması iyidir - MetaTrader 5 platformuna değil, diğer göstergelere zarar vermemeyi kastediyorum. Farklı bir iş parçacığı üzerinde çalıştıkları için, görevleri paralel olarak çok verimli bir şekilde gerçekleştirebilirler.

Hizmetler farklı şekillerde yardımcı olur. Örneğin, bu serideki İnternetteki verilere erişme (II) ve İnternetteki verilere erişme (III) makalelerinde, verilere erişmek için hizmetleri çok ilginç bir şekilde kullandık. Aslında bunu doğrudan Uzman Danışmanda da yapabilirdik, ancak diğer makalelerde daha önce açıkladığım gibi bu en uygun yöntem değildir.

Komut dosyaları, yalnızca belirli bir süre için var oldukları, çok spesifik bir şey yaptıkları ve ardından grafikten kayboldukları için bize çok benzersiz bir şekilde yardımcı olur. Ya da örneğin zaman dilimi gibi bazı grafik ayarlarını değiştirene kadar orada kalabilirler.

Bu, olasılıkları biraz sınırlıyor, ancak bu, olduğu gibi kabul etmemiz gereken şeyin bir parçasıdır. Uzman Danışmanlar ise tam tersine bir ticaret sistemiyle çalışmaya özgüdür. Uzman Danışmanlarda ticaret sisteminin parçası olmayan fonksiyonlar ve kodlar ekleyebilsek de yüksek performanslı ya da yüksek güvenilirlikli sistemlerde bu çok uygun değildir. Bunun nedeni, ticaret sisteminin bir parçası olmayan her şeyin Uzman Danışmanda olmaması gerektiğidir: işler doğru yerlere yerleştirilmeli ve doğru şekilde ele alınmalıdır.

Bu nedenle, sağlamlığı artırmak için yapılacak ilk şey, ticaret sisteminin bir parçası olmayan her şeyi koddan kesinlikle çıkarmak ve onları göstergelere veya benzeri bir şeye dönüştürmektir. Uzman Danışman kodunda kalacak olan tek şey, emirleri veya pozisyonları yönetmek, analiz etmek ve işlemekten sorumlu olan kısımlardır. Diğer tüm şeyler kaldırılacaktır.

O halde başlayalım.


2.0. Uygulama

2.0.1. Uzman Danışman arka planını kaldırma

Bu Uzman Danışmana zarar vermez veya herhangi bir soruna neden olmazken, bazı insanlar bazen ekranlarının boş olmasını ve üzerinde yalnızca belirli öğelerin görüntülenmesini ister. Bu yüzden, bu kısmı Uzman Danışmandan kaldıracağız ve bir göstergeye dönüştüreceğiz. Uygulaması çok kolaydır. Sınıfların hiçbirine dokunmayacağız, ancak aşağıdaki kodu oluşturacağız:

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Auxiliar\C_Wallpaper.mqh>
//+------------------------------------------------------------------+
input string                    user10 = "Wallpaper_01";        //Used BitMap
input char                      user11 = 60;                    //Transparency (from 0 to 100)
input C_WallPaper::eTypeImage   user12 = C_WallPaper::IMAGEM;   //Background image type
//+------------------------------------------------------------------+
C_Terminal      Terminal;
C_WallPaper WallPaper;
//+------------------------------------------------------------------+
int OnInit()
{
        IndicatorSetString(INDICATOR_SHORTNAME, "WallPaper");
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);

        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
        break;
        }
        ChartRedraw();
}
//+------------------------------------------------------------------+

Gördüğünüz gibi, her şey oldukça doğal ve anlaşılabilir. Kodu Uzman Danışmandan sildik ve grafiğe eklenebilecek bir göstergeye dönüştürdük. Böylece, şeffaflık seviyesi veya hatta grafikten kaldırma gibi arka planda yapılacak herhangi bir değişikliğin Uzman Danışman eylemleri üzerinde hiçbir etkisi olmayacaktır.

Şimdi Uzman Danışman performansının gerçekten düşmesine neden olan şeyleri silmeye başlayacağız. Bunlar zaman zaman veya her fiyat hareketinde çalışan şeylerdir ve bu nedenle bazen Uzman Danışmanın yavaşlamasına neden olabilir, bu da gerçek işini yapmasını engeller - grafikteki emirlerde veya pozisyonlarda neler olduğunu izlemek.


2.0.2. Hacim profilini bir göstergeye dönüştürme

Öyle görünmese de, hacim profili sistemi zaman tüketir ve bu genellikle bir Uzman Danışman için kritik öneme sahiptir. Fiyatların fazla bir yön olmadan çılgınca dalgalandığı yüksek volatilite anlarını kastediyorum. Bu zamanlarda Uzman Danışman, görevini tamamlamak için mevcut her makine döngüsüne ihtiyaç duyar. Bir gösterge işi devralmaya karar verdiği için iyi bir fırsatı kaçırmak üzücü olur. Öyleyse, onu Uzman Danışmandan kaldıralım ve aşağıdaki kodu oluşturarak gerçek bir göstergeye dönüştürelim:

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Tape Reading\C_VolumeAtPrice.mqh>
//+------------------------------------------------------------------+
input color             user0   = clrBlack;                     //Bar color
input   char            user1   = 20;                                   //Transparency (from 0 to 100 )
input color     user2 = clrForestGreen; //Buying
input color     user3 = clrFireBrick;   //Selling
//+------------------------------------------------------------------+
C_Terminal                      Terminal;
C_VolumeAtPrice VolumeAtPrice;
//+------------------------------------------------------------------+
int OnInit()
{
        Terminal.Init();
        VolumeAtPrice.Init(user2, user3, user0, user1);
        EventSetTimer(1);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnTimer()
{
        VolumeAtPrice.Update();
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        VolumeAtPrice.DispatchMessage(id, sparam);
        ChartRedraw();
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+

Bu en kolay kısımdı. Uzman Danışmandan kodu kaldırdık ve göstergeye koyduk. Kodu Uzman Danışmana geri koymak istiyorsanız, gösterge kodunu kopyalamanız ve Uzman Danışmana geri koymanız yeterlidir.

Bu yüzden basit bir şeyle başladık. Ancak şimdi işler daha karmaşık hale geliyor - “zaman ve ticaret” sistemini Uzman Danışmandan kaldıracağız.


2.0.3. “Zaman ve ticaret” sistemini bir göstergeye dönüştürme

Hem bir Uzman Danışmanda hem de bir göstergede kullanılabilecek kodu oluşturmayı hedefliyorsak, bu o kadar basit değildir. Bir alt pencerede çalışan bir gösterge olarak, onu bir göstergeye dönüştürmek kolay olacak gibi görünüyor. Ancak bir alt pencerede çalıştığı için tam olarak kolay değildir. Asıl sorun, her şeyi önceki durumlarda olduğu gibi yaparsak, gösterge penceresinde aşağıdaki sonuca sahip olacağımızdır:

Bu tür şeylerin gösterge penceresine yerleştirilmesi, göstergeyi ekrandan kaldırmak istediğinde kullanıcının kafasını karıştıracağı için önerilmez. Dolayısıyla, bu farklı bir şekilde yapılmalıdır. Oldukça kafa karıştırıcı gibi görünen ama aslında basit bir dizi yönerge ve biraz düzenlemeden ibaret olan bu yolun sonunda gösterge penceresinde aşağıdaki sonucu alacağız.

Bu tam olarak kullanıcının beklediği şeydir - yukarıdaki görüntüde görülen karmaşa değil.

Zaman ve ticaret göstergesinin tam kodu aşağıdadır:

#property copyright "Daniel Jose"
#property version   "1.00"
#property indicator_separate_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Tape Reading\C_TimesAndTrade.mqh>
//+------------------------------------------------------------------+
C_Terminal        Terminal;
C_TimesAndTrade   TimesAndTrade;
//+------------------------------------------------------------------+
input int     user1 = 2;      //Scale
//+------------------------------------------------------------------+
bool isConnecting = false;
int SubWin;
//+------------------------------------------------------------------+
int OnInit()
{
        IndicatorSetString(INDICATOR_SHORTNAME, "Times & Trade");
        SubWin = ChartWindowFind();
        Terminal.Init();
        TimesAndTrade.Init(user1);
        EventSetTimer(1);
                
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        if (isConnecting)
                TimesAndTrade.Update();
        return rates_total;
}
//+------------------------------------------------------------------+
void OnTimer()
{
        if (TimesAndTrade.Connect())
        {
                isConnecting = true;
                EventKillTimer();
        }
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        TimesAndTrade.Resize();
        break;
        }
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+

Kod, Uzman Danışman kodunda bulunmayan vurgulanmış satır dışında Uzman Danışmanda kullanılana benzer görünüyor. O zaman sorun ne? Yoksa hiçbir sorun yok mu? Aslında kod tam olarak aynı değildir, içerisinde gösterge veya Uzman Danışman kodunda değil, sınıf kodunda olan bir fark vardır. Ancak aradaki farka bakmadan önce şunu düşünelim: derleyiciye neyi derleyip neyi derlemeyeceğini nasıl söyleriz? Belki de programlama yaparken bu konuda hiç endişelenmiyorsunuz - belki de sadece kod oluşturuyorsunuz ve herhangi bir şeyi beğenmediğinizde siliyorsunuz.

Deneyimli programcıların bir kuralı vardır: bir şeyi yalnızca kesinlikle çalışmadığında kaldırın, aksi takdirde aslında derlenmemiş olsalar bile parçaları saklayın. Ancak yazılan fonksiyonların her zaman çalışmasını istediğimizde bunu doğrusal bir kodda nasıl yapabiliriz? Soru şu: Derleyiciye neyi derleyip neyi derlemeyeceğini nasıl söyleyeceğinizi biliyor musunuz? Cevabınız "Hayır" ise, bu bir problem değildir. Ben şahsen başladığımda nasıl yapacağımı bilmiyordum. Ama çok yardımcı oluyor. Öyleyse, nasıl yapılacağını öğrenelim.

Bazı diller, yazara bağlı olarak önişlemci olarak da adlandırılabilen derleme yönergelerine sahiptir. Ancak fikir aynıdır: derleyiciye neyi derleyeceğini ve derlemeyi nasıl yapacağını söylemek. Belirli şeyleri test edebilmemiz adına kodu kasıtlı olarak izole etmek için kullanılabilecek çok özel bir yönerge türü vardır. Bunlar koşullu derleme yönergeleridir. Doğru kullanıldıklarında, aynı kodu farklı şekillerde derlememize olanak sağlarlar. Burada zaman ve ticaret sistemi örneğinde yapılan da tam olarak budur. Koşullu derlemenin oluşturulmasından kimin sorumlu olacağını seçiyoruz: Uzman Danışman ya da gösterge. Bu parametreyi tanımladıktan sonra, #define yönergesini oluşturuyoruz ve ardından kodun nasıl derleneceğini derleyiciye bildirmek için #ifdef #else #endif koşullu yönergesini kullanıyoruz.

Bunu anlamak zor olabilir, bu yüzden nasıl çalıştığını görelim.

Uzman Danışman kodunda, aşağıda vurgulanan satırları tanımlıyoruz ve ekliyoruz:

#define def_INTEGRATION_WITH_EA
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh>
#ifdef def_INTEGRATION_WITH_EA
        #include <NanoEA-SIMD\Auxiliar\C_Wallpaper.mqh>
        #include <NanoEA-SIMD\Tape Reading\C_VolumeAtPrice.mqh>
        #include <NanoEA-SIMD\Tape Reading\C_TimesAndTrade.mqh>
#endif
//+------------------------------------------------------------------+

Bir Uzman Danışmanı MQH dosyalarındaki sınıflarla derlemek istiyorsanız, Uzman Danışmanda tanımlanan #ifdefine def_INTEGRATION_WIT_EA yönergesini bırakın. Bu, Uzman Danışmanın aldığımız ve göstergelere eklediğimiz tüm sınıfları içermesini sağlayacaktır. Göstergeleri silmek istiyorsanız, kodu silmenize gerek yoktur, sadece tanımı yorum hale getirmeniz gerekir. Bu, yönergenin bildirildiği satırı bir yorum satırına dönüştürerek basitçe yapılabilir. Derleyici yönergeyi görmeyecek, mevcut değil olarak sunulacaktır; mevcut olmadığı için, #ifdef def_INTEGRATION_WITH_EA koşullu yönergesi her bulunduğunda, tamamen göz ardı edilecek ve yukarıdaki örnekte #endif kısmı ile arasındaki kod derlenmeyecektir.

C_TimesAndTrade sınıfında uyguladığımız fikir budur. İşte yeni sınıfın nasıl göründüğü. Dikkatinizi çekmek için sadece bir nokta göstereceğim:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Auxiliar\C_Canvas.mqh>
#ifdef def_INTEGRATION_WITH_EA

#include <NanoEA-SIMD\Auxiliar\C_FnSubWin.mqh>

class C_TimesAndTrade : private C_FnSubWin

#else

class C_TimesAndTrade

#endif
{
//+------------------------------------------------------------------+
#define def_SizeBuff 2048
#define macro_Limits(A) (A & 0xFF)
#define def_MaxInfos 257
#define def_ObjectName "TimesAndTrade"
//+------------------------------------------------------------------+
        private :
                string  m_szCustomSymbol;

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

}

Kod, derleme yönergelerini kullanmayanlar için garip görünebilir. def_INTEGRATION_WITH_EA yönergesi Uzman Danışmanda bildirilir. Sonra şunlar olur: Derleyici bu dosyadan nesne kodu üretirken şu ilişkiyi varsayacaktır: derlenen dosya bir Uzman Danışman ise ve bildirilmiş bir yönergeye sahipse, derleyici #ifdef def_INTEGRATION_WITH_EA ve #else koşullu yönergeleri arasında kalan kısımlarla nesne kodu üretecektir. Genellikle bu gibi durumlarda #else yönergesini kullanırız. Başka bir dosyanın derlenmesi durumunda, örneğin def_INTEGRATION_WITH_EA yönergesi tanımlanmamış göstergede, #else ve #endif yönergeleri arasındaki her şey derlenecektir. Bu şekilde çalışıyor.

Bir Uzman Danışman veya gösterge derlerken, bu testlerin her birini ve genel işleyişi anlamak için C_TimesAndTrade sınıfının tüm koduna bakın. Böylece, MQL5 derleyicisi tüm ayarları yapacak ve bizi iki farklı dosya tutma ihtiyacıyla ilişkili zaman ve çabadan kurtaracaktır.


2.0.4. Uzman Danışmanı daha çevik hale getirme

Daha önce de belirtildiği gibi, Uzman Danışman yalnızca emir sistemiyle çalışmalıdır. Şimdiye kadar, artık gösterge haline gelen özelliklere sahipti. Bunun nedeni, Uzman Danışmanın yapması gereken hesaplamalara dahil olan şeylerle ilgili olan çok kişisel bir şeydir. Ancak bu hesaplama sistemi değiştirilmiş ve başka bir yönteme geçilmiştir. Dolayısıyla, emir sisteminin Uzman Danışmanın emirlerle ilgilenmek yerine yaptığı bazı şeylerden zarar gördüğünü fark ettim. Sorunların en kötüsü OnTick olayındaydı:

void OnTick()
{
        Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, TradeView.SecureChannelPosition(), C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT]);
#ifdef def_INTEGRATION_WITH_EA
        TimesAndTrade.Update();
#endif 
}

Bu olay şimdi bu koşullu yönergeyi aldı, böylece yüksek volatilite dönemlerinde işlem yapmayanlar, istenirse tüm orijinal göstergelere sahip bir Uzman Danışmana sahip olabilirler. Ancak bunun iyi bir fikir olduğunu düşünmeden önce, zaman ve ticaret sisteminin güncelleme özelliğinin nasıl çalıştığını hatırlatmama izin verin.

inline void Update(void)
{
        MqlTick Tick[];
        MqlRates Rates[def_SizeBuff];
        int i0, p1, p2 = 0;
        int iflag;
        long lg1;
        static int nSwap = 0;
        static long lTime = 0;

        if (m_ConnectionStatus < 3) return;
        if ((i0 = CopyTicks(Terminal.GetFullSymbol(), Tick, COPY_TICKS_ALL, m_MemTickTime, def_SizeBuff)) > 0)
        {

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

        }
}

Yukarıdaki kod, C_TimesAndTrade sınıfında bulunan güncelleme fonksiyonunun bir parçasıdır. Sorun vurgulanan kısımdadır. Her çalıştırıldığında, belirli bir zamandan beri yapılan tüm ticaret fişlerini geri döndürmek için sunucuya bir istek gönderilir, bu, çok sorunlu bir şey değildir. Sorun şu ki, zaman zaman bu çağrı iki başka olayla çakışacaktır.

İlk ve en belirgin olay, OnTick fonksiyonun çok sayıda çağrı almasına neden olan çok sayıda işlemin gerçekleşebilmesidir. C_TimesAndTrade sınıfında bulunan yukarıdaki kodu çalıştırmak zorunda kalmanın yanı sıra, bu fonksiyon başka bir sorunla da ilgilenecektir: C_IndicatorTradeView sınıfında bulunan SecureChannelPosition fonksiyonunu çağırmak. Yani bu da küçük bir sorundur ama hepsi bu kadar değildir. Düşük volatiliteye rağmen zaman zaman iki olayın çakışacağını söylemiştim; bunlardan ilki bu olaydı.

İkincisi, halihazırda güncellenmiş olan ve aşağıdaki gibi görünen OnTime olayındadır:

#ifdef def_INTEGRATION_WITH_EA
void OnTimer()
{
        VolumeAtPrice.Update();
        TimesAndTrade.Connect();
}
#endif 

Uzman Danışmanı tasarlandığı şekilde kullanacaksanız, daha fazla kod aldığı da göz önüne alındığında, bazen çakışan olaylar nedeniyle sorun yaşayabilir. Bu olduğunda, Uzman Danışman (tek bir saniye için bile olsa) emir sistemiyle ilgili olmayan şeyleri yapmaya devam edecektir.

C_TimesAndTrade'de bulunan fonksiyonun aksine, bu fonksiyon C_VolumeAtPrice sınıfında bulunur ve emirleri yönetirken Uzman Danışman performansına gerçekten zarar verebilir. İşte bunun nedeni:

inline virtual void Update(void)
{
        MqlTick Tick[];
        int i1, p1;

        if (macroCheckUsing == false) return;
        if ((i1 = CopyTicksRange(Terminal.GetSymbol(), Tick, COPY_TICKS_TRADE, m_Infos.memTimeTick)) > 0)
        {
                if (m_Infos.CountInfos == 0)
                {
                        macroSetInteger(OBJPROP_TIME, m_Infos.StartTime = macroRemoveSec(Tick[0].time));
                        m_Infos.FirstPrice = Tick[0].last;
                }                                               
                for (p1 = 0; (p1 < i1) && (Tick[p1].time_msc == m_Infos.memTimeTick); p1++);
                for (int c0 = p1; c0 < i1; c0++) SetMatrix(Tick[c0]);
                if (p1 == i1) return;
                m_Infos.memTimeTick = Tick[i1 - 1].time_msc;
                m_Infos.CurrentTime = macroRemoveSec(Tick[i1 - 1].time);
                Redraw();
        };      
};

Nedeni vurgulanan kısımlarda yer alıyor, ancak en kötüsü REDRAW’dur. Uzman Danışman performansına büyük ölçüde zarar verir, çünkü belirtilen değerin ÜZERİNDE hacme sahip alınan her tikte, fiyattaki tüm hacim ekrandan kaldırılır, yeniden hesaplanır ve yerine geri konur. Bu her 1 saniyede bir gerçekleşir. Bu başka şeylerle çakışabilir, bu yüzden tüm göstergeler Uzman Danışmandan kaldırılıyor. Onları doğrudan Uzman Danışmanda kullanabilmeniz için bırakmış olsam da, daha önce açıklanan nedenlerden dolayı bunu yapmanızı önermiyorum.

Bu değişiklikler gerekliydi. Ancak daha simgesel olan ve yapılması gereken bir başka şey daha vardır. Bu seferki değişiklik OnTradeTransaction olayı ile ilgilidir. Bu olayın kullanımı, sistemi mümkün olduğunca esnek hale getirme girişimidir. Emir gerçekleştiren Uzman Danışmanları programlayanların çoğu, hangi emirlerin sunucuda olup olmadığını veya hangi pozisyonların hala açık olduğunu kontrol ettikleri OnTrade olayını kullanır. Yanlış yaptıklarını söylemiyorum. Sadece sunucu bizi neler olup bittiği hakkında bilgilendirdiği için çok verimli değildir. Ancak OnTrade olayıyla ilgili en büyük sorun, bazı şeyleri gereksiz yere kontrol etmek zorunda kalmamızdır. OnTradeTransaction olayını kullanırsak en azından hareket analizi açısından daha verimli bir sisteme sahip oluruz. Ancak buradaki amaç bu değildir. Herkes kendi kriterlerine en uygun yöntemi kullanır.

Bu Uzman Danışmanı geliştirirken, herhangi bir depolama yapısı kullanmamaya ve dolayısıyla birlikte çalışılabilecek emir veya pozisyon sayısını sınırlamamaya karar verdim. Ancak bu gerçek durumu o kadar karmaşık hale getiriyor ki, OnTrade olayına bir alternatif gereklidir ve bu alternatif OnTradeTransaction olayının kullanımında bulunabilir.

Bu olayın uygulanması çok zordur, muhtemelen bu yüzden pek çok kişi tarafından kullanılmamaktadır. Ama başka seçeneğim yoktu. Ya çalışır ya da çalışmaz, aksi takdirde işler karmaşıklaşacaktı. Ancak önceki sürümde bu olay için kod çok verimsizdi, aşağıda görebilirsiniz:

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
#define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY))

        ulong ticket;
        
        if (trans.symbol == Terminal.GetSymbol()) switch (trans.type)
        {
                case TRADE_TRANSACTION_DEAL_ADD:
                case TRADE_TRANSACTION_ORDER_ADD:
                        ticket = trans.order;
                        ticket = (ticket == 0 ? trans.position : ticket);
                        TradeView.IndicatorInfosAdd(ticket);
                        TradeView.UpdateInfosIndicators(0, ticket, trans.price, trans.price_tp, trans.price_sl, trans.volume, (trans.position > 0 ? trans.deal_type == DEAL_TYPE_BUY : def_IsBuy(trans.order_type)));
                        break;
                case TRADE_TRANSACTION_ORDER_DELETE:
                         if (trans.order != trans.position) TradeView.RemoveIndicator(trans.order);
                         else TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                         if (!PositionSelectByTicket(trans.position)) TradeView.RemoveIndicator(trans.position);
                        break;
                case TRADE_TRANSACTION_ORDER_UPDATE:
                        TradeView.UpdateInfosIndicators(0, trans.order, trans.price, trans.price_tp, trans.price_sl, trans.volume, def_IsBuy(trans.order_type));
                        break;
                case TRADE_TRANSACTION_POSITION:
                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                        break;
        }
                
#undef def_IsBuy
}

Yukarıdaki kod çalışıyor olsa da, en hafif tabirle KORKUNÇ. Yukarıdaki kod tarafından üretilen gereksiz çağrıların sayısı çılgınca. Bu düzeltilemezse hiçbir şey Uzman Danışmanı kararlılık ve güvenilirlik açısından geliştiremez.

Bu nedenle, mesajlarda bir model bulmaya çalışmak için (ki bu aslında oldukça zordur) bir demo hesapta birkaç şey yaptım. Bir model bulamadım, ancak üretilen gereksiz çağrıların çılgınlığını önleyen, kodu istikrarlı, güvenilir ve aynı zamanda piyasada herhangi bir zamanda işlem yapabilecek kadar esnek hale getiren bir şey buldum. Tabii ki, hala düzeltilmesi gereken birkaç küçük hata var, ancak kod çok iyi durumda:

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
#define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY))

        if (trans.type == TRADE_TRANSACTION_HISTORY_ADD) if (trans.symbol == Terminal.GetSymbol()) TradeView.RemoveIndicator(trans.position);
        if (trans.type == TRADE_TRANSACTION_REQUEST) if ((request.symbol == Terminal.GetSymbol()) && (result.retcode == TRADE_RETCODE_DONE)) switch (request.action)
        {
                case TRADE_ACTION_PENDING:
                        TradeView.IndicatorAdd(request.order);
                        break;
                case TRADE_ACTION_SLTP:
                        TradeView.UpdateIndicators(request.position, request.tp, request.sl, request.volume, def_IsBuy(request.type));
                        break;
                case TRADE_ACTION_DEAL:
                        TradeView.RemoveIndicator(request.position);
                        break;
                case TRADE_ACTION_REMOVE:
                        TradeView.RemoveIndicator(request.order);
                        break;
                case TRADE_ACTION_MODIFY:
                        TradeView.UpdateIndicators(request.order, request.tp, request.sl, request.volume, def_IsBuy(request.type));
                        break;
        }
                        
#undef def_IsBuy
}

Neler olduğunu hemen anlamaya çalışmayın, sadece bu özelliğin güzelliğinin tadını çıkarın. Neredeyse yaşayan bir mükemmellik. Bunu benim tarafımdan yapıldığı için değil, sahip olduğu sağlamlık ve çeviklik derecesi nedeniyle söylüyorum.

Karmaşık görünse de, bu kodda iki kontrol vardır. Neler olup bittiğini daha iyi açıklamak için aşağıda vurgulanmıştır.

if (trans.type == TRADE_TRANSACTION_HISTORY_ADD) if (trans.symbol == Terminal.GetSymbol()) TradeView.RemoveIndicator(trans.position);
if (trans.type == TRADE_TRANSACTION_REQUEST) if ((request.symbol == Terminal.GetSymbol()) && (result.retcode == TRADE_RETCODE_DONE)) switch (request.action)
{

//... inner code ...

}

YEŞİL ile vurgulanan satır, rapor edilen varlığın Uzman Danışman tarafından gözlemlenen varlıkla aynı olup olmadığını görmek için geçmişte her işlem gerçekleştiğinde kontrol edecektir. Eğer öyleyse, C_IndicatorTradeView sınıfı göstergeyi grafikten silmek için bir komut alacaktır. Bu iki durumda gerçekleşebilir: bir emir bir pozisyona dönüştüğünde ve bir pozisyon kapandığında. Lütfen sadece NETTING modunu kullandığımı, HEDGING modunu kullanmadığımı unutmayın. Böylece, ne olursa olsun, gösterge grafikten kaldırılacaktır.

Ancak şunu sorabilirsiniz: pozisyon kapatılırsa sorun yok; ama ya emir bir pozisyona dönüşürse - çaresiz mi kalacağım? Hayır. Ancak sorun hatanın içerisinde değil, C_IndicatorTradeView’da çözülür. Bunu makalenin bir sonraki bölümünde ele alacağız.

Öte yandan kırmızı satır, C_IndicatorTradeView sınıfına iletilen gereksiz mesajların miktarını şaşırtıcı bir şekilde azaltır. Bu, sunucu tarafından talebe geri döndürülen yanıtı kontrol ederek yapılır, bu nedenle Uzman Danışmanın izlediği varlığın aynı adıyla birlikte talebi ileterek onay almamız gerekir. Ancak o zaman C_IndicatorTradeView sınıfına yeni bir çağrı turu gönderilecektir.

Bu sistem hakkında söyleyebileceklerim bu kadar. Ancak hikaye henüz bitmedi. Önümüzde çok iş var ve şu andan itibaren sadece C_IndicatorTradeView sınıfına odaklanacağız. Şimdi yapılması gereken bazı değişikliklerle başlayacağız.


2.0.5. C_IndicatorTradeView tarafından oluşturulan nesne sayısını azaltma

Yeni emir sistemi (VI) makalesinde, oldukça soyut ama çok ilginç bir konsept olan emirleri veya durma seviyelerini kaydırmayı tanıttık. Konsept, pozisyon hayaletleri veya gölgeleri kullanmaktı. İşlem sunucusunun gördüklerini tanımlar ve grafikte gösterirler ve gerçek hareket gerçekleşene kadar kullanılırlar. Bu modelin küçük bir sorunu vardır: MetaTrader 5 tarafından yönetilecek nesneler ekler, ancak eklenen nesnelere çoğu durumda ihtiyaç duyulmaz, bu nedenle MetaTrader 5 genellikle işe yaramaz veya nadiren kullanılan şeylerle dolu bir nesne listesi alır.

Ancak Uzman Danışmanın sürekli olarak nesne oluşturmasını veya gereksiz nesneleri listede tutmasını istemeyiz, çünkü bu Uzman Danışmanın performansını düşürür. MetaTrader 5'i emir yönetimi için kullandığımızdan, tüm sisteme müdahale eden gereksiz nesneleri ortadan kaldırmalıyız.

Çok basit bir çözüm var. Aslında o kadar da basit değildir. C_IndicatorTradeView sınıfını iyileştirmek için bazı değişiklikler daha yapacağız. Hayaletleri ekranda tutacağız ve çok ilginç ve az kullanılan bir yöntem kullanacağız.

Eğlenceli ve ilginç olacak.

İlk olarak, seçim yapısını değiştireceğiz. Şimdi aşağıdaki gibi görünecektir:

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

Size tam olarak neyin değiştiğini söylemeyeceğim - kendiniz anlamalısınız. Ancak değişiklikler kodlama mantığının bazı yönlerini basitleştirdi.

Böylece hayalet göstergemiz artık kendi indeksine sahip olacaktır:

#define def_IndicatorGhost      2

Bu nedenle ad modellemesi de değişti:

#define macroMountName(ticket, it, ev) StringFormat("%s%c%llu%c%c%c%c", def_NameObjectsTrade, def_SeparatorInfo,\
                                                                        ticket, def_SeparatorInfo,              \
                                                                        (char)it, def_SeparatorInfo,            \
                                                                        (char)(ticket <= def_IndicatorGhost ? ev + 32 : ev))

Küçük bir şey gibi görünüyor ama yakında çok şey değişecek. Devam edelim.

Artık fiyat pozisyonu makroları her zaman doğrudandır, tekrarlama yoktur, bu nedenle kodumuz şimdi aşağıdaki gibi görünmektedir:

#define macroSetLinePrice(ticket, it, price) ObjectSetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE, price)
#define macroGetLinePrice(ticket, it) ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE)

Bu değişiklikler bizi iki fonksiyon daha oluşturmaya zorladı, şimdi birini ve sonra diğerini göstereceğim. Bunlardan ilki, göstergeleri oluşturan fonksiyonun değiştirilmesidir. Bir göstergeyi diğerinden farklı kılan şeyin ne olduğunu tam anlamıyla netleştirdi. Aşağıda görülebilir:

#define macroCreateIndicator(A, B, C, D)        {                                                                       \       
                m_TradeLine.Create(ticket, sz0 = macroMountName(ticket, A, EV_LINE), C);                                \
                m_BackGround.Create(ticket, sz0 = macroMountName(ticket, A, EV_GROUND), B);                             \
                m_BackGround.Size(sz0, (A == IT_RESULT ? 84 : 92), (A == IT_RESULT ? 34 : 22));                         \
                m_EditInfo1.Create(ticket, sz0 = macroMountName(ticket, A, EV_EDIT), D, 0.0);                           \
                m_EditInfo1.Size(sz0, 60, 14);                                                                          \
                if (A != IT_RESULT)     {                                                                               \
                        m_BtnMove.Create(ticket, sz0 = macroMountName(ticket, A, EV_MOVE), "Wingdings", "u", 17, C);    \
                        m_BtnMove.Size(sz0, 21, 23);                                                                    \
                                        }else                   {                                                       \
                        m_EditInfo2.Create(ticket, sz0 = macroMountName(ticket, A, EV_PROFIT), clrNONE, 0.0);           \
                        m_EditInfo2.Size(sz0, 60, 14);  }                                                               \
                                                }

                void CreateIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                string sz0;
                                
                                switch (it)
                                {
                                        case IT_TAKE    : macroCreateIndicator(it, clrForestGreen, clrDarkGreen, clrNONE); break;
                                        case IT_STOP    : macroCreateIndicator(it, clrFireBrick, clrMaroon, clrNONE); break;
                                        case IT_PENDING : macroCreateIndicator(it, clrCornflowerBlue, clrDarkGoldenrod, def_ColorVolumeEdit); break;
                                        case IT_RESULT  : macroCreateIndicator(it, clrDarkBlue, clrDarkBlue, def_ColorVolumeResult); break;
                                }
                                m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose);
                        }
#undef macroCreateIndicator

Kodlarımda ön işleme yönergelerini kullanmayı sevdiğimi fark etmişsinizdir. Bunu neredeyse her zaman yapıyorum. Ancak, görebileceğiniz gibi, göstergeler arasında ayrım yapmak artık oldukça kolaydır. Göstergeye istediğiniz renkleri vermek istiyorsanız, kodu uygun şekilde değiştirin. Hepsi neredeyse aynı olduğundan, bir makro kullanarak hepsinin aynı şekilde çalışmasını ve aynı öğelere sahip olmasını sağlayabiliriz. Bu, kodun ileri düzeyde yeniden kullanımıdır.

Buna çok benzer bir ada sahip başka bir fonksiyon daha vardır. Ancak farklı bir şey yapıyor ve bundan en sonda ayrıntılı olarak bahsedeceğim.

IndicatorAdd fonksiyonu değiştirildi - bazı parçaları sildik.

inline void IndicatorAdd(ulong ticket)
                        {
                                char ret;
                                
                                if (ticket == def_IndicatorTicket0) ret = -1; else
                                {
                                        if (ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, IT_PENDING, EV_LINE, false), OBJPROP_PRICE) != 0) return;
                                        if (ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, IT_RESULT, EV_LINE, false), OBJPROP_PRICE) != 0) return;
                                        if ((ret = GetInfosTradeServer(ticket)) == 0) return;
                                }
                                switch (ret)
                                {
                                        case  1:
                                                CreateIndicatorTrade(ticket, IT_RESULT);
                                                PositionAxlePrice(ticket, IT_RESULT, m_InfoSelection.pr);
                                                break;
                                        case -1:
                                                CreateIndicatorTrade(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_InfoSelection.pr);
                                                break;
                                }
                                ChartRedraw();
                                UpdateIndicators(ticket, m_InfoSelection.tp, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
				UpdateIndicators(ticket, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                        } 

Çıkarılan parçalardan biri vurgulanan parçayla değiştirildi. Bu, bekleyen emir ve 0 göstergelerinin artık oluşturulmayacağı anlamına mı geliyor? Hâlâ oluşturuluyorlar ancak farklı bir yerde. Yani, bir başka fonksiyon daha vardır.

İşte burada - bekleyen emir ve 0 göstergelerini oluşturan fonksiyon. UpdateIndicators'ın kodu aşağıdaki gibidir:

#define macroUpdate(A, B) { if (B > 0) {                                                                \
                if (b0 = (macroGetLinePrice(ticket, A) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, B);                                                        \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                \
                                        } else RemoveIndicator(ticket, A); }
                                                                        
                void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
                        {
                                double pr;
                                bool b0 = false;
                                
                                if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
                                {
                                        pr = macroGetLinePrice(ticket, IT_RESULT);
                                        if ((pr == 0) && (macroGetLinePrice(ticket, IT_PENDING) == 0))
                                        {
                                                CreateIndicator(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                                                ChartRedraw();
                                        }
                                        pr = (pr > 0 ? pr : macroGetLinePrice(ticket, IT_PENDING));
                                        SetTextValue(ticket, IT_PENDING, vol);
                                }
                                if (m_Selection.tp > 0) macroUpdate(IT_TAKE, tp);
                                if (m_Selection.sl > 0) macroUpdate(IT_STOP, sl);
                                if (b0) ChartRedraw();
                        }
#undef macroUpdate

Fonksiyon, kodda vurgulanan çok ilginç bir kontrole sahiptir. Hayalet göstergeler oluşturmaya yardımcı olacaktır, bu nedenle IndicatorAdd fonksiyonu artık bekleyen emir ve 0 göstergelerini oluşturamayacaktır. Ancak sadece bu kontrolü yapmak bir hayalet gösterge oluşturmak için yeterli değildir.

DispatchMessage fonksiyonu artık bazı ayrıntılar içeriyor, bunlar küçük değişikliklerdir, ancak hayatımızı çok daha kolaylaştırıyorlar. Değişen kısımları göstereceğim:

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

// ... Code ....

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:

// ... Code ....
                        }else if ((!bMounting) && (bKeyBuy == bKeySell) && (m_Selection.ticket > def_IndicatorGhost))
                        {
                                if (bEClick) SetPriceSelection(price); else MoveSelection(price);
                        }
                        break;

// ... Code ...

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

// ... Code ...

                                        break;
                                case EV_MOVE:
                                        CreateGhostIndicator(ticket, it);
                                        break;
                        }
                break;
        }
}

CHARTEVENT_MOUSE_MOVE değiştirilmiş bir parçaya sahiptir. Bu kod hayalet ile çalışıp çalışmadığımızı kontrol edecektir. Eğer bu bir hayalet ise, parça bloke edilir. Ancak değilse, hareket mümkündür (göstergenin kendisinin hareket edebilmesi şartıyla).

Göstergenin yeni konumuna tıkladığımız anda, hayalet tüm bileşenleriyle birlikte nesneler listesinden kaldırılacaktır. Bence burası nettir. Şimdi vurgulanan kısma dikkat edin - bu CreateGhostndicator fonksiyonunun çağrısıdır. Bu kodu bir sonraki bölümde tartışacağız.


2.0.6. CreateGhostIndicator nasıl çalışıyor?

CreateGhostIndicator garip bir fonksiyon gibi görünüyor. Aşağıdaki koduna bakalım:

CreateGhostIndicator

#define macroSwapName(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorGhost, A, B));
                void CreateGhostIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                if (GetInfosTradeServer(m_Selection.ticket = ticket) != 0)
                                {
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                        macroSwapName(it, EV_LINE);
                                        macroSwapName(it, EV_GROUND);
                                        macroSwapName(it, EV_MOVE);
                                        macroSwapName(it, EV_EDIT);
                                        macroSwapName(it, EV_CLOSE);
                                        m_TradeLine.SetColor(macroMountName(def_IndicatorGhost, it, EV_LINE), def_IndicatorGhostColor);
                                        m_BackGround.SetColor(macroMountName(def_IndicatorGhost, it, EV_GROUND), def_IndicatorGhostColor);
                                        m_BtnMove.SetColor(macroMountName(def_IndicatorGhost, it, EV_MOVE), def_IndicatorGhostColor);
                                        ObjectDelete(Terminal.Get_ID(), macroMountName(def_IndicatorGhost, it, EV_CLOSE));
                                        m_TradeLine.SpotLight();
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                                        m_Selection.it = it;
                                }else m_Selection.ticket = 0;
                        }
#undef macroSwapName

Bu fonksiyonda hiçbir şeyin oluşturulmaması çok ilginçtir. Ancak, Uzman Danışman derlenir ve çalıştırılırsa, sunucuda emir durumunu ifade eden hayaletler oluşturacaktır. Bunu anlamak için aşağıdaki videoyu izleyin. Bu, sistemin gerçekte nasıl çalıştığının bir sunumudur.



Hayalet göstergeler gerçekten grafik üzerinde oluşturulur, ancak bu aslında nasıl gerçekleşir? Kodun herhangi bir yerinde oluşturmadan göstergeleri oluşturmayı nasıl başardık?

Bunlar hayalettir. Aslında bunların oluşturulduğunu görmeyeceksiniz, kodu okuyup şu satırı bulmaya çalışmanın bir anlamı yok: “İşte... buldum... hayalet göstergeler bu noktada oluşturuluyor...” Gerçek şu ki, bunlar zaten grafiktedir, ancak biz emri veya pozisyonu değiştirmeye başlayana kadar hiçbir yerde görüntülenmezler - ancak o zaman görünür hale gelirler. Bu nasıl mümkün olabilir?

Bunu anlamak için Uzman Danışman yürütme akışını ele alalım.

Uzman Danışmanın başlatılmasından sonra, aşağıdaki yürütme akışını görürüz:

Yürütme akışı 1

init_ea <<<< Sistemi başlatma yürütme akışı

Turuncu alan Uzman Danışmanın bir parçasıdır, yeşil alan ise C_IndicatorTradeView sınıfının bir parçasıdır. Gösterge oluşturulmadan ve ekranda görüntülenmeden önce neler olduğuna bakın. Siyah oklar bekleyen emirler ve açık pozisyonlar için ortak yoldur; mavi ok pozisyonların yoludur ve mor oklar da bekleyen emirlerin göstergelerini oluşturmak için izledikleri yolu belirtir. Elbette, fonksiyonların içerisinde yürütme akışını şu ya da bu şekilde yönlendiren şeyler vardır, ancak buradaki şema her şeyin genel anlamda nasıl çalıştığını göstermeyi amaçlamaktadır.

Yukarıdaki şema yalnızca bir kez ve yalnızca sistem başlangıcı sırasında kullanılır. Şimdi, grafikte bir bekleyen emir yerleştireceğimiz her seferinde, iki farklı yürütme akışımız olacaktır: ilki, 0 göstergesini oluşturmaktan ve emri grafiğe yerleştirmeye çalışmaktan sorumludur. Bu, aşağıdaki şekilde gösterilmektedir:

Yürütme akışı 2

 <<<< 0 göstergesini başlatma yürütme akışı

Grafikte görünen emri oluşturacak olanın gerçekten sınıf olmadığını lütfen unutmayın. Sadece bunu yapmaya çalışacaktır. Her şey yolunda giderse, SetPriceSelection fonksiyonu başarıyla yürütülecek ve emri grafikte sunacak yeni bir yürütme akışı oluşturulacaktır. Böylece, aşağıdaki yürütme akışını elde edeceğiz. Aslında emri işlem sunucusunun bildirdiği yere yerleştirecektir, bu nedenle emir gerçekten başlangıçta belirttiğimiz yere ulaşana kadar beklemenin bir anlamı yoktur. Volatilite, sunucunun emri belirttiğimizden farklı bir noktada yerine getirmesine neden olursa, Uzman Danışman bunu düzeltecek ve emri doğru yerde sunacaktır. Dolayısıyla, yalnızca koşulların ticaret modeliniz için uygun olup olmadığını analiz etmeniz gerekecektir.

Yürütme akışı 3

<<<< Bekleyen emir yerleştirme yürütme akışı

Bu sadece grafik üzerinde emir yerleştirmekten sorumlu kısımdır. Burada tam bir emirden bahsediyorum, yani bir giriş noktası, bir Kârı Al ve bir Zararı Durdur olacaktır. Ancak, eşik emirlerinden biri, ister Kârı Al ister Zararı Durdur, emirden çıkarılırsa yürütme akışına ne olacak? Bu yürütme akışları buna yanıt vermiyor. Aslında, yürütme akışı buradakilerden oldukça farklı olacaktır, ancak unsurlar neredeyse aynı kalacaktır. Eşik emirlerinden birini kapatmak için düğmeye tıklarsanız akışın nasıl olacağını aşağıda görelim.

Bu garip görünebilir.

Yürütme akışı 4

<<<< Bir emri veya eşik seviyesini silme

Biri diğerinin yanında olmak üzere iki yürütme akışımız var. Mor okla işaretlenmiş olan ilk olarak yürütülecektir. Yürütülür yürütülmez, OnTradeTransaction olayı sunucudan gelen yanıtı yakalayacak ve göstergeyi ekrandan kaldırmak için sistemi tetikleyecektir. Eşik emirlerinin silinmesi ile bir pozisyonun veya emrin kapatılması arasında tek bir fark vardır: bu durumlarda SetPriceSelection fonksiyonu yürütülmez, ancak OnTradeTransaction olay akışı devam eder.

Tüm bunlar harika, ancak yine de hayaletlerin nasıl ortaya çıktığı sorusuna yanıt vermiyor.

Hayaletlerin nasıl oluşturulduğunu anlamak için, yürütme akışının nasıl gerçekleştiğini bilmemiz gerekir: Uzman Danışmanın bir bekleyen emri nasıl yerleştirdiği veya 0 göstergesinin oluşturulmasının pratikte nasıl gerçekleştiği. Bu akış yukarıdaki şekilde gösterilmektedir. Yürütme akışlarını anlarsanız, hayaletleri anlamanız daha kolay olacaktır.

Sonunda hayaletlerin nasıl oluşturulduğunu görelim. CreateGhostIndicator fonksiyonuna tekrar bakın. Hiçbir şey oluşturmaz, sadece bazı verileri manipüle eder. Neden? Çünkü bir nesne oluşturmaya çalışırsak, mevcut nesnelerin üzerine bindirilecek ve onların üzerine render edilecektir. Böylece gerekli nesneler gizlenecektir. Bu sorunun iki çözümü bulunmaktadır. Birincisi, diğerlerinden daha düşük bir set oluşturmaktır. Emirleri temsil eden diğer tüm nesnelerden önce oluşturulacaktır. Ancak bu çözümün bir sorunu vardır. Bir sürü işe yaramaz nesneye sahip olacağız. Ama bunu önlemek için tüm kod değiştirilmelidir. İkinci çözüm ise bir hayalet oluşturmak ve ardından manipüle ettiğimiz işaretçiyi silmek ve ardından yeniden oluşturmaktır. Bu çözümlerin hiçbiri çok pratik değildir, üstelik her ikisi de oldukça külfetlidir.

Dokümantasyonu incelerken dikkatimi çeken bir bilgi buldum: ObjectSetString fonksiyonu, ilk bakışta bir anlam ifade etmeyen nesne özelliğini (OBJPROP_NAME) değiştirmemize olanak tanıyor. Buna neden izin verildiğini merak ettim. Bu hiç mantıklı değil. Nesne zaten oluşturulmuşsa, adını değiştirmenin ne anlamı var?

Mesele şu. Bir nesneyi yeniden adlandırdığımızda, eski nesnenin varlığı sona erer ve yeni bir ad alır. Yeniden adlandırmadan sonra, nesne orijinal nesnenin yerine geçer, böylece Uzman Danışman orijinal nesneyi sorunsuz bir şekilde oluşturabilir ve hayalet, grafikler için yan etkiler olmadan ve iz bırakmadan görünebilir ve kaybolabilir. Kaldırılması gereken tek nesne gösterge kapatma düğmesidir. Bu, aşağıdaki satırda yapılır:

ObjectDelete(Terminal.Get_ID(), macroMountName(def_IndicatorGhost, it, EV_CLOSE));

Burada küçük bir ayrıntı vardır. ObjectSetString fonksiyonunun dokümantasyonuna baktığımızda, fonksiyonun çalışmasıyla ilgili bir uyarı görüyoruz:

Bir grafik nesnesini yeniden adlandırırken, aynı anda iki olay oluşturulur. Bu olaylar, OnChartEvent() fonksiyonu kullanılarak Uzman Danışmanda veya göstergede işlenebilir:

  • eski adlı bir nesneyi silme olayı
  • yeni bir adla bir nesneyi oluşturma olayı

Bunu dikkate almak önemlidir çünkü yeniden adlandırmak üzere olduğumuz nesnenin hazır olmadığımız bir anda ortaya çıkmasını istemeyiz. Bu yüzden, ad değişikliğinden önce ve sonra bir şey daha ekliyoruz:

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

// ... Secure code...

ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);

Kodun içerisindeki herhangi bir şey nesne oluşturma ve silme olaylarını tetiklemeyecektir. Artık hayaletlerin görüneceği kodun tamamına sahibiz ve doğru davranışı elde edeceğiz.

Belki de kodun sadece göstergeyi yeniden adlandırarak gerçekte nasıl hayaletler yarattığı henüz net değildir. Sizi bu düşünceyle baş başa bırakacağım, ancak biraz yardımcı olmak için, hayalet yürütme akışının nasıl göründüğünü göstereceğim. Bu, aşağıdaki görüntüde gösterilmektedir:

Yürütme akışı 5

<<<< Hayalet yürütme akışı

Bunun yürütme akışı 2'nin mükemmele yakın bir kopyası olduğuna dikkat edin, böylece hayaletlerin nasıl oluşturulduğunu ve yok edildiğini bilerek, ancak herhangi bir oluşturma kodu yazmadan eğlenebilirsiniz.


Sonuç

Bir yazar olarak bu makaleyi oldukça ilginç ve hatta heyecan verici buldum. Uzman Danışman kodunu çok değiştirmek zorunda kaldık. Ama tüm bunlar daha iyisi için. Daha da güvenilir hale getirmek adına hala atılması gereken birkaç adım var. Bununla birlikte, halihazırda uygulanmış değişiklikler tüm sisteme büyük fayda sağlayacaktır. İyi tasarlanmış bir programın genellikle burada uygulanan şu belirli adımlardan geçtiğini vurgulamak isterim: dokümantasyonu incelemek, yürütme akışlarını analiz etmek, kritik anlarda aşırı yüklenip yüklenmediğini görmek için sistemi karşılaştırmak ve hepsinden önemlisi, kodu gerçek bir canavara dönüştürmemek için sakin olmak. Kodumuzu bir Frankenstein'a dönüştürmekten kaçınmak çok önemlidir, çünkü bu kodu daha iyi hale getirmeyecek, yalnızca gelecekteki iyileştirmeleri ve özellikle düzeltmeleri daha zor hale getirecektir.

Bu seriyi takip eden herkese kucak dolusu sevgiler. Bir sonraki makalede görüşmek dileğiyle, çünkü henüz işimiz bitmedi ve yapacak daha çok şey var.



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

Ekli dosyalar |
MQL5'te ONNX modellerinin nasıl bir araya getirileceğine dair bir örnek MQL5'te ONNX modellerinin nasıl bir araya getirileceğine dair bir örnek
Açık sinir ağı santrali (Open Neural Network eXchange, ONNX), sinir ağlarını temsil etmek için oluşturulmuş açık bir formattır. Bu makalede, bir Uzman Danışmanda aynı anda iki ONNX modelinin nasıl kullanılacağını göstereceğiz.
Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 24): Sistemin sağlamlığını artırma (I) Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 24): Sistemin sağlamlığını artırma (I)
Bu makalede, daha istikrarlı ve güvenli bir kullanım sağlamak için sistemi daha sağlam hale getireceğiz. İstenilen sağlamlığı elde etmenin yollarından biri, kodu mümkün olduğunca yeniden kullanmaya çalışmak ve böylece farklı durumlarda sürekli olarak test edilmesini sağlamaktır. Ancak bu, yöntemlerden yalnızca bir tanesidir. Bir diğeri ise OOP kullanmaktır.
MQL5'te matrisler ve vektörler: Aktivasyon fonksiyonları MQL5'te matrisler ve vektörler: Aktivasyon fonksiyonları
Burada makine öğreniminin sadece bir yönünü - aktivasyon fonksiyonlarını - açıklayacağız. Yapay sinir ağlarında, bir nöron aktivasyon fonksiyonu, bir girdi sinyalinin veya bir dizi girdi sinyalinin değerlerine dayalı olarak bir çıktı sinyali değerini hesaplar. Sürecin iç işleyişini derinlemesine inceleyeceğiz.
Alligator göstergesine dayalı bir ticaret sistemi nasıl geliştirilir? Alligator göstergesine dayalı bir ticaret sistemi nasıl geliştirilir?
Bu makalede, yine bir başka popüler teknik göstergeye odaklanacağız ve ona dayalı bir ticaret sisteminin nasıl geliştirileceğini öğreneceğiz. Alligator göstergesini tanıyacağız ve ona dayalı bir ticaret sistemi tasarlayacağız.