الگوهای معکوس: آزمایش الگوی DOUBLE TOP/BOTTOM

آزمایش الگوی DOUBLE TOP/BOTTOM

تجزیه و تحلیل انجام شده در مقاله ?”How long is the trend” نشان می­دهد که قیمت برای %60 مواقع در trend پابرجاست. این بدین معنا است که ایجاد یک معامله در ابتدای trend بهترین نتیجه را دارد. جستجوی نقاط بازگشت trend تعداد زیادی الگوی بازگشتی ایجاد کرده است.الگوهای معکوس Double top / bottom یکی از شناخته شده ­ترین و پر استفاده ­ترین ­هاست.

جنبه­ های نظری شکل گیری در الگوهای معکوس

در این مرحله، معامله گران برخلاف trend، شروع به بازی می­کنند و از سطح، roll-back می­کنند و قیمت را به سمت correction می­برند. در حالی که جنبش correction شتاب می­گیرد، معامله گران پیرو این trend با تعیین سود یا بستن معامله­ های زیان­ده که هدف آن­ها شکسته شدن سطح بود، شروع به خروج از بازار می­کنند. این حرکت موجب تقویت جنبش و حتی بیش­تر از آن منجر به ظهور یک trend جدید می­شود.

 

Double top pattern

 

هنگام جستجو الگوهای معکوس در نمودار، جستجوی الگویی که مطابقت دقیقی با top / bottom  داشته باشد، فایده­ای ندارد. انحراف از سطح top / bottom  طبیعی تلقی می­شود. فقط مطمئن شوید که قله­ های نمودار در همان سطح حمایت / مقاومت قرار دارند. قابلیت اطمینان الگو به مقاومت سطحی که بر اساس آن است بستگی دارد.

استراتژی معامله الگوهای معکوس

محبوبیت الگوهای معکوس، در این است که استراتژی­های متعددی را در برمی­گیرد. در اینترنت، حداقل سه نقطه ورودی متفاوت برای معامله این الگو وجود دارد.

2 مورد 1

اولین نقطه ورود بر اساس شکسته شدن neckline است. stop loss فراتر از خط top / bottom  تنظیم می­شود. رویکردهای مختلفی برای تعریف “شکسته شدن neckline” وجود دارد. معامله­ گران ممکن است از bar­ای که در زیر neckline بسته می­شود و هم­چنین bar­ای که برای یک فاصله ثابت از neckline می­شکند استفاده کنند. هر دو رویکرد، موافقان و مخالفان خود را دارند. در صورت حرکت sharp، ممکن است یک candle با فاصله کافی از neckline بسته شود و باعث ناکارآمد بودن الگو شود.

آزمایش الگوی DOUBLE TOP/BOTTOM

 

اشکال این روش، سطح نسبتاً بالای stop loss است که باعث کاهش نسبت سود / ریسک استراتژی استفاده شده می­شود.

2. مورد 2

نقطه ورود دوم بر اساس تئوری سطحmirror  است، هنگامی که neckline از حالت حمایت به حالت مقاومت تبدیل می­شود و برعکس. در این­جا ورود هنگامی انجام می­شود که قیمت پس از شکسته شدن neckline، roll back می­کند. در این حالت، یک stop loss فراتر از اکسترمم آخرین correction تنظیم می­شود که میزان stop loss را به طور قابل توجهی کاهش می­دهد. متأسفانه، قیمت، همیشه پس از شکسته شدن neckline، آزمایش نمی­شود، بنابراین تعداد ورودی­ ها کاهش می­یابد.

ازمایش الگو ها

2 مورد 3

سومین ورودی بر اساس تئوری trend است. این با دستیابی به موفقیت در خط trend ای که از نقطه شروع حرکت تا اکسترمم neckline ساخته شده است تعریف می­شود. همانند حالت اول، stop loss فراتر از خط top / bottom  تنظیم می­شود. ورود زود هنگام در مقایسه با نقطه ورود اول، سطح stop loss کم­تری را فراهم می­کند. هم­چنین در مقایسه با حالت دوم سیگنال­ های بیش­تری ارائه می­دهد. در همان زمان، چنین نقطه­ ای ورودی سیگنال­ های کاذب بیش­تری می­دهد، زیرا ممکن است یک کانال بین خطوط اکسترمم و neckline تشکیل شود، یا ممکن است  pennant وجود داشته باشد. هر دو مورد نشان دهنده ادامه trend است.

آزمایش الگوی DOUBLE TOP/BOTTOM

هر سه استراتژی دستورالعمل خروج در سطح برابر با فاصله بین اکسترمم و neckline را دارند.

الگوهای معکوس

 

هم­چنین، هنگام تعیین الگوهای معکوس نمودار، باید توجه داشته باشید که double top/bottom باید به وضوح از حرکت قیمت جدا شود. هنگام توصیف الگو، اغلب محدودیتی اضافه می­شود: باید حداقل شش bar بین دو top/bottom وجود داشته باشد.

علاوه بر این، از آن­جا که شکل گیری الگوهای معکوس بر اساس تئوری سطح قیمت است، الگوی معامله نباید منافاتی با آن داشته باشد. بنابراین، بر اساس هدف مورد نظر، neckline نباید پایین­تر از  Fibo level 50 از حرکت اولیه باشد. علاوه بر این، برای فیلتر کردن سیگنال­های کاذب، ممکن است مینیمم سطح اولین correction (تشکیل neckline) را به عنوان شاخص قدرت سطح قیمت اضافه کنیم.

ایجاد EA از بلوک جست و جوی الگوهای معکوس

 

3.1 در جستجوی اکسترمم­ها

ما از بلوک جستجوی الگوهای معکوس شروع به توسعه EA می­کنیم. بیایید از شاخص ZigZag از تحویل استاندارد MetaTrader 5 برای جستجوی اکسترمم­های قیمت استفاده کنیم. همان­طور که در مقاله توضیح داده شده است، قسمت محاسبه indicator، را به کلاس منتقل کنید[1]. indicator حاوی دو بافر indicator است که حاوی مقدار قیمت در نقاط اکسترمم است. بافرهای indicator حاوی مقادیر خالی بین اکسترمم­ها هستند. برای این­که دو بافر indicator حاوی مقادیر خالی متعدد ایجاد نشوند، با مجموعه­ ای از ساختارها حاوی اطلاعاتی در مورد اکسترمم­ها  جایگزین شدند. ساختار ذخیره اطلاعات در مورد اکسترمم­ها به شرح زیر است:

   struct s_Extremum
     {
      datetime          TimeStartBar;
      double            Price;
      
      s_Extremum(void)  :  TimeStartBar(0),
                           Price(0)
         {
         }
      void Clear(void)
        {
         TimeStartBar=0;
         Price=0;
        }
     };

 

اگر حداقل یک بار از ایندیکاتور ZigZag استفاده کرده باشید، می­دانید که هنگام جستجوی پارامترهای بهینه، چه تعداد compromise باید انجام دهید. مقادیر خیلی کوچک پارامتر، یک حرکت بزرگ را به بخش­های کوچک تقسیم می­کند، در حالی که مقادیر خیلی بزرگ پارامتر، حرکت کوتاه را رد می­کند. الگوریتم جستجوی الگوهای گرافیکی از نظر کیفیت یافتن اکسترمم­ها بسیار مورد نیاز است. در حالی که سعی در یافتن یک حد وسط داشتم، تصمیم گرفتم از indicator با مقادیر پارامتر کوچک استفاده کرده و یک روبنای اضافی با ترکیب حرکات یک جهته با اصلاحات کوتاه در یک حرکت ایجاد کنم.

کلاس CTrends برای حل این مسئله ساخته شده است. هدر کلاس در زیر ارائه شده است. در حین مقداردهی اولیه، ارجاع به شی کلاس indicator و حداقل مقدار حرکت در نظر گرفته شده به عنوان ادامه trend به کلاس منتقل می­شود.

class CTrends : public CObject
  {
private:
   CZigZag          *C_ZigZag;         // Link to the ZigZag indicator object
   s_Extremum        Trends[];         // Array of extremums
   int               i_total;          // Total number of saved extremums
   double            d_MinCorrection;  // Minimum movement value for trend continuation

public:
                     CTrends();
                    ~CTrends();
//--- Class initialization method
   virtual bool      Create(CZigZag *pointer, double min_correction);
//--- Get info on the extremum
   virtual bool      IsHigh(s_Extremum &pointer) const;
   virtual bool      Extremum(s_Extremum &pointer, const int position=0);
   virtual int       ExtremumByTime(datetime time);
//--- Get general info
   virtual int       Total(void)          {  Calculate(); return i_total;   }
   virtual string    Symbol(void) const   {  if(CheckPointer(C_ZigZag)==POINTER_INVALID) return "Not Initilized"; return C_ZigZag.Symbol();  }
   virtual ENUM_TIMEFRAMES Timeframe(void) const   {  if(CheckPointer(C_ZigZag)==POINTER_INVALID) return PERIOD_CURRENT; return C_ZigZag.Timeframe();  }
   
protected:
   virtual bool      Calculate(void);
   virtual bool      AddTrendPoint(s_Extremum &pointer);
  };

 

برای به دست آوردن اطلاعات در مورد اکسترمم­ها، روش­های زیر در کلاس ارائه شده است:

  • ExtremumByTime – برای مدت زمان مشخصی عدد اکسترمم را در پایگاه داده دریافت کنید،
  • Extremum – بازگشت اکسترمم در یک موقعیت مشخص در پایگاه داده،
  • IsHigh – اگر اکسترمم مشخص شده بالا باشد true را برمی­گرداند و اگر پایین باشد false را برمی­گرداند.

بلوک اطلاعات عمومی از روش­ هایی برخوردار است که تعداد کل اکسترمم­های ذخیره شده، نماد و بازه زمانی استفاده شده را برمی­گرداند.

منطق اصلی کلاس در متد Calculate اجرا می­شود. بیایید نگاهی دقیق­ تر به آن بیندازیم.

در ابتدای روش، ارتباط اشاره­ گر به شی کلاس indicator و وجود قسمت­ های اکسترمم پیدا شده توسط indicator را بررسی کنید.

bool CTrends::Calculate(void)
  {
   if(CheckPointer(C_ZigZag)==POINTER_INVALID)
      return false;
//---
   if(C_ZigZag.Total()==0)
      return true;

بعد، تعداد اکسترمم­های پردازش نشده را تعریف کنید. در صورت پردازش همه اکسترمم­ها، از متد، خارج شوید و نتیجه true  را به دست آورید.

   int start=(i_total<=0 ? C_ZigZag.Total() : C_ZigZag.ExtremumByTime(Trends[i_total-1].TimeStartBar));
   switch(start)
     {
      case 0:
        return true;
        break;
      case -1:
        start=(i_total<=1 ? C_ZigZag.Total() : C_ZigZag.ExtremumByTime(Trends[i_total-2].TimeStartBar));
        if(start<0 || ArrayResize(Trends,i_total-1)<=0)
          {
           ArrayFree(Trends);
           i_total=0;
           start=C_ZigZag.Total();
          }
        else
           i_total=ArraySize(Trends);
        if(start==0)
           return true;
        break;
     }

پس از آن، مقدار لازم از اکسترمم را از کلاس indicator درخواست کنید.

   s_Extremum  base[];
   if(!C_ZigZag.Extremums(base,0,start))
      return false;
   int total=ArraySize(base);
   if(total<=0)
      return true;

اگر تا این زمان در پایگاه داده، اکسترمم وجود نداشته است، با فراخوانی متد AddTrendPoint، قدیمی­ ترین اکسترمم را به پایگاه داده اضافه کنید.

   if(i_total==0)
      if(!AddTrendPoint(base[total-1]))
         return false;

بعد، حلقه را با تکرار در همه اکسترمم­های دانلود شده مرتب کنید. از اکسترمم­های قبلی، قبل از آخرین موارد ذخیره شده صرف نظر می­شود.

   for(int i=total-1;i>=0;i--)
     {
      int trends_pos=i_total-1;
      if(Trends[trends_pos].TimeStartBar>=base[i].TimeStartBar)
         continue;

در مرحله بعدی، یک طرفه بودن نقاط اکسترمم را بررسی کنید. اگر اکسترمم جدید دیگری مورد قبلی را دوباره ترسیم کرد، داده­ها را به روز کنید.

      if(IsHigh(Trends[trends_pos]))
        {
         if(IsHigh(base[i]))
           {
            if(Trends[trends_pos].Price<base[i].Price)
              {
               Trends[trends_pos].Price=base[i].Price;
               Trends[trends_pos].TimeStartBar=base[i].TimeStartBar;
              }
            continue;
           }

برای نقاط اکسترمم جهت مخالف، بررسی کنید که آیا حرکت جدید ادامه trend قبلی است. اگر بله، داده­ها را در مورد اکسترمم­ها به روز کنید. اگر خیر، با فراخوانی متد AddTrendPoint اطلاعات مربوط به اکسترمم را اضافه کنید.

         else
           {
            if(trends_pos>1 && Trends[trends_pos-1].Price>base[i].Price  && Trends[trends_pos-2].Price>Trends[trends_pos].Price)
              {
               double trend=fabs(Trends[trends_pos].Price-Trends[trends_pos-1].Price);
               double correction=fabs(Trends[trends_pos].Price-base[i].Price);
               if(fabs(1-correction/trend)>d_MinCorrection)
                 {
                  Trends[trends_pos-1].Price=base[i].Price;
                  Trends[trends_pos-1].TimeStartBar=base[i].TimeStartBar;
                  i_total--;
                  ArrayResize(Trends,i_total);
                  continue;
                 }
              }
            AddTrendPoint(base[i]);
           }
        }

کد کامل همه کلاس­ها و متدهای آن­ها در پیوست موجود است.

3.2 جستجوی الگوهای معکوس

پس از تعریف اکسترمم­های قیمت، بلوک جستجوی نقاط ورود به بازار را ایجاد کنید. این کار را به دو مرحله فرعی تقسیم کنید:

  1. جستجوی الگوهای معکوس بالقوه ورود به بازار.
  2. نقطه ورود به بازار.

این قابلیت به کلاس CPttern اختصاص داده شده است. هدر آن در زیر آورده شده است.

class CPattern : public CObject
  {
private:
   s_Extremum     s_StartTrend;        //Trend start point
   s_Extremum     s_StartCorrection;   //Correction start point
   s_Extremum     s_EndCorrection;     //Correction end point
   s_Extremum     s_EndTrend;          //Trend completion point
   double         d_MinCorrection;     //Minimum correction
   double         d_MaxCorrection;     //Maximum correction
//---
   bool           b_found;             //"Pattern detected" flag
//---
   CTrends       *C_Trends;
public:
                     CPattern();
                    ~CPattern();
//--- Class initialization
   virtual bool      Create(CTrends *trends, double min_correction, double max_correction);
//--- Methods for searching the pattern and entry points
   virtual bool      Search(datetime start_time);
   virtual bool      CheckSignal(int &signal, double &sl, double &tp1, double &tp2);
//--- Method of comparing the objects
   virtual int       Compare(const CPattern *node,const int mode=0) const;
//--- Methods of getting data on the pattern extremums
   s_Extremum        StartTrend(void)        const {  return s_StartTrend;       }
   s_Extremum        StartCorrection(void)   const {  return s_StartCorrection;  }
   s_Extremum        EndCorrection(void)     const {  return s_EndCorrection;    }
   s_Extremum        EndTrend(void)          const {  return s_EndTrend;         }
   virtual datetime  EndTrendTime(void)            {  return s_EndTrend.TimeStartBar;  }
  };

این الگو با استفاده از چهار اکسترمم مجاور تعریف شده است. داده­های موجود در آن­ها در s_StartTrend ، s_StartCorrection ، s_EndCorrection و s_EndTrend ذخیره می­شوند. برای شناسایی الگو، به مینیمم و ماکزیمم سطوح correction نیز نیاز داریم که در متغیرهای d_MinCorrection و d_MaxCorrection ذخیره می­شوند. ما از نمونه کلاس CTrends که قبلاً ایجاد شده بود، اکسترمم را به دست خواهیم آورد.

در طول مقداردهی اولیه کلاس، ما اشاره گر را به شی کلاس CTrends و محدوده سطح correction  منتقل می­کنیم. در داخل متد، اعتبار اشاره­ گر پاس داده شده را بررسی کنید، اطلاعات دریافت شده را ذخیره کرده و ساختار اکسترمم را پاک کنید.

bool CPattern::Create(CTrends *trends,double min_correction,double max_correction)
  {
   if(CheckPointer(trends)==POINTER_INVALID)
      return false;
//---
   C_Trends=trends;
   b_found=false;
   s_StartTrend.Clear();
   s_StartCorrection.Clear();
   s_EndCorrection.Clear();
   s_EndTrend.Clear();
   d_MinCorrection=min_correction;
   d_MaxCorrection=max_correction;
//---
   return true;
  }

جستجوی الگوهای بالقوه قرار است در متد ()Search انجام شود. این متد در پارامترها تاریخ شروع جستجو را دریافت می­کند و مقدار منطقی اطلاعات نتایج را برمی­گرداند. بیایید الگوریتم متد را با جزئیات در نظر بگیریم.

ابتدا ارتباط اشاره ­گر با شی کلاس CTrends و وجود اکسترمم­های ذخیره شده را بررسی کنید. در صورت نتیجه منفی، با نتیجه false از متد خارج شوید.

bool CPattern::Search(datetime start_time)
  {
   if(CheckPointer(C_Trends)==POINTER_INVALID || C_Trends.Total()<4)
      return false;

در مرحله بعد، نقطه اکسترمم مربوط به تاریخ مشخص شده در ورودی­ ها است. اگر هیچ اکسترممی پیدا نشد، با نتیجه false از متد خارج شوید.

   int start=C_Trends.ExtremumByTime(start_time);
   if(start<0)
      return false;

بعد، حلقه را برای تکرار در شروع تمام اکسترمم­ها با تاریخ مشخص شده و تا آخرین مورد مشخص شده مرتب کنید. ابتدا چهار اکسترمم متوالی را به دست می­آوریم. اگر حداقل یکی از اکسترمم­ها به دست نیامده، به سمت اکسترمم بعدی حرکت کنید.

   b_found=false;
   for(int i=start;i>=0;i--)
     {
      if((i+3)>=C_Trends.Total())
         continue;
      if(!C_Trends.Extremum(s_StartTrend,i+3) || !C_Trends.Extremum(s_StartCorrection,i+2) ||
         !C_Trends.Extremum(s_EndCorrection,i+1) || !C_Trends.Extremum(s_EndTrend,i))
         continue;

در مرحله بعدی، بررسی کنید که آیا اکسترمم­ها مطابق با الگوی لازم است. اگر نبود، به سمت اکسترمم حرکت کنید. اگر الگو شناسایی شد، فلگ را روی true تنظیم کنید و با همان نتیجه از متد خارج شوید.

      double trend=s_StartCorrection.Price-s_StartTrend.Price;
      double correction=s_StartCorrection.Price-s_EndCorrection.Price;
      double re_trial=s_EndTrend.Price-s_EndCorrection.Price;
      double koef=correction/trend;
      if(koef<d_MinCorrection || koef>d_MaxCorrection || (1-fmin(correction,re_trial)/fmax(correction,re_trial))>=d_MaxCorrection)
         continue;
      b_found= true; 
//---
      break;
     }
//---
   return b_found;
  }

مرحله بعدی تشخیص نقطه ورود است. ما از مورد دوم برای آن استفاده خواهیم کرد. برای کاهش خطر عدم بازگشت قیمت به neckline، ما در بازه زمانی پایین­تر به دنبال تأیید سیگنال می­گردیم.

برای پیاده سازی این قابلیت، بیایید متد ()CheckSignal  را ایجاد کنیم. جدا از خود سیگنال، این متد، stop loss را برمی­گرداند و سطح سود را به دست می­آورد. بنابراین، ما قصد داریم از متغیرهای اشاره گر در پارامترهای متد استفاده کنیم.

در ابتدای متد، فلگ را از نظر وجود الگوهای معکوس که قبلاً شناسایی شده بررسی کنید. اگر الگویی پیدا نشد، از متد با نتیجه’false’ خارج شوید.

bool CPattern::CheckSignal(int &signal, double &sl, double &tp1, double &tp2)
  {
   if(!b_found)
      return false;

سپس، زمان بستن  تشکیل الگوی candle را تعیین کرده و داده­ های بازه زمانی مورد نظر خود را از ابتدای شکل گیری الگو تا لحظه فعلی بارگیری کنید.

بعد از آن، حلقه را مرتب کنید، که در آن ما شکسته شدن neckline، اصلاح candle و بسته شدن candle فراتر از neckline را در bar جهت حرکت مورد انتظار بررسی می­کنیم.

من محدودیت­ های بیش­تری در این­جا اضافه کردم:

  • اگر قیمت از سطح tops/bottoms شکسته شود، این الگو نامعتبر تلقی می­شود.
  • اگر قیمت به سطح سود مصرفی برسد، این الگو نامعتبر تلقی می­شود.
  • اگر بیش از دو candle قبل از ایجاد معامله از زمان فعال شدن سیگنال تشکیل شده باشد، یک سیگنال ورود به بازار نادیده گرفته شود.

اگر یکی از رویدادهای لغو الگو شناسایی شد،  با نتیجه false  از متد خارج شوید.

   signal=0;
   sl=tp1=tp2=-1;
   bool up_trend=C_Trends.IsHigh(s_EndTrend);
   double extremum=(up_trend ? fmax(s_StartCorrection.Price,s_EndTrend.Price) : fmin(s_StartCorrection.Price,s_EndTrend.Price));
   double exit_level=2*s_EndCorrection.Price - extremum;
   bool break_neck=false;
   for(int i=0;i<total;i++)
     {
      if(up_trend)
        {
         if(rates[i].low<=exit_level || rates[i].high>extremum)
            return false;
         if(!break_neck)
           {
            if(rates[i].close>s_EndCorrection.Price)
               continue;
            break_neck=true;
            continue;
           }
         if(rates[i].high>s_EndCorrection.Price)
           {
            if(sl==-1)
               sl=rates[i].high;
            else
               sl=fmax(sl,rates[i].high);
           }
         if(rates[i].close<s_EndCorrection.Price || sl==-1)
            continue;
         if((total-i)>2)
            return false;

پس از شناسایی سیگنال ورود به بازار، نوع سیگنال (“1-” sell- ، “1” buy-) و سطح معاملات را مشخص کنید. stop loss پس از شکسته شدن در حداکثر عمق correction نسبت به neckline تنظیم می­شود. برای سود خود دو سطح تعیین کنید:

  1. با %90 از خط اکسترمم به neckline در جهت موقعیت.
  2. با %90 از حرکت قبلی.

محدودیت را اضافه کنید: سطح سود مصرف اول نمی­تواند از سطح دوم بیش­تر شود.

کد کامل همه کلاس­ها و متدها در پیوست موجود است.

3.3 توسعه EA

پس از کار مقدماتی، تمام بلوک­ها را در یک EA واحد جمع کنید. متغیر خارجی را اعلام کرده و آن­ها را به سه بلوک تقسیم کنید:

  • پارامترهای شاخص ZigZag ؛
  • پارامترهای جستجو برای الگوها و نقاط ورود.
  • پارامترهای انجام عملیات تجاری.
sinput   string            s1             =  "---- ZigZag Settings ----";     //---
input    int               i_Depth        =  12;                              // Depth
input    int               i_Deviation    =  100;                             // Deviation
input    int               i_Backstep     =  3;                               // Backstep
input    int               i_MaxHistory   =  1000;                            // Max history, bars
input    ENUM_TIMEFRAMES   e_TimeFrame    =  PERIOD_M30;                      // Work Timeframe
sinput   string            s2             =  "---- Pattern Settings ----";    //---
input    double            d_MinCorrection=  0.118;                           // Minimal Correction
input    double            d_MaxCorrection=  0.5;                             // Maximal Correction
input    ENUM_TIMEFRAMES   e_ConfirmationTF= PERIOD_M5;                       // Timeframe for confirmation
sinput   string            s3             =  "---- Trade Settings ----";      //---
input    double            d_Lot          =  0.1;                             // Trade Lot
input    ulong             l_Slippage     =  10;                              // Slippage
input    uint              i_SL           =  350;                             // Stop Loss Backstep, points

در متغیرهای سراسری، آرایه­ ای را برای ذخیره اشاره­ گرها به اشیای الگو، نمونه کلاس عملیات معاملاتی، نمونه کلاس جستجو در الگوهای معکوس که در آن اشاره­ گر به کلاس پردازش شده اشاره می­کند و متغیر زمان شروع ذخیره الگوی بعدی اعلام کنید.

CArrayObj         *ar_Objects;
CTrade            *Trade;
CPattern          *Pattern;
datetime           start_search;

برای فعال کردن قابلیت تنظیم همزمان دو سود، از فناوری ارائه شده در مقاله استفاده کنید [2].

تمام اشیای لازم را در تابع ()OnInit  شروع کنید. از آن­جا که ما هرگز نمونه­ های کلاس CZigZag و CTrends را اعلام نکردیم، به سادگی آن­ها را مقدار دهی می­کنیم و به این اشیا اشاره­گر به آرایه خود اضافه می­کنیم. در صورت خطای مقداردهی اولیه، در هر مرحله از تابع با نتیجه INIT_FAILED خارج شوید.

int OnInit()
  {
//--- Initialize object array
   ar_Objects=new CArrayObj();
   if(CheckPointer(ar_Objects)==POINTER_INVALID)
      return INIT_FAILED;
//--- Initialize ZigZag indicator class
   CZigZag *zig_zag=new CZigZag();
   if(CheckPointer(zig_zag)==POINTER_INVALID)
      return INIT_FAILED;
   if(!ar_Objects.Add(zig_zag))
     {
      delete zig_zag;
      return INIT_FAILED;
     }
   zig_zag.Create(_Symbol,i_Depth,i_Deviation,i_Backstep,e_TimeFrame);
   zig_zag.MaxHistory(i_MaxHistory);
//--- Initialize the trend movement search class
   CTrends *trends=new CTrends();
   if(CheckPointer(trends)==POINTER_INVALID)
      return INIT_FAILED;
   if(!ar_Objects.Add(trends))
     {
      delete trends;
      return INIT_FAILED;
     }
   if(!trends.Create(zig_zag,d_MinCorrection))
      return INIT_FAILED;
//--- Initialize the trading operations class
   Trade=new CTrade();
   if(CheckPointer(Trade)==POINTER_INVALID)
      return INIT_FAILED;
   Trade.SetAsyncMode(false);
   Trade.SetDeviationInPoints(l_Slippage);
   Trade.SetTypeFillingBySymbol(_Symbol);
//--- Initialize additional variables
   start_search=0;
   CLimitTakeProfit::OnlyOneSymbol(true);
//---
   return(INIT_SUCCEEDED);
  }

نمونه اشیای اعمال شده را در تابع ()OnDeinit پاک کنید.

void OnDeinit(const int reason)
  {
//---
   if(CheckPointer(ar_Objects)!=POINTER_INVALID)
     {
      for(int i=ar_Objects.Total()-1;i>=0;i--)
         delete ar_Objects.At(i);
      delete ar_Objects;
     }
   if(CheckPointer(Trade)!=POINTER_INVALID)
      delete Trade;
   if(CheckPointer(Pattern)!=POINTER_INVALID)
      delete Pattern;
  }

طبق معمول، تابع اصلی در تابع ()OnTick پیاده­ سازی می­شود. می­توان آن را به دو بلوک تقسیم کرد:

  1. بررسی سیگنال­ های ورود به بازار در الگوهای معکوس قبلاً شناسایی شده. این هر بار که candle جدیدی در بازه زمانی کوچکی از جستجو برای تأیید سیگنال ظاهر می­شود، راه اندازی می­شود.
  2. جستجوی الگوهای معکوس جدید. این هر بار که candle جدیدی در یک بازه زمانی کاری ظاهر می­شود (برای indicator مشخص شده است) راه ­اندازی می­شود.

در ابتدای تابع، وجود یک bar جدید را در بازه زمانی تأیید نقطه ورود بررسی کنید. اگر bar تشکیل نشده است، تا tick بعدی از تابع خارج شوید. لازم به ذکر است که این رویکرد فقط درصورتی درست کار می­کند که بازه زمانی تأیید یک نقطه ورود بیش از بازه زمانی کار نباشد. در غیر این صورت، به جای خروج از تابع، باید به بلوک جستجوی الگو بروید.

void OnTick()
  {
//---
   static datetime Last_CfTF=0;
   datetime series=(datetime)SeriesInfoInteger(_Symbol,e_ConfirmationTF,SERIES_LASTBAR_DATE);
   if(Last_CfTF>=series)
      return;
   Last_CfTF=series;

اگر bar جدیدی ظاهر شد، حلقه را برای بررسی همه الگوهای معکوس ذخیره شده قبلی برای وجود سیگنال ورود به بازار ترتیب دهید. ما دو شی آرایه اول را از نظر سیگنال بررسی نخواهیم کرد، زیرا ما اشاره­گرها را برای کلاس­های جستجوی اکسترمم در این سلول­ها ذخیره می­کنیم. اگر اشاره­گر ذخیره شده نامعتبر باشد یا تابع بررسی سیگنال false را برگرداند، اشاره­گر از آرایه حذف می­شود. سیگنال­های الگو در تابع ()CheckPattern  بررسی می­شوند. الگوریتم آن در زیر ارائه می­شود.

   int total=ar_Objects.Total();
   for(int i=2;i<total;i++)
     {
      if(CheckPointer(ar_Objects.At(i))==POINTER_INVALID)
         if(ar_Objects.Delete(i))
           {
            i--;
            total--;
            continue;
           }
//---
      if(!CheckPattern(ar_Objects.At(i)))
        {
         if(ar_Objects.Delete(i))
           {
            i--;
            total--;
            continue;
           }
        }
     }

پس از بررسی الگوهای قبلاً شناسایی شده، نوبت به رفتن به بلوک دوم می­رسد – جستجوی الگوهای جدید. برای این کار، در دسترس بودن یک bar جدید را در بازه زمانی بررسی کنید. اگر یک bar جدید تشکیل نشده است، از تابع منتظر یک tick جدید خارج شوید.

   static datetime Last_WT=0;
   series=(datetime)SeriesInfoInteger(_Symbol,e_TimeFrame,SERIES_LASTBAR_DATE);
   if(Last_WT>=series)
      return;

هنگامی که یک bar جدید ظاهر می­شود، تاریخ اولیه جستجو برای الگوهای معکوس را تعیین کنید (با توجه به عمق تاریخ تجزیه و تحلیل مشخص شده در پارامترها). بعد، ارتباط اشاره­ گر را با شی کلاس CPattern بررسی کنید. اگر اشاره­گر نامعتبر است، یک نمونه کلاس جدید ایجاد کنید.

   start_search=iTime(_Symbol,e_TimeFrame,fmin(i_MaxHistory,Bars(_Symbol,e_TimeFrame)));
   if(CheckPointer(Pattern)==POINTER_INVALID)
     {
      Pattern=new CPattern();
      if(CheckPointer(Pattern)==POINTER_INVALID)
         return;
      if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
        {
         delete Pattern;
         return;
        }
     }
   Last_WT=series;

پس از آن، روش جستجوی الگوهای معکوس بالقوه را در یک حلقه فراخوانی کنید. در صورت جستجوی موفقیت آمیز، تاریخ شروع جستجو را به یک الگوی جدید تغییر دهید و وجود الگوهای معکوس شناسایی شده را در آرایه الگوهای معکوس قبلی پیدا کنید. اگر الگوی موجود در آرایه وجود دارد، به جستجوی جدید بروید.

   while(!IsStopped() && Pattern.Search(start_search))
     {
      start_search=fmax(start_search,Pattern.EndTrendTime()+PeriodSeconds(e_TimeFrame));
      bool found=false;
      for(int i=2;i<ar_Objects.Total();i++)
         if(Pattern.Compare(ar_Objects.At(i),0)==0)
           {
            found=true;
            break;
           }
      if(found)
         continue;

اگر الگوهای معکوس جدیدی یافت شد، با فراخوانی تابع () CheckPattern سیگنال ورود به بازار را بررسی کنید. در صورت لزوم، الگو را در آرایه ذخیره کنید و نمونه کلاس جدید را برای جستجوی بعدی مقداردهی اولیه کنید. این حلقه تا زمانی که متد ()Search  در طول یکی از جستجوهای بعدی false بازگرداند، ادامه می­یابد.

      if(!CheckPattern(Pattern))
         continue;
      if(!ar_Objects.Add(Pattern))
         continue;
      Pattern=new CPattern();
      if(CheckPointer(Pattern)==POINTER_INVALID)
         break;
      if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
        {
         delete Pattern;
         break;
        }
     }
//---
   return;
  }

بیایید نگاهی به الگوریتم تابع () CheckPattern بیندازیم تا تصویر کامل شود. این روش اشاره­ گر را در پارامترها به نمونه کلاس CPatern دریافت می­کند و مقدار منطقی نتیجه عملیات را برمی­گرداند. اگر تابع false برگرداند، الگوی تحلیل شده از آرایه اشیای ذخیره شده حذف می­شود.

در ابتدای تابع، روش جستجوی سیگنال ورود به بازار از کلاس CPatern را فراخوانی کنید. در صورت عدم موفقیت در بررسی، با نتیجه false از تابع خارج شوید.

bool CheckPattern(CPattern *pattern)
  {
   int signal=0;
   double sl=-1, tp1=-1, tp2=-1;
   if(!pattern.CheckSignal(signal,sl,tp1,tp2))
      return false;

اگر جستجوی سیگنال ورود به بازار موفقیت آمیز است، سطح معاملات را تنظیم کنید و مطابق سیگنال، سفارش ورود به بازار را ارسال کنید.

   double price=0;
   double to_close=100;
//---
   switch(signal)
     {
      case 1:
        price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
        CLimitTakeProfit::Clear();
        if((tp1-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
           if(CLimitTakeProfit::AddTakeProfit((uint)((tp1-price)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100)))
              to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100);
        if(to_close>0 && (tp2-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
           if(!CLimitTakeProfit::AddTakeProfit((uint)((tp2-price)/_Point),to_close))
              return false;
        if(Trade.Buy(d_Lot,_Symbol,price,sl-i_SL*_Point,0,NULL))
           return false;
        break;
      case -1:
        price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
        CLimitTakeProfit::Clear();
        if((price-tp1)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
           if(CLimitTakeProfit::AddTakeProfit((uint)((price-tp1)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100)))
              to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100);
        if(to_close>0 && (price-tp2)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
           if(!CLimitTakeProfit::AddTakeProfit((uint)((price-tp2)/_Point),to_close))
              return false;
        if(Trade.Sell(d_Lot,_Symbol,price,sl+i_SL*_Point,0,NULL))
           return false;
        break;
     }
//---
   return true;
  }

اگر معامله با موفقیت ایجاد شد، از تابع با نتیجه false خارج شوید. این کار به منظور حذف الگوهای معکوس استفاده شده از آرایه انجام می­شود. این به ما اجازه می­دهد تا از ایجاد معامله در همان الگو جلوگیری کنیم.

کد کامل همه متدها و توابع در پیوست ارائه شده است.

 آزمایش استراتژی

اکنون که EA توسعه یافته است، زمان بررسی کار آن بر روی داده­ های گذشته فرا رسیده است. این آزمون در دوره 9 ماهه 2018 برای EURUSD انجام میشود. جستجوی الگوها قرار است در M30 انجام شود، در حالی که نقاط ورود موقعیت باید در M5 شناسایی شود.

آزمایش استراتژی

الگوهای معکوس

نتایج آزمون، توانایی سودآوری EA را نشان داد. EA در دوره آزمون 90 معامله (70 مورد سودآور بود) انجام داد. ضریب سود 2.02 ، ضریب بازیابی 4.77 است که نشان دهنده امکان استفاده از EA در حساب­ های واقعی است. نتایج کامل آزمون در زیر نشان داده شده است.

الگوهای معکوس

الگوهای معکوس

 

نتیجه

در این مقاله، ما EA را بر اساس الگوهای معکوس  Double top/bottom trend توسعه داده­ ایم. آزمایش EA بر روی داده­ های گذشته نتایج قابل قبولی را نشان داده و توانایی EA در تولید سود را تأیید می­کند که امکان اعمال الگوهای معکوس Double top/bottom  را به عنوان یک سیگنال معکوس trend کارآمد هنگام جستجوی نقاط ورود به بازار تأیید می­کند.

 

منابع

  1. Implementing indicator calculations into an Expert Advisor code
  2. Using limit orders instead of Take Profit without changing the EA’s original code
برنامه های مورد استفاده در مقاله :
# Name Type Description
1 ZigZag.mqh Class library Zig Zag indicator class
2 Trends.mqh Class library Trend search class
3 Pattern.mqh Class library Class for working with patterns
4 LimitTakeProfit.mqh Class library Class for replacing order take profit with limit orders
5 Header.mqh Library EA headers file
6 DoubleTop.mq5 Expert Advisor EA based on the Double top/bottom strategy

Attached files | Download ZIP

MQL5.zip (183.66 KB)

این مقاله ترجمه شده توسط تیم آکادمی ایران ام کیو ال می باشد. 

صفحه اصلی مقاله

سایر مقالات مرتبط

متا تریدر چیست؟
متاتریدر

متا تریدر چیست؟

متا تریدر چیست؟ اولین سوالی که هر فرد وقتی می خواهد آموزش های متاتریدر مانند آموزش صفر تا صد mql5،

کامل ترین و بهترین آموزش متاتریدر 4
mql4

کامل ترین آموزش متاتریدر 4

بهترین آموزش متاتریدر 4 متاتریدر4 یک پلتفرم معاملاتی محسوب می‌ شود که دارای رابط کاربری ساده است و همین یادگیری

پاسخ‌ها

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *