English Русский 中文 Español Deutsch 日本語 Português
Örneklerle formasyonlar (Bölüm I): Çoklu tepe

Örneklerle formasyonlar (Bölüm I): Çoklu tepe

MetaTrader 5Alım-satım sistemleri | 27 Ağustos 2024, 13:37
312 0
Evgeniy Ilin
Evgeniy Ilin

İçindekiler


Özet

Formasyonlar birçok yatırımcı tarafından kullanıldığı için internette sıklıkla tartışılmaktadır. Formasyonlar, takip eden fiyatlamanın yönünü belirlemek için görsel analiz kriterleri olarak adlandırılabilir. Algo alım-satım bundan farklıdır. Algoritmik alım-satım için görsel kriterler olamaz. Uzman Danışmanlar ve göstergeler, fiyat serileriyle çalışmak için ayrı yöntemlere sahiptir. Her iki uçta da avantajlar ve dezavantajlar vardır. Kod, insan düşüncesinin genişliğinden ve insan analizinin kalitesinden yoksundur, ancak kodun başka değerli avantajları vardır: kıyaslanamaz hız ve birim zamanda işlenen sayısal veya mantıksal verilerin kıyaslanamaz miktarı. Makineye ne yapması gerektiğini söylemek kolay değildir. Bu biraz pratik gerektirir. Zamanla programcı makineyi, makine de programcıyı anlamaya başlar. Bu makale serisi, düşüncelerini nasıl yapılandıracaklarını ve karmaşık görevleri daha basit adımlara nasıl böleceklerini öğrenecek olan yeni başlayanlar için yararlı olacaktır.


Terse dönüş formasyonları hakkında

Şahsen benim için terse dönüş formasyonlarının çok muğlak bir tanımı var. Dahası, bunların altında yatan herhangi bir matematik yoktur. Dürüst olmak gerekirse, herhangi bir formasyonun altında yatan bir matematik yoktur ve dolayısıyla burada dikkate alınabilecek tek matematik istatistiktir. İstatistikler doğrunun tek ölçütüdür, ancak istatistikler gerçek alım-satıma dayalı olarak derlenir. Açıkçası, çok kesin istatistikler sağlayabilecek hiçbir kaynak bulunmamaktadır. Belirli bir araştırma problemi için bu tür verileri sağlamanın bir anlamı bile yoktur. Buradaki tek çözüm, strateji sınayıcıda geriye dönük test ve görselleştirmedir. Bu yaklaşım daha düşük veri kalitesi sunsa da, veri miktarı ile birlikte hız gibi yadsınamaz bir avantaja sahiptir. 

Elbette, terse dönüş formasyonları trend terse dönüşlerini belirlemek için yeterli bir araç değildir, ancak seviyeler veya mum çubuğu analizi gibi diğer analiz yöntemleriyle birlikte kullanıldıklarında istenen sonucu verebilirler. Bu makale serisinde, formasyonlar özellikle ilginç bir analiz yöntemi olarak görülmemektedir, ancak algoritmik alım-satım becerilerini pratik etmek için kullanılabilirler. Pratik yapmanın yanı sıra, ilginç ve kullanışlı bir yardımcı araç elde edeceksiniz - algo alım-satım için değilse de, yatırımcı gözü için. Faydalı göstergelere büyük değer verilmektedir.


Neden çoklu tepe - spesifik özellikleri

Bu formasyon, basitliği nedeniyle internette oldukça popüler hale gelmiştir. Formasyon, farklı alım-satım enstrümanlarında ve çeşitli grafik zaman dilimlerinde oldukça yaygındır, çünkü bu formasyonda karmaşık bir şey yoktur. Ayrıca, formasyona daha yakından bakarsanız, yöntem konseptinin algo alım-satım ve MQL5 dil yetenekleri kullanılarak genişletilebileceğini görebilirsiniz. Sadece çift tepe ile sınırlı kalmayacak bazı genel kodlar oluşturmayı deneyebiliriz. Akıllıca oluşturulmuş bir prototip, tüm formasyon melezlerini ve devam ürünlerini keşfetmek için kullanılabilir.

Çoklu tepenin klasik devamı, çok popüler olan "baş ve omuzlar" formasyonudur. Ne yazık ki, bu formasyonla nasıl alım-satım yapılacağına dair yapılandırılmış bir bilgi yoktur. Bu sorun pek çok popüler strateji için ortaktır - çünkü pek çok güzel kelime vardır ancak istatistik yoktur. Bu makalede bunları algoritmik alım-satım çerçevesinde kullanmanın mümkün olup olmadığını anlamaya çalışacağız. Bir demo veya gerçek hesapta alım-satım yapmadan istatistik toplamanın tek yöntemi, strateji sınayıcının yeteneklerini kullanmaktır. Bu araç olmadan, belirli bir stratejiye ilişkin herhangi bir karmaşık sonuç çıkaramazsınız.


Çift tepe konsepti genişletilebilir mi?

Makalenin konusuyla ilgili olarak, çift tepeden başlayan bir formasyon ağacı olarak bir diyagram çizmeye çalışacağım. Bu, bu konseptin olanaklarının ne kadar geniş olduğunun anlaşılmasına yardımcı olacaktır:

Tree

Birkaç formasyon konseptini, yaklaşık olarak aynı fikre dayandıkları varsayımıyla birleştirmeye karar verdim. Bu fikrin basit bir başlangıcı vardır - herhangi bir yönde iyi bir hareket bul ve terse dönmesi gereken yeri doğru bir şekilde belirle. Formasyonu gördükten sonra, yatırımcı, formasyonun belirli kriterleri karşılayıp karşılamadığını değerlendirmenin yanı sıra, piyasaya giriş noktasını ve hedef ve Zararı Durdur seviyesini belirlemede yardımcı olacak bazı yardımcı çizgileri doğru bir şekilde çizmelidir. Burada hedef yerine Kârı al kullanılabilir.

Formasyonlar, bu formasyonların konseptinin birleştirilebileceği bazı ortak yapı ilkelerine sahip olabilir. Bu net tanım, algoritmik yatırımcıları manuel yatırımcılardan ayıran şeydir. Belirsizlik ve aynı ilkelerin birden fazla yorumlanması hayal kırıklığı yaratan sonuçlara yol açabilir.

Temel formasyonlar aşağıdaki gibidir:

  1. Çift tepe
  2. Üçlü tepe
  3. Baş ve omuzlar

Bu formasyonlar benzer yapılara ve kullanım ilkelerine sahiptir. Bunların hepsi terse dönüşleri tespit etmeyi amaçlamaktadır. Her üç formasyon da yardımcı çizgiler konusunda benzer bir mantığa sahiptir. Bunu çift tepe örneği ile açıklayacağım:

Double extremum

Yukarıdaki şekilde, gerekli tüm çizgiler numaralandırılmıştır ve aşağıdaki anlama gelmektedir:

  1. Trend direnci
  2. Kötümser bir tepe seviyesi tanımlamak için yardımcı çizgi (kimisi bunun bir boyun olduğunu düşünür)
  3. Boyun çizgisi
  4. İyimser hedef (aynı zamanda alım-satım için bir Kârı Al seviyesidir)
  5. İzin verilen maksimum Zararı Durdur seviyesi (en üste ayarlanır)
  6. İyimser tahmin çizgisi (önceki trend hareketine eşit)

Kötümser hedef, boyun çizgisinin piyasaya en yakın kenarla kesiştiği noktaya göre belirlenir - "t" olarak gösterilen "1" ve "2" arasındaki mesafeyi alırız ve terse dönüş yönünde aynı mesafeyi ölçeriz. İyimser hedefin minimum değeri de benzer şekilde belirlenir, ancak mesafe "5" ile "3" arasında ölçülür ve "s" olarak gösterilir.


Çoklu tepe çizmek için kod yazma

Bu formasyonları tanımlamak için akıl yürütme mantığını tanımlayarak başlayalım. Bir formasyon bulmak için çubuk mantığına bağlı kalmalıyız, yani tiklere göre değil, çubuklara göre çalışacağız. Bu durumda, gereksiz hesaplamalardan kaçınılacağı için terminal üzerindeki yükü büyük ölçüde azaltacaktır. Öncelikle, formasyonu arayacak bağımsız bir gözlemciyi simgeleyen bir sınıf belirleyelim. Doğru bir formasyon tespiti için gereken tüm çalışma örneğin bir parçası olacaktır, bu nedenle arama bunun içinde gerçekleştirilecektir. Bu çözümü, işlevselliği genişletmemiz veya mevcut özellikleri değiştirmemiz gerektiğinde daha fazla kod değişikliğini mümkün kılmak için seçtim.

Sınıf haritası

Sınıf içeriğini inceleyerek başlayalım:

class ExtremumsPatternFamilySearcher// class simulating an independent pattern search
   {
   private:
   int BarsM;// how many bars on chart to use
   int MinimumSeriesBarsM;// the minimum number of bars in a row to detect a top
   int TopsM;// number of tops in the pattern
   int PointsPessimistM;// minimum distance in points to the nearest target
   double RelativeUnstabilityM;// maximum excess of the head size relative to the minimum shoulder
   double RelativeUnstabilityMinM;// minimum excess of the head size relative to the minimum shoulder
   double RelativeUnstabilityTimeM;// maximum excess of head and shoulders sizes
   bool bAbsolutelyHeadM;// whether a pronounced head is required
   bool bRandomExtremumsM;// random selection of extrema
     


   struct Top// top data
      {
      datetime Datetime0;// time of the candlestick closest to the market
      datetime Datetime1;// time of the next candlestick
      int Index0;// index of the candlestick closest to the market
      int Index1;// index of the next candlestick
      datetime DatetimeExtremum;// time of the top
      int IndexExtremum;// index of the top
      double Price;// price of the top
      bool bActive;// if the top is active (if not, then it does not exist)
      };
   
   struct Line// line
      {
      double Price0;// price of the candlestick closest to the market, to which the line is bound
      datetime Time0;// time of the candlestick closest to the market, to which the line is bound
      double Price1;// price of the farthest candlestick to which the line is bound
      datetime Time1;// time of the farthest candlestick to which the line is bound
      datetime TimeX;// time of the X point
      int Index1;// index of the left edge
      bool DirectionOfFormation;// direction
      double C;// free coefficient in the equation
      double K;// aspect ratio
   
      void CalculateKC()// find unknowns in the equation
         {
         if ( Time0 != Time1 ) K=double(Price0-Price1)/double(Time0-Time1);
         else K=0.0;
         C=double(Price1)-K*double(Time1);
         }
      
      double Price(datetime T)// function of line depending on time
         {
         return K*T+C;
         }
      };
   
   public:   
   
   ExtremumsPatternFamilySearcher(int BarsI,int MinimumSeriesBarsI,int TopsI,int PointsPessimistI, double RelativeUnstabilityI,
   double RelativeUnstabilityMinI,double RelativeUnstabilityTimeI,bool bAbsolutelyHeadI,bool bRandomExtremumsI)// parametric constructor
      {
      BarsM=BarsI;
      MinimumSeriesBarsM=MinimumSeriesBarsI;
      TopsM=TopsI;
      PointsPessimistM=PointsPessimistI;
      RelativeUnstabilityM=RelativeUnstabilityI;
      RelativeUnstabilityMinM=RelativeUnstabilityMinI;
      RelativeUnstabilityTimeM=RelativeUnstabilityTimeI;
      bAbsolutelyHeadM=bAbsolutelyHeadI;
      bRandomExtremumsM=bRandomExtremumsI;
      bPatternFinded=bFindPattern();
      }
      
   int FormationDirection;// direction of the formation (multiple top or bottom, or none at all) ( -1,1,0 )      
   bool bPatternFinded;// if the pattern was found during formation
   Top TopsUp[];// required upper extrema
   Top TopsDown[];// required lower extrema
   Top TopsUpAll[];// all upper extrema
   Top TopsDownAll[];// all lower extrema
   int RandomIndexUp[];// array for the random selection of the tops index
   int RandomIndexDown[];// array for the random selection of the bottoms index
   Top StartTop;// where the formation starts (top farthest from the market)
   Top EndTop;// where the formation ends (top closest to the market)
   Line Neck;// neck
   Top FarestTop;// top farthest from the neck (will be used to determine the head or the formation size) or the same as the head
   Line OptimistLine;// line of optimistic forecast
   Line PessimistLine;// line of pessimistic forecast
   Line BorderLine;// line at the edge of the pattern
   Line ParallelLine;// line parallel to the trend resistance
   
      
   private:
   void SetTopsSize();// setting sizes for arrays with tops
   bool SearchFirstUps();// search for tops
   bool SearchFirstDowns();// search for bottoms
   void CalculateMaximum(Top &T,int Index0,int Index1);// calculate the maximum price between two bars
   void CalculateMinimum(Top &T,int Index0,int Index1);// calculate the minimum price between two bars
   bool PrepareExtremums();// prepare extrema
   bool IsExtremumsAbsolutely();// control the priority of tops
   void DirectionOfFormation();// determine the direction of the formation
   void FindNeckUp(Top &TStart,Top &TEnd);// find neck for the bullish pattern
   void FindNeckDown(Top &TStart,Top &TEnd);// find neck for the bearish pattern
   void SearchFarestTop();// find top farthest from the neck
   bool bBalancedExtremums();// initial balancing of extrema (so that they do not differ much)
   bool bBalancedExtremumsHead();// if a pattern has more than 2 tops, we can check for a pronounced head
   bool bBalancedExtremumsTime();// require that the extrema be not very far in time relative to the minimum distance
   bool bBalancedHead();// balance the head (in other words, require that it be neither the first nor the last one on the list of tops, if there are more than three of them)
   bool CorrectNeckUpLeft();// adjust the neck so as to find the intersection of price and neck (this creates prerequisites for the previous trend) 
   bool CorrectNeckDownLeft();// similarly for the bottom
   int CorrectNeckUpRight();// adjust the neck so as to find the intersection of price and neck on the right or at the current price position, which is the same (to determine the entry point)
   int CorrectNeckDownRight();// similarly for the bottom
   void SearchLineOptimist();// calculate the optimistic forecast line
   bool bWasTrend();// determine whether a trend preceded the pattern definition (in this case the optimistic target line is considered as the trend beginning)
   void SearchLineBorder();// determine trend resistance or support (usually a sloping line)
   void CalculateParallel();// determine a line parallel to support or resistance (crosses the neck at the pattern low or high)
   bool bCalculatePessimistic();// calculate the line of the pessimistic target
   bool bFindPattern();// perform all the above actions
   int iFindEnter();// find intersection with the neck
   public:
   void CleanAll();// clean up objects
   void DrawPoints();// draw points
   void DrawNeck();// draw the neck
   void DrawLineBorder();// line at the border
   void DrawParallel();// line parallel to the border
   void DrawOptimist();// line of optimistic forecast
   void DrawPessimist();// line of pessimistic forecast
   };

Bir sınıf, bir kişinin bir makinenin yerinde olması durumunda gerçekleştireceği sıralı işlemleri temsil eder. Her halükarda, herhangi bir formasyonun tespiti birbirini takip eden bir dizi basit işleme ayrılabilir. Matematikte bir kural vardır: bir denklemi nasıl çözeceğinizi bilmiyorsanız, basitleştirin. Bu kural sadece matematik için değil, herhangi bir algoritma için de geçerlidir. İlk olarak tespit mantığı net değildir. Ancak tespite nereden başlayacağınızı bilirseniz, görev çok daha basit hale gelir. Bu durumda, tüm formasyonu bulmak için ya tepeleri ya da dipleri ya da aslında her ikisini de ararız.

Tepeleri ve dipleri belirleme

Tepeler ve dipler olmadan tüm formasyon anlamsızdır, çünkü tepelerin ve diplerin varlığı formasyon için gerekli bir koşuldur, ancak bu koşul tek başına yeterli değildir. Tepeleri belirlemenin farklı yolları vardır. En önemli koşul, belirgin bir yarım dalganın varlığıdır; yarım dalga ise iki belirgin zıt hareketle belirlenir; bizim durumumuzda bu hareketlerin tek yönde arka arkaya birkaç çubuk olması gerekir. Bu amaçla, bir yönde hareketin varlığını gösteren minimum çubuk sayısını belirlememiz gerekiyor. Bunun için bir girdi değişkeni sağlayalım. 

bool ExtremumsPatternFamilySearcher::SearchFirstUps()// find tops
   {
   int NumUp=0;// the number of found tops
   int NumDown=0;// the number of found bottoms
   bool bDown=false;// an auxiliary boolean which shows if a segment of bearish candlesticks has been found
   bool bUp=false;// an auxiliary boolean which shows if a segment of bullish candlesticks has been found
   bool bNextUp=true;// can we move on to searching for the next top
   bool bNextDown=true;// can we move on to searching for the next bottom
   
   for(int i=0;i<ArraySize(TopsUp);i++)// before search, set all necessary tops to an inactive state
      {
      TopsUp[i].bActive=false;
      }
   for(int i=0;i<ArraySize(TopsUpAll);i++)// before search, set all tops to an inactive state
      {
      if (!TopsUpAll[i].bActive) break;
      TopsUpAll[i].bActive=false;
      }
               
   
   for(int i=0;i<BarsM;i++)
      {
      if ( i+MinimumSeriesBarsM-1 < BarsM )// if remaining bars are enough to determine the extremum and we can start searching for the next top
         {
         if ( bNextUp )// if it is allowed to search for the next top
            {
            bDown=true;
            for(int j=i;j<i+MinimumSeriesBarsM;j++)// determine the first extrema for upper tops
               {
               if ( Open[j]-Close[j] < 0 )// if at least one of the selected candlesticks was upward
                  {
                  bDown=false;
                  break;
                  }
               }
            if ( bDown )
               {
               TopsUpAll[NumUp].Datetime0=Time[i+MinimumSeriesBarsM-1];
               TopsUpAll[NumUp].Index0=i+MinimumSeriesBarsM-1;
               bNextUp=false;
               }
            }        
         }

      if ( MinimumSeriesBarsM+i < BarsM && bDown )// if the remaining bars are enough to determine the second half of the extremum and the previous half has been found
         {
         bUp=true;                  
         for(int j=i;j<MinimumSeriesBarsM+i;j++)//determine further candlesticks in the opposite direction
            {
            if ( Open[j]-Close[j] > 0 )//if at least one of the selected candlesticks was downward
               {
               bUp=false;
               break;
               }
            }
         if ( bUp )
            {
            TopsUpAll[NumUp].Datetime1=Time[i];
            TopsUpAll[NumUp].Index1=i;
            TopsUpAll[NumUp].bActive=true;
            bNextUp=false;
            }   
         } 
      // after that, register the found formation as a top, if it is a top
      if ( bDown && bUp )
         {
         CalculateMaximum(TopsUpAll[NumUp],TopsUpAll[NumUp].Index0,TopsUpAll[NumUp].Index1);// calculate extremum between two bars
         bNextUp=true;
         bDown=false;
         bUp=false;
         NumUp++;
         }
      }
   if ( NumUp >= TopsM ) return true;// if the required number of tops have been found
   else return false;
   }

Dipler ise tam tersi şekilde tanımlanır:

bool ExtremumsPatternFamilySearcher::SearchFirstDowns()// find bottoms
   {
   int NumUp=0;
   int NumDown=0;
   bool bDown=false;// an auxiliary boolean which shows if a segment of bearish candlesticks has been found
   bool bUp=false;// an auxiliary boolean which shows if a segment of bullish candlesticks has been found
   bool bNextUp=true;// can we move on to searching for the next top
   bool bNextDown=true;// can we move on to searching for the next bottom

   for(int i=0;i<ArraySize(TopsDown);i++)// before search, set all necessary bottoms to an inactive state
      {
      TopsDown[i].bActive=false;
      }
   for(int i=0;i<ArraySize(TopsDownAll);i++)// before search, set all bottoms to an inactive state
      {
      if (!TopsDownAll[i].bActive) break;
      TopsDownAll[i].bActive=false;
      }

   for(int i=0;i<BarsM;i++)
      {
      if ( i+MinimumSeriesBarsM-1 < BarsM )// if remaining bars are enough to determine the extremum and we can start searching for the next top
         {
         if ( bNextDown )// if it is allowed to search for the next bottom
            {
            bUp=true;               
            for(int j=i;j<i+MinimumSeriesBarsM;j++)// determine the first extrema for upper tops
               {
               if ( Open[j]-Close[j] > 0 )//if at least one of the selected candlesticks was downward
                  {
                  bUp=false;
                  break;
                  }
               }
            if ( bUp )
               {
               TopsDownAll[NumDown].Datetime0=Time[i+MinimumSeriesBarsM-1];
               TopsDownAll[NumDown].Index0=i+MinimumSeriesBarsM-1;
               bNextDown=false;
               }
            }        
         }

      if ( MinimumSeriesBarsM+i < BarsM && bUp )// if the remaining bars are enough to determine the second half of the extremum and the previous half has been found
         {   
         bDown=true;                              
         for(int j=i;j<MinimumSeriesBarsM+i;j++)//determine further candlesticks in the opposite direction
            {
            if ( Open[j]-Close[j] < 0 )// if at least one of the selected candlesticks was upward
               {
               bDown=false;
               break;
               }
            }
         if ( bDown )
            {
            TopsDownAll[NumDown].Datetime1=Time[i];
            TopsDownAll[NumDown].Index1=i;
            TopsDownAll[NumDown].bActive=true;
            bNextDown=false;              
            }
         } 
      // after that, register the found formation as a bottom, if it is a bottom
      if ( bDown && bUp )
         {
         CalculateMinimum(TopsDownAll[NumDown],TopsDownAll[NumDown].Index0,TopsDownAll[NumDown].Index1);// calculate extremum between two bars
         bNextDown=true;
         bDown=false;
         bUp=false;            
         NumDown++;
         }
      }
      
   if ( NumDown == TopsM ) return true;//if the required number of bottoms have been found
   else return false;
   }

Bu durumda fraktal mantığını kullanmadım. Bunun yerine, tepeleri ve dipleri belirlemek için kendi mantığımı oluşturdum. Fraktallardan daha iyi veya daha kötü olduğunu düşünmüyorum, ancak en azından herhangi bir harici işlevsellik kullanmaya gerek yok. Ayrıca, bazen gerekli olmayan gereksiz yerleşik dil fonksiyonlarını kullanmaya gerek yoktur. Bu fonksiyonlar iyi olabilir, ancak bu durumda gereksizdirler. Fonksiyon, gelecekte birlikte çalışacağımız tüm tepe ve dipleri belirler. Aşağıdaki resim, bu fonksiyonda neler olup bittiğinin görsel bir temsilini sunmaktadır:

Searching for tops & bottoms

İlk olarak, 1. hareketi arar, daha sonra 2. hareketi arar ve son olarak 3. hareket tepe veya dibin belirlenmesi anlamına gelir. 3 için mantık, aşağıdaki gibi görünen iki ayrı fonksiyonda uygulanmaktadır:

void ExtremumsPatternFamilySearcher::CalculateMaximum(Top &T,int Index0,int Index1)// if 2 intermediate points are found, find High between them
   {
   double MaxValue=High[Index0];
   datetime MaxTime=Time[Index0];
   int MaxIndex=Index0;
   for(int i=Index0;i<=Index1;i++)
      {
      if ( High[i] >  MaxValue )
         {
         MaxValue=High[i];
         MaxTime=Time[i];
         MaxIndex=i;
         }
      }
   T.DatetimeExtremum=MaxTime;
   T.IndexExtremum=MaxIndex;
   T.Price=MaxValue;
   }
   
void ExtremumsPatternFamilySearcher::CalculateMinimum(Top &T,int Index0,int Index1)//if 2 intermediate points are found, find Low between them
   {
   double MinValue=Low[Index0];
   datetime MinTime=Time[Index0];
   int MinIndex=Index0;
   for(int i=Index0;i<=Index1;i++)
      {
      if ( Low[i] <  MinValue ) 
         {
         MinValue=Low[i];
         MinTime=Time[i];
         MinIndex=i;
         }
      } 
   T.DatetimeExtremum=MinTime;
   T.IndexExtremum=MinIndex;
   T.Price=MinValue;      
   }

Ardından, tüm bunları önceden hazırlanmış bir depoya (container) koyarız. Mantık şu şekildedir: sınıf içinde kullanılan tüm yapılar kademeli olarak veri eklenmesini gerektirir. Tüm adımlar ve aşamalar geçildikten sonra gerekli veri çıktısı alınır. Bu veriler kullanılarak formasyon grafik üzerinde gösterilebilir. Elbette tepe ve dip belirleyici mantık farklı olabilir. Amacım sadece karmaşık şeyler için basit bir algılama mantığı göstermektir.

Çalışmak için tepeleri seçme

Bulduğumuz tepe ve dip noktalar sadece ara seviyelerdir. Bunları bulduktan sonra, omuz görevi görmesi için en uygun olduğunu düşündüğümüz tepeleri seçmemiz gerekir. Bunu kesin olarak belirleyemiyoruz çünkü kod makine görüşüne sahip değil (genel olarak, bu tür karmaşık tekniklerin kullanımının performansa fayda sağlaması pek olası değildir). Şimdilik, piyasaya en yakın tepeleri seçelim:

bool ExtremumsPatternFamilySearcher::PrepareExtremums()// assign the tops with which we will work
   {
   int Quantity;// an auxiliary counter for random tops
   int PrevIndex;// an auxiliary index for maintaining the order of indexes (increment only)
   
   for(int i=0;i<TopsM;i++)// simply select the tops that are closest to the market
      {
      TopsUp[i]=TopsUpAll[i];
      TopsDown[i]=TopsDownAll[i];
      }
   return true;   
   }

Sembol grafiğinde görsel olarak mantık, mor çerçevedeki varyanta eşdeğer olacaktır. Seçim için biraz daha varyant çizeceğim:

Choose tops & bottoms

Bu durumda, seçim mantığı çok basittir. Seçilen varyantlar 0 ve 1'dir çünkü bunlar piyasaya en yakın olanlardır. Burada her şey çift tepe için geçerlidir. Ancak aynı mantık üçlü veya daha büyük çoklu tepeler için de kullanılacaktır, tek fark seçilen tepelerin sayısında olacaktır.

Bu fonksiyon gelecekte, yukarıdaki resimde mavi renkle gösterildiği gibi tepelerin rastgele seçilebilmesini sağlayacak şekilde genişletilecektir. Bu, formasyon bulucuların birden fazla örneğini simüle edecektir. Bu sayede otomatik modda tüm formasyonlar daha verimli ve daha sık bir şekilde bulunabilecektir.

Formasyon yönünü belirleme

Tepeleri ve dipleri belirledikten sonra, piyasada belirli bir noktada böyle bir formasyon varsa, formasyonun yönünü belirlemeliyiz. Bu aşamada, uç türü piyasaya en yakın olan yöne daha fazla öncelik vermeyi düşünüyorum. Bu mantığa dayanarak, şekildeki 0 varyantını kullanalım, çünkü piyasaya en yakın olan tepe değil diptir (piyasadaki durumun şekildekiyle tamamen aynı olması koşuluyla). Bu kısım kodda basittir:

void ExtremumsPatternFamilySearcher::DirectionOfFormation()// determine whether it is a double top (1) or double bottom (-1) (only if all tops and bottoms are found - if not found, then 0)
   {
   if ( TopsDown[0].DatetimeExtremum > TopsUp[0].DatetimeExtremum && TopsDown[ArraySize(TopsDown)-1].bActive )
      {
      StartTop=TopsDown[ArraySize(TopsDown)-1];
      EndTop=TopsDown[0];    
      FormationDirection=-1;
      }
   else if ( TopsDown[0].DatetimeExtremum < TopsUp[0].DatetimeExtremum && TopsUp[ArraySize(TopsUp)-1].bActive )
      {
      StartTop=TopsUp[ArraySize(TopsUp)-1];
      EndTop=TopsUp[0]; 
      FormationDirection=1;  
      }
   else FormationDirection=0;   
   }

Daha ileri adımlar için net bir yön belirlenmesi gerekmektedir. Yön, formasyon türüne eşdeğerdir:

  1. Çoklu tepe
  2. Çoklu dip

Bu kurallar aynı zamanda baş ve omuz formasyonu ve diğer tüm hibrit formasyonlar için de geçerlidir. Sınıfın bu ailenin tüm formasyonları için ortak olması gerekiyordu - bu genelleştirme halihazırda kısmen çalışıyor.

Geçersiz formasyonları dışlamak için filtreler

Şimdi daha ileri gidelim. Bir yönümüz olduğunu ve tepe ve dipleri seçme yollarından birine sahip olduğumuzu bilerek, çoklu tepe için şunları sağlamalıyız: seçilenler arasındaki tepeler, seçilenlerin en düşüğünden daha düşük olmalıdır. Çoklu dip için, bu dipler seçilenlerin en yükseğinden daha yüksek olmalıdır. Bu durumda, tepeler rastgele seçilirse, seçilen tüm tepeler net bir şekilde ayırt edilebilir. Aksi takdirde, bu kontrol gerekli değildir:

bool ExtremumsPatternFamilySearcher::IsExtremumsAbsolutely()// require the selected extrema to be the most extreme ones
   {
   if ( bRandomExtremumsM )// check only if we have a random selection of tops (in other case the check should be considered completed)
      {
      if ( FormationDirection == 1 )
         {
         int StartIndex=RandomIndexUp[0];
         int EndIndex=RandomIndexUp[ArraySize(RandomIndexUp)-1];
         for(int i=StartIndex+1;i<EndIndex;i++)// check all tops between the selected ones
            {
            for(int j=0;j<ArraySize(TopsUp);j++)
               {
               if ( TopsUpAll[i].Price >= TopsUp[j].Price )
                  {
                  for(int k=0;k<ArraySize(RandomIndexUp);k++)
                     {
                     if ( i != RandomIndexUp[k] ) return false;
                     }
                  }
               }
            }
         return true;
         }
      else if ( FormationDirection == -1 )
         {
         int StartIndex=RandomIndexDown[0];
         int EndIndex=RandomIndexDown[ArraySize(RandomIndexDown)-1];
         for(int i=StartIndex+1;i<EndIndex;i++)// check all tops between the selected ones
            {
            for(int j=0;j<ArraySize(TopsDown);j++)
               {
               if ( TopsDownAll[i].Price <= TopsDown[j].Price )
                  {
                  for(int k=0;k<ArraySize(RandomIndexDown);k++)
                     {
                     if ( i != RandomIndexDown[k] ) return false;
                     }
                  }
               }
            }
         return true;      
         }
      else return false;      
      }
   else
      {
      return true;
      }
   }

Yukarıdaki boolean fonksiyon tarafından gerçekleştirilen rastgele tepe seçiminin doğru ve yanlış varyantını görsel olarak gösterirsek, aşağıdaki gibi görünecektir:

Control of unaccounted tops


Bu kriterler boğa ve ayı formasyonları için de geçerlidir. Şekilde örnek olarak bir boğa formasyonu gösterilmektedir. İkinci durum kolayca hayal edilebilir.

Tüm hazırlık işlemlerini tamamladıktan sonra boyun araştırmasına geçebiliriz. Farklı yatırımcılar boynu farklı şekillerde çizer. Koşullu olarak birkaç tür belirledim:

  1. Görsel olarak eğik (gölgelere göre değil)
  2. Görsel olarak yatay (gölgelere göre değil)
  3. En yüksek veya en düşük nokta, eğik (gölgelere göre)
  4. En yüksek veya en düşük nokta, yatay (gölgelere göre)

Güvenlik nedenleriyle ve kâr olasılığını artırmak için en uygun varyantın 4 olduğuna inanıyorum. Bunu aşağıdaki sebeplerden dolayı seçtim:

  • Bir terse dönüş hareketinin başlangıcı daha açık bir şekilde görülür
  • Bu yaklaşımın kodda uygulanması daha kolaydır
  • Eğim kesin olarak belirlenir (yatay olarak)

Belki de yapı açısından bu tamamen doğru değildir, ancak net bir kural bulamadım. Bu, algo alım-satım açısından kritik değildir. Bu formasyonda rasyonel bir şey bulursak, sınayıcı veya görselleştirme bize mutlaka bir şey gösterecektir. Daha ileri bir görev, alım-satım sonuçlarının güçlendirilmesi anlamına gelir, ancak bu kesinlikle farklı bir görevdir.

Boynun gerekli tüm parametrelerini tanımlayan boğa ve ayı formasyonları için iki ayna fonksiyon oluşturdum:

void ExtremumsPatternFamilySearcher::FindNeckUp(Top &TStart,Top &TEnd)// find the neck line based on the two extreme tops (for the classic multiple top)
   {
   double PriceMin=Low[TStart.IndexExtremum];
   datetime TimeMin=Time[TStart.IndexExtremum];
   for(int i=TStart.IndexExtremum;i>=TEnd.IndexExtremum;i--)// define the lowest point
      {
      if ( Low[i] < PriceMin )
         {
         PriceMin=Low[i];
         TimeMin=Time[i];
         }
      }
   // define the parameters of the anchor point and all parameters of the line equation
   Neck.Price0=PriceMin;
   Neck.TimeX=TimeMin;
   Neck.Time0=Time[0];
   Neck.Price1=PriceMin;
   Neck.Time1=TStart.DatetimeExtremum;
   Neck.DirectionOfFormation=true;
   Neck.CalculateKC();
   }
   
void ExtremumsPatternFamilySearcher::FindNeckDown(Top &TStart,Top &TEnd)// find the neck line based on two extreme bottoms (for the classic multiple bottom)
   {
   double PriceMax=High[TStart.IndexExtremum];
   datetime TimeMax=Time[TStart.IndexExtremum];
   for(int i=TStart.IndexExtremum;i>=TEnd.IndexExtremum;i--)// define the lowest point
      {
      if ( High[i] > PriceMax )
         {
         PriceMax=High[i];
         TimeMax=Time[i];         
         }
      }
   // define the parameters of the anchor point and all parameters of the line equation
   Neck.Price0=PriceMax;
   Neck.TimeX=TimeMax;
   Neck.Time0=Time[0];
   Neck.Price1=PriceMax;
   Neck.Time1=TStart.DatetimeExtremum;
   Neck.DirectionOfFormation=false;
   Neck.CalculateKC();
   }

Boynun doğru ve basit bir şekilde çizilmesi için, seçilen ailenin tüm formasyonlarının boyun oluşumu için aynı kuralları kullanmak daha iyidir. Bir yandan bu, bizim durumumuzda hiçbir şey sağlamayacak olan gereksiz ayrıntıları ortadan kaldırır. Herhangi bir karmaşıklıktaki bir çoklu tepeye bir boyun oluşturmak için, formasyonun iki uç tepesini kullanmak daha iyidir. Bu tepelerin indeksleri, piyasanın seçilen segmentinde en düşük veya en yüksek fiyatı arayacağımız indeksler olacaktır. Boyun düzgün bir yatay çizgi olacaktır. İlk çapa noktaları tam olarak bu seviyede olmalı, çapa zamanı ise uç tepelerin veya diplerin zamanına tam olarak eşit olmalıdır (hangi formasyonu dikkate aldığımıza bağlı olarak). Resimde şu şekilde görünecektir:

Neck

Düşük veya yüksek arama penceresi tam olarak ilk ve son tepe arasındadır. Bu kural, bu ailedeki herhangi bir formasyon için, herhangi bir sayıda tepe ve dip için geçerlidir.

İyimser hedefi belirlemek için öncelikle formasyon büyüklüğünü tanımlamamız gerekir. Formasyon büyüklüğü, puan cinsinden baştan boyuna olan dikey mesafedir. Mesafeyi belirlemek için öncelikle boyundan en uzak olan tepeyi bulmamız gerekir. Bu tepe formasyonun sınırı olacaktır:

void ExtremumsPatternFamilySearcher::SearchFarestTop()// define the farthest top
   {
   double MaxTranslation;// temporary variable to determine the highest top
   if ( FormationDirection == 1 )// if we deal with a multiple top
      {
      MaxTranslation=TopsUp[0].Price-Neck.Price0;// temporary variable to determine the highest top
      FarestTop=TopsUp[0];
      for(int i=1;i<ArraySize(TopsUp);i++)
         {
         if ( TopsUp[i].Price-Neck.Price0 > MaxTranslation ) 
            {
            MaxTranslation=TopsUp[i].Price-Neck.Price0;
            FarestTop=TopsUp[i];
            }
         }      
      }
   if ( FormationDirection == -1 )// if we deal with a multiple bottom
      {
      MaxTranslation=Neck.Price0-TopsDown[0].Price;// temporary variable to determine the lowest bottom
      FarestTop=TopsDown[0];      
      for(int i=1;i<ArraySize(TopsDown);i++)
         {
         if ( Neck.Price0-TopsDown[i].Price > MaxTranslation ) 
            {
            MaxTranslation=Neck.Price0-TopsDown[0].Price;
            FarestTop=TopsDown[i];
            }
         }      
      }
   }

Tepelerin çok farklı olmadığından emin olmak için ek bir kontrol gereklidir. Yalnızca kontrol başarılı olursa diğer adımlara geçebiliriz. Daha doğrusu, iki kontrol olmalıdır: biri tepenin dikey büyüklüğü için, diğeri yatay büyüklüğü (zaman) için. Eğer tepeler zaman olarak çok uzaksa, böyle bir varyant da uygun değildir. İşte dikey büyüklük için kontrol:

bool ExtremumsPatternFamilySearcher::bBalancedExtremums()// balance the tops
   {
   double Lowest;// the lowest top for the multiple top
   double Highest;// the highest bottom for the multiple bottom
   double AbsMin;// distance from the neck to the nearest top
   if ( FormationDirection == 1 )// for the multiple top
      {
      Lowest=TopsUp[0].Price;
      for(int i=1;i<ArraySize(TopsUp);i++)// find the lowest top
         {
         if ( TopsUp[i].Price < Lowest ) Lowest=TopsUp[i].Price;
         }
      AbsMin=Lowest-Neck.Price0;// determine distance from the lowest top to the neck
      if ( AbsMin == 0.0 ) return false;
      if ( ((FarestTop.Price - Neck.Price0)-AbsMin)/AbsMin >= RelativeUnstabilityM ) return false;// if the head is too much bigger than the lowest leverage
      }
   else if ( FormationDirection == -1 )// for the multiple bottom
      {
      Highest=TopsDown[0].Price;
      for(int i=1;i<ArraySize(TopsDown);i++)// find the highest top
         {
         if ( TopsDown[i].Price > Highest ) Highest=TopsDown[i].Price;
         }
      AbsMin=Neck.Price0-Highest;// determine distance from the highest top to the neck
      if ( AbsMin == 0.0 ) return false;
      if ( ((Neck.Price0-FarestTop.Price)-AbsMin)/AbsMin >= RelativeUnstabilityM ) return false;// if the head is too much bigger than the lowest leverage
      }
   else return false;
   return true;   
   }

Tepelerin doğru dikey büyüklüğünü belirlemek için iki tepeye ihtiyacımız var. Birincisi boyundan en uzak olanı, ikincisi ise ona en yakın olanıdır. Bu büyüklükler büyük ölçüde farklıysa, bu formasyon geçersiz olabilir ve risk almamak ve geçersiz olarak işaretlemek daha iyidir. Bir önceki boolean fonksiyonda olduğu gibi, tüm bunlara neyin doğru neyin yanlış olduğuna dair uygun bir grafiksel gösterim eşlik edebilir:

Control of vertical size

Bunları görsel olarak belirlemek kolaydır, ancak kodun nicel bir ölçüme ihtiyacı vardır. Bu durumda, aşağıdaki kadar basittir:

  • K = (Max-Min)/Min
  • K <= RelativeUnstabilityM

Bu metrik, oldukça fazla sayıda yanlış formasyonu filtrelemek için oldukça etkilidir. En sofistike kod bile gözümüzden daha verimli olamaz. Yapabileceğimiz tek şey mantığı gerçeğe olabildiğince yakın hale getirmektir - ancak burada nerede duracağımızı bilmeliyiz.

Yatay kontrol benzer görünecektir. Tek fark, büyüklük olarak çubuk indekslerini kullanmamızdır (zamanı kullanabilirsiniz, temel bir fark yoktur):

bool ExtremumsPatternFamilySearcher::bBalancedExtremumsTime()// balance the sizes of shoulders and head along the horizontal axis
   {
   double Lowest;// minimum distance between the tops
   double Highest;// maximum distance between the tops
   if ( FormationDirection == 1 )// for the multiple top
      {
      Lowest=TopsUp[1].IndexExtremum-TopsUp[0].IndexExtremum;
      Highest=TopsUp[1].IndexExtremum-TopsUp[0].IndexExtremum;
      for(int i=1;i<ArraySize(TopsUp)-1;i++)// find the lowest top
         {
         if ( TopsUp[i+1].IndexExtremum-TopsUp[i].IndexExtremum < Lowest ) Lowest=TopsUp[i+1].IndexExtremum-TopsUp[i].IndexExtremum;
         if ( TopsUp[i+1].IndexExtremum-TopsUp[i].IndexExtremum > Highest ) Highest=TopsUp[i+1].IndexExtremum-TopsUp[i].IndexExtremum;
         }
      if ( double(Highest-Lowest)/double(Lowest) > RelativeUnstabilityTimeM ) return false;// if the width of one of the waves differs much
      }
   else if ( FormationDirection == -1 )// for the multiple bottom
      {   
      Lowest=TopsDown[1].IndexExtremum-TopsDown[0].IndexExtremum;
      Highest=TopsDown[1].IndexExtremum-TopsDown[0].IndexExtremum;
      for(int i=1;i<ArraySize(TopsDown)-1;i++)// find the lowest top
         {
         if ( TopsDown[i+1].IndexExtremum-TopsDown[i].IndexExtremum < Lowest ) Lowest=TopsDown[i+1].IndexExtremum-TopsDown[i].IndexExtremum;
         if ( TopsDown[i+1].IndexExtremum-TopsDown[i].IndexExtremum > Highest ) Highest=TopsDown[i+1].IndexExtremum-TopsDown[i].IndexExtremum;
         }
      if ( double(Highest-Lowest)/double(Lowest) > RelativeUnstabilityTimeM ) return false;// if the width of one of the waves differs much 
      }
   else return false;
   return true;
   }

Bu kontrol için benzer bir metrik kullanabiliriz. Görsel olarak aşağıdaki gibi ifade edilebilir:

Control of horizontal size

Bu durumda, nicel kriterler aynı olacaktır. Ancak bu kez puan yerine indeks veya zaman kullanıyoruz. Karşılaştırma yaptığımız sayıyı ayrı olarak uygulamak daha iyi olabilir, bu da esnek ayarlamalara yer açacaktır:

  • K = (Max-Min)/Min
  • K <= RelativeUnstabilityTimeM

Boyun çizgisi soldaki fiyatı geçmelidir - bu, formasyonun öncesinde bir trend olduğu anlamına gelir:

bool ExtremumsPatternFamilySearcher::CorrectNeckUpLeft()// next the neck line must be corrected so that it finds an intersection with the price on the left
   {
   bool bCrossNeck=false;// indicates if the neck was crossed
   if ( Neck.DirectionOfFormation )// if the neck is found for a double top
      {
      for(int i=StartTop.Index1;i<BarsM;i++)// define the intersection point
         {
         if ( High[i] >= FarestTop.Price )// if the movement goes beyond the formation, then the formation is fake
            {
            return false;
            }         
         if ( Close[i] < Neck.Price0 && Open[i] < Neck.Price0 && High[i] < Neck.Price0 && Low[i] < Neck.Price0   )
            {
            Neck.Time1=Time[i];
            Neck.Index1=i;
            return true;
            }
         }
      }
   return false;
   }
   
bool ExtremumsPatternFamilySearcher::CorrectNeckDownLeft()// next the neck line must be corrected so that it finds an intersection with the price on the left
   {
   bool bCrossNeck=false;// indicates if the neck was crossed
   if ( !Neck.DirectionOfFormation )// if the neck is found for a double bottom
      {
      for(int i=StartTop.Index1;i<BarsM;i++)// define the intersection point
         {
         if ( Low[i] <= FarestTop.Price )//  if the movement goes beyond the formation, then the formation is fake
            {
            return false;
            }         
         if ( Close[i] > Neck.Price0 && Open[i] > Neck.Price0 && High[i] > Neck.Price0 && Low[i] > Neck.Price0 )
            {
            Neck.Time1=Time[i];
            Neck.Index1=i;
            return true;
            }
         }
      }
   return false;
   }

Yine, boğa ve ayı formasyonları için iki ayna fonksiyon vardır. Aşağıda bu boolean fonksiyonun grafiksel bir gösterimi yer almaktadır:

Left and right intersection control

Mavi kutular, kesişimi kontrol ettiğimiz piyasa segmentlerini işaret etmektedir. Her iki segment de formasyonun arkasında, uç tepelerin solunda ve sağındadır. 

Sadece iki kontrol kaldı:

  1. Şu anda (sıfır mumunda) boyun çizgisini kesen bir formasyona ihtiyacımız var
  2. Formasyondan önce, formasyonun kendisinden daha büyük veya ona eşit bir hareket gelmelidir

İlk madde algoritmik alım-satım için gereklidir. Bu fonksiyon da sağlanmış olmasına rağmen, formasyonları yalnızca görüntülemek için tespit etmeye değeceğini düşünmüyorum. Hem tespit etmeye hem de tam olarak alım-satım yapabileceğimiz noktayı bulmaya ihtiyacımız var - giriş noktasında olduğumuzu bilerek hemen bir pozisyon açabileceğimiz bir yer. İkinci madde gerekli koşullardan biridir, çünkü iyi bir önceki hareket olmadan formasyonun kendisi işe yaramaz.

Sıfır mumun çaprazlanması (sağdaki kesişme noktasının kontrol edilmesi) aşağıdaki gibi belirlenir:

int ExtremumsPatternFamilySearcher::CorrectNeckUpRight()// next the neck line must be corrected so that it finds an intersection with the price on the right
   bool bCrossNeck=false;// indicates if the neck was crossed
   if ( Neck.DirectionOfFormation )// if the neck is found for a double top
      {
      for(int i=EndTop.IndexExtremum;i>1;i--)// define the intersection point
         {
         if ( High[i] > FarestTop.Price || Low[i] < Neck.Price0 )// if the movement goes beyond the formation, then the formation is fake
            {
            return -1;
            }         
         }
      }
      
   if ( Close[0] <= Neck.Price0 )
      {
      Neck.Time0=Time[0];
      return 1;
      }      
   return 0;
   }

int ExtremumsPatternFamilySearcher::CorrectNeckDownRight()// next the neck line must be corrected so that it finds an intersection with the price on the right
   {
   bool bCrossNeck=false;// indicates if the neck was crossed
   if ( !Neck.DirectionOfFormation )// if the neck is found for a double bottom
      {
      for(int i=EndTop.IndexExtremum;i>1;i--)// define the intersection point
         {
         if ( Low[i] < FarestTop.Price || High[i] > Neck.Price0  )// if the movement goes beyond the formation, then the formation is fake
            {
            return -1;
            }         
         }
      }
      
   if ( Close[0] >= Neck.Price0 )
      {
      Neck.Time0=Time[0];
      return 1;
      }   
      
   return 0;
   }

Yine iki ayna fonksiyonumuz var. Fiyat formasyonun ötesine geçip sonra geri dönmüşse sağdaki kesişimin geçerli kabul edilmediğini lütfen unutmayın - bu davranış burada ele alınmıştır ve önceki şekilde gösterilmiştir.

Şimdi, öncül trendin nasıl bulunacağını belirleyelim. Şimdiye kadar bu amaç için iyimser tahmin çizgisini kullanıyorum. Boyun ile iyimser tahmin çizgisi arasında bir piyasa segmenti varsa, bu istenen harekettir. Bu hareket zaman içinde çok uzun olmamalıdır, aksi takdirde net bir hareket değildir:

bool ExtremumsPatternFamilySearcher::bWasTrend()// did we find the movement preceding the formation (also move here the anchor point to the intersection)
   {
   bool bCrossOptimist=false;// denotes if the neck is crossed
   if ( FormationDirection == 1 )// if the optimistic forecast is at the double top
      {
      for(int i=Neck.Index1;i<BarsM;i++)// define the intersection point
         {
         if ( High[i] > Neck.Price0 )// if the movement goes beyond the neck, then the formation is fake
            {
            return false;
            }         
         if ( Low[i] < OptimistLine.Price0 )
            {
            OptimistLine.Time1=Time[i];
            return true;
            }
         }
      }
   else if ( FormationDirection == -1 )// if the optimistic forecast is at the double bottom
      {
      for(int i=Neck.Index1;i<BarsM;i++)// define the intersection point
         {
         if ( Low[i] < Neck.Price0 )//  if the movement goes beyond the neck, then the formation is fake
            {
            return false;
            }         
         if ( High[i] > OptimistLine.Price0 )
            {
            OptimistLine.Time1=Time[i];
            return true;
            }
         }      
      }
   return false;
   }

Son boolean fonksiyon da grafiksel olarak aşağıdaki gibi gösterilebilir:

Prior movement

Kodu incelemeyi burada bitirelim ve görsel değerlendirmelere geçelim. Yöntemin ana fikirlerinin bu makalede yeterince açıklandığını düşünüyorum. Bu serinin bir sonraki makalesinde daha fazla fikir ele alınacaktır.

MetaTrader 5 Strateji Sınayıcı görselleştiricisinde sonucu kontrol etme

Hızlı, basit ve net olduğu için grafikte her zaman çizgi çizimini kullanıyorum. MQL5 Yardımı, çizgiler de dahil olmak üzere tüm grafiksel nesnelerin kullanımına ilişkin örnekler sağlamaktadır. Çizim kodunu burada vermeyeceğim, ancak yürütme sonucunu görebilirsiniz. Elbette her şey daha iyi yapılabilirdi ama elimizde sadece bir prototip var. Dolayısıyla, burada "gereklilik ve yeterlilik" ilkesini kullanabileceğimize inanıyorum:

Triple top in the MetaTrader 5 strategy tester visualizer

İşte üçlü tepeye bir örnek. Bu örnek bana daha ilginç geldi. Çift tepeler de benzer şekilde algılanır - sadece parametrelerde istenen tepe sayısını ayarlamanız gerekir. Kod bu tür formasyonlara pek sık rastlamaz, ancak bu sadece bir gösterimdir. Kod daha da geliştirilebilir (ki bunu daha sonra yapmayı planlıyorum).


Daha fazla geliştirme fikirleri

Daha sonra, bu makalede söylenmemiş olanları ele alacağız ve tüm formasyonlar için arama kalitesini artıracağız. Ayrıca sınıfı, baş ve omuz formasyonlarını tespit etmesini sağlayacak şekilde geliştireceğiz. Ayrıca bu formasyonların olası melez fonksiyonlarını bulmaya çalışacağız; bunlardan biri "N tepeler ve çoklu omuzlar" olabilir. Seri sadece bu formasyon ailesine adanmış değildir ve yeni ilginç ve faydalı materyaller içerecektir. Formasyon aramaya farklı yaklaşımlar vardır ve bu serinin amacı, farklı örnekler kullanarak mümkün olduğunca çok sayıda formasyon göstermek ve böylece karmaşık bir görevi bir dizi daha basit olana ayırmanın farklı olası yollarını kapsamaktır. Seri şunları içerecektir:

  1. Diğer ilginç formasyonlar
  2. Farklı formasyon türlerini tespit etmek için diğer yöntemler
  3. Geçmiş verileri kullanarak alım-satım yapma ve farklı enstrümanlar ve zaman dilimleri için istatistik toplama
  4. Çok fazla formasyon var ve ben hepsini bilmiyorum (bu yüzden potansiyel olarak sizin formasyonlarınızı dikkate alabilirim)
  5. Ayrıca seviyeleri de dikkate alacağız (seviyeler genellikle terse dönüşleri tespit etmek için kullanıldığından)


    Sonuç

    Materyali herkes için basit ve anlaşılır hale getirmeye çalıştım. Umarım herkes burada faydalı bir şeyler bulabilir. Bu makalenin sonucu, görsel strateji sınayıcıdan da görülebileceği gibi, basit bir kodun karmaşık formasyonları bulabilmesidir. Bu yüzden illa sinir ağları kullanmamız ya da bazı karmaşık makine görüşü algoritmaları yazmamız/kullanmamız gerekmiyor. MQL5 dili, en karmaşık algoritmaları bile uygulamak için zengin işlevselliğe sahiptir. Olasılıklar sadece sizin hayal gücünüz ve çalışkanlığınızla sınırlıdır. 

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

    Ekli dosyalar |
    Prototype.zip (309.42 KB)
    Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler
    Yeni terminalin ve dilin tüm yeni olanaklarını ve özelliklerini listelemeyeceğim. Bunlar sayısızdır ve bazı yenilikler ayrı bir makalede tartışılmaya değerdir. Ayrıca burada nesne yönelimli programlama ile yazılmış bir kod yoktur, geliştiriciler için ek avantajlar olarak bir bağlamda basitçe bahsedilemeyecek kadar ciddi bir konudur. Bu makalede, MQL4'e kıyasla göstergeleri, yapılarını, çizimlerini, türlerini ve programlama ayrıntılarını ele alacağız. Umarım bu makale hem yeni başlayanlar hem de deneyimli geliştiriciler için faydalı olacaktır, belki bazıları yeni bir şeyler bulacaktır.
    Swaplar (Bölüm I): Kilitleme ve sentetik pozisyonlar Swaplar (Bölüm I): Kilitleme ve sentetik pozisyonlar
    Bu makalede klasik swap alım-satım yöntemleri konseptini genişletmeye çalışacağım. Bu kavramın neden özel bir ilgiyi hak ettiği ve üzerinde çalışılması gerektiği sonucuna vardığımı açıklayacağım.
    İşte Karışınızda Yeni MetaTrader 5 ve MQL5 İşte Karışınızda Yeni MetaTrader 5 ve MQL5
    Bu MetaTrader 5 ile ilgili sadece kısa bir inceleme. Sistemin tüm yeni özelliklerini bu kadar kısa sürede açıklayamam, test süreci 09.09.2009’da başladı. Bu sembolik bir tarihtir ve şanslı sayı olacağına eminim. MetaTrader 5 terminalinin ve MQL5’in beta sürümünü beş gün önce aldım. Tüm özelliklerini deneme şansım olmadı ama şimdiden etkilendim.
    MetaTrader'da çoklu robot: Tek bir grafikten birden fazla robot başlatma MetaTrader'da çoklu robot: Tek bir grafikten birden fazla robot başlatma
    Bu makalede, yalnızca bir grafiğe eklenerek birden fazla grafikte kullanılabilen evrensel bir MetaTrader robotu oluşturmak için basit bir şablonu inceleyeceğiz, böylece robotun her bir örneğini her bir grafikte yapılandırmaya gerek kalmayacaktır.