English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Alım Satım Robotunun Prototipi

Alım Satım Robotunun Prototipi

MetaTrader 5Örnekler | 9 Aralık 2021, 11:07
150 0
---
---

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.

Bu üç ilke, tüm uzmanlar için kolayca değiştirebileceğiniz ve herhangi bir göreve uyarlayabileceğiniz tek bir prototip oluşturmayı mümkün kılar.

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.

Tüm farklı uzmanlar, bu üç olay kaynağının birleşimine dayanmaktadır

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ı

Ş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.

Öneri: Alım satım sistemini tanımlarken "1. Sinyali al ... emri aç" gibi kelimelerle başlamayın, bunun yerine her birinde gerekli sinyalleri analiz etmek için hemen bölümlere ayırın: "a) Emirlerin açılması için koşul, b) Emirlerin sürdürme koşulları vb.".

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

Ş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ı.

Şemalardan da görülebileceği gibi, herhangi bir alım satım sisteminin doğrudan mantık kullanılarak tanımlanması çok kolaydır.


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ı

Ş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

Ekli dosyalar |
emarti.mq5 (3.57 KB)
emyea.mq5 (5.9 KB)
expertadvisor.mqh (17.92 KB)
Kendi Takip Eden Durdurma (Trailing Stop) Emrinizi Nasıl Oluşturabilirsiniz? Kendi Takip Eden Durdurma (Trailing Stop) Emrinizi Nasıl Oluşturabilirsiniz?
Yatırımcının temel kuralı - kârın büyümesine izin verin, zararları kesin! Bu makale, bu kuralın izlenmesine izin veren temel tekniklerden birini, pozisyon kârını artırdıktan sonra koruyucu durdurma seviyesini (Zararı Durdur seviyesi), yani Takip Eden Durdurma (Trailing Stop) seviyesini hareket ettirmeyi ele almaktadır. SAR ve NRTR göstergelerinde takip eden durdurma için bir sınıf oluşturmaya yönelik adım adım prosedürü bulacaksınız. Herkes bu takip eden durdurmayı kendi uzmanlarına ekleyebilecek veya hesaplarındaki pozisyonları kontrol etmek için bağımsız olarak kullanabilecek.
MQL5'te 20 Alım Satım Sinyali MQL5'te 20 Alım Satım Sinyali
Bu makale size bir alım satım sisteminin çalışması için gerekli olan alım satım sinyallerini nasıl alacağınızı öğretecektir. 20 alım satım sinyali oluşturma örneği burada, Uzman Danışmanlar geliştirilirken kullanılabilecek ayrı özel fonksiyonlar olarak verilmiştir. Size kolaylık sağlamak için makalede kullanılan tüm fonksiyonlar, gelecekteki bir Uzman Danışmana kolayca bağlanabilen tek bir mqh içerme dosyasında birleştirilmiştir.
MQL5'te Çok Renkli Göstergeler Oluşturma MQL5'te Çok Renkli Göstergeler Oluşturma
Bu makalemizde çok renkli göstergelerin nasıl oluşturulacağını veya var olan göstergelerin çok renkli hale nasıl dönüştürüleceğini ele alacağız. MQL5, bilgilerin uygun biçimde temsil edilmesini sağlar. Artık göstergelere sahip bir düzine grafiğe bakmak ve RSI veya Stokastik seviyelerinin analizlerini yapmak gerekli değildir, sadece göstergelerin değerlerine bağlı olarak mumları farklı renklerle boyamak daha iyidir.
Başka Bir Göstergeye Dayalı Bir Gösterge Nasıl Yazılır? Başka Bir Göstergeye Dayalı Bir Gösterge Nasıl Yazılır?
MQL5'te, hem sıfırdan hem de zaten mevcut olan, istemci terminalinde yerleşik veya özel bir gösterge temelinde bir gösterge yazabilirsiniz. Burada ayrıca iki yolunuz var - bir göstergeyi ona yeni hesaplamalar ve grafik stilleri ekleyerek geliştirmek veya istemci terminalinde yerleşik bir gösterge veya iCustom() ya da IndicatorCreate() fonksiyonları aracılığıyla özel bir gösterge kullanmak.