English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
preview
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)

MetaTrader 5Ticaret | 20 Eylül 2022, 09:15
304 0
Daniel Jose
Daniel Jose

Giriş

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 sadece fiyat hareketlerine dayalı ticaret yapanlar tarafından da kullanılabilir. Bu, belirli bir fiyat aralığında gerçekleşen işlem hacmini analiz etmek için kullanılabilecek son derece kullanışlı bir yatay hacim göstergesidir. Ancak, göstergeyi doğru bir şekilde okumak önemlidir. Bu konuda daha fazla bilgi edinebilmeniz için makalenin sonuna bir link ekleyeceğim.

Burada gösterge değerlerinin nasıl okunacağı üzerinde durmayacağız, çünkü bu konu, bu makalenin kapsamı dışındadır. Bu makalenin amacı, bu göstergenin MetaTrader 5 platformunun performansını düşürmeyecek şekilde nasıl tasarlanacağını ve oluşturulacağını göstermektir. İşte size ilginç bir not: birçok kişi bu göstergenin gerçek zamanlı olarak güncellenmesi gerektiğini düşünmektedir, aslında onun için küçük bir gecikme kabul edilebilir. Kendi deneyimlerime dayanarak, bilgilerin güncellenmesinde yaklaşık 1 saniyelik gecikmenin var olmasıyla şu ana kadar büyük bir sorunla karşılaşmadım. Ancak, göstergeyi gerçek zamanlı olarak kullanmak sizin için önemliyse, bu amaçla bazı küçük değişikliklerin yapılması gerekli olacaktır. Bu değişiklikler göstergenin kendisinde değil, çağrının gerçek zamanlı olarak gerçekleşmesi için Uzman Danışmanın göstergeyi çağırdığı noktalarda yapılmalıdır. Fakat, göstergenin gerçek zamanlı olarak güncellenmesinin onun okunması üzerinde minimum düzeyde etki yaratacağını düşündüğümden burada gecikme ihmal edilebilir.


Arayüz

Hacim profili sınıfının kontrol arayüzü çok basittir ancak onun üzerinde tam kontrol sağlamak adına göstergenin uygulanacağı grafiğin doğru özelliklere sahip olduğundan emin olmak gerekir. Ana kontrol öğesi vurgulanmış olarak aşağıda grafik özellikleri penceresinde gösterilmektedir.

Izgara görünmüyorsa, aşağıdaki animasyonlarda gösterildiği gibi göstergeyi yeniden boyutlandırmak mümkün olmayacaktır. Arayüzün kullanımı çok kolaydır: biri boyutu, diğeri hacim analizi için başlangıç noktasını gösteren yalnızca iki kontrole sahiptir.


Bu göstergenin uygulanma ve yapılandırılma sürecinin genel olarak oldukça ilginç olduğunu düşünüyorum. Bu makalede onun en temel seviyesiyle çalışacağız, bir sonraki makalede de onu geliştireceğiz.

Arayüz hakkında şu an için söylenecek başka bir şey yok. Hadi şimdi kodu uygulamaya geçelim.


Uygulama

Göstergeyi oluştururken olabildiğince az iş yapmak adına kaynak kodumuzu parçalara ayıracağız ve onun üzerinde bazı değişiklikler ve eklemeler yapacağız. İhtiyacımız olan şeylerin çoğu halihazırda yazılı durumdadır. Ana kısım C_Wallpaper sınıfındadır. Ne yapacağız? Bir bitmap’e dayalı bir gösterge mi oluşturacağız? Evet, bilgisayar ekranındaki herhangi bir görüntü bir BITMAP olarak ele alınmalı, ancak özel bir şekilde oluşturulmalıdır. Böylece, yeni C_Wallpaper nesne sınıfı şu şekilde görünecektir:

class C_WallPaper : public C_Canvas
{
        protected:
                enum eTypeImage {IMAGEM, LOGO, COR};
//+------------------------------------------------------------------+
        private :
        public  :
//+------------------------------------------------------------------+
                ~C_WallPaper()
                        {
                                Destroy();
                        }
//+------------------------------------------------------------------+
                bool Init(const string szName, const eTypeImage etype, const char cView = 100)
                        {
                                if (etype == C_WallPaper::COR) return true;
                                if (!Create(szName, 0, 0, Terminal.GetWidth(), Terminal.GetHeight())) return false;
                                if(!LoadBitmap(etype == C_WallPaper::IMAGEM ? "WallPapers\\" + szName : "WallPapers\\Logos\\" + _Symbol, cView)) return false;
                                ObjectSetInteger(Terminal.Get_ID(), szName, OBJPROP_BACK, true);

                                return true;
                        }
//+------------------------------------------------------------------+
                void Resize(void)
                        {
                                ResizeBitMap(Terminal.GetWidth(), Terminal.GetHeight());
                        }
//+------------------------------------------------------------------+
};


Bakın, kod çok daha kompakt hale geldi: C_Wallpaper ve C_VolumeAtPrice sınıfları arasında ortak olan kısımları kaldırdık ve her şeyi C_Canvas adlı başka bir sınıfa koyduk.

Ama neden MetaTrader 5’te bulunan C_Canvas sınıfını kullanmadım? Bu tercih pratikten daha çok kişisel bir durumdur. Yazdığım ve geliştirdiğim her şey üzerinde daha fazla kontrol sahibi olmayı seviyorum, ancak bu bir C programcısı için gerçekten gerekli bir şeyden çok kötü bir alışkanlıktır. Dolayısıyla ekranda nesneleri çizmek için başka bir sınıf oluşturuyorum. Elbette MetaTrader 5'te halihazırda mevcut olan sınıfı da kullanabilirsiniz. Şimdi ise bu makalenin ana konusu olan C_VolumeAtPrice sınıfına odaklanalım. Sınıf aşağıdaki tabloda gösterilen yedi fonksiyona sahiptir.

Fonksiyon Açıklama Erişim türü 
Init Kullanıcı tarafından belirtilen değerlerle sınıfı başlatır. Public
Update Belirli aralıklarla hacim profili verilerini günceller. Public
Resize Grafikteki hacim profilinin görüntüsünün boyutunu değiştirerek ayrıntıları analiz etmeyi kolaylaştırır. Public
DispatchMessage  Bir nesne sınıfına mesaj göndermek için kullanılır. Public
FromNowOn  Sistem değişkenlerini başlatır. Private
SetMatrix Hacim verileriyle bir matris oluşturur ve onu sürdürür. Private
Redraw Bir hacim görüntüsü oluşturur. Private

Aşağıdaki kodla değişkenleri bildirelim:

#define def_SizeMaxBuff                 4096
//+------------------------------------------------------------------+
#define def_MsgLineLimit                "Starting point from Volume At Price"
//+------------------------------------------------------------------+
class C_VolumeAtPrice : private C_Canvas
{
#ifdef macroSetInteger
        ERROR ...
#endif
#define macroSetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_Infos.szObjEvent, A, B)
        private :
                uint    m_WidthMax,
                        m_WidthPos;
                bool    m_bChartShift,
                        m_bUsing;
                double  m_dChartShift;
                struct st00
                {
                        ulong   nVolBuy,
                                nVolSell,
                                nVolTotal;
                        long    nVolDif;
                }m_InfoAllVaP[def_SizeMaxBuff];
                struct st01
                {
                        ulong    memTimeTick;
                        datetime StartTime,
                                 CurrentTime;
                        int      CountInfos;
                        ulong    MaxVolume;
                        color    ColorSell,
                                 ColorBuy,
                                 ColorBars;
                        int      Transparency;
                        string   szObjEvent;
                        double   FirstPrice;
                }m_Infos;


Koddaki vurgulanan alan, dikkat etmemiz gereken alandır. Bu alan, tanımın o dosyada kullanacağımız tanımla çelişecek şekilde başka bir dosyadan gelmemesini sağlar. MQL5 derleyicisi, mevcut bir tanımın üzerine yazılmaya çalışıldığında bir uyarı gösterir ve bazı durumlarda onun nasıl çözüleceğini bulmak zor olabilir. Bu yüzden hayatımızı biraz daha kolaylaştırmak adına yukarıda kodda vurgulanan testi kullanıyoruz. Bu koddaki diğer şeyler özel olarak farklı değildir. Ancak dikkat edilmesi gereken bir şey daha var, bu, def_SizeMaxBuff tanımıdır. Bu tanım, hacim veri dizimizin boyutunun ne olacağını söyler. Gerekirse, bu değeri başka bir değerle değiştirebilirsiniz, ancak gözlemlediğim kadarıyla şu ana kadar mevcut değer fazlasıyla yeterli oldu. Bu değer belirli bir fiyat aralığındaki tik sayısını temsil eder, dolayısıyla mevcut değer çok çeşitli durumları kapsayabilir haldedir.


Init fonksiyonu: her şeyin başladığı yer

Tüm değişkenleri doğru şekilde başlatan fonksiyon bu fonksiyondur. Uzman Danışmanda şu şekilde çağrılır:

//.... Initial data....

input color     user10   = clrForestGreen;      //Take Profit line color
input color     user11   = clrFireBrick;        //Stop line color
input bool      user12   = true;                //Day Trade?
input group "Volume At Price"
input color     user15  = clrBlack;             //Color of bars
input char      user16  = 20;                   //Transparency (from 0 to 100 )
//+------------------------------------------------------------------+
C_SubWindow             SubWin;
C_WallPaper             WallPaper;
C_VolumeAtPrice         VolumeAtPrice;
//+------------------------------------------------------------------+          
int OnInit()
{
        Terminal.Init();
        WallPaper.Init(user03, user05, user04);
        if ((user01 == "") && (user02 == "")) SubWin.Close(); else if (SubWin.Init())
        {
                SubWin.ClearTemplateChart();
                SubWin.AddThese(C_TemplateChart::SYMBOL, user02);
                SubWin.AddThese(C_TemplateChart::INDICATOR, user01);
        }
        SubWin.InitilizeChartTrade(user06, user07, user08, user09, user10, user11, user12);
        VolumeAtPrice.Init(user10, user11, user15, user16);

// ... Rest of the code

Burada çok fazla parametre yoktur. Onlar esas olarak göstergenin kullanacağı renkler hakkındaki bilgileri temsil etmektedir. Şimdi ise bu fonksiyonun dahili koduna bakalım. Aşağıdaki kod, her şeyin nasıl başlatıldığını göstermektedir:

void Init(color CorBuy, color CorSell, color CorBar, char cView)
{
        m_Infos.FirstPrice = Terminal.GetRatesLastDay().open;
        FromNowOn(macroSetHours(macroGetHour(Terminal.GetRatesLastDay().time), TimeLocal()));
        m_Infos.Transparency = (int)(255 * macroTransparency(cView));
        m_Infos.ColorBars = CorBar;
        m_Infos.ColorBuy = CorBuy;
        m_Infos.ColorSell = CorSell;
        if (m_bUsing) return;
        m_Infos.szObjEvent = "Event" + (string)ObjectsTotal(Terminal.Get_ID(), -1, OBJ_EVENT);
        CreateObjEvent();
        m_bChartShift = ChartGetInteger(Terminal.Get_ID(), CHART_SHIFT);
        m_dChartShift = ChartGetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE);
        ChartSetInteger(Terminal.Get_ID(), CHART_SHIFT, true);
        ChartSetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE, 0.1);
        Create("VaP" + (string)MathRand(), 0, 0, 1, 1);
        Resize();
        m_bUsing = true;
};


Gördüğünüz gibi, burada her şey çok basittir. Yine de kodu ilginç kılan bazı özellikler vardır. Bunlardan biri Terminal.GetRatesLastDay().open’dır. Garip görünse de aslında Nesne Yönelimli Programlama (OOP) ilkelerini takip ediyorsak bu çok yaygın bir durumdur. Bu ilkelerden biri şunu belirtir: sınıfın dışındaki hiçbir şeyin sınıfın dahili değişkenlerine erişimi olmaması gerekir. Ama o zaman sınıf içerisindeki değişkenlerin değerleri nasıl elde edilecek? Doğru yol, yalnızca OOP'de görünen bir form kullanmaktır, bu amaçla GetRatesLastDay fonksiyonunun C_Terminal sınıfı içerisinde nasıl bildirildiğini görelim. Bu, aşağıdaki kodda görülebilir:

inline MqlRates GetRatesLastDay(void) const { return m_Infos.Rates; }


Tam olarak nasıl çalıştığını görelim. Ayrılmış kelime olan inline ile başlayalım. Derleyiciye, göründüğü tüm konumlara ilgili kodun yerleştirilmesi gerektiğini söyler. Fonksiyon çağrısı oluşturmak yerine, derleyici, fonksiyondaki tüm kodu, fonksiyonun referans olarak belirtildiği noktaya kopyalar. Bu, daha az bellek tüketimi sayesinde kodun yürütülmesini hızlandırır. Ancak bizim durumuzda m_Infos.Rates değişkeni referans olarak belirtilecektir. Bu değişken MqlRates türüne sahiptir, yani MqlRates yapısının değerlerine erişebiliriz. Burada biz değişken referansının adresini geçiş yapmıyoruz. Ancak bazı durumlarda, kodu daha hızlı hale getirmek adına referansın adresinin geçişini yaparız, bu durumda prensipte yasaklı olması gereken sınıf içerisindeki bir değişkenin değerinin değiştirilmesi mümkün hale gelir. Bunun olmasını önlemek için, değişkenin sınıfın kendisi olmadan asla değiştirilemeyeceğini garanti eden const ayrılmış kelimesini kullanıyoruz. C++'da bulunan ayrılmış kelimelerin çoğu MQL5'te de dokümante biçimde mevcut olsa da, bazıları henüz dokümante edilmemiştir. Ancak MQL5, C++'a çok yakın olduğundan, onlar da MQL5'in bir parçasıdır. C++ hakkında daha fazla bilgi edinmek ve aynı bilgileri MQL5 programlamada kullanmak isteyenler için makalenin sonuna linkler ekleyeceğim.

Şimdi Init fonksiyonu kodundaki ilginç bir kısımdan daha bahsedelim, onu aşağıda vurguladım:

m_bChartShift = ChartGetInteger(Terminal.Get_ID(), CHART_SHIFT);
m_dChartShift = ChartGetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE);
ChartSetInteger(Terminal.Get_ID(), CHART_SHIFT, true);
ChartSetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE, 0.1);


Uzman Danışman başlatıldığında grafiği değiştirir, ancak kullanıcı kapattığında sistemi ilk durumuna sıfırlamak iyi bir pratiktir. Bu nedenle, grafiğin kayma ayarlarını kaydediyoruz ve ardından minimal bir kayma oluşturuyoruz. Bu, yukarıda vurgulanan kısımla yapılır, dolayısıyla ayarlamanın yapılabilmesi için ızgaranın grafik üzerinde görünür olması gerekir ve bu makalenin başında gösterildiği gibi etkileşimli olarak yapılır. Daha fazla bilgi için CHART_SHIFT’e bakabilirsiniz.


Ekrandaki nesneleri koruma

Hacim analizinin başlangıcını gösteren işaretin silinmesine izin vermeyecek bir güvenlik sistemine ihtiyacımız vardır:


İşaret çok küçüktür, bu yüzden fark edilmesi, yanlışlıkla silinmesi zor olabilir.

ÖNEMLİ NOT: Analizin başlangıcını değiştirmek istiyorsanız, grafiğin zaman dilimine dikkat edin. Örneğin, analizi 9:00'dan 9:02'ye taşımak istiyorsanız, 1 dakikalık veya 2 dakikalık zaman dilimini kullanmanız gerekir. Örneğin 5 dakikalık zaman dilimini kullanırsanız, bunu yapamazsınız.

Aşağıdaki kodla kullanıcının bu işareti yanlışlıkla silmesini önleriz:

void DispatchMessage(int iMsg, string sparam)
{
        switch (iMsg)
        {

// ... The inside of the code

                case CHARTEVENT_OBJECT_DELETE:
                        if ((sparam == m_Infos.szObjEvent) && (m_bUsing))
                        {
                                m_bUsing = false;
                                CreateObjEvent();
                                Resize();
                                m_bUsing = true;
                        }
                break;
        }                       
};


Sınıf, nesnenin silindiğini fark ettiğinde, hemen onu yeniden oluşturacaktır, böylece kullanıcı, sınıf için gerekli olan nesneden mahrum bırakılmayacak ve dolayısıyla Uzman Danışmanı yeniden başlatmak zorunda kalmayacaktır. Kullanıcının hassas bir nesneyi silmemesini sağlamanız gerektiğinde, kodda gösterilen modeli kullanın. Ancak olayın Uzman Danışman tarafından fark edilmesini sağlamak adına aşağıdaki ek kodu eklememiz gerekir:

ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);

Bu basit satır, MetaTrader 5'in nesnenin silinmesini rapor etmesini sağlar. Daha fazla bilgi için CHART_EVENT_OBJECT_DELETE’e bakabilirsiniz.


Hacim profili oluşturma

Bu, sınıfın kalbidir, üç fonksiyona sahiptir: bir public ve iki private. Public fonksiyonla başlayalım, aşağıda gösterilmektedir:

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

        if (m_bUsing == 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();
        };      
};

Vurgulanan satırlar sistem için çok önemlidir. Sistem çalışmaya başladığında hesaplamaya tam olarak nereden başlayacağını bilemez. Bu satırlar bu konumu günceller ve analizin nereden başladığını ve başlangıç fiyatının ne olduğunu bildirir, böylece sistem dahili bir tablo oluşturabilir. Sistem her zaman yeni bir tikin gelmesini bekler. Yeni tik geldiğinde, ekranda sunmak için ayrıştırılacak ve toplanacak verilerimiz olur. Bu da bizi aşağıdaki fonksiyona yönlendirir:

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


Bu fonksiyon o kadar önemli olmayabilir, çünkü yalnızca fiyatlara ilişkin hacim değerlerini saklar, ancak içerisindeki vurgulanan satırlar sistemin ruhudur. Bu iki satırda neler olup bittiğini gerçekten anlamak için biraz düşünmemiz gerekiyor. Hayal edelim, şunlardan hangisi daha hızlıdır: fiyatların her birini saklamak ve her birindeki hacimleri not etmek mi yoksa yalnızca hacimleri saklamak ve fiyatı sıraya dayalı şekilde başlangıca göre bulmak mı? İkinci seçenek daha hızlıdır, dolayısıyla hacimleri kaydedelim ve başlangıca göre fiyatın nerede olduğunu bulalım. Peki sistemdeki başlangıç fiyatını nasıl belirleyeceğiz? İlk işlem gören tik fiyatını kullanmaya ne dersiniz? (Yukarıda bir önceki kodda vurgulanan kısım bu görevi yapar, başlangıç fiyatını belirler ve bildirir, böylece sistem hesaplamaya başlayabilir) Evet, harika. Ancak bir sorunumuz var: fiyat yükselirse, tüm veriler bir dizide kolayca saklanabilir, ama ya düşerse? Bu durumda negatif değerlere sahip olacağız ve negatif indekslere sahip bir diziye erişemeyiz. Bir yerine iki dizi kullanılabilir, ancak bu gereksiz yüke yol açacaktır. Basit bir çözüm var. Aşağıdaki tabloya bakalım:


İndeks pozitifse endişelenmemize gerek yok, ancak negatifse sorun yaşarız çünkü çift yönlü bir dizi kullanıyoruz, burada sıfır değeri ilk tik fiyatını, negatif değerler aşağı hareket edenleri ve pozitif değerler de yukarı hareket edenleri temsil etmektedir. İndeksleri 2 ile çarparak orta sütunu elde edelim. Bu, yardım etmedi gibi görünüyor. Devamında, negatif değerleri pozitife çevirip 1 çıkaralım ve sağdaki sütunu elde edelim. Böylece negatif değerlerin pozitif hale gelmesini ve tüm değerlerin dizi şeklinde elde edilebilir olmasını sağlamış oluruz. İşte yukarıdaki kodda vurgulanan iki satır bunu oluşturma sürecini gerçekleştirmektedir. Bu çok iyi bir çözüm olsa da, verileri ekranda gösteremezsek yaptıklarımız hiçbir şey işe yaramaz. Aşağıdaki fonksiyon da tam olarak bunu gerçekleştirir.

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

Bu fonksiyon, grafik üzerinde hacim profilini çizer. Vurgulanan kısım, yukarıda yapılan hesaplamayı tersine çevirir. Ayrıca göstergenin uygun şekilde görüntülenebilmesi adına da fiyat az miktarda kaydırılır. Kodla ilgili olarak bazı ek açıklamalar yapmamız gereklidir. Kodda iki FillRectangle çağrısı olduğuna dikkat edin. Peki neden? İlk çağrı hangi hacmin daha büyük olduğunu ifade eder: satıcılar veya alıcılar. İkinci çağrı ise asıl olarak hacmi çizer. Neden hacim profilini alıcılar ve satıcılar için olacak şekilde kısımlara parçalamıyoruz da, hangisinin daha büyük olduğunu yan tarafta belirtiyoruz? Bunun nedeni, belirli bir fiyat aralığının hacmi arttıkça, diğer daha küçük hacimli fiyat aralıklarına müdahale etmesidir. Dolayısıyla, daha küçük hacimler daha da küçük olarak görüntülenir. Bu da küçük hacimlerde alıcıların mı yoksa satıcıların mı daha hakim olduğunun belirlenmesini zorlaştırır. Mevcut şekilde düzen yapıldığında ise bu sorun ortadan kalkar ve verilerin okunması daha kolay ve daha anlaşılır hale gelir. Sonuç olarak, grafik aşağıdaki gibi görünecektir:


Diğer tüm sınıf fonksiyonları, yukarıda açıklanan fonksiyonlar için destek görevi görür, bu nedenle ayrıntılı olarak ele alınacak kadar önemli değillerdir.


Sonuç

Burada size çok basit bir hacim profili göstergesi sundum ancak yine de son derece etkilidir. Kodlama öğrenmeye başlıyor ve Nesne Yönelimli Programlamaya (OOP) odaklanmak istiyorsanız, bu kodu dikkatli bir şekilde incelemenizi tavsiye ederim, çünkü içerisinde çok iyi konseptler barındırmakta ve

tam olarak nesne yönelimli yaklaşıma dayanmaktadır.


Kullanışlı linkler



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

Ekli dosyalar |
EA_1.06.zip (3280.48 KB)
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)
Yeni bir işlevselliği uygulamanın en kolay yolu nedir? Bu makalede, önce bir adım geri, sonra iki adım ileri gideceğiz.
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.
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.
ATR göstergesine dayalı bir ticaret sistemi nasıl geliştirilir? ATR göstergesine dayalı bir ticaret sistemi nasıl geliştirilir?
Bu makalede de, basit ticaret sistemleri tasarlamayı öğrendiğimiz serinin devamı olarak ticarette kullanılabilecek yeni bir teknik aracı inceleyeceğiz. Bu sefer ise başka bir popüler teknik gösterge olan Average True Range (ATR) ile çalışacağız.