MQL5'te Olay Odaklı Mimari: Bir Uzman Danışmanı Tam Teşekküllü Bir Alım-Satım Sistemine Dönüştürme
Giriş
MQL5'te bir Uzman Danışman geliştirirken, birçok kişi en bariz çözümle başlar: tüm mantığı OnTick metoduna koyarlar. Bu şekilde başlamak gerçekten daha kolay. Ancak bu yaklaşımın gizli bir maliyeti vardır. Proje büyüdükçe, alım-satım kuralları, koşul kontrolü, emir işleme, veri güncelleme, arayüz, hesaplamalar ve günlük kaydı tek bir işleyicide birleştirilir. Sonuç olarak, kod artık programlanamaz bir duruma gelir ve aslında tamamen şans eseri bir arada tutulur. Bir yerdeki herhangi bir değişiklik sistemin tamamen farklı kısımlarını etkilemeye başlar. Görsel paneli düzeltirsiniz - ve aniden alım-satım senaryosu bozulur. Giriş filtresini değiştirirsiniz - ve arka plan kontrolünde bir hata görünür. Böyle bir Uzman Danışman, karmaşıklığın geliştiricinin güveninden daha hızlı arttığı kırılgan bir tek parça yapıya dönüşür.
MetaTrader 5, bir fiyat akışından daha fazlasıdır. Olaylara dayanmaktadır. Terminal sürekli olarak tikler, zamanlayıcı sinyalleri, kullanıcı eylemleri, işlem durumu değişiklikleri ve piyasa derinliği olayları alır. Bu mesajlar ayrı ayrı işlenmelidir. Bunu başarmak için MQL5, her biri kendi sorumluluk alanına sahip farklı işleyicilere sahiptir. OnTick piyasa güncellemelerinden sorumludur. OnTimer - periyodik ve arka plan görevleri için. OnChartEvent - grafik arayüzüne ve kullanıcı eylemlerine tepki vermek için. Mantık amacına göre dağıtıldığında, kod karmaşık olmaktan çıkar. Her modülün kendi işini yaptığı ve komşularına müdahale etmediği iyi yapılandırılmış bir mühendislik sistemine benzemeye başlar.
Bu tasarım, Uzman Danışman tek bir sembolün ötesine geçip aynı anda birden fazla işlevi yerine getirmeye başladığında özellikle önemlidir. Piyasayı izlememiz, arayüzü sürdürmemiz, düğmeye basıldığında yanıt vermemiz, dahili durumu senkronize etmemiz, bileşenler arasında sinyal iletmemiz ve hatta bazen birden fazla enstrümanı yönetmemiz gerekir. Bu noktada, olay mimarisi artık güzel bir teori gibi görünmüyor. Pratik bir gereklilik haline geliyor. Arka plandaki işleri OnTimer'a, kullanıcının eylemlerine verilen yanıtları ise OnChartEvent'e taşırsak, ana alım-satım devresi gereksiz yükten kurtulur. Bu, sistem davranışını daha öngörülebilir hale getirir ve bakımı önemli ölçüde basitleştirir.
Özel olaylar ve hizmetler bu tür bir mimaride ayrı bir rol oynar. Özel olaylar modüller arasında dahili bir mesajlaşma hattı düzenlememize imkan verirken, hizmetler yardımcı mantığı belirli bir grafiğin dışına taşımamıza olanak sağlar. Bu artık sadece bir Uzman Danışman değil, her şeyi tek bir fonksiyona karıştırmadan komut ve sinyal alışverişi yapabilen bir bileşenler sistemidir. Bu, kontrol panelleri, arka plan analizleri, modüller arası veri alışverişi ve rollerin net bir şekilde ayrılması ile alım-satım uygulaması düzeyinde çözümler oluşturmayı mümkün kılar.
Ek olarak, olay odaklı yaklaşım test edilebilirliği önemli ölçüde artırır. Mantık işleyiciler arasında dağıtıldığında, parçalar halinde test etmek daha kolaydır. Zamanlayıcıya, kullanıcı arayüzü olaylarına ve alım-satım işlemlerine verilen yanıtları ayrı ayrı analiz edebiliriz. Bu, özellikle bir hatanın zaman ve paraya mal olabileceği görevlerde önemlidir. Yapı ne kadar iyi olursa, sistem davranışını kontrol etmek ve arızaların nedenlerini hızlı bir şekilde bulmak o kadar kolay olur.
Bu makalede, her şeyin OnTick'te yönetildiği modelden daha olgun bir olay mimarisine nasıl geçileceğini ele alacağız. Önceden tanımlanmış işleyicilerin ve özel olayların yanı sıra bir grafiğe bağlı olmayan hizmetlerin rollerine göz atacağız. Gerçek çalışma başlamadan önce bile mimariyi bozan tipik hatalara daha yakından bakacağız. Buradaki ana fikir basittir: MQL5 amacına uygun olarak kullanıldığında, yalnızca alım-satım robotları değil, aynı zamanda tam teşekküllü uygulama sistemleri de oluşturmamıza olanak tanır.

Önceden tanımlanmış olaylar
MQL5'teki önceden tanımlanmış olaylar, programın tüm mantığının dayandığı çerçevedir. Bu sadece bir işleyici fonksiyonları kümesi değil, ortamdaki değişikliklere kesin olarak tanımlanmış bir yanıt modelidir. Ve geliştirici bunları OnTick'e bir ek olarak algılamayı ne kadar çabuk bırakırsa, kod da o kadar hızlı bir mimari kazanmaya başlar.
Herhangi bir programın yaşam döngüsü OnInit ile başlar ve OnDeinit ile sona erer. Bunlar bir tür giriş ve çıkış noktalarıdır. OnInit temeli atar: EventSetTimer aracılığıyla zamanlayıcılar ayarlanır, grafiksel nesneler oluşturulur, dahili yapılar ve harici bileşenler başlatılır. Ayrıca burada hizmet mekanizmalarını başlatmak ve ortamı hazırlamak da uygundur.
int OnInit() { EventSetTimer(30); // poll every 30 seconds return(INIT_SUCCEEDED); }
OnDeinit ise disiplin gerektirir. Oluşturulan her şey doğru şekilde serbest bırakılmalıdır: nesneler silinir, zamanlayıcı EventKillTimer aracılığıyla devre dışı bırakılır, kaynaklar kapatılır. Bu simetriyi göz ardı etmek, çalışma zamanı ortamının kirlenmesine ve fark edilmesi zor aksaklıklara neden olan klasik bir hatadır.
void OnDeinit(const int reason) { EventKillTimer(); // disable the timer when unloading }
OnTick geleneksel olarak Uzman Danışmanın kalbi olarak görülür. Bu kısmen doğrudur. Amacı, belirli bir sembolün piyasa değişikliklerine tepki vermektir. Burası sinyalleri hesapladığınız, giriş koşullarını kontrol ettiğiniz ve pozisyonları yönettiğiniz yerdir. Ancak, temel nokta OnTick'in varlığının zorunlu olmamasıdır. Mantık diğer olaylara taşınırsa, Uzman Danışman onsuz da çalışabilir. Dahası, olgun mimaride OnTick genellikle evrenin merkezi yerine hafif bir yönlendirici haline gelir.
OnTimer birçok kişinin hafife aldığı bir araçtır. Terminal bu olayları belirli bir aralıkta üretir, bu da arka plan görevlerini piyasa görevlerinden ayırmaya olanak tanır. İstatistiklerin toplanması, önbelleklerin güncellenmesi, çeşitli sembollerin sorgulanması, göstergelerin yeniden hesaplanması - tüm bunlar mantıksal olarak zamanlayıcıya sığar. Bu yaklaşım OnTick'i rahatlatır ve özellikle yüksek volatilite dönemlerinde sistem davranışını daha istikrarlı hale getirir. Basit ama önemli bir kuralı unutmayın: zamanlayıcı yalnızca OnInit'te ayarlanmamalı, aynı zamanda OnDeinit'te de mutlaka kaldırılmalıdır.
OnChartEvent etkileşimli uygulamalar dünyasının kapısını açar. Bu grafik arayüzü olayları için bir işleyicidir: fare tıklamaları, tuş basmaları, nesnelerle etkileşimler ve en önemlisi özel olaylar. Kontrol panelleri, düğmeler ve mod değiştirme anahtarları bunun içine yerleştirilir. Burası, diğer programlardan gelen harici sinyallere verilen yanıtın uygulandığı yerdir. Olay odaklı mimari bağlamında, bu artık basit bir kullanıcı arayüzü işleyicisi değil, tam teşekküllü bir iletişim kanalıdır.
OnTradeTransaction bir işlem durumu kontrol noktasıdır. Bir işlemin yalnızca sonucunun kontrol edildiği basitleştirilmiş yaklaşımların aksine, burada tüm MqlTradeTransaction yapısı mevcuttur. Bu, emir yaşam döngüsünün hassas bir şekilde izlenmesine olanak tanır: göndermeden gerçekleştirmeye ve sonraki değişikliklere kadar. Bu ayrıntı düzeyi, yalnızca hedefin değil, aynı zamanda hedefe giden yolun da önemli olduğu karmaşık pozisyon yönetimi senaryolarında özellikle önemlidir.
OnBookEvent daha az kullanılır, ancak belirli görevlerde yeri doldurulamaz. Piyasa derinliğindeki değişikliklere tepki verir ve MarketBookAdd aracılığıyla etkinleştirilir. Burası zaten sadece fiyatın değil, likidite yapısının da önemli olduğu daha incelikli stratejilerin alanıdır. Yüksek frekanslı yaklaşımlarda veya mikro piyasa hareketlerini analiz ederken, bu işleyici önemli bir sinyal kaynağı haline gelir.
Buradaki temel çıkarım basittir: MQL5'teki her program türü kendi olay kümesine sahip olur ve davranışları bunlar aracılığıyla şekillenir. Bu modeli göz ardı etmeye ve her şeyi tek bir işleyiciye indirgemeye çalışmak kaçınılmaz olarak karmaşıklığa yol açar. Aksine, mantığın olaylar arasında uygun şekilde dağıtılması net, öngörülebilir ve ölçeklenebilir bir sistem oluşturur - bu, ilerlemenin olmazsa olmaz temelidir.
Özel olaylar
MQL5'teki özel olaylar, olay mimarisi oluşturmak için en pratik araçlardan biridir. Onların yardımıyla, program artık kendi kendine yeten bir yapıya sahip olmaktan çıkar. Tek bir sistemin düğümlerinin hizmet mesajlarını birbirleriyle paylaştığı kadar doğal bir şekilde, kendi bileşenleri arasında sinyal iletme yeteneği kazanır. Bu, kendi olayımızı programlı olarak belirli bir grafiğe göndermemize ve ardından OnChartEvent içinde işlememize olanak tanıyan EventChartCustom fonksiyonu ile gerçekleştirilir. Bu dahili bir mesajlaşma hattıdır. Özellikle tek bir programın piyasaya yanıt vermesi, arayüzün, hizmet modüllerinin ve işlem mantığının çalışmasını koordine etmesi gereken durumlarda değerlidir.
Bu yaklaşımın temel avantajı, sorumlulukların modüller arasında ayrılmasına yardımcı olmasıdır. Bir bileşen piyasa analizi ile meşgul olabilir. Diğeri - panel durumunu görüntülemeyle. Üçüncüsü - alım-satım eylemlerini yürütmeyle. Dördüncüsü - arka plan hesaplamalarına hizmet etmeyle. Tüm bunları doğrudan çağrılar ve paylaşılan global değişkenlerle birbirine bağlamaya çalışırsak, kod hızla karmaşıklaşmaya başlayacaktır. Bakım, her bir dişlinin diğerleriyle iç içe geçtiği bir mekanizmayı onarmak gibi olacaktır. Özel olaylar bu karışıklığı önler: modül başkasının iç mantığına müdahale etmez, sadece alınması gereken yere düzgün bir sinyal gönderir.
Hedefli olay iletimi yeteneği ayrıca belirtilmeye değerdir. EventChartCustom'da, yalnızca geçerli grafiği değil, aynı zamanda başka bir pencerenin chartID'sini de belirtebiliriz. Bu, Uzman Danışmanların, göstergelerin ve hizmetlerin farklı örnekleri arasında iletişimin yolunu açar. Bu mekanizma özellikle çok sembollü ve çok grafikli sistemlerde kullanışlıdır. Bileşenlerden biri bilgi toplar veya üretir, diğeri ise bu bilgiyi alır ve bir eylem gerçekleştirir. Bu durumda, özel olay, farklı parçaları tek bir mimaride birleştiren bağlantı noktası haline gelir.
Olay değişimi fikri özellikle başlatma aşamasında belirgindir. Bazen, OnInit'i hazırlık eylemleriyle aşırı yüklememiz gerekir: dahili nesneler oluşturma, verileri yükleme, önbellekleri oluşturma, arayüzü yapılandırma, hizmetleri başlatma. Tüm bunlar hemen başlatma içinde yapılırsa, metot ağırlaşır ve program başlangıcını yavaşlatmaya başlar. Ayrıca, başlatma çok uzun sürerse, zaman sınırına takılma riski vardır veya yavaş, hantal bir başlangıç elde edilir. OnInit'te yalnızca minimum miktarda hazırlık bırakmak ve zahmetli kısmı ayrı bir işleyiciye taşıyacak özel bir olay oluşturmak çok daha mantıklıdır. Ardından program hızlı bir şekilde başlar ve ana hazırlık olay modunda gerçekleştirilir.
Bu durumun bir diğer önemi de, MQL5'te bir fonksiyonun uzun süreli çalışmasının diğer olayların işlenmesini engellemesidir. Birkaç küçük fonksiyonu arka arkaya çağırmak sorunu çözmez: zincir tamamlanana kadar hiçbir şey olayların akışına müdahale edemez. Bu özellikle kullanıcı arayüzüne sahip programlarda fark edilir. Kullanıcı panel ile etkileşime girer ve bir yanıt bekler, ancak program uzun bir kod yürüttüğü için yanıt vermez. Bir donma hissi vardır, ancak aslında sistemin kontrolü olay işleyiciye aktaracak zamanı yoktur. Olay modelinin burada mimari açıdan gerekli olmasının nedeni de tam olarak budur. Arayüzü engellemeden veya programı tepkisiz hale getirmeden adımları sırayla çalıştıran bir olay işleyicisine koordinasyonun devredilmesine olanak sağlar.
Bu esnekliğin bir dezavantajı vardır: özel olaylar bir sistem olmadan gönderilemez. Burada net bir protokole ihtiyaç vardır: hangi ID'lerin ne anlama geldiği, mesaj kodunun nerede saklandığı, bir olayın uygulamaya ait olup olmadığının nasıl belirleneceği. Genellikle, özel ID aralıkları bu amaçla ayarlanır, dizge parametrelerinde net etiketler kullanılır ve olayın gerçekten bu modüle yönelik olup olmadığından emin olunur. Aksi takdirde, mantığı mimari kargaşaya sürükleyen alakasız bir sinyali kolayca alabiliriz.
Genellikle göz ardı edilen bir başka kural daha vardır: gerçek bir ihtiyaç olmadıkça çok sık olay oluşturmayın. Terminal olay kuyruğu gereksiz gürültüden hoşlanmaz. Olaylar çok sık gönderilirse, mesaj kuyruğu aşırı yüklenir ve sinyal işleme bozulur. Yalnızca önemli durumları iletmek daha iyidir: verilerin hazır olduğu bilgisi, bir sinyalin ortaya çıkması, arayüzü güncellemek için bir komut, eylem yürütme onayı. O zaman olaylar kaotik bir telgraf akışı yerine düzenli notlar gibi işlev görecektir.
Bu nedenle özel olaylar olgun bir sistemde özellikle değerlidir. Modülleri katı bağlantılar olmadan birbirine bağlar, net etkileşim protokolleri oluşturmaya izin verir ve MQL5'i tam teşekküllü olay odaklı geliştirme için bir ortama dönüştürür.
Hizmetler
MQL5'teki hizmet bir arka plan terminal hizmetidir. Grafik yok, sembol bağlama yok, tik bekleme yok. Başlatıldıktan sonra, ne yapması gerektiğini hatırlatmaya ihtiyaç duymayan işini gayet iyi yapan bir görevli gibi kendi başına çalışır.
Tüm mantık OnStart fonksiyonunda bulunur. Çoğu zaman, içeride bir döngü düzenlenir: durumu kontrol etme, görevi yerine getirme, Sleep aracılığıyla duraklama ve ardından bir sonraki adım. Bu ritim basit ve güvenilirdir. Piyasa olayları gerektirmez ve grafik etkinliğine bağlı değildir. Hizmet kendi akışını belirler.
Pratik faydalar hızla ortaya çıkar. Harici bir kaynaktan düzenli olarak veri indirmeniz gerekiyorsa, hizmet bunu halledebilir. Özel bir sembol için tikler oluşturmanız mı gerekiyor? Hizmet bunu da yapabilir. Birkaç saniyede bir sinyal göndermeniz veya sistem durumunu güncellemeniz mi gerekiyor? Bu da yine hizmetin işidir. Tekrarlanan her şey, piyasa gürültüsüne bağlı olmaması gereken her şey, mantıksal olarak buraya getirilmelidir.
Hizmeti özellikle kullanışlı kılan bir ayrıntı daha vardır. Manuel olarak durdurmazsanız, terminali bir sonraki başlatışınızda otomatik olarak başlayacaktır. Bu, onu rutin işler için bir araca dönüştürür: başlangıçta durumu kontrol etme, verileri hazırlama, senkronizasyon - tüm bunlar kullanıcı müdahalesi olmadan yapılabilir. Bir kez ayarlandıktan sonra, hizmet her gün saat gibi çalışır.
Mimaride bu çok sağlıklı görünüyor. Grafikteki Uzman Danışman piyasaya tepki verir. Aynı zamanda arayüz kullanıcı odaklıdır. Bu arada hizmet arka planda sessiz ve düzenli bir şekilde çalışır. Telaş yok, OnTick aşırı yüklemesi yok. Bu rol dağılımı sistemi gereksiz stresten kurtarır ve daha istikrarlı hale getirir. Her bir parça görevini yerine getirdiğinde, tüm yapı gözle görülür şekilde daha temiz çalışmaya başlar.
Programlar arası iletişim
Bir alım-satım çözümü birden fazla bileşenden oluştuğunda, bir mesajlaşma mekanizmasına ihtiyaç duyar. Bir modül veri toplayabilir. İkincisi onları panel üzerinde gösterir. Üçüncüsü bir işlem kararı verir. Dördüncüsü arka plan mantığını işler. Ve her biri kendi evreninde yaşamaya başlarsa, sonuç bir mimari değil, bayraklar, global değişkenler ve rastgele kontrollerden oluşan bir karmaşa olacaktır. Bu nedenle olay odaklı yapı burada özellikle işe yarıyor.
Olay tabanlı mimarinin temiz versiyonu, göstergelerin sinyal kaynağı haline geldiği durumdur; ancak bu, geleneksel şekilde (bir arabellek istedi - bir değer aldı) değil, tam teşekküllü olay üreteçleri olarak gerçekleşir.
Fikir basittir. Her bir gösterge hafif bir adaptörle donatılır. Dahili olarak, tam olarak tek bir şey yapar: bir sinyalin oluşmasını izler ve koşul karşılandığında özel bir olay gönderir. Her şey hazır. Dışarıdan sürekli istek yok, gereksiz gürültü yok.
int OnCalculate(const int32_t rates_total, const int32_t prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int32_t &spread[]) { if(prev_calculated==rates_total) return prev_calculated; //--- if(BarsCalculated(handle) < rates_total) return(prev_calculated); vector<double> sig, main; if(!main.CopyIndicatorBuffer(handle, 0, 1, 2) || !sig.CopyIndicatorBuffer(handle, 1, 1, 2)) return(prev_calculated); if(sig[0] > main[0] && sig[1] <= main[1]) { if(!EventChartCustom(ChartID(), BuyID, MagicNumber, main[0], IndComment)) return(prev_calculated); } if(sig[0] < main[0] && sig[1] >= main[1]) { if(!EventChartCustom(ChartID(), SellID, MagicNumber, main[0], IndComment)) return(prev_calculated); } //--- return value of prev_calculated for the next call return(rates_total); }
Örnek olarak, iki klasik göstergeyi ele alalım - MACD ve RSI. Her biri kendi zaman diliminde bağımsız olarak çalışır. Sadece yeni çubuğu işlerler. MACD, histogram ile sinyal çizgisinin çaprazlamasını kontrol eder. RSI, aşırı alış ve aşırı satış seviyelerinin geçilmesini kontrol eder. Bir sinyal oluştuğunda, önceden tanımlanmış özel olaylar oluştururlar. Bu temel bir noktadır: gösterge arabellekte bir sinyal saklamaz, bir olay üretir.
Daha sonra Uzman Danışman devreye girer, ancak tamamen farklı bir rolde. Her tikte göstergeleri sorgulamaz. Geçmişe gitmez, arabellekleri senkronize etmez, bir sinyal olup olmadığını tahmin etmeye çalışmaz. Sadece OnChartEvent içindeki olayları dinler.
void OnChartEvent(const int32_t id, const long &lparam, const double &dparam, const string &sparam) { //--- if(lparam != MagicNumber) return; switch(id - CHARTEVENT_CUSTOM) { case MACD1Buy: indSignals[0] = 1; break; case MACD1Sell: indSignals[0] = -1; break; case MACD2Buy: indSignals[1] = 1; CloseSellSignal = true; CloseBuySignal = false; break; case MACD2Sell: indSignals[1] = -1; CloseBuySignal = true; CloseSellSignal = false; break; case RSI1CrossOverBoughtDown: indSignals[2] = -1; break; case RSI1CrossOverSoldUp: indSignals[2] = 1; break; case RSI2CrossOverBoughtDown: indSignals[3] = -1; CloseBuySignal = true; CloseSellSignal = false; break; case RSI2CrossOverSoldUp: indSignals[3] = 1; CloseSellSignal = true; CloseBuySignal = false; break; } BuySignal = true; SellSignal = true; for(uint i = 0; i < indSignals.Size() && (BuySignal || SellSignal); i++) { BuySignal = BuySignal && (indSignals[i] == 1); SellSignal = SellSignal && (indSignals[i] == -1); } }
Uzman Danışman gelen olayları işler. Hiçbir şey gelmezse, hiçbir şey yapmaz.
Bu, yükün yapısını önemli ölçüde değiştirir. Göstergelerin yoklandığı klasik yapıda, her tikte aynı şey olur - arabellekler talep edilir, değerler kontrol edilir. Bazen, sinyali kaçırmamak için geçmişe bir göz atmamız da gerekebilir. Bu, özellikle birkaç gösterge ve farklı zaman dilimlerinde zorlaşır. Sinyaller arasında her zaman bir zaman boşluğu vardır ve onu yakalamak için sürekli geriye bakmamız gerekir.
Olay modeli basitçe bu fazla çabaları ortadan kaldırır. Her sinyal hazır bir olaydır. Bunu aramaya, yeniden hesaplamaya veya geriye dönük olarak yeniden oluşturmaya gerek yoktur. Ortaya çıkış anında gelir.
Sonuç, düzgün ve hızlı bir diyagramdır:
- Gösterge sadece kendi hesaplamasından ve kendi sinyalinden sorumludur;
- Bir olay durum değişikliği anını kaydeder;
- Uzman Danışman sinyalleri toplar ve bir karar verir.
Bu durum özellikle çok göstergeli stratejilerde kendini göstermektedir. Örneğin, bir sinyal M5'ten, diğeri M30'dan gelir. Klasik modelde, verileri senkronize etmek, gecikmeleri dikkate almak ve geçmişi kontrol etmek gerekir. Burada her şey daha basittir: her gösterge doğru anda kontrol edilir, Uzman Danışman durumu kaydeder ve tüm koşul kümesini bekler.
Performans açısından bu, gözle görülür bir fayda sağlar. Göstergelere sürekli referanslar yoktur, her tikte boş kontroller yoktur. OnTick kolay ve hızlı hale gelir, bu da piyasaya verilen tepkinin daha hızlı olmasını sağlar. Alım-satımda bu sadece bir teori değil, tamamen pratik bir avantajdır: daha az gecikme, daha net mantık, daha doğru uygulama.
void OnTick() { //--- if(BuySignal) { cSymbol.Refresh(); cSymbol.RefreshRates(); if(!cTrade.Buy(InpLot, cSymbol.Name(), cSymbol.Ask(), cSymbol.Ask() - SL * cSymbol.Point(), cSymbol.Ask() + TP * cSymbol.Point(), "Event Example")) { PrintFormat("Error open Buy position: %d", GetLastError()); return; } BuySignal = false; ArrayFill(indSignals, 0, indSignals.Size(), 0); } //--- if(SellSignal) { cSymbol.Refresh(); cSymbol.RefreshRates(); if(!cTrade.Sell(InpLot, cSymbol.Name(), cSymbol.Bid(), cSymbol.Bid() + SL * cSymbol.Point(), cSymbol.Bid() - TP * cSymbol.Point(), "Event Example")) { PrintFormat("Error open Sell position: %d", GetLastError()); return; } SellSignal = false; ArrayFill(indSignals, 0, indSignals.Size(), 0); } //--- if(CloseBuySignal) { if(cPosition.SelectByMagic(cSymbol.Name(), MagicNumber) && cPosition.PositionType() == POSITION_TYPE_BUY) { if(!cTrade.PositionClose(cPosition.Ticket())) { PrintFormat("Error close Buy position: %d", GetLastError()); return; } } } //--- if(CloseSellSignal) { if(cPosition.SelectByMagic(cSymbol.Name(), MagicNumber) && cPosition.PositionType() == POSITION_TYPE_SELL) { if(!cTrade.PositionClose(cPosition.Ticket())) { PrintFormat("Error close Sell position: %d", GetLastError()); return; } } } //--- }
Aynı derecede önemli olan şey, kodun daha şeffaf hale gelmesidir. Sinyal artık yorumlanması gereken bir arabellek değeri değildir ve net bir olaya dönüşür: oldu - kaydedildi - işlendi. Mimari, sistemin hem okunabilirliğini hem de davranışını doğrudan iyileştirdiğinde durum tam olarak budur.
Uzman Danışmanı MetaTrader 5 strateji sınayıcıda çalıştırmak ilginç bir tablo ortaya çıkardı. Belirli bir karakteri olan bir sistemimiz var - aktif olarak kazandırıyor, ancak aynı zamanda riskleri makul sınırlar içinde tutuyor.

1000.0 USD başlangıç bakiyesiyle elde edilen sonuç 2427.52 USD oldu; bu da 3 yılda %143'lük bir artışa denk geliyor. Sonuç, özellikle ılımlı düşüş göz önüne alındığında güçlüdür. Varlık eğrisi düzgün görünüyor: büyüme kademeli, geri dönüşler var, ancak uzun süreli düşüşler yok. Maksimum düşüş yaklaşık %12-13, Düzelme Faktörü ise yaklaşık 4.6'dır - sistem düşüşlerden takılıp kalmadan kurtulabilmektedir. Bu, sürdürülebilirliğin önemli bir işaretidir.
Geleneksel kar mekaniği işliyor. Toplamda 18 işlem ve %50 kazanç oranı. Ancak ortalama kar, ortalama zarardan neredeyse 4 kat daha fazladır. Bu da Kar Faktörünün 3.9 seviyesine ulaşmasını sağlamıştır. Strateji sıklıktan değil, sinyallerin kalitesinden ve pozisyonların tutulmasından para kazanmaktadır. Ancak burada bir sınırlama söz konusudur. İşlem sayısının az olması istatistikleri kırılgan hale getirmektedir.
Varlık eğrisi, herhangi bir kaotik sıçrama olmaksızın oldukça düzgündür. Bu da dolaylı olarak sinyallerin rastgele olmadığını teyit etmektedir.
Ve burada önemli bir mimari nokta vardır. Olay odaklı model, göstergelerin sürekli sorgulanmasını ortadan kaldırır. Uzman Danışman yalnızca sinyale tepki verir. Daha az gereksiz operasyon, daha hızlı yanıt anlamına gelir. Sınayıcıda bu sadece bir nüanstır, ancak canlı alım-satımda bir avantaj haline gelir.
Sonuç basit. Burada iyi bir risk/getiri oranına ve iyi tasarlanmış bir mimariye sahip bir sinyal modelimiz var. Ama şimdilik bu bir prototip. Örneklem dışı testlere ve genişletilmiş istatistiklere ihtiyaç vardır.
Tipik hatalar
Bir mimari olay odaklı hale geldiğinde, kod daha temiz ve daha hızlı hale gelir. Ancak aynı zamanda hataların niteliği de değişmektedir. Bunlar artık yüzeyde değil, işleyiciler arasındaki bağlantılarda ve kaynaklarla çalışma disiplininde kendini göstermektedir.
İlk ve en acil sorun, aynı eski aşırı yüklü OnTick'tir. Olay modeliyle tanıştıktan sonra bile, her ihtimale karşı mantığın bir kısmını orada bırakma eğilimi vardır. Sonuç bir melezdir: zamanlayıcılar ve olaylar vardır, ancak işin büyük kısmı hala tek bir metot tarafından gerçekleştirilir. Bu taviz bizi hızlı bir şekilde başa döndürür: ağır, bakımı kötü kod. Deneyimlerimiz gösteriyor ki, eğer rolleri zaten paylaştırmışsak, bunu sonuna kadar götürmemiz gerekiyor.
Bir sonraki hata katmanı kaynak yönetimiyle ilgilidir. Olay modeli daha fazla varlık içerir: zamanlayıcılar, nesneler, abonelikler, yardımcı yapılar. Ve eğer dikkatli bir şekilde yönetilmezlerse, sistem gürültü yapmaya başlar. Unutulmuş EventKillTimer, kaldırılmamış grafiksel öğeler, bırakılmış global değişkenler - tüm bunlar programı hemen bozmaz, ancak öngörülebilirliğini kademeli olarak azaltır. Sonuç olarak, klasik bir durumla karşılaşırız: hata, meydana geldiği yerden başka bir yerde görünür.
void OnDeinit(const int reason) { //--- for(uint i = 0; i < handles.Size(); i++) if(handles[i] != INVALID_HANDLE) IndicatorRelease(handles[i]); }
Olayların doğası özel bir dikkat gerektirmektedir. İşleyiciler sırayla yürütülür ve bir olaydaki uzun bir işlem diğerlerini engeller. Bu durum özellikle arayüzde göze çarpmaktadır: kullanıcı panel ile etkileşime girer, ancak program yanıt vermez. Hesaplamalarla meşguldür. Burada da mimarinin ana fikri ortaya çıkmaktadır: yürütmeyi aşamalara bölmek ve iş parçacığını gerçekten gerekenden daha uzun süre meşgul etmemek.
Bir sistemde birden fazla olay kaynağı göründüğünde (göstergeler, bir panel, bir hizmet), tanımlama disiplini ön plana çıkar. Nesne adları ve olay kodları için net kurallar getirmezsek, karışıklık başlar: sinyaller çakışır, işleyiciler yanlış şeylere tepki verir. Önekleri ve ID aralıklarını içeren basit bir kural önemsiz gibi görünse de, sistem büyümeye başladığında düzeni sağlayan şeydir.
#define MACD1Buy 1 #define MACD1Sell 2 #define MACD2Buy 3 #define MACD2Sell 4 #define RSI1CrossOverBoughtUp 5 #define RSI1CrossOverBoughtDown 6 #define RSI1CrossOverSoldUp 7 #define RSI1CrossOverSoldDown 8 #define RSI2CrossOverBoughtUp 9 #define RSI2CrossOverBoughtDown 10 #define RSI2CrossOverSoldUp 11 #define RSI2CrossOverSoldDown 12
Ve son olarak, en zor an programın başlangıcıdır. Olay odaklı bir modelde, olaylar neredeyse anında, hatta başlatma tam olarak tamamlanmadan önce bile gelebilir. Yapılar bu noktada hazır değilse, boş verilere veya yanlış bir duruma erişmek kolaydır. Bu nedenle, başlatma ya işleme başlamadan önce kesinlikle tamamlanmalı ya da - genellikle daha uygun olan - aynı özel olaylar aracılığıyla hazır olma kontrolü ile aşamalara ayrılmalıdır.
Sonuç olarak, olayların kendilerinin bir sistemi güvenilir kılmadığı açıktır. Onlar sadece araçlar sağlar. Güvenilirlik, bu araçlara mühendislik disiplini eklendiğinde ortaya çıkar. Ve eğer varsa, olay odaklı mimari sadece kullanışlı bir teknik olmaktan çıkar - istikrarlı ve öngörülebilir bir alım-satım sisteminin temeli haline gelir.
Sonuç
Program mantığı olaylar üzerine inşa edildiğinde, platform sadece Uzman Danışmanlar için bir ortam olmaktan çıkar ve tam teşekküllü uygulama çözümlerinin temeli haline gelir. Bu modelde, programlama zaten klasik bir masaüstü uygulamasının geliştirilmesine benzemektedir: düğmeler ve paneller içeren bir arayüz görünür, arka plan hizmetleri başlatılır ve farklı olay türleri için ayrı işleyiciler oluşturulur. Her şey yerli yerinde ve bu yüzden sistem bakım konusunda gözle görülür şekilde daha istikrarlı.
Olay tabanlı yaklaşım, özellikle geleneksel bir Uzman Danışmanın artık tüm durumlar için tek bir dosya rolüyle başa çıkamadığı koşullarda değerlidir. Durumdaki anlamsal değişikliklere yanıt veren çok sembollü asistanlar, esnek kontrol panelleri ve karmaşık denetleyiciler oluşturmaya olanak tanır. Bu mimariye yeni işlevler eklemek daha kolaydır: tüm kodu elden geçirmek zorunda kalmadan sadece başka bir olay veya başka bir işleyici eklememiz gerekir.
Olay odaklı mimarinin temel avantajı budur: esnekliği kaybetmeden düzen sağlar. İşleyicilerin, hizmetlerin ve iletişim kanallarının uygun şekilde ayrılması, güvenilir, ölçeklenebilir ve gerçekten dinamik alım-satım sistemlerinin oluşturulmasına olanak tanır. Burada MQL5, geleneksel Uzman Danışman formatının ötesine geçerek, yalnızca piyasa tepkisinin değil, aynı zamanda tüm programın olgun mühendislik organizasyonunun da önemli olduğu karmaşık alım-satım uygulamaları için bir ortam haline gelir.
Makalede kullanılan programlar
| # | Ad | Tür | Açıklama |
|---|---|---|---|
| 1 | EventExample.mq5 | Uzman Danışman | Olay işleme Uzman Danışmanı |
| 2 | EventMACD.mq5 | Gösterge | Çizgiler çaprazladığında olay üreten MACD göstergesi |
| 3 | EventRSI.mq5 | Gösterge | Seviyeler geçildiğinde olay üreten RSI göstergesi |
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/22383
Uyarı: Bu materyallerin tüm hakları MetaQuotes Ltd'ye aittir. Bu materyallerin tamamen veya kısmen kopyalanması veya yeniden yazdırılması yasaktır.
Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler
MQL5'te CPU'dan GPU'ya: Araştırma, Optimizasyon ve Formasyon Analizinin Hızlandırılması için Pratik Bir OpenCL Çerçevesi
İşte Karışınızda Yeni MetaTrader 5 ve MQL5
MetaTrader 5 ve MQL5 Ekonomik Takvim: Haberler Nasıl Tekrarlanabilir Bir Alım-Satım Sistemine Dönüştürülür?
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Web sitesi politikasını ve kullanım şartlarını kabul edersiniz
Danışman içinde tampon içermeyen göstergelerle çalışmak – muhtemelen örnek olarak uygun olacaktır.
Örnekte, sembol verilerinin sıkı bir şekilde güncellenmesi gerçekleştiriliyor (MQL_TESTER kullanılması iyi olurdu).
Ancak, sinyal ve tik olayları aracılığıyla hesaplanan verilerin güncelliği kontrol edilmiyor. Ve bu gerçek bir sorundur.
Bu sorun, asenkron OrderSend komutlarıyla hafifletilebilir, ancak tamamen çözülemez. Bu nedenle, böyle bir örnekte bile ChartEvent olayında, olayın oluştuğu tik verilerinin ilave olarak aktarılması gerekir.