English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Alım Satım Fikirlerinin Grafik Üzerinde Hızlı Testi

Alım Satım Fikirlerinin Grafik Üzerinde Hızlı Testi

MetaTrader 5Ticaret sistemleri | 13 Ocak 2022, 08:59
72 0
Vladimir Kustikov
Vladimir Kustikov

Giriş

Altıncı Otomatik Alım Satım Şampiyonası sonunda başladı. Tüm ilk heyecan sona erdi ve sonunda biraz rahatlayabilir ve gönderilen alım satım robotlarını inceleyebiliriz. Modern alım satım robotlarının en dikkat çeken özelliklerini bulmak ve alım satım faaliyetlerinden neler bekleyebileceğimizi tanımlamak için biraz araştırma yapmaya karar verdim.

Bu yeterince zor oldu. Bu nedenle, sahip olduğum tek şey Uzman Danışman açıklamaları ve nadir geliştirici yorumları olduğundan hesaplamalarımın tam olarak doğru veya eksiksiz olduğu söylenemez. Ancak yine de bazı sonuçlar çıkarabiliriz; hesaplamalarımın sonuçları aşağıda yer almaktadır: Şampiyonaya 451 Uzman Danışman katılıyor ve bunlardan sadece 316'sında bazı anlamlı açıklamalar yer alıyor. Kalanların geliştiricileri, açıklamalarını arkadaşlarına ve ailelerine selamlar, dünya dışı medeniyetlere mesajlar veya kendilerine övgülerle doldurmuş.

ATC 2012'deki en popüler stratejiler:

  • çeşitli grafik yapıları (önemli fiyat seviyeleri, destek-direnç seviyeleri, kanallar) kullanarak alım satım – 55;
  • fiyat hareketi analizi (çeşitli zaman aralıkları için) – 33;
  • trend takip sistemleri (sanırım bu cafcaflı kelimelerin arkasında, hareketli ortalamaların aşırı optimize edilmiş kombinasyonları var, ama yanılıyor olabilirim) :) – 31;
  • istatistiksel fiyat modelleri – 10:
  • arabuluculuk, sembol korelasyon analizi – 8;
  • volatilite analizi – 8;
  • sinir ağları – 7;
  • mum analizi – 5;
  • ortalamalar – 5;
  • strateji paketleri – 5;
  • alım satım oturum süresi – 4;
  • rastgele sayı oluşturucu – 4;
  • haber alım satımı – 3,
  • Elliott Dalgaları – 2.

Gösterge stratejileri elbette geleneksel olarak en popüler olanlardır. Belirli bir Uzman Danışmanda her bir belirli göstergenin rolünü tanımlamak zordur, ancak kullanımlarının mutlak sayısını tahmin etmek mümkündür:

  • Hareketli Ortalama - 75;
  • MACD – 54;
  • Stokastik Osilatör – 25;
  • RSI – 23;
  • Bollinger Bantları – 19;
  • Fraktallar – 8;
  • CCI, ATR – her biri 7 gösterge;
  • Zigzag, Parabolik SAR – her biri 6 gösterge;
  • ADX – 5;
  • Momentum – 4;
  • kişiye özel göstergeler (ne kadar ilgi çekici :) ) – 4;
  • Ichimoku, AO – her biri 3 gösterge;
  • ROC, WPR, StdDev, Hacimler – her biri 2 gösterge.

Veriler aşağıdaki sonuçları ortaya koyar: Çoğu katılımcı alım satım takip stratejilerini göstergeler ile kullanmaktadır. Belki de verileri toplarken bir şeyi kaçırdım ve otomatik alım satım alanında bazı seçkin kişiliklerin ortaya çıktığını görürüz, ancak bu şimdilik olası görünmüyor. Bence asıl sorun, pazarın ilgisini çektiği yeni gelenlerin çoğu durumda bilgi yerine kuralları almasıdır.

Örneğin, işte MACD kullanma kuralları, işte sinyaller; şimdi parametreleri optimize edin ve para kazanın. Beyninizi biraz kullanmaya ne dersiniz? Saçmalık! Standartlar zaten geliştirildi! Neden tekerleği yeniden icat edelim? Ancak, şu anda çok popüler olan göstergelerin tıpkı benim ve sizin gibi yatırımcılar tarafından da icat edildiğini sık sık unutuyoruz. Onların da standartları ve yetkileri vardı. Belki on yıl içinde adınızı taşıyan yeni bir gösterge standart hale gelecek.

Alım satım fikirleri için arama yöntemimi ve bu fikirleri hızlı bir şekilde test etmek için kullandığım yöntemi paylaşmak istiyorum.


Yöntem Açıklaması

Tüm teknik analizler basit bir aksiyoma dayanır: Fiyatlar her şeyi dikkate alır. Ancak bir sorun var: bu ifadenin dinamikleri yok. Grafiğe bakıyoruz ve statik bir görüntü görüyoruz: fiyat gerçekten de her şeyi dikkate almış. Ancak, kâr elde edebilmek için, gelecekte belirli bir zaman periyodunda fiyatın neleri dikkate alacağını ve nereye gideceğini bilmek istiyoruz. Fiyattan elde edilen göstergeler, gelecekteki olası hareketleri tam olarak tahmin etmek için tasarlanmıştır.

Fizikten bildiğimiz gibi, büyüklüğün birinci derece türevi hızdır. Bu nedenle, göstergeler mevcut fiyat değişim hızını hesaplar. Bunun yanı sıra, önemli dış kuvvetlerin müdahalesi olmaksızın, önemli büyüklüklerin hızın değerindeki ani değişiklikleri önleyen bir atalete sahip olduğunu biliyoruz. Trend kavramına kademeli olarak bu şekilde yaklaşıyoruz: Dış kuvvetlerin (haberler, merkez bankalarının politikaları vb.) piyasayı etkilemediği zaman sürecinde birinci derece türevinin (hızının) değerini koruduğu fiyat durumu.

Şimdi başladığımız yere geri dönelim: Fiyatlar her şeyi dikkate alır. Yeni fikirler geliştirmek için fiyatın ve türevlerinin davranışını aynı zaman aralığında incelememiz gerekir. Yalnızca fiyat grafiklerinin dikkatle incelenmesi, alım satımınızı körü körüne inançtan asli anlayış seviyesine yükseltecektir.

Bu, alım satım sonuçlarında ani değişikliklere yol açmayabilir; ancak birçok "neden" sorusuna cevap verme yeteneği eninde sonunda olumlu bir rol oynayacaktır. Ayrıca, grafiklerin ve göstergelerin görsel analizi, geliştiricilerin tamamen öngöremediği fiyatlar ve göstergeler arasında yepyeni korelasyonlar bulmanızı sağlayacaktır.

Görünürde lehinize çalışan yeni bir korelasyon bulduğunuzu varsayalım. Sırada ne var? En kolay yol, bir Uzman Danışman yazmak ve varsayımınızın doğru olduğundan emin olmak için bunu geçmiş veriler üzerinde test etmektir. Durum bu değilse, parametreleri optimize etmenin yaygın bir yolunu seçmeliyiz. Bunun en kötü yanı, "neden" sorusuna cevap verememiş olmamızdır. Uzman Danışmanımız neden zarar/kâr etti? Neden bu kadar büyük bir düşüş oldu? Cevaplar olmadan aklınızdakini verimli bir şekilde uygulayamazsınız.

Elde edilen bir korelasyon sonuçlarını grafik üzerinde görselleştirmek için aşağıdaki eylemleri gerçekleştiriyorum:

  1. Bir sinyal oluşturacak şekilde gerekli göstergeyi oluşturuyorum veya değiştiriyorum: satış için -1 ve alış için 1.
  2. Grafiğe giriş ve çıkış noktalarını gösteren bakiye göstergesini bağlıyorum. Gösterge, sinyali işlerken bakiye ve öz varlıktaki (puan cinsinden) değişiklikleri de gösterir.
  3. Varsayımlarımın hangi durumlarda ve koşullarda doğru olduğunu analiz ediyorum.

Yöntemin belirli avantajları vardır.

  • İlk olarak, bakiye göstergesi, giriş hesaplama dizilerinde azami hesaplama hızı ve geçmiş verilerin otomatik kullanılabilirliğini sağlayan OnCalculate yöntemi kullanılarak bir bütün olarak hesaplanır.
  • İkinci olarak, sinyalin mevcut göstergeye eklenmesi, Sihirbaz aracılığıyla bir Uzman Danışman oluşturmak ile bunu kendi başınıza geliştirmek arasında yer alan bir ara adımdır.
  • Üçüncüsü, bir fikir ve nihai bir sonuç tek bir grafik üzerinde görülebilir. Tabii ki, yöntemin bazı sınırlamaları vardır: Sinyal çubuğun kapanış fiyatına bağlanır, bakiye sabit lot için hesaplanır, bekleyen emirleri kullanarak alım satım için herhangi bir seçenek bulunmaz. Ancak, tüm bu sınırlamalar kolayca düzeltilebilir/geliştirilebilir.


Uygulama

Nasıl çalıştığını anlamak ve yöntemin uygunluğunu değerlendirmek için basit bir sinyal göstergesi geliştirelim. Mum modellerini uzun zamandır biliyorum. Öyleyse neden pratikteki nasıl çalıştıklarına bakmıyoruz? Alış ve satış sinyalleri olarak sırasıyla "çekiç" ve "kayan yıldız" ters modellerini seçtim. Aşağıdaki görüntüler bunların şematik görünümlerini gösterir:

Şekil 1. "Çekiç" ve "kayan yıldız" mum modelleri

Şekil 1. "Çekiç" ve "kayan yıldız" mum modelleri

Şimdi "çekiç" modeli göründüğünde piyasaya giriş kurallarını tanımlayalım.

  1. Mumun en düşük değeri, önceki beş mumdan daha düşük olmalıdır;
  2. Mumun gövdesi, toplam yüksekliğinin %50'sini geçmemelidir;
  3. Mumun üst gölgesi, toplam yüksekliğinin %0'ını geçmemelidir;
  4. Mumun yüksekliği, önündeki beş mumun ortalama yüksekliğinin %100'ünden az olmamalıdır;
  5. Modelin kapanış fiyatı 10-dönem Hareketli Ortalamadan düşük olmalıdır.

Bu şartlar sağlanırsa bir uzun pozisyon açmalıyız. Kurallar "kayan yıldız" modeli için de aynıdır. Tek fark, kısa bir pozisyon açmamız gerektiğidir:

  1. Mumun en yüksek değeri, önceki beş mumdan daha yüksek olmalıdır;
  2. Mumun gövdesi, toplam yüksekliğinin %50'sini geçmemelidir;
  3. Mumun alt gölgesi, toplam yüksekliğinin %0'ını geçmemelidir;
  4. Mumun yüksekliği, önündeki beş mumun ortalama yüksekliğinin %100'ünden az olmamalıdır;
  5. Modelin kapanış fiyatı, 10dönem Hareketli Ortalamadan yüksek olmalıdır.

Gelecekte optimize edilebilecek (model kabul edilebilir sonuçlar gösteriyorsa) çizimlere dayalı olarak kullandığım parametreler için kalın yazı stil kullandım. Uygulamak istediğim sınırlamalar, modelleri uygunsuz görünüme sahip olanlardan (s. 1-3) ve ayrıca sinyal olarak kabul edilemeyecek, bilinçli olarak zayıf olanlardan temizlememize olanak sağlıyor.

Ayrıca, çıkış anlarını da belirlememiz gerekiyor. Bahsedilen modeller trend ters çevirme sinyalleri olarak göründüğünden, trend uygun mum göründüğü anda mevcuttur. Dolayısıyla fiyatın devamındaki hareketli ortalama da mevcut olacaktır. Çıkış sinyali, fiyat ve bunun 10 dönem hareketli ortalamasının kesişmesiyle oluşur.

Şimdi, biraz programlama yapma zamanı. MQL5 Sihirbazında yeni bir özel gösterge geliştirelim, buna PivotCandles adını verelim ve davranışını açıklayalım. Bakiye göstergesini bağlamak için döndürülen değerleri tanımlayalım:

  • -1 – bir satış pozisyonu aç;
  • -2 – bir alış pozisyonunu kapat;
  • 0 – sinyal yok;
  • -1 – alış pozisyonu aç;
  • -2 – satış pozisyonunu kapat.

Bildiğiniz gibi gerçek programcılar kolay yolların peşine düşmezler. En kolayının peşine düşerler :) Ben de bir istisna değilim. Kulaklığımdan müzik dinlerken ve aromalı kahvemi içerken, bir göstergede ve bir Uzman Danışmanda (göstergeden yola çıkarak geliştirmeye karar vermem durumunda) uygulayacağım sınıfa sahip dosyayı oluşturdum. Belki diğer mum modellerinde bile değiştirilebilir. Kodda yeni bir şey yok. Koda uygulanan yorumların her türlü olası soruyu içerdiğine inanıyorum.

//+------------------------------------------------------------------+
//|                                            PivotCandlesClass.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input int      iMaxBodySize            = 50;  // Maximum candle body, %
input int      iMaxShadowSize          = 0;   // Maximum allowed candle shadow, %
input int      iVolatilityCandlesCount = 5;   // Number of previous bars for calculation of an average volatility
input int      iPrevCandlesCount       = 5;   // Number of previous bars, for which the current bar should be an extremum
input int      iVolatilityPercent      = 100; // Correlation of a signal candle with a previous volatility, %
input int      iMAPeriod               = 10;  // Period of a simple signal moving average
//+------------------------------------------------------------------+
//| Class definition                                                 |
//+------------------------------------------------------------------+
class CPivotCandlesClass
  {
private:
   MqlRates          m_candles[];              // Array for storing the history necessary for calculations
   int               m_history_depth;          // Array length for storing the history
   int               m_handled_candles_count;  // Number of the already processed candles
   
   double            m_ma_value;               // Current calculated moving average value
   double            m_prev_ma_value;          // Previous calculated moving average value
   bool              m_is_highest;             // Check if the current candle is the highest one
   bool              m_is_lowest;              // Check if the current candle is the lowest one
   double            m_volatility;             // Average volatility
   int               m_candle_pattern;         // Current recognized pattern
   
   void              PrepareArrayForNewCandle();        // Prepare the array for accepting the new candle
   int               CheckCandleSize(MqlRates &candle); // Check the candle for conformity with patterns
   void              PrepareCalculation();
protected:
   int               DoAnalizeNewCandle();              // Calculation function
public:
   void              CPivotCandlesClass(); 
   
   void              CleanupHistory();                  // Clean up all calculation variables  
   double            MAValue() {return m_ma_value;}     // Current value of the moving average
   int               AnalizeNewCandle(MqlRates& candle);
   int               AnalizeNewCandle( const datetime time,
                                       const double open,
                                       const double high,
                                       const double low,
                                       const double close,
                                       const long tick_volume,
                                       const long volume,
                                       const int spread );
  };
//+------------------------------------------------------------------+
//| CPivotCandlesClass                                               |
//+------------------------------------------------------------------+
//| Class initialization                                             |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::CPivotCandlesClass()
  {
   // History depth should be enough for all calculations
   m_history_depth = (int)MathMax(MathMax(
      iVolatilityCandlesCount + 1, iPrevCandlesCount + 1), iMAPeriod);
   m_handled_candles_count = 0;
   m_prev_ma_value = 0;
   m_ma_value = 0;
   
   ArrayResize(m_candles, m_history_depth);
  }  
//+------------------------------------------------------------------+
//| CleanupHistory                                                   |
//+------------------------------------------------------------------+
//| Clean up the candle buffer for recalculation                     |
//+------------------------------------------------------------------+
void CPivotCandlesClass::CleanupHistory()
  {
   // Clean up the array
   ArrayFree(m_candles);
   ArrayResize(m_candles, m_history_depth);
   
   // Null calculation variables
   m_handled_candles_count = 0;
   m_prev_ma_value = 0;
   m_ma_value = 0;   
  }
//+-------------------------------------------------------------------+
//| AnalizeNewCandle                                                  |
//+-------------------------------------------------------------------+
//| Preparations for analyzing the new candle and the analysis itself |
//| based on candle's separate parameter values                       |
//+-------------------------------------------------------------------+
int CPivotCandlesClass::AnalizeNewCandle( const datetime time,
                                          const double open,
                                          const double high,
                                          const double low,
                                          const double close,
                                          const long tick_volume,
                                          const long volume,
                                          const int spread )
  {
   // Prepare the array for the new candle
   PrepareArrayForNewCandle();

   // Fill out the current value of the candle
   m_candles[0].time          = time;
   m_candles[0].open          = open;
   m_candles[0].high          = high;
   m_candles[0].low           = low;
   m_candles[0].close         = close;
   m_candles[0].tick_volume   = tick_volume;
   m_candles[0].real_volume   = volume;
   m_candles[0].spread        = spread;

   // Check if there is enough data for calculation
   if (m_handled_candles_count < m_history_depth)
      return 0;
   else
      return DoAnalizeNewCandle();
  }  
//+-------------------------------------------------------------------+
//| AnalizeNewCandle                                                  |
//+-------------------------------------------------------------------+
//| Preparations for analyzing the new candle and the analysis itself |
//| based on the received candle                                      |
//+-------------------------------------------------------------------+
int CPivotCandlesClass::AnalizeNewCandle(MqlRates& candle)
  {
   // Prepare the array for the new candle   
   PrepareArrayForNewCandle();

   // Add the candle 
   m_candles[0] = candle;

   // Check if there is enough data for calculation
   if (m_handled_candles_count < m_history_depth)
      return 0;
   else
      return DoAnalizeNewCandle();
  }
//+------------------------------------------------------------------+
//| PrepareArrayForNewCandle                                         |
//+------------------------------------------------------------------+ 
//| Prepare the array for the new candle                             |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::PrepareArrayForNewCandle()
  {
   // Shift the array by one position to write the new value there
   ArrayCopy(m_candles, m_candles, 1, 0, m_history_depth-1);
   
   // Increase the counter of added candles
   m_handled_candles_count++;
  }
//+------------------------------------------------------------------+
//| CalcMAValue                                                      |
//+------------------------------------------------------------------+ 
//| Calculate the current values of the Moving Average, volatility   |
//|   and the value extremality                                      |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::PrepareCalculation()
  {
   // Store the previous value
   m_prev_ma_value = m_ma_value;
   m_ma_value = 0;
   
   m_is_highest = true; 	// check if the current candle is the highest one
   m_is_lowest = true;  	// check if the current candle is the lowest one
   m_volatility = 0;  	// average volatility
   
   double price_sum = 0; // Variable for storing the sum
   for (int i=0; i<m_history_depth; i++)
     {
      if (i<iMAPeriod)
         price_sum += m_candles[i].close;
      if (i>0 && i<=iVolatilityCandlesCount)
         m_volatility += m_candles[i].high - m_candles[i].low;
      if (i>0 && i<=iPrevCandlesCount)
        {
         m_is_highest = m_is_highest && (m_candles[0].high > m_candles[i].high);
         m_is_lowest = m_is_lowest && (m_candles[0].low < m_candles[i].low);
        }
     }
   m_ma_value = price_sum / iMAPeriod;
   m_volatility /= iVolatilityCandlesCount;
   
   m_candle_pattern = CheckCandleSize(m_candles[0]);
  }
//+------------------------------------------------------------------+
//| CheckCandleSize                                                  |
//+------------------------------------------------------------------+
//| Check if the candle sizes comply with the patterns               |
//| The function returns:                                            |
//|   0 - if the candle does not comply with the patterns            |
//|   1 - if "hammer" pattern is detected                            |
//|   -1 - if "shooting star" pattern is detected                    |
//+------------------------------------------------------------------+ 
int CPivotCandlesClass::CheckCandleSize(MqlRates &candle)
  {
   double candle_height=candle.high-candle.low;          // candle's full height
   double candle_body=MathAbs(candle.close-candle.open); // candle's body height

   // Check if the candle has a small body
   if(candle_body/candle_height*100.0>iMaxBodySize)
      return 0;

   double candle_top_shadow=candle.high-MathMax(candle.open,candle.close);   // candle upper shadow height
   double candle_bottom_shadow=MathMin(candle.open,candle.close)-candle.low; // candle bottom shadow height

   // If the upper shadow is very small, that indicates the "hammer" pattern
   if(candle_top_shadow/candle_height*100.0<=iMaxShadowSize)
      return 1;
   // If the bottom shadow is very small, that indicates the "shooting star" pattern
   else if(candle_bottom_shadow/candle_height*100.0<=iMaxShadowSize)
      return -1;
   else
      return 0;
  }
//+------------------------------------------------------------------+
//| DoAnalizeNewCandle                                               |
//+------------------------------------------------------------------+
//| Real analysis of compliance with the patterns                    |
//+------------------------------------------------------------------+ 
int CPivotCandlesClass::DoAnalizeNewCandle()
  {
   // Prepare data for analyzing the current situation
   PrepareCalculation();
   
   // Process prepared data and set the exit signal
   int signal = 0;
   
   ///////////////////////////////////////////////////////////////////
   // EXIT SIGNALS                                                  //
   ///////////////////////////////////////////////////////////////////
   // If price crosses the moving average downwards, short position is closed
   if(m_candles[1].close > m_prev_ma_value && m_candles[0].close < m_ma_value)
      signal = 2;
   // If price crosses the moving average upwards, long position is closed 
   else if (m_candles[1].close < m_prev_ma_value && m_candles[0].close > m_ma_value)
      signal = -2;
      
   ///////////////////////////////////////////////////////////////////
   // ENTRY SIGNALS                                                 //
   ///////////////////////////////////////////////////////////////////
   // Check if the minimum volatility condition is met
   if (m_candles[0].high - m_candles[0].low >= iVolatilityPercent / 100.0 * m_volatility)
     {
      // Checks for "shooting star" pattern
      if (m_candle_pattern < 0 && m_is_highest && m_candles[0].close > m_ma_value)
         signal = -1;
      // Checks for "hammer" pattern
      else if (m_candle_pattern > 0 && m_is_lowest && m_candles[0].close < m_ma_value)
         signal = 1;
     }
     
   return signal;
  }
//+------------------------------------------------------------------+

Bütün hesaplama kısmının CPivotCandlesClass sınıfı tarafından yapıldığını görebiliriz. Hesaplama kısmını görsel kısımdan ayırmanın iyi bir programlama olduğu düşünülür ve bu tavsiyeye uymak için elimden geleni yapıyorum. Faydaları gelmekte gecikmiyor: Göstergenin kendisinin kodu aşağıdadır:

//+------------------------------------------------------------------+
//|                                                 PivotCandles.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

// Use four buffers, while drawing two
#property indicator_buffers 4
#property indicator_plots   2
//--- plot SlowMA
#property indicator_label1  "SlowMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAliceBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot ChartSignal
#property indicator_label2  "ChartSignal"
#property indicator_type2   DRAW_COLOR_ARROW
#property indicator_color2  clrLightSalmon,clrOrangeRed,clrBlack,clrSteelBlue,clrLightBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  3

#include <PivotCandlesClass.mqh>
//+------------------------------------------------------------------+
//| Common arrays and structures                                     |
//+------------------------------------------------------------------+
//--- Indicator buffers                                                
double   SMA[];            // Values of the Moving Average
double   Signal[];         // Signal values
double   ChartSignal[];    // Location of signals on the chart
double   SignalColor[];    // Signal color array
//--- Calculation class
CPivotCandlesClass PivotCandlesClass;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,SMA,INDICATOR_DATA);
   SetIndexBuffer(1,ChartSignal,INDICATOR_DATA);
   SetIndexBuffer(2,SignalColor,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(3,Signal,INDICATOR_CALCULATIONS);

//--- set 0 as an empty value
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int 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 int &spread[])
  {
   // If there have not been calculations yet or (!) the new history is uploaded, clean up the calculation object
   if (prev_calculated == 0)
      PivotCandlesClass.CleanupHistory();
   
   int end_calc_edge = rates_total-1;   
   if (prev_calculated >= end_calc_edge)
      return end_calc_edge;
   
   for(int i=prev_calculated; i<end_calc_edge; i++)
     {
      int signal = PivotCandlesClass.AnalizeNewCandle(time[i],open[i],high[i],low[i],close[i],tick_volume[i],volume[i],spread[i]);
      Signal[i] = signal;
      SMA[i] = PivotCandlesClass.MAValue();
      
      // Signals are processed, display them on the chart
      // Set the location of our signals...
      if (signal < 0)
         ChartSignal[i]=high[i];
      else if (signal > 0)
         ChartSignal[i]=low[i];
      else
         ChartSignal[i]=0;
      // .. as well as their color
      // Signals have a range of [-2..2], while color indices - [0..4]. Align them 
      SignalColor[i]=signal+2;
     }
   
   // Set the Moving Average value similar to the previous one to prevent it from sharp fall
   SMA[end_calc_edge] = SMA[end_calc_edge-1];

//--- return value of prev_calculated for next call
   return(end_calc_edge);
  }
//+------------------------------------------------------------------+

Gösterge hazır. Şimdi, bunu herhangi bir grafik üzerinde test edelim. Bunun için, derlenmiş göstergeyi grafiğe yükleyin. Şimdi, aşağıdaki görüntüde gösterilene benzer bir şey göreceğiz.

Şekil 2. "Çekiç" ve "kayan yıldız" mum modeli göstergesi

Şekil 2. "Çekiç" ve "kayan yıldız" mum modeli göstergesi

Renkli noktalar olası piyasaya girişleri ve çıkışları belirtir. Renkler aşağıdaki gibi seçilir:

  • koyu kırmızı - satış;
  • koyu mavi – alış;
  • açık kırmızı – uzun kapanış pozisyonu;
  • açık kırmızı – kısa kapanış pozisyonu.

Fiyat, hareketli ortalamasına her ulaştığında kapanış sinyalleri oluşturulur. O anda pozisyon yoksa, sinyal yok sayılır.

Şimdi makalenin ana konusuna geçelim. Sadece belirli sinyalleri oluşturan sinyal tamponlu göstergemiz var. Bu sinyallerin, gerçekten takip edilmesi durumunda, ne kadar kârlı/zararlı olabileceğini aynı grafiğin ayrı bir penceresinde gösterelim. Gösterge bu durum için özel olarak geliştirilmiştir. Gösterge, başka bir göstergeye bağlanabilir ve sanal pozisyonları gelen sinyallere göre açıp kapatabilir.

Önceki göstergede olduğu gibi, kodu iki kısma ayırmalıyız: hesaplama ve görsel. Aşağıda uykusuz bir gecenin sonucu var, umarım buna değer :)

//+------------------------------------------------------------------+
//|                                                 BalanceClass.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Common structures                                                |
//+------------------------------------------------------------------+
// Structure for returning calculation results 
// using only return command;
struct BalanceResults
  {
   double balance;
   double equity;
  };
//+------------------------------------------------------------------+
//| Common function                                                  |
//+------------------------------------------------------------------+
//  Function for searching for the indicator handle by its name
int FindIndicatorHandle(string _name)
  {
   // Receive the number of open charts
   int windowsCount = (int)ChartGetInteger(0,CHART_WINDOWS_TOTAL);
   
   // Search all of them
   for(int w=windowsCount-1; w>=0; w--)
     {
      // How many indicators are attached to the current chart
      int indicatorsCount = ChartIndicatorsTotal(0,w);

      // Search by all chart indicators
      for(int i=0;i<indicatorsCount;i++)
        {
         string name = ChartIndicatorName(0,w,i);
         // If such an indicator is found, return its handle
         if (name == _name)
            return ChartIndicatorGet(0,w,name);
        }
     }  
     
   // If there is no such an indicator, return the incorrect handle 
   return -1;
  }
//+------------------------------------------------------------------+
//| Base calculation class                                           |
//+------------------------------------------------------------------+
class CBaseBalanceCalculator
  {
private:
   double            m_position_volume; // Current open position volume
   double            m_position_price;  // Position opening price
   double            m_symbol_points;   // Value of one point for the current symbol
   BalanceResults    m_results;         // Calculation results
public:
   void              CBaseBalanceCalculator(string symbol_name = "");
   void              Cleanup();
   BalanceResults    Calculate( const double _prev_balance, 
                                const int    _signal,
                                const double _next_open,
                                const double _next_spread );
  };
//+------------------------------------------------------------------+
//| CBaseBalanceCalculator                                           |
//+------------------------------------------------------------------+
void CBaseBalanceCalculator::CBaseBalanceCalculator(string symbol_name = "")
  {
   // Clean up state variables
   Cleanup();
   
   // Define point size (because we will calculate the profit in points)
   if (symbol_name == "")
      m_symbol_points = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
   else 
      m_symbol_points = SymbolInfoDouble(symbol_name, SYMBOL_POINT);
  }
//+------------------------------------------------------------------+
//| Cleanup                                                          |
//+------------------------------------------------------------------+
//| Clean up data on positions and prices                            |
//+------------------------------------------------------------------+
void CBaseBalanceCalculator::Cleanup()
  {
   m_position_volume = 0;
   m_position_price = 0;  
  }
//+------------------------------------------------------------------+
//| Calculate                                                        |
//+------------------------------------------------------------------+
//| Main calculation block                                           |
//+------------------------------------------------------------------+
BalanceResults CBaseBalanceCalculator::Calculate(
                                       const double _prev_balance,
                                       const int _signal,
                                       const double _next_open,
                                       const double _next_spread )
  {
   // Clean up the output structure from the previous values
   ZeroMemory(m_results);
   
   // Initialize additional variables
   double current_price = 0; // current price (bid or ask depending on position direction)
   double profit = 0;        // profit calculated value
   
   // If there was no signal, the balance remains the same 
   if (_signal == 0)
      m_results.balance = _prev_balance;
   // the signal coincides with the direction or no positions are opened yet
   else if (_signal * m_position_volume >= 0)
     {
      // Position already exists, the signal is ignored
      if (m_position_volume != 0)
         // Balance is not changed
         m_results.balance = _prev_balance;
      // No positions yet, buy signal

      else if (_signal == 1)
        {
         // Calculate current ASK price, recalculate price, volume and balance
         current_price = _next_open + _next_spread * m_symbol_points;
         m_position_price = (m_position_volume * m_position_price + current_price) / (m_position_volume + 1);
         m_position_volume = m_position_volume + 1;
         m_results.balance = _prev_balance;
        }
      // No positions yet, sell signal
      else if (_signal == -1) 
        {
         // Calculate current BID price, recalculate price, volume and balance
         current_price = _next_open;
         m_position_price = (-m_position_volume * m_position_price + current_price) / (-m_position_volume + 1);
         m_position_volume = m_position_volume - 1; 
         m_results.balance = _prev_balance;      
        }
      else
         m_results.balance = _prev_balance;
     }
   // Position is set already, the opposite direction signal is received
   else 
     {
      // buy signal/close sell position
      if (_signal > 0)
        {
         // Close position by ASK price, recalculate profit and balance
         current_price = _next_open + _next_spread * m_symbol_points;
         profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
         m_results.balance = _prev_balance + profit;
          
         // If there is a signal for opening a new position, open it at once
         if (_signal == 1)
           {
            m_position_price = current_price;
            m_position_volume = 1;
           }
         else
            m_position_volume = 0;
        }
      // sell signal/close buy position
      else 
        {
         // Close position by BID price, recalculate profit and balance
         current_price = _next_open;
         profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
         m_results.balance = _prev_balance + profit;
         
         // If there is a signal for opening a new position, open it at once
         if (_signal == -1)
           {
            m_position_price = current_price;
            m_position_volume = -1;
           }
         else 
           m_position_volume = 0;
        }
     }
    
   // Calculate the current equity
   if (m_position_volume > 0)
     {
      current_price = _next_open;
      profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
      m_results.equity = m_results.balance + profit;
     }
   else if (m_position_volume < 0)
     {
      current_price = _next_open + _next_spread * m_symbol_points;
      profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
      m_results.equity = m_results.balance + profit;
     }
   else
      m_results.equity = m_results.balance;    
   
   return m_results;
  }
//+------------------------------------------------------------------+

Hesaplama sınıfı hazır. Şimdi, nasıl çalıştığını görmek için gösterge ekranını uygulamalıyız.

//+------------------------------------------------------------------+
//|                                                      Balance.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window

#property indicator_buffers 4
#property indicator_plots   3
#property indicator_level1  0.0 
#property indicator_levelcolor Silver 
#property indicator_levelstyle STYLE_DOT
#property indicator_levelwidth 1  
//--- plot Balance
#property indicator_label1  "Balance"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrBlue,clrRed
#property indicator_style1  STYLE_DOT
#property indicator_width1  1
//--- plot Equity
#property indicator_label2  "Equity"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLime
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Zero
#property indicator_label3  "Zero"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGray
#property indicator_style3  STYLE_DOT
#property indicator_width3  1

#include <BalanceClass.mqh>
//+------------------------------------------------------------------+
//| Input and global variables                                       |
//+------------------------------------------------------------------+
input string   iParentName        = "";             // Indicator name for balance calculation
input int      iSignalBufferIndex = -1;            // Signal buffer's index number
input datetime iStartTime         = D'01.01.2012';  // Calculation start date
input datetime iEndTime           = 0;             // Calculation end date
//--- Indicator buffers 
double   Balance[];       // Balance values
double   BalanceColor[];  // Color index for drawing the balance
double   Equity[];        // Equity values
double   Zero[];          // Zero value for histogram's correct display
//--- Global variables
double   Signal[1];       // Array for receiving the current signal
int      parent_handle;   // Indicator handle, the signals of which are to be used 

CBaseBalanceCalculator calculator; // Object for calculating balance and equity
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {     
   // Binding indicator buffers
   SetIndexBuffer(0,Balance,INDICATOR_DATA);
   SetIndexBuffer(1,BalanceColor,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2,Equity,INDICATOR_DATA);
   SetIndexBuffer(3,Zero,INDICATOR_DATA);
  
   // Search for indicator handle by its name
   parent_handle = FindIndicatorHandle(iParentName);
   if (parent_handle < 0)
     {
      Print("Error! Parent indicator not found");
      return -1;
     } 
   
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int 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 int &spread[])
  {   
   // Set the borders for calculating the indicator
   int start_index = prev_calculated;
   int end_index = rates_total-1;
   
   // Calculate balance and equity values
   for(int i=start_index; i<end_index; i++)
     {
      // Check if the balance calculation corresponds the interval
      if (time[i] < iStartTime)
        {
         Balance[i] = 0;
         Equity[i] = 0; 
         continue;
        }
      if (time[i] > iEndTime && iEndTime != 0)
        {
         Equity[i] = (i==0) ? 0 : Equity[i-1];
         Balance[i] = Equity[i]; 
         continue;
        }
      
      // Request a signal from the parent indicator
      if(CopyBuffer(parent_handle,iSignalBufferIndex,time[i],1,Signal)==-1) // Copy the indicator main line data
        {
         Print("Data copy error: " + IntegerToString(GetLastError()));
         return(0);  // Finish the function operation and send indicator for the full recalculation
        }
      
      // Initialize balance and equity calculation
      // Since the signal is formed when the candle is closing, we will be able 
      //   to perform any operation only at the next candle's opening price
      BalanceResults results = calculator.Calculate(i==0?0:Balance[i-1], (int)Signal[0], open[i+1], spread[1+1]);
      
      // Fill out all indicator buffers
      Balance[i] = results.balance;
      Equity[i] = results.equity; 
      Zero[i] = 0;
      if (Balance[i] >= 0)
         BalanceColor[i] = 0;
      else
         BalanceColor[i] = 1;
     }
     
   // Fill out buffers for the last candle 
   Balance[end_index] = Balance[end_index-1];
   Equity[end_index] = Equity[end_index-1]; 
   BalanceColor[end_index] = BalanceColor[end_index-1];
   Zero[end_index] = 0;
     
   return rates_total;
  }
//+------------------------------------------------------------------+

Sonunda bitti! Derleyelim ve sonuçları inceleyelim.


Kullanım Talimatları

Yeni geliştirilen göstergemizin çalışmasını değerlendirmek için, göstergenin en az bir sinyal göstergesi içeren çizelgeye eklenmesi gerekiyor. Tüm adımları izlediyseniz, halihazırda böyle bir göstergemiz var: PivotCandles. Öyleyse şimdi giriş parametrelerini yapılandırmamız gerekiyor. Bakalım neyi belirtmemiz gerekiyor:

  • Bakiye hesaplaması için gösterge adı (dize) – bakiye göstergesi bağlamanın isme göre yapıldığını unutmamalıyız. Dolayısıyla, bu alan zorunludur.
  • Sinyal tamponunun indis numarası (tamsayı) – başka bir kritik parametre. Sinyal göstergesi, önceden tanımlanan algoritmaya göre birkaç sinyal oluşturabilir. Bu nedenle, bakiye göstergesinde hesaplaması gereken tampon sinyali ile ilgili veriler olmalıdır.
  • Hesaplama başlangıç ​​tarihi (tarih/saat) – bakiye hesaplamasının başlangıç ​​tarihi.
  • Hesaplama bitiş tarihi (tarih/saat) – bakiye hesaplamasının bitiş tarihi. Tarih seçilmezse (sıfıra eşit), son çubuğa kadar hesaplama yapılır.

Şekil 3, bakiye göstergesini PivotCandles göstergesinin üçüncü tamponuna eklenmesi için ilk iki parametrenin konfigürasyonunu gösterir. Kalan iki parametre istediğiniz şekilde ayarlanabilir.

Şekil 3. Bakiye göstergesi parametreleri

Şekil 3. Bakiye göstergesi parametreleri

Önceki tüm adımlar doğru bir şekilde yapıldıysa, aşağıda gösterilene çok benzer bir görüntü görmeniz gerekir.

Şekil 4. PivotCandles göstergesinin sinyalleri kullanılarak oluşturulan bakiye ve öz varlık eğrileri

Şekil 4. PivotCandles göstergesinin sinyalleri kullanılarak oluşturulan bakiye ve öz varlık eğrileri

Artık farklı zaman aralıklarını ve sembollerini deneyebilir ve en kârlı ve zarara neden olan piyasa girişlerini bulabiliriz. Bu yaklaşımın, alım satım sonuçlarınızı etkileyen piyasa korelasyonlarını bulmaya da yardımcı olduğu eklenmelidir.

Başlangıçta, Uzman Danışmanı aynı sinyallere dayanarak test etmek için harcanan süreyi yukarıda açıklanan yöntemi kullanarak harcanan süre ile karşılaştırmak istedim. Ama daha sonra göstergenin yeniden hesaplanması yaklaşık bir saniye sürdüğü için bu fikirden vazgeçtim. Bu kadar kısa bir süre, geçmiş yüklemeleri ve tik oluşturma algoritmalarına sahip Uzman Danışman ile elde edilememektedir.


Sonuç

Yukarıda açıklanan yöntem çok hızlıdır. Ayrıca, pozisyon açılış/kapanış sinyalleri oluşturan göstergelerin testinde netlik sağlar. Yatırımcıların tek bir grafik penceresinde sinyalleri ve mevduatın bunlara verdiği yanıtları analiz etmelerini sağlar. Ancak yine de farkında olmamız gereken sınırlamaları vardır:

  • Analiz edilen göstergenin sinyal tamponu önceden hazırlanmalıdır;
  • Sinyaller yeni çubuğun açık zamanına bağlıdır;
  • Bakiyeyi hesaplarken ММ yoktur.

Yine de, bu eksikliklere rağmen, faydalarının daha da önemli hale geleceğini ve piyasa davranışını analiz etmek ve piyasa tarafından üretilen sinyalleri işlemek için tasarlanmış diğer araçlar arasında bu test yönteminin de yerini alacağını umuyorum.

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/505

Ekli dosyalar |
balanceclass.mqh (8.07 KB)
balance.mq5 (5.57 KB)
pivotcandles.mq5 (4.14 KB)
MetaTrader 5 için Alım Satım Sinyalleri: PAMM Hesaplarına Daha İyi Bir Alternatif! MetaTrader 5 için Alım Satım Sinyalleri: PAMM Hesaplarına Daha İyi Bir Alternatif!
MetaTrader 5'in artık Alım Satım Sinyalleri içerdiğini ve böylece yatırımcılara ve yöneticilere güçlü bir araç sağladığını duyurmaktan mutluluk duyuyoruz. Siz başarılı bir yatırımcının alım satımlarını takip ederken, terminal otomatik olarak bunları hesabınıza kopyalayacaktır!
Alım Satımlar MetaTrader 5'ten MetaTrader 4'e Nasıl Kopyalanır? Alım Satımlar MetaTrader 5'ten MetaTrader 4'e Nasıl Kopyalanır?
Bugün gerçek bir MetaTrader 5 hesabında alım satım yapmak mümkün mü? Böyle bir alım satım nasıl organize edilir? Bu makale, bu soruların teorisini ve alım satımları MetaTrader 5 terminalinden MetaTrader 4'e kopyalamak için kullanılan çalışma kodlarını içerir. Makale, hem Uzman Danışman geliştiricileri hem de yatırımcılar için faydalı olacaktır.
MQL5 Programlama Temelleri: Diziler MQL5 Programlama Temelleri: Diziler
Diziler, değişkenler ve fonksiyonlar ile birlikte, hemen hemen tüm programlama dillerinin entegre bir parçasıdır. Bu makale öncelikle acemi MQL5 programcılarının ilgisini çekecektir, ancak tecrübeli programcılar da bilgilerini özetleme ve sistematize etme fırsatına sahip olacaktır.
MetaTrader 5 Terminalinin Strateji Test Cihazında Tik Oluşturma Algoritması MetaTrader 5 Terminalinin Strateji Test Cihazında Tik Oluşturma Algoritması
MetaTrader 5, Uzman Danışmanlar ve MQL5 dilini kullanarak gömülü bir strateji test cihazı içinde otomatik alım satımı simüle etmemizi sağlar. Bu tarz bir simülasyon, Uzman Danışmanların test edilmesi olarak adlandırılır ve çok sayıda aletin yanı sıra çok sayıda aletin kullanıldığı optimizasyon kullanılarak da uygulanabilir. Kapsamlı bir test sağlamak için, mevcut dakika tarihine dayanan bir tik jenerasyonu yapılmalıdır. Bu makalede MetaTrader 5 istemci terminalindeki geçmiş testi için tiklerin oluşturulduğu algoritmanın ayrıntılı bir açıklaması sunulur.