تجزیه و تحلیل انجام شده در مقاله ?”How long is the trend” نشان میدهد که قیمت برای %60 مواقع در trend پابرجاست. این بدین معنا است که ایجاد یک معامله در ابتدای trend بهترین نتیجه را دارد. جستجوی نقاط بازگشت trend تعداد زیادی الگوی بازگشتی ایجاد کرده است.الگوهای معکوس Double top / bottom یکی از شناخته شده ترین و پر استفاده ترین هاست.
جنبه های نظری شکل گیری در الگوهای معکوس
در این مرحله، معامله گران برخلاف trend، شروع به بازی میکنند و از سطح، roll-back میکنند و قیمت را به سمت correction میبرند. در حالی که جنبش correction شتاب میگیرد، معامله گران پیرو این trend با تعیین سود یا بستن معامله های زیانده که هدف آنها شکسته شدن سطح بود، شروع به خروج از بازار میکنند. این حرکت موجب تقویت جنبش و حتی بیشتر از آن منجر به ظهور یک trend جدید میشود.
هنگام جستجو الگوهای معکوس در نمودار، جستجوی الگویی که مطابقت دقیقی با top / bottom داشته باشد، فایدهای ندارد. انحراف از سطح top / bottom طبیعی تلقی میشود. فقط مطمئن شوید که قله های نمودار در همان سطح حمایت / مقاومت قرار دارند. قابلیت اطمینان الگو به مقاومت سطحی که بر اساس آن است بستگی دارد.
استراتژی معامله الگوهای معکوس
محبوبیت الگوهای معکوس، در این است که استراتژیهای متعددی را در برمیگیرد. در اینترنت، حداقل سه نقطه ورودی متفاوت برای معامله این الگو وجود دارد.
2 مورد 1
اولین نقطه ورود بر اساس شکسته شدن neckline است. stop loss فراتر از خط top / bottom تنظیم میشود. رویکردهای مختلفی برای تعریف “شکسته شدن neckline” وجود دارد. معامله گران ممکن است از barای که در زیر neckline بسته میشود و همچنین barای که برای یک فاصله ثابت از neckline میشکند استفاده کنند. هر دو رویکرد، موافقان و مخالفان خود را دارند. در صورت حرکت sharp، ممکن است یک candle با فاصله کافی از neckline بسته شود و باعث ناکارآمد بودن الگو شود.
اشکال این روش، سطح نسبتاً بالای 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 است.
هر سه استراتژی دستورالعمل خروج در سطح برابر با فاصله بین اکسترمم و 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 جستجوی الگوهای معکوس
پس از تعریف اکسترممهای قیمت، بلوک جستجوی نقاط ورود به بازار را ایجاد کنید. این کار را به دو مرحله فرعی تقسیم کنید:
- جستجوی الگوهای معکوس بالقوه ورود به بازار.
- نقطه ورود به بازار.
این قابلیت به کلاس 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 تنظیم میشود. برای سود خود دو سطح تعیین کنید:
- با %90 از خط اکسترمم به neckline در جهت موقعیت.
- با %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 پیاده سازی میشود. میتوان آن را به دو بلوک تقسیم کرد:
- بررسی سیگنال های ورود به بازار در الگوهای معکوس قبلاً شناسایی شده. این هر بار که candle جدیدی در بازه زمانی کوچکی از جستجو برای تأیید سیگنال ظاهر میشود، راه اندازی میشود.
- جستجوی الگوهای معکوس جدید. این هر بار که 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 کارآمد هنگام جستجوی نقاط ورود به بازار تأیید میکند.
منابع
- Implementing indicator calculations into an Expert Advisor code
- 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
این مقاله ترجمه شده توسط تیم آکادمی ایران ام کیو ال می باشد.
پاسخها