Alım Satım Fikirlerinin Grafik Üzerinde Hızlı Testi
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:
- Bir sinyal oluşturacak şekilde gerekli göstergeyi oluşturuyorum veya değiştiriyorum: satış için -1 ve alış için 1.
- 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.
- 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
Şimdi "çekiç" modeli göründüğünde piyasaya giriş kurallarını tanımlayalım.
- Mumun en düşük değeri, önceki beş mumdan daha düşük olmalıdır;
- Mumun gövdesi, toplam yüksekliğinin %50'sini geçmemelidir;
- Mumun üst gölgesi, toplam yüksekliğinin %0'ını geçmemelidir;
- Mumun yüksekliği, önündeki beş mumun ortalama yüksekliğinin %100'ünden az olmamalıdır;
- 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:
- Mumun en yüksek değeri, önceki beş mumdan daha yüksek olmalıdır;
- Mumun gövdesi, toplam yüksekliğinin %50'sini geçmemelidir;
- Mumun alt gölgesi, toplam yüksekliğinin %0'ını geçmemelidir;
- Mumun yüksekliği, önündeki beş mumun ortalama yüksekliğinin %100'ünden az olmamalıdır;
- 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
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
Ö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
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
- Ücretsiz ticaret 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