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

Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 08): Kavramsal sıçrama (I)

MetaTrader 5Örnekler | 22 Eylül 2022, 14:27
366 0
Daniel Jose
Daniel Jose

Giriş

Bazen, bazı sistemleri geliştirirken, onun için kullanışlı olabilecek, ona büyük iyileştirmeler getirebilecek yeni özellikler keşfederiz. Ancak devamında şu soru ortaya çıkar: yeni bir işlevselliği sistemimize en kolay şekilde nasıl uygulayabiliriz?

Çünkü en büyük sorun şu ki, bu amaçla bazen halihazırda geliştirilmiş olan her şeyi unutup sıfırdan başlamak gerekebilir, ki bu da oldukça moral bozucudur. C++’da 20 yılı aşkın programlamadan sonra, artık belirli bir düşünce çizgisi geliştirdim. Zamanla sistemlerimiz üzerinde minimum çabayla planlamalar ve değişiklikler yapmamıza yardımcı olan bazı konseptler geliştiriyoruz, ancak yine de bu konuda kimi zaman işler ilk başta düşündüğümüzden çok daha karmaşık hale gelebiliyor. Dolayısıyla sistemlerimize yeni bir işlevselliği eklemekle ilgili olarak daha kolay bir yol bulmalıyız.

Şimdiye kadar geliştirmekte olduğumuz Uzman Danışmanı, mevcut işlevselliklerini kaybetmeden yeni kodlar alabilecek şekilde inşa ettik: sınıflar oluşturuyor ve ekliyorduk. Ancak şimdi, önce bir adım geri, sonra iki adım ileri gideceğiz. Bu atacağımız geri adım, Uzman Danışmana yeni işlevselliği eklememize olanak sağlayacaktır. Bu işlevsellik, şablonlarla ilişkili olarak bilgiler içeren bir pencere sınıfıdır. Kavramsal sıçrama başlıklı iki makalemizin bu ilk bölümünde bu konuyla ilgileneceğiz. Kodu, sahip olduğumuz tüm işlevsellikleri koruyarak, kökten değiştireceğiz. İkinci makalemiz de ise IDE ile ilgileneceğiz.


Planlama

Uzman Danışmanımızı nesne sınıfı çerçevesinde yapılandırdık. Bu, aşağıdaki şemadan görülebilir:

Sistem şu anda iyi çalışıyor ve kararlı halde. Ancak şimdi Uzman Danışmanı aşağıda gösterildiği gibi yeniden yapılandıracağız. Bu yeni yapıda bir sınıfın eklendiğini ve C_TemplateChart ve C_SubWindow sınıflarının konumlarının da değiştiğini görebilirsiniz:


Böyle bir yeniden yapılandırmaya neden ihtiyacımız var? Çünkü mevcut yapı, varlık verilerini içeren kayan pencereler oluşturmak için uygun değildir. Dolayısıyla, böyle bir değişiklik gereklidir. Ancak bu değişiklik sadece yapının estetik açıdan bir değişikliği değildir, aynı zamanda kodda da köklü bir değişiklik gerektirir, bu nedenle yeni kod önceki koddan çok farklı olacaktır.

Öyleyse hadi başlayalım.


Uygulama

1. Uzman Danışmanın dahili kodundaki değişiklikler

İlk büyük değişiklik OnInit fonksiyonundadır. Aşağıdaki koda bakalım:

input group "Window Indicators"
input string                    user01 = "";                    //Subwindow indicators
input group "WallPaper"
input string                    user10 = "Wallpaper_01";        //Used BitMap
input char                      user11 = 60;                    //Transparency (from 0 to 100)
input C_WallPaper::eTypeImage   user12 = C_WallPaper::IMAGEM;   //Chart background type
input group "Chart Trader"
input int                       user20   = 1;                   //Leverage factor
input int                       user21   = 100;                 //Take Profit (financial)
input int                       user22   = 75;                  //Stop Loss (financial)
input color                     user23   = clrBlue;             //Price line color
input color                     user24   = clrForestGreen;      //Take Profit line color
input color                     user25   = clrFireBrick;        //Stop line color
input bool                      user26   = true;                //Day Trade?
input group "Volume At Price"
input color                     user30  = clrBlack;             //Bar color
input char                      user31  = 20;                   //Transparency (from 0 to 100 )
//+------------------------------------------------------------------+
C_TemplateChart Chart;
C_WallPaper     WallPaper;
C_VolumeAtPrice VolumeAtPrice;
//+------------------------------------------------------------------+
int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(user20, user21, user22, user23, user24, user25, user26);
        VolumeAtPrice.Init(user24, user25, user30, user31);
        
   OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+


Artık hangi şablonların yükleneceğini belirten yalnızca bir değişkenimiz olduğuna dikkat edin. Vurgulanan kısım dışında kodun geri kalanı öncekiyle aynı gibidir. Vurgulanan kısmın ne yaptığı tam olarak anlaşılır olmayabilir. Bu konu hakkında biraz daha ayrıntılı konuşalım. Uzman Danışmanı bir grafikte çalıştırdığımızda bazı öğeler oluşturulur ve normal kullanım sırasında onlar üzerinde sorunsuz bir şekilde düzenlemeler yapılabilir. Öncesinde, vurgulanan kodu eklemenin bir anlamı yoktu, çünkü her şeyin bir alanda birlikte olarak çalışması amaçlanmıştı ve onlar üzerinde yapılan düzenlemeler, Uzman Danışmanın davranışında veya görünümünde herhangi bir sorun yaratmıyordu. Ancak, kayan göstergeler eklediğimizde can sıkıcı bir şey oluyordu: zaman dilimini her değiştirdiğimizde Uzman Danışman yeniden başlıyor ve pencereler orijinal durumlarına geri dönüyordu. Şablonlar değiştirilmediği sürece vurgulanan kod, kayan pencerelerin ilgili şekillerde hatalı olarak silinmesini önleyecektir; eğer şablonlarda bir değişiklik meydana gelirse, bu sefer de Uzman Danışman normal şekilde yeniden başlatılacaktır. Bu çok basit, ancak son derece etkilidir.

Dikkat edilmesi gereken bir sonraki şey, dahili mesajlaşma sistemi ile ilgilidir. Önceden ek bir değişken daha vardı, onu burada kaldırdık ve kod şimdi şu şekilde görünmektedir:

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


Sistem artık MQL5'te bulunan mesajlaşma sistemini daha etkili bir şekilde kullanmaktadır ve mesajlaşma, OnChartEvent fonksiyonunun kendisine çok benzemektedir. Bu, parametrelerin nesne sınıflarına herhangi bir yük olmadan iletilmesine olanak sağlar, böylece her sınıf MetaTrader 5 sistemi tarafından oluşturulan olay mesajlarını en uygun şekilde işleyebilir. Bu şekilde, her nesne sınıfını daha da izole ederiz, böylece Uzman Danışman, her kullanıcı türü için, daha az çabayla daha çeşitli görünümler alabilir.


2. Alt pencere destek kodundaki değişiklikler

Bu zamana kadar, alt pencere kodu çok basitti, ancak bir sorunu vardı: Uzman Danışman, bir nedenden dolayı, oluşturulan alt pencereyi silemiyordu. Uzman Danışman yeniden açıldığında, sistemdeki kontrolün kaybolması nedeniyle yeni bir alt pencere oluşturuluyordu. Şaşırtıcı bir şekilde, bu sorunu düzeltmek çok kolaydır. İlk olarak, destek kodunun aşağıda gösterilen parçasına göz atalım:

int OnInit()
{
        IndicatorSetString(INDICATOR_SHORTNAME, "SubWinSupport");
        
        return INIT_SUCCEEDED;
}

Vurgulanan satır, destek kodu için bir alias oluşturur. Uzman Danışman bu aliası görür ve alt pencere sisteminin yüklenip yüklenmediğini kontrol eder. Bu, dosya adından bağımsız olarak gerçekleştirilir, çünkü Uzman Danışman için önemli olan aliasdır. Aynı kod türü daha sonra Uzman Danışmandaki diğer şeyleri desteklemek için de kullanılacaktır. Bu makalede bu konuyla ilgili olarak çok fazla detaya girmeyeceğiz, ancak başka bir makalede bu vurgulanan koddan nasıl yararlanılacağını daha ayrıntılı olarak ele alacağız.

Şimdi ise alt pencereleri yükleyen ve oluşturan koda göz atalım. Aşağıda gösterilmektedir:

void Init(void)
{
        int i0;
        if ((i0 = ChartWindowFind(Terminal.Get_ID(), def_Indicador)) == -1)
                ChartIndicatorAdd(Terminal.Get_ID(), i0 = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL), iCustom(NULL, 0, "::" + def_Resource));
        m_IdSubWinEA = i0;
}


Gördüğünüz gibi, çok daha basit haldedir, ancak bu kod public değildir, ona public olan başka bir kod aracılığıyla erişilir:

inline int GetIdSubWinEA(void)
{
        if (m_IdSubWinEA < 0) Init();
        return m_IdSubWinEA;
}


Ama neden bu şekilde uyguladık? Uzman Danışmanın alt pencerede herhangi bir gösterge kullanmaması gibi bir durum olabilir ve sistem bunu fark ettiğinde alt pencereyi grafikten kaldırır ve sadece gerektiğinde oluşturur. Ancak bu karar, Uzman Danışmanın kodu tarafından değil, C_TemplateChart sınıfı tarafından verilir.


3. Yeni C_TemplateChart sınıfı

Aşağıdaki animasyona bakalım:

Artık nereyi analiz ettiğimizi gösteren dikey çizgilere sahip olduğumuza dikkat edin. Önceden bu çizgiler yoktu, bu da grafikle ilişkili olarak göstergeleri analiz etmeyi zorlaştırıyordu. Bu, C_TemplateChart sınıfına eklenen iyileştirmelerden biridir. Bu sınıfta daha birçok büyük değişikliklerde mevcut olduğu için onları daha iyi anlamak adına sınıfın kodunu inceleyelim.

Değişkenlerin bildirildiği aşağıdaki koda bakalım:

class C_TemplateChart : public C_SubWindow
{
#define def_MaxTemplates                8
#define def_NameTemplateRAD     "IDE"
//+------------------------------------------------------------------+
        private :
//+------------------------------------------------------------------+
        enum eParameter {TEMPLATE = 0, PERIOD, SCALE, WIDTH, HEIGHT};
//+------------------------------------------------------------------+
                struct st
                {
                        string          szObjName,
                                        szSymbol,
                                        szTemplate,
                                        szVLine;
                        int             width,
                                        scale;
                        ENUM_TIMEFRAMES timeframe;
                        long            handle;
                }m_Info[def_MaxTemplates];
                int     m_Counter,
                        m_CPre,
                        m_Aggregate;
                struct st00
                {
                        int     counter;
                        string  Param[HEIGHT + 1];
                }m_Params;


Dikkat edilmesi gereken ilk şey, C_TemplateChart sınıfının C_SubWindow sınıfını genişleteceğidir. Bu kod parçası çok özel görünmüyor, ancak vurgulanan kısım önemlidir: kullanıcı tarafından istenen göstergelerin uygun şekilde oluşturulup sunulabilmesini sağlayan bir dahili veri analizi sistemini ifade etmektedir. Başka bir deyişle, artık kullanıcının neyi nasıl belirteceğini tanımlayan sistem standartlaşmaya başladı. İlk başta kafa karıştırıcı gibi görünse de zamanla netlik kazanacaktır. Yeni formatın daha anlaşılır olması adına, kullanıcının isteğini analiz etmekten sorumlu olan aşağıdaki kod parçasını değerlendirelim:

int GetCommand(int iArg, const string szArg)
{
        for (int c0 = TEMPLATE; c0 <= HEIGHT; c0++) m_Params.Param[c0] = "";
        m_Params.counter = 0;
        for (int c1 = iArg, c2 = 0; szArg[iArg] != 0x00; iArg++) switch (szArg[iArg])
        {
                case ')':
                case ';':
                        m_Params.Param[m_Params.counter++] = StringSubstr(szArg, c1, c2);
                        for (; (szArg[iArg] != 0x00) && (szArg[iArg] != ';'); iArg++);
                        return iArg + 1;
                case ' ':
                        c2 += (c1 == iArg ? 0 : 1);
                        c1 = (c1 == iArg ? iArg + 1 : c1);
                        break;
                case '(':
                case ',':
                        c2 = (m_Params.counter == SCALE ? (c2 >= 1 ? 1 : c2) : c2);
                        m_Params.Param[m_Params.counter++] = StringSubstr(szArg, c1, c2);
                        c2 = 0;
                        c1 = iArg + 1;
                        break;
                default:
                        c2++;
                        break;
        }
        return -1;
}


Öncelikle yapıda önceden var olan tüm verileri temizliyoruz. Ardından, eğer varsa parametreleri alarak, analiz etmeye başlıyoruz. Parametreler zorunlu değildirler, ancak görüntülenecek öğelerin kullanıcıya nasıl sunulacağını belirtirler. Bu sistem kendi kendine genişleyebilir. Daha fazla bilgi eklemek istiyorsanız, onu eParameter numaralandırmasında belirtmeniz yeterlidir. Şu anda sistemin beş parametresi vardır. Parametreler, değişkenlerin bildirildiği bir önceki kod parçasında vurgulanan eParameter numaralandırmasında olduğu şekilde aynı sırayla sağlanmalıdır. Doğru sırada olarak parametreler ve açıklamaları aşağıda gösterilmektedir:

Parametre Sonuç
1. Şablon veya varlık Hangi şablonun veya varlığın görüntüleneceğini belirtir.
2. Zaman dilimi Belirtilirse, göstergeyi ilgili zaman dilimine ayarlar.
3. Ölçek Belirtilirse, göstergeyi ilgili ölçeğe ayarlar.
4. Genişlik Belirtilirse, göstergeyi pencerede ilgili genişliğe ayarlar.
5. Yükseklik Bu yeni parametre, kayan pencere kullanımını belirtmek için kullanılır.

Şu anda 5. parametreden yararlanamayacak tek yapı IDE'dir, ancak bunu düzelteceğiz: bir sonraki makalede onun IDE'de nasıl uygulanacağından bahsedeceğiz. Bu makalede ise diğer sistemlere odaklanacağız.

Şimdi, herhangi bir nedenle, kullanıcının göstergenin dikey çizgisinin rengini kontrol etmesine izin vermek istediğimizi varsayalım. Analiz kodunda herhangi bir değişiklik yapmaya gerek yoktur - sadece aşağıdaki değişikliği yapmamız yeterlidir:

class C_TemplateChart : public C_SubWindow
{
#define def_MaxTemplates        8
#define def_NameTemplateRAD     "IDE"
//+------------------------------------------------------------------+
        private :
//+------------------------------------------------------------------+
        enum eParameter {TEMPLATE = 0, COLOR_VLINE, PERIOD, SCALE, WIDTH, HEIGHT};
//+------------------------------------------------------------------+

// ... Internal code ....

                struct st00
                {
                        int     counter;
                        string  Param[HEIGHT + 1];
                }m_Params;


Böylece sistem, bir çağrıda altı parametre olabileceğini otomatik olarak algılayacaktır. Şimdi ise meydana gelebilecek bir soruna göz atacağız. Yukarıdaki GetCommand kodu iyi çalışıyor olsa da, içerisinde bir hata vardır. Oluşturduğumuz sistemi kendimiz kullanırken çoğu zaman bu hatayı fark etmeyiz. Ancak, sistem diğer kişilerin kullanımına sunulduğunda, bu hata açık hale gelebilir ve daha az deneyimli programcılar bu sorunu nasıl çözeceklerini bilemeyebilir. Bu nedenle nesne yönelimli programlama çok değerlidir ve programlarda kullanılabilecek en uygun modeldir. Çünkü OOP'nin ön koşullarından biri, verilerin ve sınıfların değişkenlerinin doğru şekilde başlatıldığından emin olmaktır. Bunu garanti etmek için her şey test edilmelidir. GetCommand kodu doğru görünse de, bir hata içermektedir: maksimum parametre sınırını kontrol etmemektedir. Program yalnızca beş parametre kabul ediyorsa, kullanıcı altı parametre ayarlarsa ne olur? Bu kaçınılması gereken bir durumdur: her şeyin çalışacağını varsaymamalıyız, garanti etmeliyiz. Dolayısıyla, kodun aşağıda gösterildiği gibi düzeltilmesi gerekir (düzeltmeler vurgulanmıştır):

int GetCommand(int iArg, const string szArg)
{
        for (int c0 = TEMPLATE; c0 <= HEIGHT; c0++) m_Params.Param[c0] = "";
        m_Params.counter = 0;
        for (int c1 = iArg, c2 = 0; szArg[iArg] != 0x00; iArg++) switch (szArg[iArg])
        {
                case ')':
                case ';':
                        m_Params.Param[m_Params.counter++] = StringSubstr(szArg, c1, c2);
                        for (; (szArg[iArg] != 0x00) && (szArg[iArg] != ';'); iArg++);
                        return iArg + 1;
                case ' ':
                        c2 += (c1 == iArg ? 0 : 1);
                        c1 = (c1 == iArg ? iArg + 1 : c1);
                        break;
                case '(':
                case ',':
                        if (m_Params.counter == HEIGHT) return StringLen(szArg) + 1;
                        c2 = (m_Params.counter == SCALE ? (c2 >= 1 ? 1 : c2) : c2);
                        m_Params.Param[m_Params.counter++] = StringSubstr(szArg, c1, c2);
                        c2 = 0;
                        c1 = iArg + 1;
                        break;
                default:
                        c2++;
                        break;
        }
        return -1;
}

Basitçe yukarıdaki tek satırı ekleyerek sistemin beklenmedik bir sonuç üretmesini önleyeceğiz. Çünkü beklenen son parametre yükseklik iken son parametre o değilse, bu mantıksal olarak bir şeyin yanlış olduğu anlamına gelir. Dolayısıyla, sistem daha sonra bildirilen her şeyi yok sayar ve böylece sorundan kaçınmış olur.

Sistemin parametreleri nasıl tanıdığını anlayamamış olabilirsiniz. Şu şekilde tanır:

Parametre_00 (Parametre_01, Parametre_02, Parametre_03, Parametre_04)

Burada Parametre_00 kullanılacak şablonu belirtir, diğer parametreler de virgülle (,) ayrılır. Parametreler, sırasıyla eParameter numaralandırmasında tanımlanan değerleri belirtir. Örneğin, sadece Parametre_03'ü değiştirmek istiyorsak, aşağıdaki görüntüde ADX için gösterildiği gibi diğer parametreleri boş bırakabiliriz. Örnek:

       

Fonksiyon çağrılarına çok benzeyen standartlaştırılmış bir göstergeye sahip olduğumuza dikkat edin, ancak bu size karmaşık görünmüş olabilir. Örnekte ne olduğuna bakalım: RSI şablonunu belirttik, ayraç içerisinde onun için zaman dilimini ve ölçeği belirtmedik. Sistemin bu iki parametre adına ana grafiği takip etmesi gerektiğini anlaması için onları boş bıraktık. Genişlik ve yüksekliği ise belirttik. Yükseklik parametresiyle sistem RSI’ın kayan pencerede görüntülenmesi gerektiğini anlar. ADX şablonunda ise sadece genişliği belirttik, böylece sistem onu alt pencerede, belirtilen genişlikle gösterdi. Stoch göstergesi için ise diğer parametreleri belirtmedik, dolayısıyla o da alt pencerede ADX’ten geriye kalan alana yerleşti. Varsayalım ki ADX göstergesi için yükseklik parametresini belirtmek istedik:

Sistem, ADX'in sunulma şeklini hemen değiştirdi - tüm alt pencereyi Stoch'a bıraktı ve ADX’i ise kayan pencereye yerleştirdi. Her kayan pencere birbirinden ayrı olarak görüntülenir. Ama şimdi durum görünenin ötesine geçiyor, aşağıdaki animasyona bakalım:

Artık gerekmediğinden alt pencerenin silindiğine dikkat edin. Peki tüm bunlardan hangi fonksiyon sorumludur? Aşağıda gösterilmektedir - şunu görebiliriz, sadece birkaç değişiklikle her şeyi nasıl çok ilginç hale getirebiliyoruz:

void AddTemplate(void)
{
        ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
        string sz0 = m_Params.Param[PERIOD];
        int w, h, i;
        bool bIsSymbol;

        if (sz0 == "1M") timeframe = PERIOD_M1; else
        if (sz0 == "2M") timeframe = PERIOD_M2; else
        if (sz0 == "3M") timeframe = PERIOD_M3; else
        if (sz0 == "4M") timeframe = PERIOD_M4; else
        if (sz0 == "5M") timeframe = PERIOD_M5; else
        if (sz0 == "6M") timeframe = PERIOD_M6; else
        if (sz0 == "10M") timeframe = PERIOD_M10; else
        if (sz0 == "12M") timeframe = PERIOD_M12; else
        if (sz0 == "15M") timeframe = PERIOD_M15; else
        if (sz0 == "20M") timeframe = PERIOD_M20; else
        if (sz0 == "30M") timeframe = PERIOD_M30; else
        if (sz0 == "1H") timeframe = PERIOD_H1; else
        if (sz0 == "2H") timeframe = PERIOD_H2; else
        if (sz0 == "3H") timeframe = PERIOD_H3; else
        if (sz0 == "4H") timeframe = PERIOD_H4; else
        if (sz0 == "6H") timeframe = PERIOD_H6; else
        if (sz0 == "8H") timeframe = PERIOD_H8; else
        if (sz0 == "12H") timeframe = PERIOD_H12; else
        if (sz0 == "1D") timeframe = PERIOD_D1; else
        if (sz0 == "1S") timeframe = PERIOD_W1; else
        if (sz0 == "1MES") timeframe = PERIOD_MN1;
        if ((m_Counter >= def_MaxTemplates) || (m_Params.Param[TEMPLATE] == "")) return;
        bIsSymbol = SymbolSelect(m_Params.Param[TEMPLATE], true);
        w = (m_Params.Param[WIDTH] != "" ? (int)StringToInteger(m_Params.Param[WIDTH]) : 0);
        h = (m_Params.Param[HEIGHT] != "" ? (int)StringToInteger(m_Params.Param[HEIGHT]) : 0);
        i = (m_Params.Param[SCALE] != "" ? (int)StringToInteger(m_Params.Param[SCALE]) : -1);
        i = (i > 5 || i < 0 ? -1 : i);
        if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else
        {
                SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w);
                if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl");
                if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD)
                {
                        C_Chart_IDE::Create(GetIdSubWinEA());
                        m_Info[m_Counter - 1].szVLine = "";
                }else
                {
                        m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
                        ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0);
                        ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack);
                }
                ChartRedraw(m_Info[m_Counter - 1].handle);
        }
}

Vurgulanan kısımlar, verilerin ekranda nasıl sunulacağının seçimini yapar. Bu kod, öncekinden çok farklı değildir. Ancak, sistemin kullanıcının istediği gibi davranacağını garanti eden tam da bu kısımlardır. Tüm bunlar şimdiye kadar yeni modelde kodun cidden yeniden yapılandırılmasını gerektirmedi, ancak alt pencereyi yeniden boyutlandırmaktan sorumlu olan fonksiyona baktığımızda işler değişmektedir:

void Resize(void)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_Info[c0].szObjName, A, B)
        int x0, x1, y;
        if (!ExistSubWin()) return;
        x0 = 0;
        y = (int)(ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS, GetIdSubWinEA()));
        x1 = (int)((Terminal.GetWidth() - m_Aggregate) / (m_Counter > 0 ? (m_CPre == m_Counter ? m_Counter : (m_Counter - m_CPre)) : 1));
        for (char c0 = 0; c0 < m_Counter; x0 += (m_Info[c0].width > 0 ? m_Info[c0].width : x1), c0++)
        {
                macro_SetInteger(OBJPROP_XDISTANCE, x0);
                macro_SetInteger(OBJPROP_XSIZE, (m_Info[c0].width > 0 ? m_Info[c0].width : x1));
                macro_SetInteger(OBJPROP_YSIZE, y);
                if (m_Info[c0].szTemplate == "IDE") C_Chart_IDE::Resize(x0);
        }
        ChartRedraw();
#undef macro_SetInteger
}

Vurgulanan satır, sistemin eski temel üzerine kurulmasını engeller. Bu satır, Uzman Danışman tarafından açılan ve sürdürülen bir alt pencere olup olmadığını kontrol eder, eğer böyle bir alt pencere yoksa fonksiyon geri döner ve başka hiçbir şey yapmaz. Ancak böyle bir alt pencere varsa, alt pencerenin içerisindeki her şey gerektiği gibi yeniden boyutlandırılır. Yalnızca bu sınama nedeniyle sistem tamamen yenilenmiştir.

Üzerinde değişiklik yapılan başka bir fonksiyon da aşağıda gösterilmektedir:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        int mx, my;
        datetime dt;
        double p;

        C_Chart_IDE::DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        mx = (int)lparam;
                        my = (int)dparam;
                        ChartXYToTimePrice(Terminal.Get_ID(), mx, my, my, dt, p);
                        for (int c0 = 0; c0 < m_Counter; c0++)  if (m_Info[c0].szVLine != "")
                        {
                                ObjectMove(m_Info[c0].handle, m_Info[c0].szVLine, 0, dt, 0);
                                ChartRedraw(m_Info[c0].handle);
                        }
                        break;
                case CHARTEVENT_CHART_CHANGE:
                        Resize();
                        for (int c0 = 0; c0 < m_Counter; c0++)
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_Info[c0].szObjName, OBJPROP_PERIOD, m_Info[c0].timeframe);
                                ObjectSetInteger(Terminal.Get_ID(), m_Info[c0].szObjName, OBJPROP_CHART_SCALE,(m_Info[c0].scale < 0 ? ChartGetInteger(Terminal.Get_ID(),CHART_SCALE):m_Info[c0].scale));
                        }
                        break;
        }
}


Vurgulanan kısım gerçekten özel ilgiyi hak ediyor. Ne yapıyor? Bu kod, dikey çizgiyi doğru yerde ve doğru şablonda sunar. Kodun geri kalanı ise grafik değiştikçe şablonları sürdürür ve ayarlar.

Bunu burada, nesne sınıfında yapmanın, Uzman Danışmanda, OnChartEvent olay sistemi içerisinde yapmaktan çok daha fazla avantajı vardır. Asıl mesele, MetaTrader 5'in Uzman Danışmana gönderdiği olayları her sınıfın düzgün bir şekilde işleyebilmesidir. Her şeyi tek bir fonksiyonda merkezileştirmek yerine, her sınıfın işini yapmasına izin veriyoruz. Eğer sınıfı Uzman Danışmanda kullanmak istemezsek, kodun geri kalanı üzerinde herhangi bir yan etkisi olmadan onu basitçe kaldırabiliriz.

Programlama çok güzel bir şey değil mi? Programlamayı seviyorum...

Makaledeki bir sonraki konuya geçmeden önce, 1. ve 2. parametrelerde kullanılabilecek değerler hakkında kısaca konuşalım. 1. parametreye şu değerler atanabilir: 1M, 2M, 3M, 4M, 5M, 6M, 10M, 12M, 15M, 20M, 30M, 1H, 2H, 3H, 4H, 6H, 8H, 12H, 1D, 1S, 1MES. Bu değerler rastgele değildir - ENUM_TIMEFRAME numaralandırmasından gelirler. Böylece göstergeler, grafik üzerinde normal bir şekilde görüntülüyormuş gibi tam olarak zaman dilimlerine karşılık gelirler. 2. parametre 0'dan 5'e kadar değerler alabilir, burada 0 en uzak, 5 ise en yakındır. Daha fazla ayrıntı için CHART_SCALE’e bakabilirsiniz.


4. Kayan pencereleri destekleme

Şimdi kayan pencerelerin nasıl oluşturulduğunu ve sürdürüldüğünü anlayalım, çünkü onu anlamadan sistemden gerçek anlamda yararlanmak imkansız olacaktır. Ondan sorumlu nesne sınıfını C_ChartFloating olarak adlandırdık. Şunu düşünebilirsiniz: Neden standart MQL5 kütüphanesindeki Control sınıfını kullanmıyoruz? Aslında nedeni basit. Control sınıfı, makinede bulunan işletim sistemine çok benzer bir işlevselliğe sahip bir pencere oluşturmamıza ve sürdürmemize olanak tanır, ancak bizim amacımız için çok abartılıdır. Çok daha basit bir şeye ihtiyacımız vardır. İstediğimizi yapmak için Control sınıfını kullanmak, bir sineği öldürmek için bir bazuka kullanmak gibi olurdu, bu nedenle C_ChartFloating sınıfını tasarladık - kayan pencereleri desteklemek için gerekli olan minimum öğeyi içerir.

Sınıfın kendisi çok fazla açıklamaya ihtiyaç duymaz, çünkü yaptığımız tek şey 4 grafik nesnesi oluşturmaktır. Ancak dahili fonksiyonlar arasında özel ilgiyi hak eden iki tanesi vardır. Pencereyi oluşturan fonksiyonla başlayalım:

bool AddIndicator(string sz0, int x = 0, int y = -1, int w = 300, int h = 200, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1)
{
        m_LimitX = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS);
        m_LimitY = (int)ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS);
        if (m_MaxCounter >= def_MaxFloating) return false;
        y = (y < 0 ? m_MaxCounter * def_SizeBarCaption : y);
        CreateBarTitle();
        CreateCaption(sz0);
        CreateBtnMaxMin();
        CreateRegion(TimeFrame, Scale);
        m_Win[m_MaxCounter].handle = ObjectGetInteger(Terminal.Get_ID(), m_Win[m_MaxCounter].szRegionChart, OBJPROP_CHART_ID);
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, sz0 + ".tpl");   
        m_Win[m_MaxCounter].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
        ObjectCreate(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJ_VLINE, 0, 0, 0);
        ObjectSetInteger(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJPROP_COLOR, clrBlack);
        m_Win[m_MaxCounter].PosX = -1;
        m_Win[m_MaxCounter].PosY = -1;
        m_Win[m_MaxCounter].PosX_Minimized = m_Win[m_MaxCounter].PosX_Maximized = x;
        m_Win[m_MaxCounter].PosY_Minimized = m_Win[m_MaxCounter].PosY_Maximized = y;
        SetDimension(w, h, true, m_MaxCounter);
        SetPosition(x, y, m_MaxCounter);
        ChartRedraw(m_Win[m_MaxCounter].handle);
        m_MaxCounter++;
        return true;
}

Bu kod, şablonu CHART nesnesine uygulayabilmemiz için gerekli tüm desteği oluşturur. Vurgulanan kod bölümünde uygulanır. Bu fonksiyonu çağırmak için gerçekten gerekli olan tek parametrenin şablon adı olduğuna dikkat edin. Diğer tüm değerler önceden başlatılır, ancak hiçbir şey hangilerini istediğinizi belirtmenizi engellemez. Oluşturulan her yeni pencere için, bir sonraki pencere, varsayılan olarak diğer pencerelerle çakışmayacak şekilde hafifçe kaydırılır. Bu, aşağıdaki satırla elde edilir:

y = (y < 0 ? m_MaxCounter * def_SizeBarCaption : y);


Bu sınıftaki bir sonraki ilginç fonksiyon, mesajların işlenmesinden sorumlu olan fonksiyondur. İşte kodu:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        int mx, my;
        datetime dt;
        double p;
        static int six = -1, siy = -1, sic = -1;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        mx = (int)lparam;
                        my = (int)dparam;
                        if ((((int)sparam) & 1) == 1)
                        {
                                if (sic == -1)  for (int c0 = m_MaxCounter - 1; (sic < 0) && (c0 >= 0); c0--)
                                        sic = (((mx > m_Win[c0].PosX) && (mx < (m_Win[c0].PosX + m_Win[c0].Width)) && (my > m_Win[c0].PosY) && (my < (m_Win[c0].PosY + def_SizeBarCaption))) ? c0 : -1);
                                if (sic >= 0)
                                {
                                        if (six < 0) ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, false);
                                        six = (six < 0 ? mx - m_Win[sic].PosX : six);
                                        siy = (siy < 0 ? my - m_Win[sic].PosY : siy);
                                        SetPosition(mx - six, my - siy, sic);
                                }
                        }else
                        {
                                if (six > 0) ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, true);
                                six = siy = sic = -1;
                        }
                        ChartXYToTimePrice(Terminal.Get_ID(), mx, my, my, dt, p);
                        for (int c0 = 0; c0 < m_MaxCounter; c0++)
                                ObjectMove(m_Win[c0].handle, m_Win[c0].szVLine, 0, dt, 0);
                        break;
                case CHARTEVENT_OBJECT_CLICK:
                        for (int c0 = 0; c0 < m_MaxCounter; c0++) if (sparam == m_Win[c0].szBtnMaxMin)
                        {
                                SwapMaxMin((bool)ObjectGetInteger(Terminal.Get_ID(), m_Win[c0].szBtnMaxMin, OBJPROP_STATE), c0);
                                break;
                        }
                        break;
                case CHARTEVENT_CHART_CHANGE:
                        for(int c0 = 0; c0 < m_MaxCounter; c0++)
                        {
                               ObjectSetInteger(Terminal.Get_ID(), m_Win[c0].szRegionChart, OBJPROP_PERIOD, m_Win[c0].TimeFrame);
                               ObjectSetInteger(Terminal.Get_ID(), m_Win[c0].szRegionChart, OBJPROP_CHART_SCALE,(m_Win[c0].Scale < 0 ? ChartGetInteger(Terminal.Get_ID(),CHART_SCALE):m_Win[c0].Scale));
                        }
                        m_LimitX = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS);
                        m_LimitY = (int)ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS);
                        break;
        }
        for (int c0 = 0; c0 < m_MaxCounter; c0++)
                ChartRedraw(m_Win[c0].handle);
}


Bu fonksiyon, C_ChartFloating sınıfı tarafından desteklenen tüm olayların işlenmesine odaklanır. Mevcut pencerelerin sayısı ne olursa olsun, hepsini aynı şekilde işler. Bunu Uzman Danışmanda, OnChartEvent fonksiyonunun içerisinde yapsaydık, fonksiyon son derece karmaşık ve kararsız olurdu. Ancak onu burada, nesne sınıfında yaparak, kodun bütünlüğünü garanti ediyoruz. Peki, kayan pencereler kullanmak istemiyorsak, tek yapmamız gereken sınıfı ve ona erişilebilecek noktaları kaldırmaktır. Bu şekilde uygulamak, kodu çok daha hızlı ve kolay hale getirir.

Yukarıdaki kod içerisinde de bir ilginç kısım vardır (vurgulanmıştır). Dahili kodu aşağıdadır:

void SwapMaxMin(const bool IsMax, const int c0)
{
        m_Win[c0].IsMaximized = IsMax;
        SetDimension((m_Win[c0].IsMaximized ? m_Win[c0].MaxWidth : 100), (m_Win[c0].IsMaximized ? m_Win[c0].MaxHeight : 0), false, c0);
        SetPosition((m_Win[c0].IsMaximized ? m_Win[c0].PosX_Maximized : m_Win[c0].PosX_Minimized), (m_Win[c0].IsMaximized ? m_Win[c0].PosY_Maximized : m_Win[c0].PosY_Minimized), c0);
}


Peki bu kod ne yapıyor? Çok kafa karıştırıcı mı görünüyor? Anlamak için aşağıdaki animasyona bakalım.


Bir kayan pencere oluşturulduğunda, programcı veya sınıfın kendisinin konumlandırma sistemi tarafından belirtilen bir başlangıç çapa noktasına sahip olur. Bu çapa noktası hem büyütülmüş hem de simge durumuna küçültülmüş pencere için aynıdır. Bu değerler sabit değildir yani kullanıcı bu noktaları kolaylıkla değiştirebilir.

Kolaylık olması adına örneğin şu yapılabilir: kayan pencere grafiğin temiz ve okunması rahat bir yerine yerleştirilebilir, devamında simge durumuna küçültülüp, ekranın köşesi gibi farklı bir yere taşınabilir. Sistem bu süreci hatırlar ve pencere tekrar büyütüldüğünde, simge durumuna küçültülmeden önceki son çapa noktasına atlar. Aynı atlama, pencere simge durumuna küçültülürken de olur.


Sonuç

Şimdilik bu kadar. Bir sonraki makalede bu işlevselliği IDE destek sınıfına genişleteceğiz.


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

Ekli dosyalar |
EA.zip (3616.46 KB)
Parabolic SAR göstergesine dayalı bir ticaret sistemi nasıl geliştirilir? Parabolic SAR göstergesine dayalı bir ticaret sistemi nasıl geliştirilir?
Bu makalede de en popüler göstergeleri kullanarak ticaret sistemleri oluşturma konulu serimize devam ediyoruz. Bu sefer Parabolic SAR göstergesinden bahsedeceğiz. Ticarette nasıl yararlı olabileceğini anlamak adına bu göstergeyi ayrıntılı olarak inceleyeceğiz ve basit stratejilerle MetaTrader 5 işlem platformu için ona dayalı bir ticaret sistemi geliştireceğiz.
Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 07): Hacim profili ekleme (I) Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 07): Hacim profili ekleme (I)
Bu, şu anda mevcut olan en güçlü göstergelerden biridir. Belirli bir güven derecesine sahip olmaya çalışan tüm yatırımcıların bu göstergeyi grafiğinde bulundurması gerekir. Çoğu zaman gösterge, piyasa seviyelerini izleyerek (bant okuyarak) ticaret yapanlar tarafından kullanılır. Ayrıca fiyat hareketlerine dayalı ticaret yapanlar tarafından da kullanılabilir.
Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 09): Kavramsal sıçrama (II) Sıfırdan bir ticaret Uzman Danışmanı geliştirme (Bölüm 09): Kavramsal sıçrama (II)
Bu makalede ticaret arayüzümüzü kayan pencereye yerleştireceğiz. Önceki makalede, şablonların kayan pencerede kullanılmasına olanak sağlayan temel bir sistem oluşturmuştuk.
Veri Bilimi ve Makine Öğrenimi (Bölüm 03): Matris Regresyonları Veri Bilimi ve Makine Öğrenimi (Bölüm 03): Matris Regresyonları
Bu sefer matrisleri kullanarak modeller oluşturacağız. Matrisler, modellere esneklik sağlar. Yalnızca beş bağımsız değişkeni değil, bilgisayarın hesaplama yeteneklerinin izin verdiği ölçüde çok sayıda değişkeni işleyebilen güçlü modeller oluşturmamıza olanak tanır.