ویژگی‌های ساخت اندیکاتورهای سفارشی

ویژگی‌های سـاخت انـدیکاتورهای سفا

ویژگی‌های سـاخت انـدیکاتورهای سفارشی

ساخت اندیکاتورهای سفارشی در سیستم معاملاتی متاتریدر، چند ویژگی به‌همراه دارد.

  • اگر بخواهیم یک برنامه را اندیکاتور سفارشی تلقی کنیم، باید یک یا دو تعریف برای آن درنظر بگیریم:
#property  indicator_chart_window      // an indicator is drawn in the main chart window

یا

#property  indicator_separate_window   // an indicator is drawn in a separate window
  • برای تعیین مقیاسِ یک پنجرۀ اندیکاتور جداگانه، از این تعاریف استفاده می‌شود:
#property  indicator_minimum Min_Value
#property  indicator_maximum Max_Value

که در اینجا، “Min_Value” و “Max_Value” مقادیر مرتبط هستند. برای مثال، این مقادیر برای اندیکاتور سفارشی RSI بایستی به‌ترتیب صفر و ۱۰۰ باشند.

  • تعداد آرایه‌های اندیکاتور مورد نیاز برای ترسیم یک اندیکاتور، می‌بایستی اینگونه تعریف شود:
#property  indicator_buffers N

که در اینجا N می‌تواند مقداری از ۱ تا ۸ داشته باشد.

  • رنگ خطوط در یک اندیکاتور، با این تعاریف، مشخص می‌شود:
#property  indicator_color1  Silver
#property  indicator_color2  Red
...
#property  indicator_colorN  <SomeColor>

که در اینجا N تعداد آرایه‌های اندیکاتوری است که توسط “#define indicator_buffer” تعریف شده‌است.

  • توابعی وجود دارند که به ما اجازه می‌دهند فرآیند محاسباتی اندیکاتور و تصویرسازی آن را کنترل کنیم. از اندیکاتور سفارشی Ishimoku Kinko Hyo در اینجا برای نشان دادن این موضوع استفاده شده‌است:
//+------------------------------------------------------------------+
//|                                                     Ichimoku.mq4 |
//|                      Copyright © ۲۰۰۴, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © ۲۰۰۴, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
 
#property indicator_chart_window
#property indicator_buffers 7
#property indicator_color1 Red
#property indicator_color2 Blue
#property indicator_color3 SandyBrown
#property indicator_color4 Thistle
#property indicator_color5 Lime
#property indicator_color6 SandyBrown
#property indicator_color7 Thistle
//---- input parameters
extern int Tenkan=9;
extern int Kijun=26;
extern int Senkou=52;
//---- indicator buffers
double Tenkan_Buffer[];
double Kijun_Buffer[];
double SpanA_Buffer[];
double SpanB_Buffer[];
double Chinkou_Buffer[];
double SpanA2_Buffer[];
double SpanB2_Buffer[];
//---- span_a drawing begin
int a_begin;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//----
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,Tenkan_Buffer);
   SetIndexDrawBegin(0,Tenkan-1);
   SetIndexLabel(0,"Tenkan Sen");
//----
   SetIndexStyle(1,DRAW_LINE);
   SetIndexBuffer(1,Kijun_Buffer);
   SetIndexDrawBegin(1,Kijun-1);
   SetIndexLabel(1,"Kijun Sen");
//----
   a_begin=Kijun; if(a_begin&lt;Tenkan) a_begin=Tenkan;
   SetIndexStyle(2,DRAW_HISTOGRAM,STYLE_DOT);
   SetIndexBuffer(2,SpanA_Buffer);
   SetIndexDrawBegin(2,Kijun+a_begin-1);
   SetIndexShift(2,Kijun);
   SetIndexLabel(2,NULL);
   SetIndexStyle(5,DRAW_LINE,STYLE_DOT);
   SetIndexBuffer(5,SpanA2_Buffer);
   SetIndexDrawBegin(5,Kijun+a_begin-1);
   SetIndexShift(5,Kijun);
   SetIndexLabel(5,"Senkou Span A");
//----
   SetIndexStyle(3,DRAW_HISTOGRAM,STYLE_DOT);
   SetIndexBuffer(3,SpanB_Buffer);
   SetIndexDrawBegin(3,Kijun+Senkou-1);
   SetIndexShift(3,Kijun);
   SetIndexLabel(3,NULL);
   SetIndexStyle(6,DRAW_LINE,STYLE_DOT);
   SetIndexBuffer(6,SpanB2_Buffer);
   SetIndexDrawBegin(6,Kijun+Senkou-1);
   SetIndexShift(6,Kijun);
   SetIndexLabel(6,"Senkou Span B");
//----
   SetIndexStyle(4,DRAW_LINE);
   SetIndexBuffer(4,Chinkou_Buffer);
   SetIndexShift(4,-Kijun);
   SetIndexLabel(4,"Chinkou Span");
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Ichimoku Kinko Hyo                                               |
//+------------------------------------------------------------------+
int start()
  {
   int    i,k;
   int    counted_bars=IndicatorCounted();
   double high,low,price;
//----
   if(Bars&lt;=Tenkan || Bars&lt;=Kijun || Bars&lt;=Senkou) return(0);
//---- initial zero
   if(counted_bars&lt;1)
     {
      for(i=1;i&lt;=Tenkan;i++)    Tenkan_Buffer[Bars-i]=0;
      for(i=1;i&lt;=Kijun;i++)     Kijun_Buffer[Bars-i]=0;
      for(i=1;i&lt;=a_begin;i++) { SpanA_Buffer[Bars-i]=0; SpanA2_Buffer[Bars-i]=0; }
      for(i=1;i&lt;=Senkou;i++)  { SpanB_Buffer[Bars-i]=0; SpanB2_Buffer[Bars-i]=0; }
     }
//---- Tenkan Sen
   i=Bars-Tenkan;
   if(counted_bars>Tenkan) i=Bars-counted_bars-1;
   while(i>=0)
     {
      high=High[i]; low=Low[i]; k=i-1+Tenkan;
      while(k>=i)
        {
         price=High[k];
         if(high&lt;price) high=price;
         price=Low[k];
         if(low>price)  low=price;
         k--;
        }
      Tenkan_Buffer[i]=(high+low)/2;
      i--;
     }
//---- Kijun Sen
   i=Bars-Kijun;
   if(counted_bars>Kijun) i=Bars-counted_bars-1;
   while(i>=0)
     {
      high=High[i]; low=Low[i]; k=i-1+Kijun;
      while(k>=i)
        {
         price=High[k];
         if(high<price) high=price;
         price=Low[k];
         if(low>price)  low=price;
         k--;
        }
      Kijun_Buffer[i]=(high+low)/2;
      i--;
     }
//---- Senkou Span A
   i=Bars-a_begin+1;
   if(counted_bars>a_begin-1) i=Bars-counted_bars-1;
   while(i>=0)
     {
      price=(Kijun_Buffer[i]+Tenkan_Buffer[i])/2;
      SpanA_Buffer[i]=price;
      SpanA2_Buffer[i]=price;
      i--;
     }
//---- Senkou Span B
   i=Bars-Senkou;
   if(counted_bars>Senkou) i=Bars-counted_bars-1;
   while(i>=0)
     {
      high=High[i]; low=Low[i]; k=i-1+Senkou;
      while(k>=i)
        {
         price=High[k];
         if(high&lt;price) high=price;
         price=Low[k];
         if(low>price)  low=price;
         k--;
        }
      price=(high+low)/2;
      SpanB_Buffer[i]=price;
      SpanB2_Buffer[i]=price;
      i--;
     }
//---- Chinkou Span
   i=Bars-1;
   if(counted_bars>1) i=Bars-counted_bars-1;
   while(i>=0) { Chinkou_Buffer[i]=Close[i]; i--; }
//----
   return(0);
  }
//+------------------------------------------------------------------+
  • تابع “SetIndexStyle”، پارامترهای ترسیم یک آرایۀ اندیکاتور را کنترل می‌کند. حالت ترسیم DRAW_LINE، فرض را بر این می‌گذارد که خطوط بین مقادیر تعریف‌شده در یک آرایۀ اندیکاتور مرتبط، رسم می‌شوند. حالت ترسیم DRAW_HISTOGRAM نیز، که در پنجره‌ی اصلی اندیکاتور اِعمال شده‌است، ویژگی‌های خاص خود را دارد. یک هیستوگرام بین مقادیر مربوطۀ دو آرایه شاخص ترسیم شده‌است: زوج (در اینجا: SpanA_Buffer) و فَرد (در اینجا: SpanB_Buffer). در اینجا، از رنگ آرایۀ شاخصی استفاده می‌شود که مقدار آن بالاتر است.
  • تابع “SetIndexDrawBegin” مشخص می‌کند دیتای قابل‌توجهی از آرایۀ اندیکاتور، از کدام اِلمان آغاز می‌شود.
  • تابع “SetIndexBuffer”، اجازه می‌دهد هر آرایۀ تک-بُعدی از نوع “double” را به‌عنوان آرایۀ شاخص، اعلام کنیم. در اینجا، خود سیستم آرایه‌های شاخص را مدیریت می‌کند. به همین دلیل نیازی نیست این آرایه‌ها را مشخص کنیم.
//---- indicator buffers
double Tenkan_Buffer[];
double Kijun_Buffer[];
double SpanA_Buffer[];
double SpanB_Buffer[];
double Chinkou_Buffer[];
double SpanA2_Buffer[];
double SpanB2_Buffer[];

تابع ArrayResize، را نمی‌توان بر آرایه‌های اندیکاتور اِعمال کرد، چراکه بی‌فایده‌ است. همچنین اِعمال تابع ArrayInitialize بر آرایه‌های اندیکاتور نیز ‌بی‌فایده‌ است، به‌خصوص اگر به‌عنوان تابع ‘init’ باشد، درحالیکه آرایه‌های اندیکاتور هنوز اختصاص نیافته‌اند. آرایه‌های اندیکاتور به‌شکل خودکار حین تخصیص حافظه و تخصیص مجدد، مقداردهی اولیه می‌شوند. EMPTY_VALUE، یا مقداری که توسط تابع SetIndexEmptyValue مشخص شده‌است، به‌عنوان مقادیر مقداردهی اولیه استفاده می‌شوند. مقادیر “خالی” نمایش داده نمی‌شوند.

  • تابع “SetIndexLabel” نام را برای نمایش در نکات ابزار و در پنجره دیتا به‌همراه مقدار مربوطه، تعیین می‌کند (“ValueN” به‌طور پیش‌فرض تعیین شده‌است، که در اینجا N شمارۀ آرایۀ شاخص است). اگر به‌جای یک اسم، NULL منتقل شود، مقدار مربوطه نه در نکات ابزار و نه در پنجره دیتا، نمایش داده نخواهد شد. در این شرایط، اَبرها با استفاده از یک هیستوگرام بیرون آمده، و توسط یک خط محدود می‌شوند. در این لحظه، مقادیر آرایه‌های مربوط به “خط” و “هیستوگرام” یکسان هستند و می‌توان تنها یکی از آن‌ها را نشان داد.
  • تابع “IndicatorCounted”، اجازه می‌دهد محاسبه‌ای اقتصادی از یک اندیکاتور را سازماندهی کنیم. این تابع تعداد کندل‌ها تا لحظه‌ی شروع قبلی اندیکاتور را برمی‌گرداند، به‌عبارت دیگر، تعداد کندل‌هایی که قبلاً محاسبه‌ شده‌اند (به‌شکل بالقوه، در صورت عدم خطا یا خاتمۀ زودهنگام حین راه‌اندازی قبلی) و نیازی به محاسبۀ مجدد ندارند. هنگام مقداردهی مجدد اندیکاتور سفارشی یا آپدیت مهم هیستوری دیتا، این مقدار به‌طور خودکار ری‌ست و صفر می‌شود.
  • بیایید یک مثال دیگر را بررسی کنیم – اندیکاتور سفارشی با نام Accelerator/Decelerator Oscillator
//+------------------------------------------------------------------+
//|                                                  Accelerator.mq4 |
//|                      Copyright © ۲۰۰۵, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property  copyright "Copyright © ۲۰۰۵, MetaQuotes Software Corp."
#property  link      "https://www.metaquotes.net/"
//---- indicator settings
#property  indicator_separate_window
#property  indicator_buffers 3
#property  indicator_color1  Black
#property  indicator_color2  Green
#property  indicator_color3  Red
//---- indicator buffers
double     ExtBuffer0[];
double     ExtBuffer1[];
double     ExtBuffer2[];
double     ExtBuffer3[];
double     ExtBuffer4[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- ۲ additional buffers are used for counting.
   IndicatorBuffers(5);
//---- drawing settings
   SetIndexStyle(0,DRAW_NONE);
   SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexStyle(2,DRAW_HISTOGRAM);
   IndicatorDigits(Digits+2);
   SetIndexDrawBegin(0,38);
   SetIndexDrawBegin(1,38);
   SetIndexDrawBegin(2,38);
//---- ۴ indicator buffers mapping
   SetIndexBuffer(0,ExtBuffer0);
   SetIndexBuffer(1,ExtBuffer1);
   SetIndexBuffer(2,ExtBuffer2);
   SetIndexBuffer(3,ExtBuffer3);
   SetIndexBuffer(4,ExtBuffer4);
//---- name for DataWindow and indicator subwindow label
   IndicatorShortName("AC");
   SetIndexLabel(1,NULL);
   SetIndexLabel(2,NULL);
//---- initialization done
   return(0);
  }
//+------------------------------------------------------------------+
//| Accelerator/Decelerator Oscillator                               |
//+------------------------------------------------------------------+
int start()
  {
   int    limit;
   int    counted_bars=IndicatorCounted();
   double prev,current;
//---- last counted bar will be recounted
   if(counted_bars>0) counted_bars--;
   limit=Bars-counted_bars;
//---- macd counted in the 1-st additional buffer
   for(int i=0; i&lt;limit; i++)
      ExtBuffer3[i]=iMA(NULL,0,5,0,MODE_SMA,PRICE_MEDIAN,i)-
                    iMA(NULL,0,34,0,MODE_SMA,PRICE_MEDIAN,i);
//---- signal line counted in the 2-nd additional buffer
   for(i=0; i<limit; i++)
      ExtBuffer4[i]=iMAOnArray(ExtBuffer3,Bars,5,0,MODE_SMA,i);
//---- dispatch values between 2 buffers
   bool up=true;
   for(i=limit-1; i>=0; i--)
     {
      current=ExtBuffer3[i]-ExtBuffer4[i];
      prev=ExtBuffer3[i+1]-ExtBuffer4[i+1];
      if(current>prev) up=true;
      if(current&lt;prev) up=false;
      if(!up)
        {
         ExtBuffer2[i]=current;
         ExtBuffer1[i]=0.0;
        }
      else
        {
         ExtBuffer1[i]=current;
         ExtBuffer2[i]=0.0;
        }
       ExtBuffer0[i]=current;
     }
//---- done
   return(0);
  }
//+------------------------------------------------------------------+
  • تابع “IndicatorBuffers”، تعداد بافرها را برای استفاده در محاسبات اندیکاتور، مشخص می‌کند. به‌طور کلی، این تابع زمانی فراخوانده می‌شود که آرایه‌های شاخص بیشتری نسبت به تعداد لازم برای ترسیم یک اندیکاتور، استفاده شوند. در این هنگام، خود سیستم مدیریت آرایه‌های اضافی را برعهده می‌گیرد.
  • تابع “SetIndexDigits”، دقیق بودن اطلاعات خروجی را مدیریت می‌کند. در این شرایط، وقتیکه تفاوت بین دو مووینگ اَورج، و نیز تفاوت‌های دیگر بین نتیجه و خط سیگنال محاسبه می‌شود، دقت استاندارد تا ۴ رقم پس از نقطه، بدیهی‌ است که کافی نیست.
  • تابع “SetIndexDrawBegin”، اِلمان شروع قابل‌توجه آرایۀ اندیکاتور را تعیین می‌کند. در مثال ما، خط سیگنال به‌عنوان مووینگ اَورج ساده از مووینگ اَورج ساده دیگری، محاسبه می‌شود. به همین دلیل است که ۳۸ مقدار اولیۀ اندیکاتور، خالی (Empty) درنظر گرفته شده‌اند و قرار نیست رسم شوند.
  • تابع “IndicatorShortName”، به‌اصطلاح یک نام اختصاری برای اندیکاتور تعیین می‌کند تا در گوشه‌ی بالا سمت چپِ پنجره‌ی اندیکاتور و در “DataWindow” نمایش داده شود. اگر نام اختصاری تعیین نشود، نام اندیکاتور سفارشی به‌شکل سابق استفاده می‌شود. در مثال ارائه‌شده، نیازی به استفاده از تابع SetIndexLabel نیست، زیرا فقط یک مقدار خروجی داریم. بنابراین، نام اندیکاتور برای خروجی یک مقدار سیگنال، کافی است.
  • تابع “SetIndexStyle”، پارامترهای ترسیم یک آرایۀ اندیکاتور را مدیریت می‌کند. حالت ترسیم DRAW_NONE یعنی خط نیازی به رسم‌شدن ندارد. موضوع این است هیستوگرامِ اندیکاتور موجود باید دو رنگ متفاوت داشته باشد. داده‌ها از ExtBuffer0، در دو آرایه‌ی دیگر اختصاص داده می‌شوند، ExtBuffer1 و ExtBuffer2. برای اینکه داده‌های مضاعف در نکات ابزار یا Data Window، خروجی گرفته نشوند، از تابع SetIndexLabel با پارامتر NULL استفاده شده‌است. حالت ترسیم DRAW_HISTOGRAM، که بر اندیکاتور یک پنجره‌ی مجزا، اِعمال شده‌است، اجازه می‌دهد هیستوگرام را بین مقدار صفر و مقدار یک آرایۀ مرتبط (در مقایسه با ترسیم یک هیستوگرام در پنجره‌ی اصلی که قبل‌تر توضیح داده شد) رسم کنیم.
  • پارامترهای ورودی که توسط اندیکاتورهای سفارشی و توابع، برای محاسبات استفاده می‌شوند، باید به‌عنوان “extern” تعریف شوند و ممکن است از هر نوعی باشند.
  • اگر پارامترهای ورودی تعیین نشده باشند، اندیکاتور سفارشی مربوطه، در ساده‌ترین شکل ممکن، فراخوانده می‌شود.
double current_AC = iCustom( NULL, 0, "Accelerator", 0, 0 );

انتقال دو مقدار اول “NULL” و “۰” یعنی از نمودار کنونی استفاده خواهد شد. نام فایل مربوطه (بدون پسوند mq4)، به‌عنوان نام اندیکاتور سفارشی استفاده می‌شود. اگر پارامتر یکی‌مانده به آخر ۰ باشد، بدان معنی خواهد بود که ما به داده‌های اولین آرایۀ اندیکاتور علاقه‌مند هستیم. و اگر آخرین پارامتر ۰ باشد، این یعنی ما به مقدار آخرین اِلمان آرایۀ اندیکاتور درخواست‌شده، علاقه‌مند هستیم (به‌عبارت دیگر، تازه‌ترین مقدار فعلی).

  • پارامترها، با ترتیبی که توصیف شدند، به تابع محاسبۀ اندیکاتور سفارشی منتقل می‌شوند. برای مثال، اندیکاتور سفارشی “Ichimoku” با پارامترهای (۵۲،۲۶،۹)، اینگونه فراخوانده خواهد شد:
iCustom( NULL, 0, "Ichimoku", 9, 26, 52, 0, shift );

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

iCustom( NULL, 0, "Ichimoku", 0, shift );

این یعنی از مقادیری استفاده می‌شود که متغیرهای “Tenkan”، “Kijun”، “Senkou”، را مقداردهی اولیه می‌کنند، یعنی ۹، ۲۶، و ۵۲. با این حال، اگر یک اندیکاتور سفارشی، که مجموعه‌های مختلفی از پارامترها را دارد، در یک اکسپرت فراخوانده شود، شدیداً توصیه بر این است که از تنظیمات پیش‌فرض استفاده نشود.

باید توجه داشت که مازاد اندیکاتورهای سفارشی، درست همانند اندیکاتورهایی که اشتباه نوشته شده‌اند، باعث کاهش بسیار زیاد سرعت نرم‌افزار می‌شوند!

M23admin

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

رویداد‌ها در متاتریدر ۴

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

کارگزار ECN یا ECN Broker چیست ؟

۲۵ مورد نظر

نوشتن نظر شما

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