Sıfırdan bir alım-satım Uzman Danışmanı geliştirme (Bölüm 30): Bir gösterge olarak alım-satım arayüzü?
Giriş
Sıfırdan bir alım-satım Uzman Danışmanı geliştirme (Bölüm 29): Konuşan platform başlıklı önceki makalede, alım-satım arayüzünü Uzman Danışmandan kaldırmıştık. Daha önce, Uzman Danışman performansını ve güvenilirliğini artırmak için ‘hacim profili’ ve ‘zaman ve alım-satım’ gibi diğer bazı düşünceler için de aynısını yaptık. Alım-satım arayüzünü Uzman Danışmandan kaldırdığımızda, yalnızca temel emir sistemiyle baş başa kalırız. Bu bazı kullanıcılar için yetersiz görünse de, Uzman Danışman aslında tüm işi yapabilir. Ancak piyasada işlemlere girip çıkmayı seven insanlar var, ama bunları bekleyen emirler olarak yerleştirmeyi sevmiyorlar ve işleme girmek veya işlemden çıkmak için fiyatın belirli bir seviyeye ulaşmasını bekliyorlar.
İşlem yaptığımız varlık ile MetaTrader 5 platformunu kullandığımızda (bundan bahsediyorum çünkü bu serinin 11. bölümünde tartıştığımız çapraz emir sistemini kullanabiliriz), piyasa emirleri yerleştiren düğmelere sahip hızlı işleme erişebileceğiz. Sol üst köşede bulunurlar. Şu şekilde görünürler:
Bu düğmeler temel alım-satım arayüzü gibi çalışır, ancak hiç görünmeyecekleri için çapraz emir sistemi için kullanılamazlar. Bu durumda, alım-satım arayüzümüze geri dönmemiz gerekecektir. Ancak artık Uzman Danışman içinde kullanılmayacak ve Uzman Danışman kodunun bir parçası olmayacaktır. Şu andan itibaren alım-satım arayüzü sadece bir gösterge olacaktır.
Alım-satım arayüzü için neden bunu yapmamız gerekiyor? Bunun nedeni, Uzman Danışmanın yalnızca alım-satım sisteminden sorumlu olması ve bu sistemin bir parçası olmayan her şeyin bir şekilde Uzman Danışman kodundan çıkarılması gerektiğidir. Bu şu anda anlamsız görünebilir, ancak yakında bunu daha net olarak anlayacaksınız, çünkü bunu tam olarak neden yaptığımı açıklayacağım bir devam makalesi hazırlıyorum.
Alım-satım arayüzünün bir komut dosyası olarak kullanılmasını bile sağlayabilir, ancak bunun bir dezavantajı olacaktır: grafik zaman dilimini her değiştirdiğimizde, komut dosyası kapanacak ve böylece onu manuel olarak tekrar çalıştırmamız gerekecektir.
Bir gösterge olarak kullanırsak durum böyle değildir. Bunun nedenlerinden biri, alım-satım arayüzünün göstergelerin yürütme iş parçacığını etkilemeyecek olmasıdır, bu nedenle Uzman Danışman özgür olacak, koduna ve yalnızca emirlerin ve pozisyonların yönetimiyle ilgili olana odaklanacaktır.
Orijinal sürümün tüm kontrollerine ve tüm bilgilerine sahip olmasak da, bir gösterge olarak bu alım-satım arayüzü çok daha basittir ve hala çalışmaktadır. MetaTrader 5 sistemi oldukça işlevsel ve bir o kadar da basittir. Ancak, alım-satım arayüzünde sahip olacağımız belirli bilgilere erişim sağlamaz.
Öyleyse başlayalım - konu çok ilginç olacak.
2.0. Bir alım-satım arayüzü göstergesi geliştirme
Bu göstergeyi oluşturmak için oldukça fazla değişiklik yapmamız gerekiyor. Alım-satım arayüzünü Uzman Danışman kodunun bir parçası haline getirmeden grafiğe döndürmek için bu değişiklikleri uygulayacağız.
İlk bakışta, aşağıda gösterilen kodun alım-satım arayüzünün en azından derlenmesi için yeterli olacağı düşünülebilir. Ancak hayır, çünkü kapatmamız gereken birkaç şeyle özünde bağlantılıdır. Bir gün Uzman Danışmanın içine geri döndürmek isteyebileceğimiz için tamamen silmiyoruz.
#property copyright "Daniel Jose" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ #include <NanoEA-SIMD\SubWindow\C_TemplateChart.mqh> //+------------------------------------------------------------------+ C_TemplateChart Chart; C_Terminal Terminal; //+------------------------------------------------------------------+ int OnInit() { Chart.AddThese("IDE(,,170, 240)"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+
Bu kodu derlemeye çalışırsanız, çok fazla hata alırsınız, ancak kodu bir gösterge olarak kullanılmak üzere uyarlarken Uzman Danışman ile uyumlu tutmak için bunları tek tek düzelteceğiz.
Yapmamız gereken ilk şey, alt pencereleri oluşturan veya yöneten sistemi izole etmektir. Alım-satım arayüzü bu alt pencereleri kullanmayacağından, bu kodun göstergeye gömülü olmasına gerek yoktur. Yapması oldukça kolay. C_Chart_IDE.mqh dosyasını aşağıdaki gibi görünecek şekilde düzenlememiz gerekir:
#ifdef def_INTEGRATION_CHART_TRADER #include <NanoEA-SIMD\SubWindow\C_SubWindow.mqh> #include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh> #else #include <NanoEA-SIMD\SubWindow\C_ChartFloating.mqh> #include <NanoEA-SIMD\Auxiliar\C_Terminal.mqh> #endif //+------------------------------------------------------------------+ #ifdef def_INTEGRATION_CHART_TRADER class C_Chart_IDE : public C_SubWindow #else class C_Chart_IDE : public C_ChartFloating #endif
Böylece, alım-satım arayüzü ile Uzman Danışmanın emir görüntüleme sistemi arasında herhangi bir bağlantıdan kaçınırken, sistemi alt pencerelerden tamamen izole ediyoruz. Bunun def_INTEGRATION_CHART_TRADER tanımı tarafından kontrol edileceğini unutmayın, ancak bu tanım yalnızca Uzman Danışmanda kullanıldığından, içindeki herhangi bir şey göstergeye derlenmeyecektir.
Gösterge alt pencereleri kullanmayacağından, birkaç şey üzerinde de çalışmalıyız. Bunlardan biri aşağıda gösterilmektedir:
bool Create(bool bFloat) { m_CountObject = 0; if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false; FileReadInteger(m_fp, SHORT_VALUE); for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = ""; m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA()); m_szLine = ""; while (m_szLine != "</chart>") // ... Rest of the code...
GetIdSubWinEA fonksiyonu, göstergenin içinde bulunduğu pencerenin numarasını geri döndürür. Bu çağrı birden fazla farklı noktada gerçekleştirilir. Bunun için iki çözüm bulunmaktadır. İlk çözüm, çağrının gerçekleştiği her noktada yukarıdaki fonksiyonun aşağıdaki şekilde değiştirilmesidir.
bool Create(bool bFloat) { m_CountObject = 0; if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false; FileReadInteger(m_fp, SHORT_VALUE); for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = ""; #ifdef def_INTEGRATION_CHART_TRADER m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA()); #else m_SubWindow = 0; #endif // ... The rest of the code...
Bu sorunu çözebilirdi, ancak o zaman o kadar çok değişiklik yapmamız gerekirdi ki bir süre sonra kodun anlaşılması çok zor hale gelirdi, çünkü bu koşullu derleme yönergelerinden çok fazla olurdu. Çok daha basit, ancak aynı derecede etkili bir çözümüm var: bunu ve tanım aracılığıyla yapılan diğer tüm çağrıları "taklit etmek" için, aşağıdaki parçayı sistem koduna ekleyebiliriz.
#ifndef def_INTEGRATION_CHART_TRADER #define GetIdSubWinEA() 0 #define ExistSubWin() false #endif
Bu kod çağrı sorununu çözecektir. Bu, göstergeyi derlenebilir hale getirmek için yeterli olacaktır (birkaç ayrıntıyla birlikte).
İkinci en büyük sorun, farenin kendisi değil, C_Mouse sınıfının fare olaylarına ilişkin işlevleriyle ilgilidir.
Anlaşılması gereken şey, alım-satım arayüzü Uzman Danışmanın bir parçası olduğunda, fare konumlarına (diğer şeylerin yanı sıra) erişmenin mümkün olduğudur, ancak göstergeye sadece C_Mouse sınıfını eklersek, gösterge ile Uzman Danışman arasında bir anlaşmazlık yaşayacağız. Anlaşmazlığın nasıl çözüldüğünü şimdi göstermeyeceğim. Her şey çözülene ve orijinal alım-satım arayüzünün en azından bazı işlevlerini elde edene kadar farklı bir yöne gideceğiz, ancak yalnızca geçici olarak.
Bu amaçla, C_Mouse sınıfından bazı şeyleri yeni göstergemize taşımamız gerekir. Ama endişelenmeyin, bu küçük bir şey. İlk değişiklik C_TemplateChart sınıfında gerçekleşir - DispatchMessage fonksiyonunda aşağıdaki değişikliği yapacağız:
void DispatchMessage(int id, long lparam, double dparam, string sparam) { datetime dt; double p; C_Chart_IDE::DispatchMessage(id, lparam, dparam, sparam); switch (id) { case CHARTEVENT_MOUSE_MOVE: #ifdef def_INTEGRATION_CHART_TRADER Mouse.GetPositionDP(dt, p); #else { int w; ChartXYToTimePrice(Terminal.Get_ID(), (int)lparam, (int)dparam, w, dt, p); } #endif for (int c0 = 0; c0 < m_Counter; c0++) if (m_Info[c0].szVLine != "") // ... Rest of the code...
Vurgulanan bölümü ekleyerek, C_Mouse sınıfı hala varmış gibi işlevselliği zaten elde ediyoruz. Bir sonraki değişiklik C_ChartFloating sınıfında olacak ve burada daha önce yaptığımıza benzer bir şey yapacağız. Ancak kod biraz farklıdır:
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: #ifdef def_INTEGRATION_CHART_TRADER Mouse.GetPositionXY(mx, my); if ((Mouse.GetButtonStatus() & 0x01) == 1) #else mx = (int)lparam; my = (int)dparam; if (((uint)sparam & 0x01) == 1) #endif { if (sic == -1) for (int c0 = m_MaxCounter - 1; (sic < 0) && (c0 >= 0); c0--) // ... Rest of the code...
Şimdi göstergeyi derleyebiliriz ve böylece aşağıdaki videoda gösterilen sonucu elde ederiz:
Uzman Danışman henüz tam olarak işlevsel olmasa da, şu kararı vermemiz gerekiyor: alım-satım arayüzü ya Uzman Danışman ile aynı yeteneklere sahip olacak ya da daha önce olduğu ile şu anda MetaTrader 5'te sağlanan arasında bir şeye sahip olacak şekilde azaltılabilir. Benim konumum oldukça radikal olduğu için, alım-satım arayüzünü Uzman Danışmanda sahip olduğu neredeyse aynı özelliklerle bırakacağım. İsterseniz bunu azaltabilirsiniz.
Bu kararı verdikten sonra, alım-satım arayüzü bir gösterge haline geldiğinden bir sonraki bölüme geçebiliriz.
2.1. Alım-satım arayüzü göstergesi nasıl işlevsel hale getirilir?
Şimdi işler daha da zorlaşacak. Öyleyse, bu göstergeyi nasıl işlevsel hale getireceğimizi görelim, böylece emir gönderebilir, pozisyonları kapatabilir ve hatta işlemlerin sonuçlarını raporlayabiliriz. Aslında bu ilk bakışta göründüğü kadar zor değildir, çünkü MetaTrader 5 platformu izlenecek bir yol sunar, böylece bu minimum çabayla yapılabilir.
Burada, farklı süreçler arasında veri aktarmak için dahili bir istemci-sunucu sistemi (platformun içinde) oluşturmak için MetaTrader 5'in bazı özelliklerini kullandığımız Bölüm 16'daki ile aynı şeyi yapacağız. Burada benzer bir şey yapacağız, sadece sunum biraz farklı olacak, çünkü iki yönlü iletişime ihtiyacımız var ve kullanıcı için görünmez kalması gerekiyor.
Kullanacağımız yöntem mümkün olan tek yöntem değildir. Bu aktarımı etkinleştirmek için bir DLL kullanmak gibi başka yollar da vardır. Bununla birlikte, gezinmesi, bakımı ve gerektiğinde değiştirilmesi en kolay olan MetaTrader 5 değişkenlerini kullanacağız.
Bu yola girmeye karar verdiğimize göre, ikinci önemli karar var: kim sunucu ve kim istemci olacak? Bu karar, sistemin gerçekte nasıl uygulanacağını etkileyecektir. Aşağıda gösterilen mesajlaşma protokolümüzü kullanacağız:
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ #define def_GlobalVariableLeverage (_Symbol + "_Leverage") #define def_GlobalVariableTake (_Symbol + "_Take") #define def_GlobalVariableStop (_Symbol + "_Stop") #define def_GlobalVariableResult (_Symbol + "_Result") #define def_GlobalVariableButton (_Symbol + "_ButtonState") //+------------------------------------------------------------------+ #define def_ButtonDTSelect 0x01 #define def_ButtonSWSelect 0x02 #define def_ButtonBuyMarket 0x04 #define def_ButtonSellMarket 0x08 #define def_ButtonClosePosition 0x10 //+------------------------------------------------------------------+
Hepsi bu kadar. Mesaj protokolünü zaten tanımladığımız için aslında kimin istemci kimin sunucu olacağını belirlememize gerek yok. Bu protokol en başından itibaren tanımlanmalıdır, çünkü bu diğer her şeyin geliştirilmesini kolaylaştıracaktır.
Alım-satım arayüzü - Uzman Danışman setinin mevcut olduğu her varlık için 5 değişken kullanacağımızı lütfen unutmayın. Değişken adları, setin bağlı olduğu varlığa bağlı olacaktır, böylece seti aynı anda birkaç varlık için kullanabiliriz.
Burada önemli bir soru ortaya çıkıyor: sunucunun işini kim yapacak? Bu tür değişkenlerin oluşturulmasından sorumlu olacaktır. Şahsen, Uzman Danışmanı sunucu olarak kullanmayı ve alım-satım arayüzünü istemci olarak bırakmayı daha pratik buluyorum. Buradaki fikir, Uzman Danışmanın her zaman grafikte olması ve alım-satım arayüzünün gerektiğinde belirli zamanlarda orada olmasıdır. Böylece, Uzman Danışman 5 değişkenden 4'ünün oluşturulmasından sorumlu olacaktır, çünkü bunlardan biri hangi düğmeye basıldığını bildirmekten sorumlu olacaktır. Dolayısıyla, bu alım-satım arayüzünün sorumluluğundadır.
Tüm bunlara dayanarak, veri akışı şu şekilde görünür:
- Uzman Danışman, kaldıraç, Kârı Al ve Zararı Durduru gösteren ilk değerleri içerecek global değişkenler oluşturacaktır. Sonunda, günün sonucu hakkında bilgi verecek değişkeni de oluşturacak, böylece alım-satım arayüzü bunu kullanıcıya gösterebilecek ve bu bilgiyi başka bir yerde aramaya gerek kalmayacaktır.
- Alım-satım arayüzü, düğmeye basma değerini temsil eden bir değişken oluşturacaktır. Bu değişken, Uzman Danışmana açık bir pozisyonu kapatmak veya piyasa alış veya satışını gerçekleştirmek gibi ne yapması gerektiğini söyleyecektir. Bu değişken yalnızca bu süre boyunca var olacak ve talep Uzman Danışman tarafından tamamlanır tamamlanmaz varlığı sona erecektir.
Akış basittir, ancak alım-satım arayüzünün daha önce olduğu gibi sistemin emir gönderme ve pozisyonları kapatma yeteneği de dahil olmak üzere Uzman Danışman ile etkileşime girebilmesini garanti eder.
Alım-satım arayüzünün yalnızca Uzman Danışman mevcut olduğunda grafikte var olmasını sağlamak için bazı kontroller eklememiz gerekiyor. Uzman Danışman emir göndermek için kullanılabilir değilse, alım-satım arayüzünün grafikte olmasının bir anlamı yoktur. Bu kontroller aşağıdaki iki anda yapılır:
- İlki gösterge başlatıldığında yapılır
- İkincisi, grafik üzerinde herhangi bir olay meydana geldiğinde yapılır
Bunu kodda görelim, böylece süreci anlamanız daha kolay olacaktır. Başlatma sırasında aşağıdaki kod yürütülür:
#define def_SHORTNAME "CHART TRADE" //+------------------------------------------------------------------+ int OnInit() { long lparam = 0; double dparam = 0.0; string sparam = ""; IndicatorSetString(INDICATOR_SHORTNAME, def_SHORTNAME); if(!GlobalVariableGet(def_GlobalVariableLeverage, dparam)) return INIT_FAILED; Terminal.Init(); Chart.AddThese("IDE(,,170, 215)"); Chart.InitilizeChartTrade(dparam * Terminal.GetVolumeMinimal(), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), true); OnChartEvent(CHARTEVENT_OBJECT_ENDEDIT, lparam, dparam, sparam); return INIT_SUCCEEDED; }
Vurgulanan satır global değişkenlerden birini, bu durumda kaldıraç seviyesini gösteren değişkeni değerlendirecektir. Bu değişken eksikse, başlatma başarısız olur. Bu değişkeni oluşturmaktan alım-satım arayüzünün değil Uzman Danışmanın sorumlu olduğunu unutmayın, bu şekilde gösterge Uzman Danışman grafikte mevcut olup olmadığını bilecek ve Uzman Danışman işini tamamladığında bu değişkeni MetaTrader 5'ten kaldırarak alım-satım arayüzünü de silinmeye zorlayacaktır. Bu, aynı koşulu kontrol ettiğimiz ikinci noktada da yapılır - global kaldıraç değişkeninin mevcut olup olmadığı.
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED); Chart.DispatchMessage(id, lparam, dparam, sparam); }
Yukarıda belirtilen husus nispeten yüksek bir sıklıkta uygulanacaktır. Bir göstergeye OnTime olay sistemi yerleştirmek cazip olsa da, bu tavsiye edilmez: tüm göstergeler aynı çalışma iş parçacığını kullanır ve bir göstergeye OnTime olayı yerleştirmek, bu çok dikkatli yapılmazsa, diğerlerini etkileyecektir.
Yukarıdaki kontrolün platformun genel performansını bozduğunu düşünüyorsanız, bunu bir OnTime olayının içine yerleştirebilirsiniz, ancak sonuçlarının farkında olun.
Alım-satım arayüzü göstergesinin grafikten kaldırılmasını kimin tetiklediğine bakılmaksızın, aşağıda görülebileceği gibi aynı noktada gerçekleşecektir:
void OnDeinit(const int reason) { if (reason == REASON_INITFAILED) { Print("Unable to use Chart Trade. The EA is not on the chart of this asset..."); if (!ChartIndicatorDelete(0, 0, def_SHORTNAME)) Print("Unable to delete Chart Trade from the chart."); } }
Vurgulanan satır, göstergeyi grafikten kaldıracaktır. Başarısızlık durumunda, ilgili bir mesaj gösterilecektir. Bu mesajlar aşağıda gösterildiği gibi araç kutusunda görülebilir:
Bu şekilde, bu pencerede bulunan her türlü bilgiden her zaman haberdar olmalısınız.
C_Chart_IDE sınıfında yapılan değişikliklerin daha derin bir analizine geçmeden önce eksik olan son bir fonksiyon var. Bu fonksiyon aşağıdaki gibidir:
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { double value; if (GlobalVariableGet(def_GlobalVariableResult, value)) { GlobalVariableDel(def_GlobalVariableResult); Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, value, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eROOF_DIARY]); } return rates_total; }
Bu fonksiyonun yaptığı şey global değişkenleri izlemektir. Böylece, zaman zaman, bir pozisyon kapatılırken, Uzman Danışman, adı def_GlobalVariableResult içinde tanımlanan bir global değişken oluşturacaktır. Bu değişken oluşturulduktan ve adı alım-satım arayüzü tarafından gözlemlenenle eşleştikten sonra, bu değişkenin değeri yakalanacak ve değişken hemen silinecektir. Bu, Uzman Danışmanın işleme tamamlanmadan önce güncellemeyi göndererek bu güncellemenin kaybolmasına neden olduğu durumdan kaçınmak için yapılır. Bununla birlikte, değeri silmeden önce yakalayarak, mesajları işlemekten sorumlu alım-satım arayüzü sınıfına gönderebiliriz, böylece Uzman Danışman tarafından iletilen değer mümkün olan en kısa sürede alım-satım arayüzünde gösterilir.
Böylece alım-satım arayüzü göstergesini işlevsel hale getirdiğimiz ilk bölümü tamamladık. İkinci bölüm düğmelerle ilgilidir. Onları da işlevsel hale getirmeliyiz. Bu, C_Chart_IDE sınıfındaki mesajları işleyen fonksiyonda kolayca yapılabilir:
// ... Previous code ... case CHARTEVENT_OBJECT_CLICK: if (StringSubstr(sparam, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG) { Resize(-1); return; } sparam = StringSubstr(sparam, 9, StringLen(sparam)); StringToUpper(sparam); #ifdef def_INTEGRATION_CHART_TRADER if ((sparam == szMsgIDE[eBTN_SELL]) || (sparam == szMsgIDE[eBTN_BUY])) TradeView.ExecuteOrderInMarket(m_BaseFinance.Leverange, m_BaseFinance.FinanceTake, m_BaseFinance.FinanceStop, sparam == szMsgIDE[eBTN_BUY], m_BaseFinance.IsDayTrade); if (sparam == szMsgIDE[eBTN_CANCEL]) { TradeView.CloseAllsPosition(); ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false); } #else { union u00 { double Value; ulong c; }u_local; u_local.c = 0; if (sparam == szMsgIDE[eBTN_BUY]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonBuyMarket; else if (sparam == szMsgIDE[eBTN_SELL]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonSellMarket; else if (sparam == szMsgIDE[eBTN_CANCEL]) { u_local.c = def_ButtonClosePosition; ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false); } if (u_local.Value > 0) GlobalVariableSet(def_GlobalVariableButton, u_local.Value); } #endif if (sparam == szMsgIDE[eCHECK_DAYTRADE]) InitilizeChartTrade(0, 0, 0, m_BaseFinance.IsDayTrade ? false : true); break; //... Rest of the code...
Parçanın iki kod içerdiğine dikkat edin. Mavi kod, alım-satım arayüzü Uzman Danışmana gömüldüğünde kullanılır. Yeşil olan, alım-satım arayüzü bir gösterge olarak mevcut olduğunda kullanılır.
Yeşil kodla ilgileniyoruz. Bir global değişken oluşturacak ve düğme durumlarını ayarlayacaktır. Böylece, yatırımcı bir pozisyonu kapatırsa, kapat düğmesine karşılık gelen değer değişkene yerleştirilecektir. Ancak bir piyasa emri gönderirseniz, bu değer farklı olacaktır ve artık diğer iki değerin bir kombinasyonu olacaktır: biri emrin alış mı yoksa satış mı olduğunu gösterirken, diğeri günlük işlem mi yoksa daha uzun bir işlem mi yapmak istediğinizi gösterir. Alım-satım arayüzünün Uzman Danışmana söyleyeceği tek şey budur.
ÖNEMLİ: Bu sistem kendi kendini dışlayan niteliktedir, yani Sat düğmesine tıklarsanız ve ardından Uzman Danışman herhangi bir şey yapmadan önce Al düğmesine tıklarsanız, Uzman Danışman aslında alış yapacaktır çünkü satışı gösteren değer yeni alış değeri nedeniyle kaybolacaktır. Ayrıca, halihazırda açık bir pozisyon varken satış veya alış talebinde bulunursanız ve Uzman Danışman ilgili işlemi gerçekleştirmeden önce iptal tuşuna basarsanız, pozisyon kapatılır.
Şimdi yeni modelde nasıl çalıştığını görmek için Uzman Danışman koduna geçebiliriz.
2.2. Alım-satım arayüzünden mesaj almak için Uzman Danışman kodunun değiştirilmesi
Tek yapmamız gereken birkaç küçük ayrıntıyı ayarlamak olduğu için bu kısım oldukça kolaydır. Ancak şunu unutmayın: Uzman Danışman ve alım-satım arayüzünü yükledikten sonra, Uzman Danışmanda bulunan global değişkenleri veya verileri değiştirmeyin. Bunun için emir sistemini veya alım-satım arayüzünün kendisini kullanın, aksi takdirde sorun yaşayabilirsiniz. Bazı sorunların çözümü vardır, bazılarının ise yoktur. Yani her ihtimale karşı, mevcut araçları kullanın, hayatınızı karmaşıklaştırmaya çalışmayın.
Bir önceki makalede (Bölüm 29), alım-satım arayüzünün kaldırılmasını teşvik ederken bazı değişiklikler yaptık ve bunların bir kısmı geri alınacak. Bu konuyla ilgili başka bir şeyi değiştirmemize gerek yok. Ancak, daha önce de belirtildiği gibi, bazı şeyler düzeltilebilirdir ve bazıları düzeltilebilir değildir, bu nedenle bu makalenin bir sonraki bölümünde, alım-satım arayüzü ile Uzman Danışman arasındaki ilişkideki bazı küçük sorunları ortadan kaldıracağız.
Öncelikle Uzman Danışmanda neleri geri almamız ve etkinleştirmemiz gerektiğine bakalım, böylece Uzman Danışman ve alım-satım arayüzü arasında bir iletişim sağlanabilir.
Öncelikle aşağıdakileri değiştirelim:
input int user20 = 1; //Leverage input double user21 = 100; //Take Profit input double user22 = 81.74; //Stop Loss input bool EA_user23 = true; //Day Trade ?
Uzman Danışmanın kısa veya uzun dönemli işlem açmayı tercih edip etmeyeceğini gösteren değer değişmeden kalır. Bunun alım-satım arayüzünde veya grafiğe yerleştirdiğiniz bekleyen emirde yapılması gerekecektir. Bunu nasıl yapacağınızı daha önceki makalelerde göstermiştim, şimdi kodlamaya geçelim. OnInit olayına aşağıdaki değişiklikleri ekleyelim:
int OnInit() { if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Sound.PlayAlert(C_Sounds::TRADE_ALLOWED); return INIT_FAILED; } Terminal.Init(); #ifdef def_INTEGRATION_TAPE_READING VolumeAtPrice.Init(user32, user33, user30, user31); TimesAndTrade.Init(user41); EventSetTimer(1); #endif Mouse.Init(user50, user51, user52); #ifdef def_INTEGRATION_CHART_TRADER static string memSzUser01 = ""; if (memSzUser01 != user01) { Chart.ClearTemplateChart(); Chart.AddThese(memSzUser01 = user01); } Chart.InitilizeChartTrade(EA_user20 * Terminal.GetVolumeMinimal(), EA_user21, EA_user22, EA_user23); TradeView.Initilize(); OnTrade(); #else GlobalVariableTemp(def_GlobalVariableLeverage); GlobalVariableTemp(def_GlobalVariableTake); GlobalVariableTemp(def_GlobalVariableStop); GlobalVariableTemp(def_GlobalVariableResult); GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal()); GlobalVariableSet(def_GlobalVariableTake, user21); GlobalVariableSet(def_GlobalVariableStop, user22); TradeView.Initilize(); GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof()); #endif return INIT_SUCCEEDED; }
Gördüğünüz gibi buraya iletişim için kullanılacak global değişkenleri ekliyoruz. Daha önce de belirttiğimiz gibi, Uzman Danışman her zaman alım-satım arayüzünden önce başlamalıdır, aksi takdirde göstergeyi başlatamayız. Başlangıçta alım-satım arayüzü tarafından kullanılacak değerlerin Uzman Danışmanda belirtildiğini lütfen unutmayın. Önceki alım-satım işlemleri var olsa bile başlatma tamamlanır: birikmiş değer de yeniden alım-satım arayüzüne aktarılır.
Önemli bir ayrıntıya dikkat edin: oluşturulan değişkenler geçici tiptedir, çünkü bu değişkenlerin Uzman Danışman veri dökümü durumunda kaydedilmesini istemiyoruz, çünkü belirli bir süre sonra kullanılmazlar. Bir şey olsa ve platform kapansa bile, bu değişkenler artık var olmayacaktır.
Uygulanacak bir diğer ilave aşağıda gösterilmiştir:
void OnDeinit(const int reason) { Mouse.Destroy(); TradeView.Finish(); #ifndef def_INTEGRATION_CHART_TRADER GlobalVariableDel(def_GlobalVariableLeverage); GlobalVariableDel(def_GlobalVariableTake); GlobalVariableDel(def_GlobalVariableStop); GlobalVariableDel(def_GlobalVariableResult); GlobalVariableDel(def_GlobalVariableButton); #endif #ifdef def_INTEGRATION_TAPE_READING EventKillTimer(); #endif }
Değişkenler geçici olsa da, yine de Uzman Danışmandan bunları zorla kaldırmasını istiyoruz. Bu, alım-satım arayüzünün artık grafikte kalmamasını sağlayacaktır. Bu OnDeinit olayıyla ilgili küçük bir problem var, ancak bunu bir sonraki bölümde ele alacağız. Şimdi oldukça ilginç olan bir başka noktaya bakalım. Bu iki farklı şekilde yapılabilir, ancak sonuçlar neredeyse aynı olacaktır. Neredeyse aynı diyorum, çünkü fark yaratabilecek küçük farklılıklar vardır. (Kelime oyunu için özür dilerim)
Alım-satım arayüzünün bazı düğmeleri vardır ve bunlar bir global değişken aracılığıyla Uzman Danışmana mesaj gönderir. İşte Uzman Danışmanın içinde bu tıklamalara uygun şekilde yanıt veren fonksiyon:
inline void ChartTrade_ClickButton(void) { union u00 { double Value; ulong c; }u_local; if (GlobalVariableGet(def_GlobalVariableButton, u_local.Value)) { GlobalVariableDel(def_GlobalVariableButton); if (u_local.c == def_ButtonClosePosition) TradeView.CloseAllsPosition(); else TradeView.ExecuteOrderInMarket(GlobalVariableGet(def_GlobalVariableLeverage), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), ((u_local.c & def_ButtonBuyMarket) == def_ButtonBuyMarket), ((u_local.c & def_ButtonDTSelect) == def_ButtonDTSelect)); TradeView.Initilize(); } }
Fonksiyon satır içi (inline) olarak bildirilir, yani derleyici tarafından bildirildiği konuma yerleştirilmelidir. Bu, mümkün olduğunca hızlı çalışmasını sağlayacaktır.
Çok önemli bir ayrıntı: bu fonksiyonu nereye yerleştireceğiz? Bunu bir düşünelim. Burada bir kontrol var, böylece her zaman çalışmayacak. Yani elimizde iki olasılık var. Birincisi, fonksiyonu OnTick olayının içine, ikincisi ise OnTime olayının içine yerleştirmektir. Seçim belirli bir mantığa dayanmalıdır, aksi takdirde sorunlar çıkabilir.
O zaman düşünelim: Bu fonksiyonu OnTick olayına koyarsak, işlem sunucusundan platforma gelen her yeni tikte çalıştırılacaktır. Fonksiyon birçok kez değil, yalnızca belirli zamanlarda çalıştırılacağı için bu iyi bir çözüm gibi görünüyor. Ancak bu bir sorun yaratmaktadır: İşlem yaptığımız varlığın volatilitesinin çok düşük olması durumunda, OnTick olaylarının sıklığı da çok düşük olacaktır ve bu, olayın alım-satım arayüzüne tıklama ile tetiklenmesinde sorun yaşayabileceğimiz anlamına gelir.
İkinci seçenek. Fonksiyonu OnTime içine yerleştirmek, OnTime olayı belirli bir düzenlilikle tetikleneceğinden fonksiyonun yürütülmesini sağlayacaktır. Ancak bunu yaptığımızda, OnTime olayını artık global değişkenleri gözlemlemek dışında başka bir şey için kullanamayacağımızı unutmamalıyız.
Bu aşamada, Uzman Danışman sadece piyasayı izleyeceği için çok iyi bir seçim yaptık. Önümüzdeki birkaç makale boyunca bunun giderek daha belirgin hale geleceğini göreceksiniz. O zaman OnTime olayının içine bir fonksiyon koymak iyi bir fikirdir. Ancak şimdi soru şu: OnTime olayı yalnızca her saniye ateşleniyor, değil mi?
Aslında, çoğu zaman saniyede bir kez ateşlenir, ancak EventSetMillisecondTimer fonksiyonunu kullanarak daha kısa bir süre ayarlayabiliriz. Böylece, olayı 1 saniyeden daha kısa sürede ateşleyebiliriz. Çoğu durum için 500 ms'nin yeterli olacağına inanıyorum, bu nedenle Uzman Danışmanın OnInit olayında aşağıdaki satıra sahip olacağız:
#else GlobalVariableTemp(def_GlobalVariableLeverage); GlobalVariableTemp(def_GlobalVariableTake); GlobalVariableTemp(def_GlobalVariableStop); GlobalVariableTemp(def_GlobalVariableResult); GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal()); GlobalVariableSet(def_GlobalVariableTake, user21); GlobalVariableSet(def_GlobalVariableStop, user22); TradeView.Initilize(); GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof()); EventSetMillisecondTimer(500); #endif
Bu olayı OnDeinit fonksiyonunda kapatmayı unutmayın, bunun için olaya aşağıdaki satırı eklememiz yeterlidir:
void OnDeinit(const int reason) { EventKillTimer();
Şimdi OnTime olayının neye benzediğini görelim. Sadece vurgulanan satırın gerçekten derlendiği süper basit bir koddur.
void OnTimer() { #ifndef def_INTEGRATION_CHART_TRADER ChartTrade_ClickButton(); #endif #ifdef def_INTEGRATION_TAPE_READING VolumeAtPrice.Update(); TimesAndTrade.Connect(); #endif }
Hepsi bu kadar mı? Hayır, küçük bir sorun daha var. Uzman Danışmanın başlatma değişkenlerini değiştirdiğimizi ve grafiğe her yeni bekleyen emir yerleştirildiğinde alım-satım arayüzünden veri almak istediğimizi hatırlayalım. Bunun için C_IndicatorTradeView sınıf koduna küçük bir detay eklememiz gerekiyor. Aşağıda gösterilmiştir:
case CHARTEVENT_MOUSE_MOVE: Mouse.GetPositionDP(dt, price); mKeys = Mouse.GetButtonStatus(); bEClick = (mKeys & 0x01) == 0x01; //Left mouse button click bKeyBuy = (mKeys & 0x04) == 0x04; //SHIFT pressed bKeySell = (mKeys & 0x08) == 0x08; //CTRL pressed if (bKeyBuy != bKeySell) { if (!bMounting) { #ifdef def_INTEGRATION_CHART_TRADER m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl); #else m_Selection.vol = GlobalVariableGet(def_GlobalVariableLeverage) * Terminal.GetVolumeMinimal(); valueTp = GlobalVariableGet(def_GlobalVariableTake); valueSl = GlobalVariableGet(def_GlobalVariableStop); m_Selection.bIsDayTrade = EA_user23; #endif
Vurgulanan kodlar artık global değişkenlerdeki değerleri yakalamaktadır, bu nedenle alım-satım arayüzünde ne varsa emir sistemine konulacaktır. Tek ayrıntı, bekleyen tüm emirlerin Uzman Danışman tarafından belirtilen zamanı takip edeceğidir, ancak bu doğrudan grafik üzerinde değiştirilebilir. Daha fazla ayrıntı için lütfen "Sıfırdan bir alım-satım Uzman Danışmanı geliştirme (Bölüm 27): Geleceğe doğru (II)" makalesine bakın; burada bekleyen emirlerin alım-satım arayüzünden geçmek zorunda kalmadan doğrudan grafik üzerinde nasıl değiştirileceğini gösterdim.
Ses sisteminde de küçük bir değişiklik var. Alım-satım arayüzü üzerinden çalışıyorsak, pozisyon açma ve kapama hakkında bildirim sağlamak için C_Router sınıfına sesler eklendi. Ses sistemindeki bir şeyi çıkarmak veya değiştirmek istiyorsanız, dikkat etmeniz gereken başka noktalar olduğunu unutmamalısınız.
Bitirmeden önce, OnDeinit olayının düzelteceğimiz bir sorunu olduğunu hatırlayalım. O halde bir sonraki bölüme geçelim ve bunu düzeltelim.
2.3. Alım-satım arayüzünün grafikten erken çıkmasını önleme
Grafik zaman dilimini veya Uzman Danışman parametrelerini değiştirmek gibi herhangi bir şey yaptığımızda (herhangi bir şey olabilir), MetaTrader 5 bir OnDeinit olayı oluşturur. Bu olayın tetiklenmesi, her şeyin olması gerektiği gibi çalışmaya devam ettiğinden emin olmak için her şeyin yeniden analiz edilmesi gerektiği anlamına gelir.
Çoğu durumda, bu olayın tetiklenmesi herhangi bir soruna neden olmaz. Ancak bir istemci-sunucu sistemi oluşturduğumuzdan ve sunucunun (Uzman Danışman) çalışmayı durdurup durdurmadığını öğrenmek için global değişkenler kullandığımızdan, belirli durumları nasıl aşacağımızı anlamamız gerekecektir.
Orijinal OnDeinit olayını işleyen fonksiyon şu şekildedir:
void OnDeinit(const int reason) { EventKillTimer(); Mouse.Destroy(); TradeView.Finish(); #ifndef def_INTEGRATION_CHART_TRADER GlobalVariableDel(def_GlobalVariableLeverage); GlobalVariableDel(def_GlobalVariableTake); GlobalVariableDel(def_GlobalVariableStop); GlobalVariableDel(def_GlobalVariableResult); GlobalVariableDel(def_GlobalVariableButton); #endif #ifdef def_INTEGRATION_TAPE_READING EventKillTimer(); #endif }
Vurgulanan satır, Uzman Danışmanın grafikte olup olmadığını kontrol etmek için tam olarak alım-satım arayüzü göstergesinde kullandığımız global değişkeni kaldıracaktır. Bu kontrol aşağıdaki kodda gerçekleştirilir:
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED); Chart.DispatchMessage(id, lparam, dparam, sparam); }
Yani, Uzman Danışmanda OnDeinit olayını etkinleştiren bir şey olduğunda, bu değişken silinecektir. Ve Uzman Danışman değişkeni yeniden oluşturmadan önce, gösterge grafikten zaten kaldırılmış olabilir, ki bu gerçekten garip olabilir. Bazı noktalarda bu gerçekleşecek, bazı noktalarda ise gerçekleşmeyecektir. Dolayısıyla, her şeyin beklendiği gibi çalıştığından bir şekilde emin olmalıyız.
Dokümantasyonda bu soruna bir çözüm buldum. Bu, sonlandırma neden kodları bölümünden görülebilir. Bu kodlara bakarak, OnDeinit olay işleme fonksiyonunu, Uzman Danışman grafikten kaldırılana kadar değişkenlerin silinmeyeceği şekilde yapılandırabiliriz.
Yani çözüm ve yeni işleme kodu şu şekilde olacaktır:
void OnDeinit(const int reason) { EventKillTimer(); Mouse.Destroy(); TradeView.Finish(); #ifndef def_INTEGRATION_CHART_TRADER switch (reason) { case REASON_CHARTCHANGE: break; default: GlobalVariableDel(def_GlobalVariableLeverage); GlobalVariableDel(def_GlobalVariableTake); GlobalVariableDel(def_GlobalVariableStop); GlobalVariableDel(def_GlobalVariableResult); GlobalVariableDel(def_GlobalVariableButton); }; #endif #ifdef def_INTEGRATION_TAPE_READING EventKillTimer(); #endif }
Şimdi, yalnızca grafik zaman dilimini veya çizim şeklini değiştirirsek, artık alım-satım arayüzü göstergesinin grafikten kaybolmasıyla ilgili rahatsızlığa sahip olmayacağız. Başka herhangi bir durumda, her şeyin yolunda gideceğine dair gerçek bir güvenceye sahip olmadığımız için grafikten çıkarılabilir. Bu nedenle, Uzman Danışman ve alım-satım arayüzü yüklendikten sonra, Uzman Danışman parametrelerini değiştirmenin artık bir anlamı olmayacaktır.
Sonuç
Biraz yaratıcılığın neler yapabileceğini görüyor musunuz? Bazen çözülemez gibi görünen sorunları çözmemiz gerekir. Ancak dokümantasyonu inceleyerek çözümü bulabiliriz, bu nedenle fikirleri uygulamaya koyabilmek için dokümantasyonu her zaman kontrol etmek ve anlamak çok önemlidir.
MetaQuotes Ltd tarafından Portekizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/pt/articles/10653
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz