تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

مقدمه

در مقاله‌ی قبلی (یعنیTransferring an Indicator Code into an Expert Advisor Code. Indicator Structure) ساختار کلی یک اندیکاتور و کدی که برای تبدیل شدن به کد اکسپرت مدنظر است را بررسی کردیم، و ایده‌های اصلی در تغییرات ابتدایی کد اندیکاتور را توضیح دادیم. اکنون می‌خواهیم کد بدست‌آمده را به یک تابع سفارشی تبدیل کنیم، چراکه این راه شاید مناسب‌ترین راه برای ارائه‌ی یک کد اندیکاتوری در یک کد اکسپرتی باشد. تابع سفارشی را در قالب فایل mqh ارائه می‌دهیم و عرضه‌ی آن در اکسپرت با استفاده از دستور #include، جای بسیار کمی خواهد گرفت و فراخوانی این تابع نسبت به یک اندیکاتور سفارشی خیلی سخت نیست. آنچه اهمیت دارد این است که چنین توابع سفارشی‌ایی را می‌توان بعداً به‌شکل جهانی مورد استفاده قرار داد زیرا برای هر اکسپرتی مناسب هستند.

قبل از نوشتن این تابع، بیایید این موضوع را تحلیل کنیم که چگونه این تابع، صرف‌نظر از ساختار داخلی‌اَش، با دیگر بخش‌های اکسپرت تعامل می‌کند.

ساختار اکسپرت با فراخوانی اندیکاتور سفارشی

بیایید اول ساختار شماتیک یک اکسپرت را با هم بررسی کنیم. این اکسپرت داده‌ها را از اندیکاتورهای سفارشی دریافت می‌کند و پیش از هر چیز، فقط به این قسمت علاقه‌مند هستیم. فعلاً در مورد روشی که اکسپرت با آن، این اطلاعات را پردازش می‌کند، و همینطور تبدیل آنها به سیگنال‌های ترید و نیز ساختار بخش اجرایی اکسپرت، حرفی نمی‌زنیم. بیایید به‌عنوان یک اندیکاتور سفارشی در این اکسپرت، همان ساختاری را بررسی کنیم که در مقاله‌ی قبلی به آن پرداختیم. در اینجا مثالی از ساختار اکسپرت را مشاهده می‌کنید:

//+------------------------------------------------------------------+
//|                                               ExpertIndPlan0.mqh |
//|                      Copyright © ۲۰۰۷, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © ۲۰۰۷, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- EA input parameters
extern int period10 = 15;
extern int period11 = 15;
extern int period12 = 15;
//---- EA input parameters
extern int period20 = 15;
extern int period21 = 15;
extern int period22 = 15;
//---- Declaring buffers for indicator values
double Ind_Buffer1[6];
double Ind_Buffer2[6];
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
// Here is the code of EA initialization
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation
   if(iBars(Symbol(), 0) < period10 + period11 + period12 + 10)
       return(0);
   if(iBars(Symbol(), 0) < period20 + period21 + period22 + 10)
       return(0);
//---- getting indicator values for further calculation
   for(int bar = 5; bar >= 1; bar--)
     {
       Ind_Buffer1[bar] = iCustom("IndicatorPlan", Symbol(), 0, period10, 
                                  period11, period12, 0, bar);
       Ind_Buffer2[bar] = iCustom("IndicatorPlan", Symbol(), 0, period20, 
                                  period21, period22, 0, bar);
     }
// Here is the EA code, forming trade signals 
// based on the values of indicator buffer cells 
//----
// here is the EA executive part code, 
// requesting for placing an order 
//----
   return(0);
  }
//+------------------------------------------------------------------+

در این طرح، ما روی هر تیک (tick) از بافر صفر اندیکاتور سفارشیIndicatorPlan.mq4 ، مقادیر شمارش‌شده‌ را در دو فراخوان، می‌گیریم، و آنها را در آرایه‌های مرسوم Ind_Buffer1[] و Ind_Buffer2[] جای می‌دهیم. طرح فراخوانی اندیکاتور اینگونه طراحی‌ شده‌‌است که به‌منظور محاسبات بیشتر فقط به مقادیر پنج اندیکاتور آخر، به‌استثنای اندیکاتور صفر، نیاز داریم.

ساختار اکسپرت با فراخوان تابع سفارشی

درحالیکه ما مشغول توسعه‌ی یک تابع اندیکاتور جهانی هستیم، که برای هر اکسپرتی مناسب است، [این تابع] باید مقادیر دریافت‌شده را به بافر اندیکاتور/اندیکاتورهای مشابه بفرستد، که [در آنجا] مقادیر اندیکاتور برای تمام کندل‌ها در نمودار، ذخیره می‌شوند. البته، می‌توانیم تابع اندیکاتوری را توسعه دهیم که فراخواندن آن کاملاً مشابه با فراخواندن یک اندیکاتور سفارشی باشد، اما نوشتن چنین چیزی علاوه بر طولانی بودن کد، بسیار زمان‌بر هم خواهد بود.

می‌شود این کار را ساده‌تر انجام داد. این تابع باید پارامترهای ورودی را، [به‌صورت] پارامترهای یک اندیکاتور سفارشی و بافر دریافت کند، و باید همان بافر را با حالت شبیه‌سازی اندیکاتور بازگرداند – آن‌هم در نقطه‌ای که سلول‌ها (خانه‌ها) با مقادیرِ اندیکاتورِ محاسبه‌شده، پُر شده‌اند. این کار را می‌توان به‌سادگی با آوردن یک متغیر اکسترنال مرتبط-با-مرجع (linked-by-reference) برای تابع مربوط به بافر اندیکاتور، انجام داد. در زبان MQL4، این مورد اینطور دیده خواهد شد: double& InputBuffer. تابع این اندیکاتور باید به‌عنوان یک تابع منطقی ثبت شود، که اگر محاسبات موفقیت‌آمیز بودند، به “صحیح” (true)، و اگر محاسبات، به‌دلیل عدم‌ وجودِ تعداد مناسبی از کندل‌ها روی نمودار، موفقیت‌آمیز نبودند، به “غلط” (false) بازگردد. بعد از همه‌ی این توضیحات، تابع اندیکاتور (که از روی طرح اندیکاتور ساخته شده و در مقاله‌ی قبلی به آن پرداخته شد)، قرار است چنین فرمی داشته باشد:

bool Get_IndSeries(int Number,string symbol, int timeframe, 
                   bool NullBarRecount, int period0, int period1, 
                   int period2, double& InputBuffer0[],
                   double& InputBuffer1[], double& InputBuffer2[])

این تابع اندیکاتور یک متغیر اکسترنال اضافی دیگر به‌نام Number دارد که مقدار شماره‌ی فراخوان این تابع اندیکاتور را می‌پذیرد.

طبیعی است که بغیر از بافر اندیکاتور InputBuffer0، متغیرهای اکسترنال همچنین شامل بافرهایی برای محاسبات واسطه‌ای InputBuffer1 و InputBuffer2 باشند، زیرا ساختن این بافرها درون تابع بسیار مشکل‌ساز است. بهتر است حالت اندیکاتور عملکرد این بافرها را درون تابع، شبیه‌سازی کنیم. این کار مشکلی پیش نخواهد آورد. اکنون بیایید به معنی متغیر اکسترنال NullBarRecount بپردازیم. درواقع اکثر اکسپرت‌ها نیاز به محاسبات، روی کندل صفر، ندارند، و درحالیکه ما مشغول نوشتن کد این تابع اندیکاتور جهانی هستیم، به‌صورت طبیعی مقادیر اندیکاتور روی کندل صفر محاسبه‌ی مجدد خواهند شد، که این ممکن است زمان اجرا را به‌طور قابل‌توجهی افزایش دهد. با “false” نشان دادن پارامتر اکسترنال تابع NullBarRecount، در واقع داریم محاسبات تابع را روی کندل صفر به‌نوعی ممنوع می‌کنیم – البته اگر این محاسبات را نیاز نداشته باشیم.

اکنون می‌توانیم طرح ساختار اکسپرت را برای فراخواندن اندیکاتورها ارائه کنیم. در این طرح حالت فراخوانی توابع رعایت شده‌است:

//+------------------------------------------------------------------+
//|                                               ExpertIndPlan1.mqh |
//|                      Copyright © ۲۰۰۷, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © ۲۰۰۷, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- EA input parameters
extern int period10 = 15;
extern int period11 = 15;
extern int period12 = 15;
//---- EA input parameters
extern int period20 = 15;
extern int period21 = 15;
extern int period22 = 15;
//---- Indicator buffers declaration
double Ind_Buffer10[], Ind_Buffer11[], Ind_Buffer12[];
double Ind_Buffer20[], Ind_Buffer21[], Ind_Buffer22[];
//+------------------------------------------------------------------+
//| Get_IndSeries() function                                         |
//+------------------------------------------------------------------+
//---- Declaration of the function Get_IndSeries()
bool Get_IndSeries(int Number,string symbol, int timeframe, 
                   bool NullBarRecount, int period0, int period1, 
                   int period2, double& InputBuffer0[], 
                   double& InputBuffer1[], double& InputBuffer2[]) 
  {
    //---- 
    // Here is the code of the function GetIdicator()
    //----
    return(true);
  }
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//----
// Here is the code of the EA initialization
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation
   if(iBars(Symbol(), 0) < period10 + period11 + period12 + 10)
      return(0);
   if(iBars(Symbol(), 0) < period20 + period21 + period22 + 10)
      return(0);
//---- getting indicator values for further calculation
   if(!Get_IndSeries(0,Symbol(), 0, false, period10, period11, period12,
      Ind_Buffer10, Ind_Buffer11, Ind_Buffer12))
       return(0);
   if(!Get_IndSeries(1, Symbol(), 0, false, period20, period21, period22, 
      Ind_Buffer20, Ind_Buffer21,Ind_Buffer22))
       return(0);  
//----
// Here is the EA code, forming trade signals 
// based on the values of indicator buffer cells 
//----
// here is the EA executive part code, 
// requesting for placing an order
//----
 
   return(0);
  }
//+------------------------------------------------------------------+

طرح کلی تبدیل یک کد اندیکاتور به یک تابع سفارشی

بعد از این کار مقدماتی، اکنون می‌توانیم به‌سراغ ساختن یک طرح کلی از ساختار داخلی یک تابع اندیکاتور برویم. بیایید مبنا را طرح اندیکاتور مقاله‌ی قبلی قرار دهیم. بعید است کار سختی در پیش باشد:

  • فقط محتوی تابع int start()را بردارید؛
  • اعلام تابع Get_IndSeries()را اضافه کنید:
bool Get_IndSeries(string symbol, int timeframe, bool NullBarRecount,
                   int period0, int period1, int period2, 
                   double& InputBuffer0, double& InputBuffer1, 
                   double& InputBuffer2)
  • نام‌های بافرهای اندیکاتور داخل کد را (Ind_Buffer) طبق نام‌های بافر (InputBuffer) متغیرهای اکسترنالِ تابع Get_IndSeries() تغییر دهید؛
  • اعلام متغیر LastCountBar را اضافه کنید؛
  • صحیح بودن متغیر NullBarRecount را بررسی کنید:
if(!NullBarRecount)
    LastCountBar = 1;
  • در تمام سیکل‌های محاسباتی اندیکاتور، صفر را به ‌ LastCountBarتغییر دهید؛
  • تغییرات را در همان آغاز کد صورت دهید – وقتی که دارید بررسی می‌کنید آیا عدد کندل‌ها برای محاسبات بیشتر کافی هست یا خیر: return(0) به return(false)؛
  • در آخر کد، return(0) را به return(true) تغییر دهید؛

تابع اندیکاتور آماده است:

//+------------------------------------------------------------------+
//|                                                Get_IndSeries.mqh |
//|                      Copyright © ۲۰۰۷, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+ 
bool Get_IndSeries(int Number, string symbol,int timeframe, 
                   bool NullBarRecount, int period0, int period1, 
                   int period2, double& InputBuffer0[], 
                   double& InputBuffer1[], double& InputBuffer2[])  
  {
//---- getting the number of all bars of a chart
   int IBARS = iBars(symbol, timeframe);
//---- Checking whether the bars number is enough for further calculation
   if(IBARS < period0 + period1 + period2)
      return(false);
//---- EMULATION OF INDICATOR BUFFERS
   if(ArraySize(InputBuffer0) < IBARS)
     {
       ArraySetAsSeries(InputBuffer0, false);
       ArraySetAsSeries(InputBuffer1, false);
       ArraySetAsSeries(InputBuffer2, false);
       //----  
       ArrayResize(InputBuffer0, IBARS); 
       ArrayResize(InputBuffer1, IBARS); 
       ArrayResize(InputBuffer2, IBARS); 
       //----
       ArraySetAsSeries(InputBuffer0, true);
       ArraySetAsSeries(InputBuffer1, true);
       ArraySetAsSeries(InputBuffer2, true); 
     } 
//----+ introducing static memory variables
   static int IndCounted[];
//----+ changing the size of static variables
   if(ArraySize(IndCounted) < Number + 1)
       ArrayResize(IndCounted, Number + 1); 
//----+ introducing an integer variable
   int LastCountBar;
//----+ Checking if the recalculation of the zero bar is allowed
   if(!NullBarRecount)
       LastCountBar = 1;
   else 
       LastCountBar = 0;
//----+ Inserting a variable with a floating point
   double Resalt0, Resalt1, Resalt2;
//----+ Inserting integer variables and getting already calculated bars
   int limit, MaxBar, bar, counted_bars = IndCounted[Number];
//----+ Remembering the number of all bars of a chart (we do not count the zero bar!)
   IndCounted[Number] = IBARS - 1;
//---- defining the number of the oldest bar, 
// starting from which new bars will be recalculated
   limit = IBARS - counted_bars - 1; 
//---- defining the number of the oldest bar, 
// starting from which new bars will be recalculated
   MaxBar = IBARS - 1 - (period0 + period1 + period2); 
//---- initialization of zero 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = IBARS - 1; bar >= 0; bar--)
         {
           InputBuffer0[bar] = 0.0;
           InputBuffer1[bar] = 0.0;
           InputBuffer2[bar] = 0.0;
         }
     }
//----+ THE FIRST CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= LastCountBar; bar--)
     {
       // Here code of the variable Resalt1 calculation  
       // based on the external variable period1
       InputBuffer1[bar] = Resalt1;
     }
//----+ THE SECOND CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= LastCountBar; bar--)
     {
       // Here code of the variable Resalt2 calculation 
       // based on the values of the buffer Ind_Buffer1[] 
       // and external variable period2
       InputBuffer2[bar] = Resalt2;
     }
//----+ THE MAIN CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= LastCountBar; bar--)
     {
       // Here code of the variable Resalt0 calculation 
       // based on the values of the buffer Ind_Buffer2[] 
       // and external variable period0
       InputBuffer0[bar] = Resalt0;
     }
   return(true);
  }
//+------------------------------------------------------------------+

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

مثالی از نوشتن یک تابع اندیکاتور سفارشی

اکنون بیایید یک تابع اندیکاتور بنویسیم. بیایید یک اندیکاتور بی‌نهایت ساده را انتخاب کنیم:

//+------------------------------------------------------------------+
//|                                                         RAVI.mq4 |
//|                      Copyright © ۲۰۰۵, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © ۲۰۰۵, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- drawing the indicator in a separate window
#property indicator_separate_window 
//---- number of indicator buffers
#property indicator_buffers 1 
//---- indicator color
#property indicator_color1 Red 
//---- INPUT PARAMETERS OF THE INDICATOR 
extern int Period1 = 7; 
extern int Period2 = 65; 
extern int MA_Metod = 0;
extern int PRICE = 0;
//---- indicator buffers
double ExtBuffer[]; 
//+------------------------------------------------------------------+ 
//| RAVI initialization function                                     | 
//+------------------------------------------------------------------+ 
int init() 
  { 
//---- indicator drawing style
   SetIndexStyle(0, DRAW_LINE); 
//---- indicator buffers 
   SetIndexBuffer(0,ExtBuffer); 
//---- indicator name and labels for subwindows 
   IndicatorShortName("RAVI (" + Period1+ ", " + Period2 + ")"); 
   SetIndexLabel(0, "RAVI"); 
//---- initialization end
   return(0); 
  } 
//+------------------------------------------------------------------+ 
//| RAVI iteration function                                          | 
//+------------------------------------------------------------------+ 
int start() 
  {
   int MinBars = MathMax(Period1, Period2); 
//---- checking whether the bars number is enough for further calculation
   if(Bars < MinBars)
       return(0);
//----+ Introducing variables with a floating point 
   double MA1, MA2, result; 
//----+ Introducing integer variables and getting already calculated bars
   int MaxBar, bar, limit, counted_bars = IndicatorCounted();
//---- checking for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last calculated bar should be recalculated 
   if(counted_bars > 0)
       counted_bars--;
//---- defining the number of the oldest bar, 
// starting from which all bars will be recalculated 
   MaxBar = Bars - 1 - MinBars;
//---- defining the number of the oldest bar, 
// starting from which new bars will be recalculated 
   limit = Bars - counted_bars - 1; 
//---- zero initialization
   if(limit > MaxBar)
     {
       for(int ii = Bars - 1; ii >= MaxBar; ii--)
           ExtBuffer[ii] = 0.0;
       limit = MaxBar;
     }
//---- main cycle 
   for(bar = 0; bar <= limit; bar++) 
     { 
       MA1 = iMA(NULL, 0, Period1, 0, MA_Metod, PRICE,bar); 
       MA2 = iMA(NULL, 0, Period2, 0, MA_Metod, PRICE,bar); 
       //---- 
       result = ((MA1 - MA2) / MA2)*100; 
       ExtBuffer[bar] = result; 
     }  
//---- 
   return(0); 
  } 
//+------------------------------------------------------------------+

تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

تثبیت الگوریتم

  1. از شر تمامی عناصر غیرضروری در کد اندیکاتور، خود را خلاص کنید؛
  2. کد شبیه‌سازی بافر اندیکاتور را برای یک تک بافر[۱] ExtBuffer[] بنویسید؛
  3. تابع IndicatorCounted() را جایگزین متغیر IndCounted کنید؛
  4. متغییرIndCounted  را با مقدار کندل‌های نمودار منهای ۱، شروع کنید؛
  5. کندل‌های متغیر از پیش‌ تعیین‌شده را به فراخوانی iBars (نماد اَرز، تایم‌فریم) سری‌های زمانی، تغییر دهید؛
  6. چک کردن‌های غیرضروری را برای counted_bars حذف کنید:
//---- checking possible errors
if(counted_bars < 0)
    return(-1);
//---- the last calculated bar must be recalculated 
if(counted_bars > 0) 
    counted_bars--;
  1. فقط محتوی تابع int start() را باقی بگذارید؛
  2. اعلام تابع Get_RAVISeries()را اضافه کنید:
bool Get_RAVISeries(int Number, string symbol,int timeframe, 
                    bool NullBarRecount, int Period1, 
                    int Period2, int MA_Metod, int  PRICE, 
                    double& InputBuffer[])
  1. نام‌های بافر اندیکاتور داخل کد را (ExtBuffer) عیناً مطابق با نام‌های بافر (InputBuffer) متغیرهای اکسترنالِ تابع Get_RAVISeries() جایگزین کنید؛
  2. اعلام متغیر LastCountBar را اضافه کنید؛
  3. متغیر استاتیکIndCounted  را به یک صف/آرایه IndCounted[Number] تبدیل کرده و یک کد برای تغییرات حجم متغیرها، بسته به تعداد فراخوان‌های تابع mqh، اضافه کنید:
//----+ changing the size of static variables
   if(ArraySize(IndCounted) < Number + 1)
     {
       ArrayResize(IndCounted, Number + 1); 
     }
  1. صحیح بودن متغیر NullBarRecount را بررسی کنید:
if(!NullBarRecount)
    LastCountBar = 1;
  1. در تمام سیکل‌های محاسبات اندیکاتور، صفر را به LastCountBar تغییر دهید:
for(bar = limit; bar >= LastCountBar; bar--)
  1. تغییرات را در همان آغاز کد صورت دهید – وقتی که دارید بررسی می‌کنید آیا عدد کندل‌ها برای محاسبات بیشتر کافی هست یا خیر: return(0) به return(false)؛
  2. در آخر return(0) را با return(true) جایگزین کنید.

بعد از تغییر تمامی کدها، تابع اندیکاتور Get_RAVISeries() را خواهیم داشت:

//+------------------------------------------------------------------+
//|                                               Get_RAVISeries.mqh |
//|                      Copyright © ۲۰۰۷, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
bool Get_RAVISeries(int Number, string symbol,int timeframe, 
                    bool NullBarRecount, int Period1, int Period2, 
                    int MA_Metod, int  PRICE, double& InputBuffer[])    
  {
//---- getting the number of all bars of a chart
   int IBARS = iBars(symbol, timeframe);  
//---- Checking whether the bars number is enough for further calculation
   if(IBARS < MathMax(Period1, Period2))
       return(false);
//---- EMULATION OF INDICATOR BUFFERS
   if(ArraySize(InputBuffer) < IBARS)
     {
       ArraySetAsSeries(InputBuffer, false);
       //----  
       ArrayResize(InputBuffer, IBARS); 
       //----
       ArraySetAsSeries(InputBuffer, true);
     } 
//----+  inserting static variables of memory
   static int IndCounted[]; 
//----+ changing the size of static variables
   if(ArraySize(IndCounted) < Number + 1)
     {
       ArrayResize(IndCounted, Number + 1); 
     }
 //----+ Introducing an integer variable
   int LastCountBar;
//----+ Checking whether the recalculation of the zero bar is allowed
   if(!NullBarRecount)
       LastCountBar = 1;
//----+ Introducing floating point variables 
   double MA1,MA2,result; 
//----+ Introducing integer variables and getting alreadu calculated bars
   int MaxBar, bar, limit, counted_bars = IndCounted[Number];
//----+ Remembering the amount of all chart bars
   IndCounted[Number] = IBARS - 1;
//---- determining the number of the oldest bar, 
// starting from which new bars will be recalculated
   limit = IBARS - counted_bars - 1; 
   // Print(IBARS - counted_bars); 
//---- determining the number of the oldest bar, 
// starting from which all bars will be recalculated
   MaxBar = IBARS - 1 - MathMax(Period1, Period2); 
//---- zero initialization 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = IBARS - 1; bar >= 0; bar--)
         {
           InputBuffer[bar] = 0.0;
         }
     } 
//----+ THE FIRST CYCLE OF INDICATOR CALCULATION 
   for(bar = limit; bar >= LastCountBar; bar--)
     { 
       MA1 = iMA(symbol, timeframe, Period1, 0, MA_Metod, PRICE, bar); 
       MA2 = iMA(symbol, timeframe, Period2, 0, MA_Metod, PRICE, bar); 
       //---- 
       result = ((MA1 - MA2) / MA2)*100; 
       InputBuffer[bar] = result; 
     } 
//----+  
   return(true);
  }
//+------------------------------------------------------------------+

مسلماً کار به‌خوبی پیش رفته است! شاید خیلی آسان نبود… اما یک سوال مطرح است. آیا این تابع اندیکاتور همانند یک تابع سفارشی محاسبات را انجام می‌دهد؟

تست کردن تابع اندیکاتور سفارشی جهت دقت در محاسبات

نیاز داریم بررسی کنیم آیا نتایج محاسبات این تابع برابر با نتایج محاسبات یک اندیکاتور سفارشی است یا خیر. به همین جهت، بهترین و مناسب‌ترین اکسپرت آن است که ترید نمی‌کند و فقط مقادیر را از اندیکاتور سفارشی  RAVI.mq4و تابع سفارشی Get_RAVISeries() دریافت، و تفاوت را پیدا کرده و بعد از آن مقادیر اندیکاتور، مقادیر تابع سفارشی، و اختلاف بین آنها را درون یک فایل گزارش می‌ریزد. تمامی کاری که باید بکنیم، تحلیل محتوای آن فایل گزارش است؛ تا بتوانیم نتیجه‌گیری نهایی در مورد تطبیق الگوریتم‌مان از تابع سفارشی Get_RAVISeries() و اندیکاتور RAVI.mq4 را اتخاذ کنیم:

//+------------------------------------------------------------------+
//|                                           Get_RAVISeriesTest.mq4 |
//|                      Copyright © ۲۰۰۷, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © ۲۰۰۷, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- INPUT EA PARAMETERS
extern bool NullBarRecount = true;
//---- indicator buffers
double RAVI_Buffer0[];
double RAVI_Buffer1[];
double RAVI_Buffer2[];
//+------------------------------------------------------------------+
//| Get_RAVISeries() function                                        |
//+------------------------------------------------------------------+
#include <Get_RAVISeries.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- 
   double Ind_Velue, Resalt;
//---- 
   if(!Get_RAVISeries(0, Symbol(), 0, NullBarRecount, 10, 20, 1, 0,
      RAVI_Buffer0))
       return(0);  
   if(!Get_RAVISeries(1, Symbol(), 240, NullBarRecount, 25, 66, 2, 1,
      RAVI_Buffer1))
       return(0);
   if(!Get_RAVISeries(2, Symbol(), 1440, NullBarRecount, 30, 70, 3, 3,
      RAVI_Buffer2))
       return(0);
//---- getting indicator values for the test 0
   Ind_Velue = iCustom(NULL, 0, "RAVI", 10, 20, 1, 0, 0, 2); 
   Resalt = RAVI_Buffer0[2] - Ind_Velue; 
   Print("  " + Ind_Velue + "    " + RAVI_Buffer0[2] + "    " + Resalt+"");
//---- getting indicator values for the test 1
   Ind_Velue = iCustom(NULL, 240, "RAVI", 25, 66, 2, 1, 0, 2);
   Resalt = RAVI_Buffer1[2] - Ind_Velue; 
   Print("  " + Ind_Velue + "    " + RAVI_Buffer1[2] + "    " + Resalt+"" );
//---- getting indicator values for the test 2
   Ind_Velue = iCustom(NULL, 1440, "RAVI", 30, 70, 3, 3, 0, 2);
   Resalt = RAVI_Buffer2[2] - Ind_Velue; 
   Print("  " + Ind_Velue + "    " + RAVI_Buffer2[2] + "    " + Resalt + "");
//----
   return(0);
  }
//+------------------------------------------------------------------+

اکسپرت Get_RAVISeriesTest را در استراتژی‌تستر اجرا کنید. طبیعتاً فایل کامپایل‌شده‌ی  RAVI.ex4باید درون پوشه‌ی  \expert\indicatorsباشد، و نیز فایل Get_RAVISeries.mqh در پوشه‌ی \expert \include از نرم‌افزار متاتریدر، باشد. در ژورنال استراتژی‌تستر و در یک فایل گزارش، دو ستون با مقادیر اندیکاتور و آنالوگ آن را به‌شکل یک تابع می‌بینیم؛ ستون سوم تفاوت این مقادیر را نشان می‌دهد. تمام مقادیر ستون آخر برابر با صفر هستند و این یعنی اینکه این مقادیر در هر دو مدل، کاملاً یکسان هستند. نتیجه اینکه نوشتن تابع سفارشی اندیکاتور با موفقیت انجام شده‌است!

تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

نتیجه‌گیری

موفق شدیم معضل تبدیل کد اندیکاتور از یک اندیکاتور سفارشی را به یک کد اکسپرت، حل کنیم؛ آن‌هم با ساختن نظیر اندیکاتور که به‌مثابه یک تابع سفارشی جهانی بوده، و می‌تواند در یک فایل mqh جای گرفته و در کد هر اکسپرتی به‌روش مشابه اندیکاتور سفارشی، استفاده شود.

در مقاله‌ی بعدی که مرتبط با این موضوع است، یک مثال پیچیده‌تر از نوشتن توابعی از این دست را تحلیل می‌کنیم و یک اکسپرت را برمبنای چنین توابعی اجرا خواهیم کرد.

این مقاله دارای فایل پیوست است.

۱) Single Buffer

M23admin

→ خواندن مطلب قبلی

بهینه‌سازی ربات معاملاتی در ترید واقعی

خواندن مطلب بعدی ←

رویـــکــــرد شــــیء‌گرا در MQL

۲۵ مورد نظر

نوشتن نظر شما

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