
Alım Satım Robotunun Prototipi
Giriş
Herhangi bir alım satım sisteminin yaşam döngüsü, açılış ve kapanış pozisyonlarına indirgenir. Bu şüpheye yer bırakmayacak kadar kesindir. Ancak algoritma gerçekleştirme söz konusu olduğunda, burada programcılar kadar çok sayıda görüş var. Herkes aynı sorunu kendi yöntemiyle ancak aynı nihai sonuçla çözebilecektir.
Yıllar içinde programlama uygulamasında, uzmanların mantığını ve yapısını oluşturmaya yönelik çeşitli yaklaşımlar denenmiştir. Şu anda tüm kodlarda kullanılan açık bir kalıp şablonun oluşturulduğu söylenebilir.
Bu yaklaşım %100 evrensel değildir, ancak uzmanların mantığını tasarlama yönteminizi değiştirebilir. Ayrıca konu, uzmanı kullanmak istediğiniz emirlerle çalışma yeteneklerinin hangileri olduğu değildir. Bütün mesele - bir alım satım modelini yaratma ilkesidir.
1. Alım Satım Sistemlerinin Tasarım İlkeleri ve Olay Kaynağı Türleri
Çoğunluk tarafından kullanılan algoritmanın tasarımına temel yaklaşım, açılışından kapanışına kadar bir pozisyonu izlemektir. Bu doğrusal bir yaklaşımdır. Kodda değişiklik yapmak istiyorsanız; çok sayıda koşul ortaya çıktığı ve kod yeni analiz dalları biriktirdiği için bu genellikle büyük komplikasyonlara yol açar.
Bir alım satım robotunu modellemek için en iyi çözüm "koşullara hizmet etmektir". Temel ilke bu uzman durumunun, pozisyonlarının ve emirlerinin nasıl ortaya çıktığını değil, şimdi onlarla ne yapmamız gerektiğini analiz etmektir. Bu temel ilke, alım satım yönetimini temelden değiştirmekte ve kod geliştirmeyi basitleştirmektedir.
Bunu daha ayrıntılı olarak düşünün.
1.1. "Hizmet Koşulları" İlkesi
Daha önce de belirtildiği gibi uzmanın, mevcut duruma nasıl ulaşıldığını bilmesine gerek yoktur. Ortamına göre (parametre değerleri, saklanan emir özellikleri vb.) şimdi onunla ne yapacağını bilmelidir.
Bu ilke, uzmanın döngüden döngüye (özellikle - tikten tike) ilerlediği gerçeğiyle doğrudan ilgilidir ve önceki tikteki emirlerde ne olduğu konusunda endişelenmemelidir. Bu nedenle, emirleri yönetmek için olaya dayalı bir yaklaşım kullanmalısınız. Yani mevcut tik üzerinde uzman, bir sonraki tik hakkında karar vermek için başlangıç noktası olan durumunu kaydeder.
Örneğin, bekleyen tüm uzman emirlerini kaldırmalı ve ancak bundan sonra göstergeleri analiz etmeye ve yeni emirler vermeye devam etmelisiniz. Gördüğümüz kod örneklerinin çoğu "while (true) {try to remove}" döngüsünü kullanır veya "while (k < 1000) {try to remove; k++;}" döngüsünü biraz daha yumuşaktır. Hata analizi olmadan bir kerelik kaldır komutunun çağrıldığı varyantı atlayacağız.
Bu yöntem doğrusaldır, uzmanı belirsiz bir süre "askıda tutar".
Bu nedenle, bir uzmanı döngüye sokmak değil, emirleri kaldırmak için emri saklamak daha doğru olacaktır, böylece her yeni tikte bekleyen emir silinmeye çalışılırken bu emir kontrol edilecektir. Bu durumda bir uzman, durum parametrelerini okurken o anda emirleri silmesi gerektiğini bilir. Ayrıca onları kaldırmaya çalışacaktır. Bir alım satım hatası meydana gelirse uzman daha fazla analiz yapılmasını engelleyecek ve bir sonraki döngüden önce çalışacaktır.
1.2. Tasarımın İkinci Ana İlkesi - dikkate alınan pozisyon yönü (Al/Sat), para birimi ve grafikten mümkün olan maksimum soyutlamadır. Tüm uzman fonksiyonları, gerçekten kaçınılamayan nadir durumlarda (örneğin, özelliklerden farklı kaçınma seçenekleri olmasına rağmen, açık pozisyon için fiyatın olumlu büyümesini düşündüğünüzde) yönün veya sembolün analiz edileceği şekilde uygulanmalıdır. Her zaman bu tür "düşük seviyeli" tasarımlardan kaçınmaya çalışın. Bu, kodu ve fonksiyon yazma sürecini en az iki kat azaltacaktır. Ayrıca bunları "ticaretten bağımsız hale getirecektir.
Bu ilkenin uygulanması emir türlerinin, sembol parametrelerinin ve bağımlı hesaplanan parametrelerin açık analizini makro fonksiyonlarla değiştirmektir. Sonraki makalemizde bu uygulamayı detaylı olarak ele alacağız.
1.3. Üçüncü ilke – algoritmanın mantıksal sözcük birimlerine bölünmesi (bağımsız modüller)
Pratikte en iyi yaklaşımın uzman operasyonların bireysel fonksiyonlara ayrılması olduğunu söyleyebiliriz. Uzmanın, yazma algoritmasının tamamını tek bir fonksiyonda yazmanın zor olduğunu ve sonraki analiz ve düzenlemeyi zorlaştırdığını kabul edeceğinizi düşünüyorum. Bu yüzden bunu, artık ortamınız üzerinde neredeyse tam kontrol sağlayan MQL5'te yapmamalıyız.
Bu nedenle, mantıksal sözcük birimleri (örneğin emirlerin açılması, takip edilmesi, kapatılması) çevresel parametrelerin ve olayların tam analizi ile birbirinden ayrı olarak uygulanmalıdır. Bu yaklaşım sayesinde uzman, tasarımda esnek hale gelir. Mevcut olanlara dokunmadan kolayca yeni bağımsız modüller ekleyebilir veya ana kodu değiştirmeden mevcut modülleri devre dışı bırakabilirsiniz.
Uzman sistem için olay kaynakları şunlardır:
1. Göstergeler. Bir örnek - gösterge çizgilerinin değerlerinin, bunların kesişimlerinin, kombinasyonlarının vb. analizidir. Ayrıca göstergeler şunlar olabilir: şimdiki zaman, İnternet'ten elde edilen veriler vb. Çoğu durumda gösterge olayları, emirlerin açılış ve kapanışını belirtmek için kullanılır. Bunların ayarlamaları için daha azı geçerlidir (genellikle Takip Eden Zarar Durdurma (Trailing Stop Loss) veya gösterge için bekleyen emir).
Örneğin, bir göstergenin pratik uygulamasına, pozisyonun kesişme yönüne daha fazla açılmasıyla hızlı ve yavaş MA'nın kesişimini analiz eden bir uzman denilebilir.
2. Mevcut emirler, pozisyonlar ve durumları. Örneğin, mevcut zarar veya kâr büyüklüğü, pozisyonların veya bekleyen emirlerin varlığı/yokluğu, kapatılan pozisyonun karı vb. Bu olayların pratik uygulaması, gösterge olaylarından daha fazla ilişki seçeneği olduğu için çok daha geniş ve daha çeşitlidir.
Yalnızca alım satım olayına dayanan bir uzmanın en basit örneği, mevcut pozisyonun ortalamasını almak ve bunu istenen kâra dönüştürmek için yeniden doldurmaktır. Yani, mevcut pozisyondaki zararın varlığı, yeni bir ortalama emri verme olayı olacaktır.
Alternatif olarak örneğin, Takip Eden Zarar Durdurma (Trailing Stop Loss). Bu fonksiyon, bir önceki Zararı Durdur (Stop Loss) değerinden belirli sayıda puan için fiyat kâra geçtiğinde bir olayı kontrol eder. Sonuç olarak uzman, fiyattan sonra Zararı Durdur (Stop Loss) emrini çeker.
3. Dışsal olaylar. Böyle bir olay genellikle tamamen uzman sistemlerde gerçekleşmese de genel olarak bir karar vermek için göz önünde bulundurulmalıdır. Buna emirlerin, pozisyonların ayarlanması, alım satım hatalarının işlenmesi, grafik olaylarının işlenmesi (nesnelerin taşınması/oluşturulması/silinmesi, düğmelere basılması vb.) dahildir. Genel olarak bunlar, geçmişte doğrulanamayan ve ancak uzman çalıştığında meydana gelen olaylardır.
Bu tür uzmanların çarpıcı bir örneği, grafik alım satım kontrolüne sahip alım satım bilgisi sistemleridir.
2. CExpertAdvisor temel sınıfı – uzman yapıcı
Alım satım uzmanının işi ne olacak? MQL-program etkileşimlerinin genel şeması aşağıdaki şemada gösterilmektedir.
Şekil 1. MQL-program öğeleri etkileşimlerinin genel şeması
Şemadan da görebileceğiniz gibi, önce çalışma döngüsüne giriş gelir (bu bir tik veya zamanlayıcı sinyali olabilir). İlk bloktaki bu aşamada bu tik, işlenmeden filtrelenebilir. Bu, uzmanın her tik üzerinde çalışmasına gerek olmadığı, ancak yalnızca yeni çubukta veya uzmanın çalışmasına izin verilmediği durumlarda yapılır.
Daha sonra program ikinci bloğa geçer - emirler ve pozisyonlarla çalışma modülleri, ancak o zaman modüllerden olay işleme blokları çağrılır. Her modül sadece ilgili olayı sorgulayabilir.
Bu sıralamaya doğrudan mantıklı şema denilebilir, çünkü önce uzmanın NE yapacağını (hangi olay işleme modüllerinin kullanıldığını) belirler ve ancak o zaman NASIL ve NEDEN yapacağını (olay sinyallerini alarak) uygular.
Doğrudan mantık, dünya algımız ve evrensel mantıkla tutarlıdır. Ne de olsa kişi önce somut kavramları düşünür, sonra bunları özetler ve ardından aralarındaki ilişkileri sınıflandırır ve tanımlar.
Tasarım uzmanları bu konuda bir istisna değildir. Öncelikle bir uzmanın ne yapması gerektiği (pozisyonları açıp kapatmak, koruyucu stopu çekmek) bildirilir ve ancak o zaman hangi olaylarda ve nasıl yapması gerektiği belirtilir. Ancak her durumda tersi olmaz: sinyali alın ve nerede ve nasıl işleneceğini düşünün. Bu ters mantıktır ve sonuç olarak çok sayıda koşul dalı içeren hantal kodlar alacağınız için kullanmamak daha iyidir.
Bir ters ve doğrudan mantık örneği verelim. RSI sinyali ile açılış/kapanış yapın.
- Ters mantıkta uzman, gösterge değerini alarak başlar ve ardından sinyalin yönünü ve pozisyonla ne yapmanız gerektiğini kontrol eder: Alış pozisyonu açmak ve Satış pozisyonunu kapatmak veya tam tersi - Satışı açmak ve Alışı kapatmak. Yani giriş noktası sinyali elde etmek ve analiz etmek içindir.
- Doğrudan mantıkta her şey zıttır. Uzmanın iki açma ve kapama pozisyonu modülü vardır ve bu modülleri yürütmek için koşulları kontrol eder. Yani açma modülüne girdikten sonra uzman, gösterge değerini alır ve açma sinyali olup olmadığını kontrol eder. Daha sonra emir kapatma modülüne girildikten sonra uzman, bunun pozisyonu kapatma sinyali olup olmadığını kontrol eder. Yani, giriş noktası yoktur - bağımsız olarak çalışan sistem durumu analizi modülleri vardır (tasarımın ilk ilkesi).
Şimdi, uzmanı karmaşıklaştırmak istiyorsanız, ikinci varyantı kullanmak birinciden çok daha kolay olacaktır. Yeni bir olay işleme modülü oluşturmak yeterli olacaktır.
İlk varyantta, sinyal işlemenin yapısını gözden geçirmeniz veya ayrı bir fonksiyon olarak yapıştırmanız gerekecek.
Bu yaklaşımı daha iyi anlamak için burada dört farklı uzman bağlamında farklı çalışma şemaları verilmiştir.
Şekil 2. Uzman uygulama örnekleri
a). Yalnızca bazı göstergelerin sinyallerine dayanan uzman Sinyal değiştiğinde pozisyonları açıp kapatabilir. Örnek - bir MA uzmanı.
b). Grafik alım satım kontrolü konusunda uzman.
c). Göstergelere dayalı ancak Takip Eden Zarar Durdurma (Trailing Stop Loss) ve çalışma süresinin eklendiği uzman. Örnek - MA göstergesi tarafından trende açılma pozisyonu ile haberlerin scalping’i.
d). Pozisyonların ortalaması ile göstergeleri olmayan uzman. Yeni bir çubuk açarken pozisyon parametrelerini yalnızca bir kez doğrular. Örnek - ortalama uzmanı.
3. Uzman Sınıfının Uygulanması
Gelecekteki tüm uzmanların temeli olacak, yukarıda belirtilen tüm kuralları ve gereksinimleri kullanarak bir sınıf oluşturun.
CExpertAdvisor sınıfında olması gereken minimum işlevsellik aşağıdaki gibidir:
1. Başlatma:
- Kayıt göstergeleri
- Parametrelerin başlangıç değerlerini ayarlama
- Gerekli sembole ve zaman dilimine göre ayarlama
2. Sinyal Alma Fonksiyonları
- İzin verilen çalışma süresi (işlem aralıkları)
- Pozisyonları veya emirleri açmak/kapatmak için sinyali belirleme
- Filtreyi belirleme (trend, zaman vb.)
- Zamanlayıcıyı Başlat/Durdur
3. Servis Fonksiyonları
- Açık fiyatı, SL ve TP seviyelerini, emir hacmini hesaplama
- Alım satım isteklerini gönderme (aç, kapat, değiştir)
4. Alım Satım Modülleri
- Sinyalleri ve filtreleri işleyin
- Pozisyonları ve emirleri kontrol etme
- Uzman fonksiyonlarda çalışma: OnTrade(), OnTimer(), OnTester(), OnChartEvent().
5. Sonlandırma
- Çıktı mesajları, raporlar
- Grafiği temizleme, göstergeleri kaldırma
Sınıfın tüm fonksiyonları üç gruba ayrılmıştır. İç içe fonksiyonların genel şeması ve açıklamaları aşağıda sunulmuştur.
Şekil 3. Bir uzmanın iç içe fonksiyonlarının şeması
1. Makro Fonksiyonlar
Bu küçük fonksiyon grubu, emirleri (açılış ve stoplar) ayarlamak için emir türleri, sembol parametreleri ve fiyat değerleri ile çalışmanın temelidir. Bu makro fonksiyonlar tasarımın ikinci ilkesini sağlar - soyutlama. Uzman tarafından kullanılan sembol bağlamında çalışırlar.
Dönüştürme türlerinin makro fonksiyonları, piyasanın yönü ile birlikte çalışır - alış veya satış. Bu nedenle, kendi sabitlerinizi oluşturmamak için mevcut olanları kullanmak daha iyidir - ORDER_TYPE_BUY ve ORDER_TYPE_SELL. Makro kullanmanın bazı örnekleri ve çalışmalarının sonuçları burada sunulmaktadır.
//--- Type conversion macro long BaseType(long dir); // returns the base type of order for specified direction long ReversType(long dir); // returns the reverse type of order for specified direction long StopType(long dir); // returns the stop-order type for specified direction long LimitType(long dir); // returns the limit-order type for specified direction //--- Normalization macro double BasePrice(long dir); // returns Bid/Ask price for specified direction double ReversPrice(long dir); // returns Bid/Ask price for reverse direction long dir,newdir; dir=ORDER_TYPE_BUY; newdir=ReversType(dir); // newdir=ORDER_TYPE_SELL newdir=StopType(dir); // newdir=ORDER_TYPE_BUY_STOP newdir=LimitType(dir); // newdir=ORDER_TYPE_BUY_LIMIT newdir=BaseType(newdir); // newdir=ORDER_TYPE_BUY double price; price=BasePrice(dir); // price=Ask price=ReversPrice(dir); // price=Bid
Uzmanları geliştirirken makro, işlenen yönü belirtmemenize izin verir ve daha kompakt kod oluşturmanıza yardımcı olur.
2. Servis Fonksiyonları
Bu fonksiyonlar, emirler ve pozisyonlarla çalışmak üzere tasarlanmıştır. Makro fonksiyonu gibi onlar da düşük seviyededir. Kolaylık sağlamak için iki kategoriye ayrılabilirler: bilgi fonksiyonları ve yürütücü fonksiyonlar. Hepsi de herhangi bir olayı analiz etmeden yalnızca bir tür eylem gerçekleştirir. Kıdemli uzman işleyicilerin emirlerini yerine getirirler.
Bilgi fonksiyonlarına örnekler: mevcut bekleyen emirlerin maksimum açılış fiyatını bulma; pozisyonun kâr veya zararla nasıl kapatıldığını öğrenmek; uzmanın emirlerinin bilet numarası ve listesinin alınması vb.
Yürütme fonksiyonlarına örnekler: belirtilen emirlerin kapatılması; belirtilen pozisyonda Zararı Durdurmayı değiştirme vb.
Bu grup en büyüğüdür. Bu, uzmanın tüm rutin çalışmasının dayandığı işlevsellik türüdür. Bu fonksiyonların çok sayıda örneğini https://www.mql5.com/ru/forum/107476 adresindeki forumda bulabilirsiniz. Ancak buna ek olarak MQL5 standart kitaplığı, emir verme ve pozisyon açma işinin bir kısmını üstlenen sınıfları, özellikle de CTrade sınıfını zaten içermektedir.
Ancak herhangi bir göreviniz, yeni uygulamalar oluşturmayı veya mevcut olanları biraz değiştirmeyi gerektirecektir.
3. Olay İşleme Modülleri
Bu fonksiyonların grubu, ilk iki grup üzerinde yüksek düzey bir üst yapıdır. Yukarıda belirtildiği gibi - bunlar uzmanınızın yapılandırıldığı kullanıma hazır bloklardır. Genel olarak, MQL programının olay işleme fonksiyonuna dahil edilirler: OnStart(), OnTick(), OnTimer(), OnTrade(), OnChartEvent(). Bu grup çok sayıda değildir ve bu modüllerin içeriği görevden göreve ayarlanabilir. Ama özünde hiçbir şey değişmez.
Modüllerde, aynı modülün hem alım hem de satım için çağrılabilmesi için her şey soyut olmalıdır (ikinci tasarım ilkesi). Bu elbette, makro yardımıyla elde edilir.
Bu yüzden uygulamaya devam edin
1. Başlatma, Sonlandırma
class CExpertAdvisor { protected: bool m_bInit; // flag of correct initialization ulong m_magic; // magic number of expert string m_smb; // symbol, on which expert works ENUM_TIMEFRAMES m_tf; // working timeframe CSymbolInfo m_smbinf; // symbol parameters int m_timer; // time for timer public: double m_pnt; // consider 5/3 digit quotes for stops CTrade m_trade; // object to execute trade orders string m_inf; // comment string for information about expert's work
Bu, uzman fonksiyonlarının çalışması için gereken minimum parametre setidir.
m_smb ve m_tf parametreleri uzmana, hangi para biriminde ve hangi süre üzerinde çalışacağını kolayca söylemek için uzman özelliklerine özel olarak yerleştirilir. Örneğin, m_smb = "USDJPY" atarsanız uzman, hangi sembolün çalıştırıldığına bakılmaksızın o sembol üzerinde çalışacaktır. tf = PERIOD_H1 olarak ayarlarsanız, tüm sinyaller ve göstergelerin analizi H1 grafiğinde yer alacaktır.
Ayrıca sınıf yöntemleri vardır. İlk üç yöntem, bir uzmanın başlatılması ve sonlandırılmasıdır.
public: //--- Initialization void CExpertAdvisor(); // constructor void ~CExpertAdvisor(); // destructor virtual bool Init(long magic,string smb,ENUM_TIMEFRAMES tf); // initialization
Temel sınıftaki yapıcı ve yıkıcı hiçbir şey yapmaz.
Init() yöntemi, uzman parametrelerinin sembol, zaman dilimi ve sihirli sayıya göre başlatılmasını sağlar.
//------------------------------------------------------------------ CExpertAdvisor void CExpertAdvisor::CExpertAdvisor() { m_bInit=false; } //------------------------------------------------------------------ ~CExpertAdvisor void CExpertAdvisor::~CExpertAdvisor() { } //------------------------------------------------------------------ Init bool CExpertAdvisor::Init(long magic,string smb,ENUM_TIMEFRAMES tf) { m_magic=magic; m_smb=smb; m_tf=tf; // set initializing parameters m_smbinf.Name(m_smb); // initialize symbol m_pnt=m_smbinf.Point(); // calculate multiplier for 5/3 digit quote if(m_smbinf.Digits()==5 || m_smbinf.Digits()==3) m_pnt*=10; m_trade.SetExpertMagicNumber(m_magic); // set magic number for expert m_bInit=true; return(true); // trade allowed }
2. Sinyal Alma Fonksiyonları
Bu fonksiyonlar, piyasa ve göstergeleri analiz eder.
bool CheckNewBar(); // check for new bar bool CheckTime(datetime start,datetime end); // check allowed trade time virtual long CheckSignal(bool bEntry); // check signal virtual bool CheckFilter(long dir); // check filter for direction
İlk iki fonksiyonun oldukça özel bir uygulaması vardır ve bu sınıfın diğer alt öğelerinde kullanılabilir.
//------------------------------------------------------------------ CheckNewBar bool CExpertAdvisor::CheckNewBar() // function of checking new bar { MqlRates rt[2]; if(CopyRates(m_smb,m_tf,0,2,rt)!=2) // copy bar { Print("CopyRates of ",m_smb," failed, no history"); return(false); } if(rt[1].tick_volume>1) return(false); // check volume return(true); } //--------------------------------------------------------------- CheckTime bool CExpertAdvisor::CheckTime(datetime start,datetime end) { datetime dt=TimeCurrent(); // current time if(start<end) if(dt>=start && dt<end) return(true); // check if we are in the range if(start>=end) if(dt>=start|| dt<end) return(true); return(false); }
Son ikisi, her zaman kullandığınız göstergelere bağlıdır. Bu fonksiyonları tüm durumlar için ayarlamak imkansızdır.
Ana nokta - CheckSignal() ve CheckFilter() sinyal fonksiyonlarının kesinlikle herhangi bir göstergeyi ve bunların kombinasyonlarını analiz edebileceğini anlamak önemlidir! Yani bu sinyallerin sonradan dahil edileceği alım satım modülleri kaynaklardan bağımsızdır.
Bu, daha önce yazılmış uzmanı, benzer bir prensipte çalışan diğer uzmanlar için şablon olarak kullanmanıza olanak tanır. Sadece analiz edilen göstergeleri değiştirin veya yeni filtreleme koşulları ekleyin.
3. Servis Fonksiyonları
Daha önce de belirtildiği gibi, bu fonksiyon grubu en çok sayıya sahip olandır. Makalede açıklanan pratik görevlerimiz için bu tür dört fonksiyonu uygulamak yeterli olacaktır:
double CountLotByRisk(int dist,double risk,double lot); // calculate lot by size of risk ulong DealOpen(long dir,double lot,int SL,int TP); // execute deal with specified parameter ulong GetDealByOrder(ulong order); // get deal ticket by order ticket double CountProfitByDeal(ulong ticket); // calculate profit by deal ticket
//------------------------------------------------------------------ CountLotByRisk double CExpertAdvisor::CountLotByRisk(int dist,double risk,double lot) // calculate lot by size of risk { if(dist==0 || risk==0) return(lot); m_smbinf.Refresh(); return(NormalLot(AccountInfoDouble(ACCOUNT_BALANCE)*risk/(dist*10*m_smbinf.TickValue()))); } //------------------------------------------------------------------ DealOpen ulong CExpertAdvisor::DealOpen(long dir,double lot,int SL,int TP) { double op,sl,tp,apr,StopLvl; // determine price parameters m_smbinf.RefreshRates(); m_smbinf.Refresh(); StopLvl = m_smbinf.StopsLevel()*m_smbinf.Point(); // remember stop level apr = ReversPrice(dir); op = BasePrice(dir); // open price sl = NormalSL(dir, op, apr, SL, StopLvl); // stop loss tp = NormalTP(dir, op, apr, TP, StopLvl); // take profit // open position m_trade.PositionOpen(m_smb,(ENUM_ORDER_TYPE)dir,lot,op,sl,tp); ulong order = m_trade.ResultOrder(); if(order<=0) return(0); // order ticket return(GetDealByOrder(order)); // return deal ticket } //------------------------------------------------------------------ GetDealByOrder ulong CExpertAdvisor::GetDealByOrder(ulong order) // get deal ticket by order ticket { PositionSelect(m_smb); HistorySelectByPosition(PositionGetInteger(POSITION_IDENTIFIER)); uint total=HistoryDealsTotal(); for(uint i=0; i<total; i++) { ulong deal=HistoryDealGetTicket(i); if(order==HistoryDealGetInteger(deal,DEAL_ORDER)) return(deal); // remember deal ticket } return(0); } //------------------------------------------------------------------ CountProfit double CExpertAdvisor::CountProfitByDeal(ulong ticket) // position profit by deal ticket { CDealInfo deal; deal.Ticket(ticket); // deal ticket HistorySelect(deal.Time(),TimeCurrent()); // select all deals after this uint total = HistoryDealsTotal(); long pos_id = deal.PositionId(); // get position id double prof = 0; for(uint i=0; i<total; i++) // find all deals with this id { ticket = HistoryDealGetTicket(i); if(HistoryDealGetInteger(ticket,DEAL_POSITION_ID)!=pos_id) continue; prof += HistoryDealGetDouble(ticket,DEAL_PROFIT); // summarize profit } return(prof); // return profit }
4. Alım Satım Modülleri
Son olarak bu fonksiyon grubu, hizmet fonksiyonlarını ve makroyu kullanarak sinyalleri ve olayları işleyip tüm alım satım sürecini bağlar. Alım satım operasyonlarının mantıksal sözcük birimleri azdır, bunlar sizin özel hedeflerinize bağlıdır. Ancak hemen hemen tüm uzmanlarda var olan ortak kavramları ayırt edebiliriz.
virtual bool Main(); // main module controlling trade process virtual void OpenPosition(long dir); // module of opening position virtual void CheckPosition(long dir); // check position and open additional ones virtual void ClosePosition(long dir); // close position virtual void BEPosition(long dir,int BE); // moving Stop Loss to break-even virtual void TrailingPosition(long dir,int TS); // trailing position of Stop Loss virtual void OpenPending(long dir); // module of opening pending orders virtual void CheckPending(long dir); // work with current orders and open additional ones virtual void TrailingPending(long dir); // move pending orders virtual void DeletePending(long dir); // delete pending orders
Aşağıdaki örneklerde bu fonksiyonların belirli uygulamalarını ele alacağız.
Doğru yaklaşımı seçtiğimiz ve uzman yapısını oluşturduğumuz için yeni fonksiyonlar eklemek zor olmayacak. Tam olarak bu şemayı kullanırsanız tasarımlarınız minimum çaba ve zaman gerektirecektir, kod bir yıl sonra bile okunabilir olacaktır.
Tabi ki uzmanlarınız bunlarla sınırlı değil. CExpertAdvisor sınıfında yalnızca en gerekli yöntemleri açıkladık. Alt öğe sınıflarına yeni işleyiciler ekleyebilir, var olanları değiştirebilir, kendi modüllerinizi genişletebilir, böylece tek bir kitaplık oluşturabilirsiniz. Böyle bir kitaplığa sahip olmak, "anahtar teslimi" uzmanların geliştirilmesi yarım saatten iki güne kadar sürer.
4. CExpertAdvisor Sınıfını Kullanma Örnekleri
4.1. Gösterge Sinyallerine Dayalı Çalışma Örneğiİlk örnek olarak, en basit görevle başlayalım - CExpertAdvisor sınıfını kullanan MovingAverage Uzman Danışmanını (MetaTrader 5'in temel örneği) düşünün. Bunu biraz karmaşıklaştıralım.
Algoritma:
a) Pozisyon açma koşulu
- Fiyat MA'yı aşağıdan yukarıya geçerse, Alış için pozisyon açın.
- Fiyat MA’yı yukarıdan aşağıya geçerse, Satış için pozisyon açın.
- SL (Stop Loss), TP (TakeProfit) ayarlayın.
- Pozisyon lotu, Zararı Durdur (Stop Loss) tetiklendiğinde mevduattan ne kadar kaybedileceğine dair Risk parametresi ile hesaplanır.
b) Pozisyon kapatma koşulu
- Fiyat MA'yı aşağıdan yukarıya doğru geçerse, Satış pozisyonunu kapatın.
- Fiyat MA’yı yukarıdan aşağıya doğru geçerse, Alış pozisyonunu kapatın.
c) Sınırlama
- Bir uzmanın çalışmasını HourStart öğesinden HourEnd öğesine kadar günlük zamana göre sınırlama.
- Uzman, alım satım işlemlerini yalnızca yeni çubukta yapar.
d) Pozisyon desteği
- TS mesafesinde basit bir takip eden durdurma kullanın.
Uzmanımız için CExpertAdvisor sınıfının yedi fonksiyonuna ihtiyacımız olacak:
- Sinyal fonksiyonu - CheckSignal()
- Tik filtresi - CheckNewBar()
- Zaman filtresi - CheckTime()
- Açılış pozisyonlarının servis fonksiyonu - DealOpen()
- Üç çalışma modülü - OpenPosition(), ClosePosition(), TrailingPosition()
CheckSignal() fonksiyonu ve modülleri, özellikle görevini çözmek için bir alt sınıfta tanımlanmalıdır. Ayrıca göstergenin başlatılmasını da eklememiz gerekiyor.
//+------------------------------------------------------------------+ //| Moving Averages.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #include "ExpertAdvisor.mqh" input double Risk = 0.1; // Risk input int SL = 100; // Stop Loss distance input int TP = 100; // Take Profit distance input int TS = 30; // Trailing Stop distance input int pMA = 12; // Moving Average period input int HourStart = 7; // Hour of trade start input int HourEnd = 20; // Hour of trade end //--- class CMyEA : public CExpertAdvisor { protected: double m_risk; // size of risk int m_sl; // Stop Loss int m_tp; // Take Profit int m_ts; // Trailing Stop int m_pMA; // MA period int m_hourStart; // Hour of trade start int m_hourEnd; // Hour of trade end int m_hma; // MA indicator public: void CMyEA(); void ~CMyEA(); virtual bool Init(string smb,ENUM_TIMEFRAMES tf); // initialization virtual bool Main(); // main function virtual void OpenPosition(long dir); // open position on signal virtual void ClosePosition(long dir); // close position on signal virtual long CheckSignal(bool bEntry); // check signal }; //------------------------------------------------------------------ CMyEA void CMyEA::CMyEA() { } //----------------------------------------------------------------- ~CMyEA void CMyEA::~CMyEA() { IndicatorRelease(m_hma); // delete MA indicator } //------------------------------------------------------------------ Init bool CMyEA::Init(string smb,ENUM_TIMEFRAMES tf) { if(!CExpertAdvisor::Init(0,smb,tf)) return(false); // initialize parent class m_risk=Risk; m_tp=TP; m_sl=SL; m_ts=TS; m_pMA=pMA; // copy parameters m_hourStart=HourStart; m_hourEnd=HourEnd; m_hma=iMA(m_smb,m_tf,m_pMA,0,MODE_SMA,PRICE_CLOSE); // create MA indicator if(m_hma==INVALID_HANDLE) return(false); // if there is an error, then exit m_bInit=true; return(true); // trade allowed } //------------------------------------------------------------------ Main bool CMyEA::Main() // main function { if(!CExpertAdvisor::Main()) return(false); // call function of parent class if(Bars(m_smb,m_tf)<=m_pMA) return(false); // if there are insufficient number of bars if(!CheckNewBar()) return(true); // check new bar // check each direction long dir; dir=ORDER_TYPE_BUY; OpenPosition(dir); ClosePosition(dir); TrailingPosition(dir,m_ts); dir=ORDER_TYPE_SELL; OpenPosition(dir); ClosePosition(dir); TrailingPosition(dir,m_ts); return(true); } //------------------------------------------------------------------ OpenPos void CMyEA::OpenPosition(long dir) { if(PositionSelect(m_smb)) return; // if there is an order, then exit if(!CheckTime(StringToTime(IntegerToString(m_hourStart)+":00"), StringToTime(IntegerToString(m_hourEnd)+":00"))) return; if(dir!=CheckSignal(true)) return; // if there is no signal for current direction double lot=CountLotByRisk(m_sl,m_risk,0); if(lot<=0) return; // if lot is not defined then exit DealOpen(dir,lot,m_sl,m_tp); // open position } //------------------------------------------------------------------ ClosePos void CMyEA::ClosePosition(long dir) { if(!PositionSelect(m_smb)) return; // if there is no position, then exit if(!CheckTime(StringToTime(IntegerToString(m_hourStart)+":00"), StringToTime(IntegerToString(m_hourEnd)+":00"))) { m_trade.PositionClose(m_smb); return; } // if it's not time for trade, then close orders if(dir!=PositionGetInteger(POSITION_TYPE)) return; // if position of unchecked direction if(dir!=CheckSignal(false)) return; // if the close signal didn't match the current position m_trade.PositionClose(m_smb,1); // close position } //------------------------------------------------------------------ CheckSignal long CMyEA::CheckSignal(bool bEntry) { MqlRates rt[2]; if(CopyRates(m_smb,m_tf,0,2,rt)!=2) { Print("CopyRates ",m_smb," history is not loaded"); return(WRONG_VALUE); } double ma[1]; if(CopyBuffer(m_hma,0,0,1,ma)!=1) { Print("CopyBuffer MA - no data"); return(WRONG_VALUE); } if(rt[0].open<ma[0] && rt[0].close>ma[0]) return(bEntry ? ORDER_TYPE_BUY:ORDER_TYPE_SELL); // condition for buy if(rt[0].open>ma[0] && rt[0].close<ma[0]) return(bEntry ? ORDER_TYPE_SELL:ORDER_TYPE_BUY); // condition for sell return(WRONG_VALUE); // if there is no signal } CMyEA ea; // class instance //------------------------------------------------------------------ OnInit int OnInit() { ea.Init(Symbol(),Period()); // initialize expert return(0); } //------------------------------------------------------------------ OnDeinit void OnDeinit(const int reason) { } //------------------------------------------------------------------ OnTick void OnTick() { ea.Main(); // process incoming tick }
Main() fonksiyonunun yapısını ayrıştıralım. Geleneksel olarak iki bölüme ayrılmıştır.
İlk bölümde üst öğe fonksiyonu çağrılır. Bu fonksiyon, bir uzmanın çalışmasını global olarak etkileyen olası parametreleri işler. Bunlar, bir uzman için işlem yapma izninin kontrol edilmesini ve geçmiş verilerin doğrulanmasını içerir.
İkinci bölümde piyasa olayları doğrudan işlenir.
CheckNewBar() filtresi test edilir - yeni bir çubuk kontrol edilir. İki alım satım yönü için modüller birbiri ardına çağrılır.
Modüllerde her şey oldukça soyut düzenlenmiştir (ikinci tasarım ilkesi). Sembol özelliklerine yönelik doğrudan bir adres yoktur. Üç modül - OpenPosition(), ClosePosition() ve TrailingPosition() - sadece kendilerine dışarıdan gelen parametrelere dayanır. Bu, hem Alış hem de Satış emirlerinin doğrulanması için bu modülleri çağırmanıza olanak tanır.
4.2. CExpertAdvisor - Göstergesiz Uzman Kullanımı, Pozisyon Durumunu ve Sonucu Analiz Etme Örneği
Örneği göstermek için zarardan sonra lot artışıyla yalnızca ters pozisyonda işlem gören sistemi ele alalım (bu tür uzmanlara genellikle "Martingale" denir)
a) İlk emri verin
- uzman başladığında, ilk lotla birlikte ilk Alış pozisyonunu açar
b) Sonraki pozisyonları açın
- önceki pozisyon kârla kapatılmışsa, ilk lotla aynı yönde pozisyon açın
- önceki pozisyon zararla kapatılmışsa, pozisyonu ters yönde daha büyük bir lotla açın (faktör kullanarak).
Uzmanımız için CExpertAdvisor sınıfının üç fonksiyonuna ihtiyacımız olacak:
- pozisyon açma - DealOpen()
- sözleşme bileti ile kapatılan pozisyonun kar değerini alma - CountProfitByDeal()
- çalışma modülleri - OpenPosition(), CheckPosition()
Uzman herhangi bir göstergeyi analiz etmediğinden, sadece sonuçlarla ilgilendiğinden, optimum üretkenlik için OnTrade() olaylarını kullanacağız. Yani, bir kez ilk Alış emrini vermiş olan uzman, sonraki tüm emirleri ancak bu pozisyonu kapattıktan sonra verecektir. Bu yüzden ilk emri OnTick() öğesine yerleştireceğiz ve sonraki tüm işleri OnTrade() öğesinde yapacağız.
Init() fonksiyonu her zamanki gibi, sınıfın parametrelerini uzmanın dış parametreleriyle başlatır.
OpenPosition() modülü ilk pozisyonu açar ve m_first bayrağı tarafından engellenir.
CheckPosition() modülü, pozisyonun diğer terslerini kontrol eder.
Bu modüller, uzmanın ilgili fonksiyonlarından çağrılır: OnTick() ve OnTrade().
//+------------------------------------------------------------------+ //| eMarti.mq5 | //| Copyright Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #include "ExpertAdvisor.mqh" #include <Trade\DealInfo.mqh> input double Lots = 0.1; // Lot input double LotKoef = 2; // lot multiplier for loss input int Dist = 60; // distance to Stop Loss and Take Profit //--- class CMartiEA : public CExpertAdvisor { protected: double m_lots; // Lot double m_lotkoef; // lot multiplier for loss int m_dist; // distance to Stop Loss and Take Profit CDealInfo m_deal; // last deal bool m_first; // flag of opening the first position public: void CMartiEA() { } void ~CMartiEA() { } virtual bool Init(string smb,ENUM_TIMEFRAMES tf); // initialization virtual void OpenPosition(); virtual void CheckPosition(); }; //------------------------------------------------------------------ Init bool CMartiEA::Init(string smb,ENUM_TIMEFRAMES tf) { if(!CExpertAdvisor::Init(0,smb,tf)) return(false); // initialize parent class m_lots=Lots; m_lotkoef=LotKoef; m_dist=Dist; // copy parameters m_deal.Ticket(0); m_first=true; m_bInit=true; return(true); // trade allowed } //------------------------------------------------------------------ OnTrade void CMartiEA::OpenPosition() { if(!CExpertAdvisor::Main()) return; // call parent function if(!m_first) return; // if already opened initial position ulong deal=DealOpen(ORDER_TYPE_BUY,m_lots,m_dist,m_dist); // open initial position if(deal>0) { m_deal.Ticket(deal); m_first=false; } // if position exists } //------------------------------------------------------------------ OnTrade void CMartiEA::CheckPosition() { if(!CExpertAdvisor::Main()) return; // call parent function if(m_first) return; // if not yet placed initial position if(PositionSelect(m_smb)) return; // if position exists // check profit of previous position double lot=m_lots; // initial lot long dir=m_deal.Type(); // previous direction if(CountProfitByDeal(m_deal.Ticket())<0) // if there was loss { lot=NormalLot(m_lotkoef*m_deal.Volume()); // increase lot dir=ReversType(m_deal.Type()); // reverse position } ulong deal=DealOpen(dir,lot,m_dist,m_dist); // open position if(deal>0) m_deal.Ticket(deal); // remember ticket } CMartiEA ea; // class instance //------------------------------------------------------------------ OnInit int OnInit() { ea.Init(Symbol(),Period()); // initialize expert return(0); } //------------------------------------------------------------------ OnDeinit void OnDeinit(const int reason) { } //------------------------------------------------------------------ OnTick void OnTick() { ea.OpenPosition(); // process tick - open first order } //------------------------------------------------------------------ OnTrade void OnTrade() { ea.CheckPosition(); // process trade event }
5. Olaylarla Çalışma
Bu makalede, sırasıyla OnTick() ve OnTrade() fonksiyonlarıyla temsil edilen NewTick ve Trade olmak üzere iki olayı işleme örneklerini gördünüz. Çoğu durumda bu iki olay sürekli olarak kullanılır.
Uzmanlar için olayları işlemenin dört fonksiyonu vardır:
- OnChartEvent büyük bir olay grubunu işler: grafik nesneler, klavye, fare ve özel olaylarla çalışırken. Örneğin fonksiyon, emirlerin grafiksel yönetimi ilkesine dayanan uzmanlar veya etkileşimli uzmanlar oluşturmak için kullanılır. Alternatif olarak sadece MQL-program parametrelerinin aktif kontrollerini oluşturmak için (düğmeleri ve düzenleme alanlarını kullanarak) kullanılabilir. Genel olarak, bu fonksiyon bir uzmanın dış olayını işlemek için kullanılır.
- OnTimer, sistem zamanlayıcı olayı işlendiğinde çağrılır. MQL programının, kendi ortamını düzenli olarak analiz etmesi, gösterge değerlerini hesaplaması, sürekli olarak dış sinyal kaynaklarına başvurması gerektiğinde vb. durumlarda kullanılır. Kabaca söylemek gerekirse, OnTimer() fonksiyonu aşağıdaki için bir alternatiftir, hatta onların yerine geçecek en iyi öğedir:
while(true) { /* perform analysis */; Sleep(1000); }.
Yani, uzmanın başlangıcında sonsuz bir döngüde değil ancak fonksiyonlarının çağrılarını OnTick()'ten OnTimer()'a taşımak için yeterli döngüde çalışması gereklidir. - OnBookEvent, Piyasa Derinliği kendi durumunu değiştirdiğinde oluşturulan bir olayı işler. Bu olay dış olay olarak atfedilebilir ve görevini göreve uygun olarak gerçekleştirebilir.
- Özel maks. parametresiyle genetik optimizasyon kullanılırken, test nesillerinin olası taraması için OnDeinit() fonksiyonundan önce, uzmanı belirli bir tarih aralığında test ettikten sonra OnTester çağrılır.
Herhangi bir olayın ve bunların kombinasyonlarının, belirli görevlerinin çözümü için kullanılmasının her zaman tavsiye edildiğini unutmayın.
Sonsöz
Gördüğünüz gibi doğru şemaya sahipken bir uzman yazmak fazla zaman almıyor. MQL5'teki yeni olay işleme olanakları nedeniyle, alım satım sürecini yönetmek için daha esnek bir yapıya sahibiz. Ancak tüm bunlar, yalnızca alım satım algoritmalarınızı uygun şekilde hazırladıysanız gerçekten güçlü bir araç haline gelir.
Makale, bunların oluşturulmasıyla ilgili üç ana ilkeyi açıklar - olaylarla dolu olma, soyutlama, modülerlik. Uzmanlarınızı bu "üç temele" dayandırırsanız, işlemlerinizi kolaylaştıracaksınız.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/132





- Ü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