English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
새로운 기능: MQL5의 커스텀 인디케이터

새로운 기능: MQL5의 커스텀 인디케이터

MetaTrader 5 | 1 7월 2021, 10:31
214 0
TheXpert
TheXpert

개요

드디어 새로운 거래 터미널인 MetaTrader5를 이용할 수 있게 됐습니다. 이전 버전에는 없던 많은 기능이 탑재되었습니다. 특히 주목할 만한 장점은 다음과 같습니다.

  • 객체 지향 프로그래밍을 가능하게 해주는 동시에 구조적 프로그래밍의 풍부한 장점은 그대로 간직하도록 수정된 언어.
  • MetaTrader4에 비해 크게 향상된 코드 실행 속도.
  • 다양하고 새로운 방법으로 필요한 정보 나타내기.

MetaTrader5와 MQL5의 새로운 기능 전체를 나열하지는 않겠습니다. 종류도 많은 데다가, 별도의 설명이 필요한 기능들도 있거든요. 객체 지향 프로그래밍을 이용한 코드 작성법 또한 다음에 알아보도록 하겠습니다. 다른 기능들과 함께 설명하기에는 조금 어려운 이야기일 수 있으니까요.

대신 인디케이터와 인디케이터의 구조, 드로잉 타입과 프로그래밍 디테일을 MQL4와 비교해 볼게요.

복잡한 내용은 다루지 않을 겁니다. 혹시라도 헷갈리는 게 있다면 터미널에서 첨부파일을 이용해 바로 확인해 보세요.

초보자 분들께 많은 도움이 되면 좋겠고 기존에 사용하시던 개발자 분들도 뭔가 새로운 걸 얻어 가실 수 있길 바랍니다.

 

전반 구조

MQL4와 비교했을 때 인디케이터의 전반적인 구조가 바뀌지는 않았습니다.

이전과 동일하게 세 가지 함수가 있죠. 초기화, 데이터 프로세싱, 그리고 인디케이터 초기화 해지를 할 수 있도록 말입니다.

또한 상당수의 인디케이터 매개 변수는 전과 같이 속성에서 정의할 수 있습니다(키워드 #속성 검색). 대부분이 인디케이터를 위해 특별히 고안되었죠. 속성 및 입력 변수는 전과 동일하게 전역 컨텍스트에서 정의됩니다.

RSI 인디케이터의 커스텀 컬러링 구현을 예로 들어 볼게요. 지금은 일부만을 다루겠지만 전체 코드는 Color_RSI.mq5 파일에서 찾아볼 수 있습니다.

파트를 살펴 볼게요.

//--- group of data properties
#property copyright "TheXpert"
#property link      "theforexpert@gmail.com"
#property version   "1.00"
//--- description of the indicator should not exceed 511 symbols in total
//--- including newline symbols
#property description "      "
#property description "Demonstration of the indicator creation"
#property description "by the example of RSI coloring"

위에서 언급한 속성들은 인디케이터 인포메이션 패널에 나타납니다(속성 창의 '공통' 탭). 이런 모양일 거예요.

//--- indicator properties
#property indicator_separate_window // the indicator will be displayed in a separate subwindow
#property indicator_buffers 2       // number of used buffers
#property indicator_plots   1       // number of displayed buffers
//--- plot 1
#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // use 2 colors
#property indicator_type1  DRAW_COLOR_LINE               // and the special color display type

해당 속성은 인디케이터 속성이죠. 이 밖의 디스크립션은 도움말에서 찾아 볼 수 있습니다.

//---- buffers
double Values[];                 // buffer of values
double ValuesPainting[];         // buffer of color indices
//--- indicator input parameters
input string             _1           = "RSI parameters";
input int                RSIPeriod    = 5;
input int                SmoothPeriod = 5;
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE;
input string             _2           = "Color settings";
input color              Down         = clrDarkSalmon;
input color              Up           = clrDeepSkyBlue;
//--- variable for storing the indicator handle
int RSIHandle;

여기 인디케이터 입력 변수와 전역 변수(터미널 내 전역 변수와 구분 필요)가 있는데요. 인디케이터 입력 변수는 입력 식별자로 지정되어 있습니다.

이제 열거형을 입력 변수로 사용할 수 있고, 덕분에 잘못된 변수 선택을 줄일 수 있습니다.

예를 들어, AppliedPrice 변수는 사용 가능한 변수를 나타내는 드롭 다운 목록에 포함되어 있을 거예요.

사용자 정의형을 포함한 모든 열거형이 해당 드롭 다운 목록에 표시될 겁니다. 예를 들어, 아래의 매개 변수

//...
enum DayOfWeek
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
};

input DayOfWeek Day;

//...

는 다음과 같이 나타날 겁니다.

int OnInit()
  {
//--- bind indicator buffers
//--- Values serves as a display buffer
   SetIndexBuffer(0,Values,INDICATOR_DATA);
//--- ValuesPainting serves as the buffer for storing colors
   SetIndexBuffer(1,ValuesPainting,INDICATOR_COLOR_INDEX);
//--- Set the start of drawing Values buffer
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,RSIPeriod);
//--- Set the indicator name
   IndicatorSetString(INDICATOR_SHORTNAME,"Color RSI("+string(RSIPeriod)+")");
//--- Set an empty value for plots
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
//--- Set buffer colors
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,Down);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,Up);
//--- Receive indicator handles
   RSIHandle=iRSI(NULL,0,RSIPeriod,AppliedPrice);
//--- Set the sequence of buffer indexation
   ArraySetAsSeries(Values,true);
   ArraySetAsSeries(ValuesPainting,true);
//--- successful execution
   return(0);
  }

OnInit은 인디케이터 초기화 함수입니다. 인디케이터 버퍼와 속성을 설정하고, 속성 창에서 정의될 수 없거나 동적으로 설정되어야 하는 인디케이터 변수를 정의할 수 있죠. 또한 인디케이터에 필요한 핸들 생성을 포함하는 초기 데이터 초기화도 가능합니다.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- number of bars for calculation
   int toCount=(int)MathMin(rates_total,rates_total-prev_calculated+1);
//--- try to copy iRSI indicator data
   if(CopyBuffer(RSIHandle,0,0,toCount,Values)==-1)
     {
      Print("Data copy error, №",GetLastError());
      //--- return command for recalculation of the indicator values
      return(0);
     }
//--- coloring. Yer, now it has become that easy
   for(int i=toCount-2;i>=0;--i)
     {
      //--- coloring the first line
      if(Values[i+1]!=EMPTY_VALUE && Values[i]>Values[i+1])
         ValuesPainting[i]=1;
      else
         ValuesPainting[i]=0;
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

OnCalculate는 데이터 평가 함수입니다. 두 가지로 나뉘는데요. 이건 기본 형식이고요. 자세한 건 아래에서 설명할게요.

함수

//--- this function usage in not obligatory
/*
void OnDeinit()
{

}
*/

OnDeinit는 인디케이터 초기화 해지 함수입니다. 파일 핸들 등의 리소스를 배포할 때 종종 필요하죠. 다른 경우에는 필요하지 않습니다.

 

인디케이터의 두 가지 컨셉

첫 번재는 MQL4에서 사용하던 것에서 약간 수정된 표준형입니다. Start 함수 대신 OnCalculate 함수가 사용되죠.

기본 형식은 다음과 같습니다.

int OnCalculate(const int rates_total,      // Arrays size
                const int prev_calculated,  // Bars processed on the previous call
                const datetime& time[],     // Data for the current chart and timeframe...
                const double& open[],
                const double& high[],       
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
{
   return rates_total;
}

데이터 복사에 필요한 코드의 양을 줄이기 위해 차트 데이터는 함수의 매개 변수 배열로 바로 전송됩니다. 사용 가능한 바의 개수가 함수의 첫 번째 매개 변수가 되며, 마지막 요청 이후 처리된 바의 개수 또는 0인 값은 두 번째 매개 변수가 됩니다.

값이 0인 경우는 첫 번째 인디케이터 요청을 통해 전송 가능하며, 새로운 데이터나 비어있는 데이터를 불러오는 경우에도 전송할 수 있습니다. 이 매개 변수는 대다수의 개발자들이 불편하게 여겼던 IndicatorCounted() 변수를 대체할 수 있습니다.

두 번째 컨셉이라는 건 MQL4 함수 등과 같은 i<…>OnArray의 대체 및 확장입니다. 터미널 예제에도 이런 형식의 인디케이터가 있습니다. 커스텀 이동평균이죠. 이러한 인디케이터는 커스텀 인디케이터 등과 같이 사용자의 선택에 좌우되는 데이터 프로세싱을 위해 고안되었습니다.

해당 인디테이터의 데이터 프로세싱에 사용되는 함수는 다음과 같습니다.

int OnCalculate (const int rates_total,      // the size of the price[] array
                 const int prev_calculated,  // bars calculated in the previous call
                 const int begin,            // where notional data start from
                 const double& price[]       // data array for calculation
                 )
  {
   return rates_total;
  }

함수의 마지막 매개 변수가 사용자가 선택한 데이터입니다. 버퍼가 많은 인디케이터에 적용하는 경우, 첫 번째 인디케이터 버퍼가 데이터 프로세싱을 위해 전송됩니다.

첫 번째 인디케이터 데이터는 선택된 창에 처음 추가된 인디케이터에 해당 인디케이터가 적용될 것임을 의미합니다.

이전 인디케이터 데이터는 선택된 창에 마지막으로 추가된 인디케이터에 해다 인디케이터가 적용될 것임을 의미하고요.

이러한 인디케이터는 전체 스택을 집합시키는 데에 사용됩니다. 예를 들어 커스텀 이동평균 인디케이터를 사용하면 첫 번째 인디케이터를 필요한 데이터에 적용하고, 두 번째 인디케이터를 첫 번째에, 세 번째 인디케이터를 두 번째에 적용할 수 있죠.

해당 컨셉을 구현하는 스탠다드 인디케이터의 종류는 많습니다. applied_price_or_handle 매개 변수의 프롬프트를 살펴보면 다음과 같죠.

사용자 데이터에 연산될 수 있도록 인디케이터가 구현되었음을 나타냅니다. 해당 데이터 핸들은 반드시 applied_price_or_handle 변수로 전송되어야 합니다.

같은 방법으로 데이터 프로세싱을 인디케이터 코드 자체에 넣을 수도 있습니다.

  {
   //...
   RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice);
   SmoothHandle = iMA(NULL, 0, SmoothPeriod, 0, MODE_EMA, RSIHandle);
   //...
  }

이 컨셉을 적용하는 새로운 방법이 또 있는데요. 바로 유니버설 서비스 인디케이터입니다. 해당 인디케이터의 예제는 Direction_Brush.mq5 파일에 첨부되어 있습니다.

상위 차트에 결과가 나타나 있습니다. 이 경우 방향 컬러는 독립 엔터티로 분리되어 다른 인디케이터에서 구현됩니다.

물론, 인디케이터의 제로 버퍼에만 적용할 수 있다 보니 보편성에는 한계가 있습니다. 그래도 이런 유형의 인디케이터가 유용하게 쓰일 수 있다고 생각해요.

반대로, 커스텀 인디케이터를 작성하는 경우 이를 명심할 필요가 있습니다. 제로 버퍼에서 처리되는 메인 정보가 한 인디케이터 내 다목적 머신 구현을 방지할 수 있거든요. 외부 서비스 인디케이터를 통해 다른 많은 명령을 실행할 수 있습니다. 커스텀 인디케이터에 필요한 기능을 갖춘 서비스 인디케이터를 추가하기만 하면 됩니다.

적용 범위는 생각보다 넓답니다.

  • 톤 시각화를 포함한 인디케이터 특성(차트 상단, 방향, 레벨, 부분 등) 컬러링
  • 다양한 조건을 가진 다양한 시그널
  • 데이터 분배 등의 통계 수집 및 디스플레이
  • 이동평균, 지그재그 등 하나의 버퍼로 연산 가능한 유니버설 인디케이터 작성

위의 기능이 전부가 아닙니다. 사용하다 보면 점점 더 다양한 구현 방식을 찾게 될 거예요.

 

데이터 액세스

MQL5에서는 데이터 액세스 방식이 변경되었습니다. 배열에서 직접 액세스가 가능하게 되어 계산 속도가 엄청나게 빨라졌는데요. 이제 매 값 마다 배열을 생성하고 iCustom 함수를 호출할 필요가 없어진 거죠. 한 가지 함수를 호출하여 필요한 데이터 카운트를 요청하면 요청한 데이터가 곧바로 지정된 지역 배열에 복사되어 사용할 수 있게 됩니다.

데이터 복사는 CopyBuffer라는 시스템 함수를 이용하여 구현됩니다. 함수 디스크립션은 도움말에서 찾아볼 수 있어요.

인디케이터와 정적 배열(미리 정의된 영역)에서 복사 가능한 최대 데이터 카운트는 배열의 크기에 의해 결정됩니다. 복사된 데이터 수가 기존의 크기보다 큰 경우 동적 배열의 크기는 변경될 수 있습니다.

이 밖에도 과거 데이터에 액세스할 수 있도록 만들어진 다음과 같은 함수들이 있습니다.

함수 설명
CopyBuffer 특정 버퍼 또는 인디케이터에서 데이터를 필요한 양만큼 복사.
CopyRates 특정 심볼 주기의 MqlRates 구조에서 과거 데이터를 지정된 양만큼 rates_array 배열로 복사.
CopyTime 특정 심볼 주기 쌍의 바 오프닝 시간 과거 데이터를 지정된 양만큼 time_array 배열로 복사.
CopyOpen 특정 심볼 주기 쌍의 바 시가 과거 데이터를 지정된 양만큼 open_array로 복사.
CopyHigh 특정 심볼 주기 쌍의 바 최고 가격 과거 데이터를 지정된 양만큼 high_array 배열로 복사.
CopyLow 특정 심볼 주기 쌍의 바 최저 가격 과거 데이터를 지정된 양만큼 low_array 배열로 복사.
CopyClose 특정 심볼 주기 쌍의 바 종가 과거 데이터를 지정된 양만큼 close_array 배열로 복사.
CopyTickVolume 특정 심볼 주기 쌍의 틱 볼륨 과거 데이터를 지정된 양만큼 volume_array 배열로 복사.
CopyRealVolume 특정 심볼 주기 쌍의 거래량 과거 데이터를 지정된 양만큼 volume_array 배열로 복사.
CopySpread 특정 심볼 주기 쌍의 스프레드 가격 과거 데이터를 지정된 양만큼 spread_array로 복사.

자세한 내용은 도움말에서 찾아볼 수 있습니다.

데이터는 한 가지 인디케이터 형식으로만 전달되기 때문에 다른 인디케이터에는 따로 전달해야 합니다.

또한 보통 과거 데이터의 경우 이중 배열이 불가할 수 있으므로 동적 논인디케이터 버퍼에만 저장하기를 권장합니다.

또 하나 언급하지 않은 게 있는데요. 만약 복사된 데이터의 값이 0인 경우 CopyBuffer 함수가 에러 코드 4003을 발생시키므로, 복사할 데이터의 개수는 1 이상이 되어야 합니다.

 

인디케이터 버퍼

버퍼의 개수에는 제한이 없습니다.

이제 클러스터 인디케이터를 제작하면서 어디에 정보를 저장해야 하는지, 어떻게 중간값을 효과적으로 계산할지를 고민할 필요가 없는 것이죠.

다만 버퍼를 저장하려면 메모리 공간이 필요하다는 사실을 잊어서는 안됩니다. 터미널 히스토리 너비를 바 약 1,000,000개로 설정하고 분차트에 '상당한' 수의 클러스터 인디케이터를 추가했다면, 터미널이 수 기가바이트의 메모리를 차지하게 될 겁니다.

버퍼 자체의 본질도 약간 바뀌었는데요. 사용된 버퍼의 개수는 속성에 명시됩니다.

#property indicator_buffers 2       // buffers used

이 값은 전체 버퍼 카운트와 일치해야 하죠.

표시되는 버퍼의 개수는 속성에 의해 정의되죠.

#property indicator_plots 1         // buffers displeyed

상세히 설명해 볼게요. 대부분의 드로잉 스타일은 단 하나의 INDICATOR_DATA 버퍼를 필요로 합니다. 그러나 다수의 인디케이터 버퍼를 필요로 하는 경우도 있죠.

아래의 드로잉 스타일이 그런 경우입니다.

  • DRAW_HISTOGRAM2 - 두 개의 인디케이터 버퍼(HistogramSample.mq5)

  • DRAW_FILLING - 두 개의 인디케이터 버퍼(CrossMa.mq5)

  • DRAW_CANDLES - 네 개의 인디케이터 버퍼(CandleSample.mq5)

  • DRAW_BARS - 네 개의 인디케이터 버퍼(BarsSample.mq5)

DRAW_FILLING를 제외한 위의 모든 드로잉 타입은 채색된 아날로그를 갖습니다.

이제 인디케이터 버퍼는 다음의 3가지 형식으로 나뉘죠.

  • INDICATOR_DATA - 차트에 데이터가 표시되는 버퍼 여기에 속하는 버퍼들은 iCustom 작업 또는 드로잉에 사용됩니다. 우선 레지스터에 등록되어야 하죠. 순서를 임의로 설정하는 경우(잘못된 순서의 경우), 코드가 성공적으로 컴파일되어 차트에 나타나기는 하겠지만 아마 틀리게 나타날 겁니다.

  • INDICATOR_COLOR_INDEX - 컬러를 저장하는 버퍼. 특수한 컬러 타입(#property indicator_typeN)을 갖는 INDICATOR_DATA 형식의 컬러 버퍼 인덱스를 저장하는 데 필요합니다. 이런 버퍼(이하 컬러 버퍼)는 해당 버퍼가 사용되는 메인 버퍼 바로 다음에 레지스터에 등록되어야 합니다.

  • INDICATOR_CALCULATIONS - 보조 연산 결과를 저장하는 버퍼. 이 버퍼들은 차트에 나타나지 않습니다.

int OnInit()
{
   // ...
   SetIndexBuffer(0, V2, INDICATOR_DATA);
   SetIndexBuffer(1, V2C,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2, V4, INDICATOR_DATA);
   SetIndexBuffer(3, V4C,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(4, V1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, V3, INDICATOR_CALCULATIONS);

   // ...
   return 0;
}

iCustom 함수를 이용해 인디케이터를 참조하는 기능도 있습니다.

  • 드로잉 버퍼(차트에 표시)를 읽을 수 있어요. 버퍼 넘버(인덱스)는 버퍼가 레지스터에 등록되어 있는 인덱스와 동일해야 합니다.
  • 컬러 저장 버퍼도 읽기가 가능하지만 항상 가능한 건 아닙니다. 예를 들어 위의 코드에서 V2C 버퍼는 필요한 값을 읽고 얻을 수 있지만 V4C 버퍼는 해당 기능을 수행하지 못하죠.
  • MQL4에서처럼 중간 연산 버퍼가 없는 것입니다. 외부 데이터 버퍼에 대한 보장된 액세스를 원한다면, INDICATOR_DATA로 선언하세요.

액세스가 불가능한 버퍼에 요청할 경우 에러 코드 4806번('요청하신 자료는 찾을 수 없습니다')이 발생합니다.

컬러 버퍼를 좀 더 자세히 살펴보겠습니다.

MQL4에서는 각각의 컬러에 개별 버퍼를 생성해야 했지만, 이제는 컬러 스타일을 이용해 한번에 최대 63가지 컬러를 하나의 버퍼에 지정할 수 있습니다. 이는 사용되는 버퍼의 수를 최적화시켜 메모리 사용을 줄이는 데 도움이 되기도 합니다. 특히 톤 시각화 등을 이용한 인디케이터 작성을 가능하게 하기도 하고요.

또한, 덕분에 몇몇 컬러의 경우 MQL4에 비해 로직이 매우 간단해 졌습니다. 이걸 가장 잘 보여주는 예시는 컬러별 추세 구분이 되겠네요. MQL4에서는 최소 3개의 버퍼와 복잡한 프로그래밍이 요구됐죠.

MQL5와 함께라면 더이상 쉬워질 수가 없습니다. 여기 예제 코드가 있는데요. 전체 구현 코드는 Color_RSI.mq5 파일에서 찾을 수 있습니다.

#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // use 2 colors
#property indicator_type1  DRAW_COLOR_LINE               // and the special color display type
//---- buffers
double Values[];                 // buffer of values
double ValuesPainting[];         // buffer of color indices
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- bind indicator buffers
//--- Values serves as a display buffer
   SetIndexBuffer(0,Values,INDICATOR_DATA);
//--- ValuesPainting serves as the buffer for storing colors
   SetIndexBuffer(1,ValuesPainting,INDICATOR_COLOR_INDEX);
//...
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(/*...*/)
  {
//--- number of bars for calculation
   int toCount=(int)MathMin(rates_total,rates_total-prev_calculated+1);
//--- try to copy iRSI indicator data
   if(CopyBuffer(RSIHandle,0,0,toCount,Values)==-1)
     {
      Print("Data copy error, №",GetLastError());
      //--- return command for recalculation of the indicator values
      return(0);
     }
//--- coloring. Yer, now it has become that easy
   for(int i=toCount-2;i>=0;--i)
     {
      //--- coloring the first line
      if(Values[i+1]!=EMPTY_VALUE && Values[i]>Values[i+1])
         ValuesPainting[i]=1;
      else
         ValuesPainting[i]=0;
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

코드를 조금 더 쓰다보면 다음의 결과가 나옵니다.

드로잉에 컬러 타입을 사용할 때는 주의할 점이 있습니다.

동적 컬러 스킴을 사용하는 경우, 컬러 버퍼의 최대 컬러 카운트는 indicator_colorN 속성에 정의된 컬러 카운트로 제한됩니다. 다음의 예를 들어 볼게요.

#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // use 2 colors

버퍼의 컬러 스킴은 최대 두 가지 컬러를 포함할 수 있습니다. 더 큰 숫자를 설정(PlotIndexSetInteger 사용)하더라도 말이죠.

따라서, 필요한 컬러의 수는 한 줄에 표현되어야 합니다. 바로 함수를 정의하는 라인이죠. 이 경우 동적으로 변경될 수 있습니다. 제가 생각할 수 있는 가장 짧은 색깔은 'red'네요. 하지만 다음과 같은 방식도 가능합니다.

대신

#property indicator_color1  clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, //…

다음과 같이 쓸 수도 있고요.

#define C clrRed
#property indicator_color1  C, C, C, C, C, C, C, C, C, C, C, C, C, C, //…

컬러 버퍼는 최대 63개까지 추가할 수 있습니다. 숫자가 최대 수치(indicator_colorN로 지정)를 넘어서는 경우 버퍼가 나타나지 않을 겁니다.

다음은 최대 개수의 컬러를 이용한 톤 시각화의 예입니다.

전반적으로 그릴 수 있는 게 많아졌어요. 정말 좋죠.

 

배열

인덱스를 통해 배열 데이터를 직접 참조하는 경우, 데이터 주문 형식인 AsSeries 속성을 정의해야 합니다. 배열 형식에 따라 이를 정의할 수 없는 경우도 있습니다.

해당 플래그는 다차원 배열 또는 정적 배열에는 설정될 수 없습니다. OnCalculate 함수에 전달된 배열 형식에는 이런 플래그를 설정할 수 있죠.

CopyBuffer 함수를 이용한 데이터 복사가 AsSeries 속성에 좌우되는 건 아니지만, 버퍼가 달라지면 구현 방식 또한 달라집니다.

인디케이터 버퍼의 경우 기록의 너비 전체를 복사해야 합니다. 꼭 기억하세요.

동적 배열 및 정적(미리 정의된 크기) 배열의 경우 데이터 복사는 현재에서 과거를 향해 실행됩니다.

CopyBuffer 함수는 동적 버퍼(인디케이터 버퍼 제외)의 버퍼 크기를 필요한 크기로 변경합니다.

정적 배열을 이용한 데이터 복사는 권장하지 않습니다.

항상 데이터를 어떻게 복사하는지 또 어떻게 처리하는지를 꼭 확인하는 게 좋습니다. 가장 간단하고 안전한 방법은 다음과 같습니다.

  • 기록 데이터 저장에 사용되는 모든 버퍼의 AsSeries 속성을 정의합니다.
  • 서로 다른 버퍼의 버퍼 구조를 고려합니다.
  • _LastError 변수(마지막 에러 코드) 값을 항상 확인합니다.

또한, CopyBuffer 함수 및 AsSeries 속성과 관련된 모든 함수에 대한 도움말을 꼭 읽어 보시길 바랍니다.

 

IndicatorCounted 함수

이제 IndicatorCounted 함수의 필요성에 대한 논쟁은 잊혀질 겁니다. 이전 함수 호출의 반환 값으로 해당 값을 설정할 수 있게 됐으니까요.

int OnCalculate(const int rates_total,      // array size
                const int prev_calculated,  // bars processed after last call
                //...)
  {
   return rates_total;
  }

많은 경우 현재 함수 호출의 바 카운트를 포함하는 rates_total 값만 반환해도 충분합니다.

그러나 마지막 OnCalculate() 함수 호출 이후 가격 정보가 변경된 경우(예를 들어 기록 데이터가 로드되었거나 비었던 데이터가 채워진 경우), 입력 변수 prev_calculated의 값이

클라이언트 터미널에 의해 0으로 설정됩니다.

또한, OnCalculate 함수가 0 값을 반환하는 경우, 인디케이터 값은 클라이언트 터미널 내 DataWindow에 나타나지 않습니다. 따라서, 인디케이터를 표시하고 기록 로딩 프로세스 도중 또는 연결 실패 후 재연산을 실행하고 싶다면 0 값 대신 1을 반환하세요.

또 다른 새로운 유용한 기능은 인디케이터에 몇 개의 바가 연산되었는지 구하는 것입니다. 방대한 데이터로 연산을 실행하는 엑스퍼트 어드바이저에서 특히 유용하게 쓰입니다. BarsCalculated라는 함수인데요. 도움말에서 관련 정보를 찾아볼 수 있습니다.

이 함수를 다르게 적용하는 방법도 있습니다. 인디케이터가 로드되지 않은 경우, 로딩에 시간이 좀 걸릴 수 있습니다. 인디케이터 핸들 생성과 연산 사이에 소요되는 시간이죠.

이 시간은 초기화 및 초기 전계산을 위해 꼭 필요합니다. 연산 속도 및 인디케이터 설정에 따라 시간이 달라지죠.

이 시간 동안, CopyBuffer 함수에 대한 요청은 에러 코드 4806번인 '요청하신 자료는 찾을 수 없습니다'를 발생시킵니다..

BarsCalculated 함수인디케이터의 데이터 복사 가용성판단하는 데에 사용됩니다.

//--- number of bars for calculation
   int toCount=rates_total-(int)MathMax(prev_calculated-1,0);
//--- try to copy iWPR indicator data
   int copied=CopyBuffer(WPRHandle,0,0,toCount,Values);
   int err=GetLastError();
//--- check coying result
   if(copied==-1)
     {
      //--- if error number is 4806, the data simply have not been uploaded yet
      if(err==4806)
        {
         //--- wait till the data is uploaded
         for(int i=0;i<1000;++i)
            if(BarsCalculated(WPRHandle)>0)
               break;
         //--- try to copy iWPR indicator data again
         copied=CopyBuffer(WPRHandle,0,0,rates_total,Values);
         err=GetLastError();
        }
     }
//--- check coying result
   if(copied==-1)
     {
      Print("Error when trying to get WPR values, last error is ",err," bars ",rates_total);
      return(0);
     }
//...

 

요약

우선 몇 가지 기능에 대해서만 이야기해 보았는데요. 기본 개념을 이해하는 데에 도움이 되었기 바랍니다.

이 글이 커스텀 인디케이터에 대한 이해를 도왔으면 좋겠네요. 여러분이 언제라도 참고하고 필요한 정보를 검색할 수 있는 그런 글이었으면 해요.

이 글을 읽다가 잘못된 부분 또는 무언가 눈에 띄는 게 있다면 꼭 알려주세요. 최대한 빠른 시일 내에 수정하겠습니다.

몇 가지 새로운 내용을 더 서술할 예정이며, 유용한 정보가 있다면 코멘트로 남겨주시면 좋겠습니다.

코멘트도 꼭 꼼꼼하게 읽어 보세요.

 

부록

  • Color.mqh에 포함된 파일을 MQL5/Include 폴더에 복사하세요. Toned_WPR 인디케이터에 필요한 파일입니다.
  • Color.mq5 라이브러리 파일을 MQL5/Libraries 폴더에 복사하세요. Toned_WPR 인디케이터에 필요한 파일입니다.

위의 모든 파일은 인디케이터입니다.

 

감사의 말

원고 검토, 유용한 토론 및 조언을 해주신 Victor Rustamov(granit77)씨께 다시 한번 감사드립니다.


MetaQuotes 소프트웨어 사를 통해 영어가 번역됨
원본 기고글: https://www.mql5.com/en/articles/5

파일 첨부됨 |
crossma.mq5 (2.47 KB)
candlesample.mq5 (2.31 KB)
barssample.mq5 (2.3 KB)
color.mq5 (4.28 KB)
color.mqh (0.99 KB)
toned_wpr.mq5 (4.65 KB)
color_rsi.mq5 (5.57 KB)
MQL5: 나만의 인디케이터를 만들기 MQL5: 나만의 인디케이터를 만들기
인디케이터란 무엇인가? 이는 우리가 간편하게 스크린에 표시하고싶은 계산값들의 집합을 일컫는 것 입니다. 값의 집합은 프로그램에서는 어레이로 표현되어있습니다. 따라서, 인디케이터의 생성이란 몇몇 어레이 (가격 어레이) 와 다른 어레이 (인디케이터 값) 를 다루는 알고리즘을 짜는 것을 말하는 것입니다.. 실제강도지수 인덱스의 생성을 설명하는 것으로, 저자는 MQL5에서 인디케이터를 쓰는 법에 대해서 안내합니다.
새 MetaTrader 와 MQL5를 소개해드립니다 새 MetaTrader 와 MQL5를 소개해드립니다
본 문서는 MetaTrader5의 간략 리뷰입니다. 짧은 시간 내에 시스템의 모든 세부 사항을 안내해드리기는 어렵습니다 - 테스트는 2009.09.09에 시작되었습니다. 이는 상징적인 일자로, 전 이것이 행운의 숫자가 될거라 믿어 의심치않습니다. 제가 새 MetaTrader 5 터미널과 MQL5 베타버전을 받은지 며칠이 지났습니다. 아직 모든 기능을 사용해본 것은 아니지만, 벌써부터 감명깊네요.
MQL5의 객체 생성 및 파괴 순서 MQL5의 객체 생성 및 파괴 순서
사용자 지정 개체, 동적 배열 또는 개체 배열 등 모든 개체는 특정 방식으로 MQL5 프로그램에서 생성 및 삭제됩니다. 종종 일부 개체는 다른 개체의 일부이며 초기화 해제시 개체 삭제 순서가 특히 중요합니다. 이 글에서는 개체 작업 메커니즘을 다루는 몇 가지 예를 제공합니다.
조합론과 트레이딩 확률(4부): 베르누이 논리 조합론과 트레이딩 확률(4부): 베르누이 논리
이 글에서는 잘 알려진 베르누이 기법을 알아보고 이를 트레이딩과 관련한 데이터 배열을 설명하는 데 어떻게 사용할 수 있는지 보여드리겠습니다. 그런 다음 이 모든 것이 스스로 적응하는 트레이딩 시스템을 만드는 데에 사용될 것입니다. 우리는 또한 베르누이 공식의 특별한 경우인 보다 일반적인 알고리즘을 찾아보고 관련된 응용 프로그램을 찾아볼 것입니다.