English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
초보자를 위한 실용적인 MQL5 디지털 필터 구현

초보자를 위한 실용적인 MQL5 디지털 필터 구현

MetaTrader 5 | 5 7월 2021, 13:04
99 0
Nikolay Kositsin
Nikolay Kositsin

개요

관련 자료 목록에 있는 바로 전 글에서 간단한 인디케이터 코드를 분석하고 MetaTrader5 클라이언트 터미널에서의 인디케이터 상호작용에 대해서도 조금 알아보았는데요. 다른 내용으로 넘어가기 전에, MetaEditor의 '도구상자' 창 내 '에러' 탭에서 컴파일 결과를 확인해 보아야겠습니다. 그러고 나면 SMA 인디케이터 코드에 대해 조금 더 깊게 파고들 수 있을 거예요.

인디케이터 컴파일 에러

MetaTrader의 경우, 둘 중 어느 버전의 코드라도 컴파일 시 변경 사항이 없으면 원하는 결과가 나옵니다.


에러가 없으며, .mq5 확장자를 갖는 인디케이터와 함께 .ex5 확장자를 갖는 파일이 생성되었습니다.

하지만 일반적으로 에러는 꼭 발생하게 됩니다. 프로그래머들도 자주 하는 실수죠. 이를 대비해 MetaEditor에는 컴파일 에러를 탐지하고 에러 발생 시 모든 에러 목록을 보여주는 내장 매커니즘이 탑재되어 있습니다.

'도구상자' 창에서 에러가 포함된 라인을 더블클릭하면 에러의 정확한 위치를 파악할 수 있습니다. 대부분의 경우 컴파일러는 적절한 아이콘을 이용해 에러가 발생한 라인을 정확하게 표시하죠.

한 가지 명심할 것이 있습니다. 작은 실수 하나가 수많은 컴파일 에러를 발생시킬 수 있다는 건데요. 다시 말하면 제일 처음 에러가 발견된 지점의 코드를 수정하면 나머지 에러가 모두 해결될 수도 있다는 것이기도 합니다. 동일한 컴파일 에러는 자연스레 여러 곳에서 발생하기 마련입니다. 따라서 오류를 수정할 때마다 재컴파일해야 하며, 컴파일러가 에러를 발견하는 경우 '도구상자' 창의 '에러' 탭에서 첫 번째 라인의 위치를 확인해야 합니다.

어쩌면 이것을 이해하는 가장 효과적인 방법은 일부러 오류를 발생시킨 후 컴파일러의 반응을 확인하는 것이 되겠네요. 방법은 아주 간단합니다. 코드의 특정 부분에 에러를 작성한 후 MetaEditor에서 '컴파일' 버튼을 누르고 결과를 확인하세요. 어떤 실수가 어떻게 코드를 망치게 되는지 이해하면 더 좋습니다. MQL5로 코딩을 하다가 같은 일이 발생할 수도 있으니까요.

인디케이터 소스 코드를 망치게 되는 실수는 다음과 같습니다.

  1. 오퍼레이터 또는 변수에 빈칸 삽입
  2. 세미콜론 삭제
  3. 세미콜론 추가
  4. 오퍼레이터 삭제
  5. 괄호 또는 중괄호 삭제 및 추가
  6. 콤마 삭제
  7. OnCalculate() 함수에 입력 변수 추가
  8. 변수를 0으로 나누기
  9. if 오퍼레이터에서 '==' 대신 '=' 사용
  10. bar++에서 bar--로의 증분 함수 방향 전환

물론, 컴파일러가 항상 정확하게 에러의 위치를 파악하지는 못합니다. 그러니까 만일의 상황을 대비해 우리가 미리 알아두는 게 좋겠죠. 에러에 대해서 딱 한마디만 더 할게요. MetaEditor 컴파일러는 MQL5 언어 자체의 오류를 탐지할 뿐, 프로그래밍의 논리적 오류는 거의 찾지 못 한답니다!

MQL5 보카

누군가의 말을 잘 들어보면 사용할 수 있는 어휘가 무궁무진함에도 불구하고 생각과 필요를 표현하는 데 필요한 소수만을 사용한다는 걸 알 수 있죠. 실제로 사용되는 어휘가 존재하는 어휘에 비해 훨씬 적다는 거죠. MQL5도 마찬가지입니다. MQL5를 처음 접할 때에는 우선 가장 흔하게 사용되는 오퍼레이터나 표현 등을 익혀야 합니다. 나머지는 언어를 배우면서 점차 넓혀나가면 돼요.

예를 들어, 네 가지 변수형(int, double, bool, string)을 사용할 수 있겠고, if-else 조건 연산자, for 루프 연산자, {} 복합 할당 연산자, 그리고 return 연산자도 필요하겠네요. 세미콜론 ';'과 콤마 ','의 사용법 또한 정확히 알아야 합니다. 함수 및 삼각함수를 배우는 게 좋을 수도 있고요. 이 정도만 알아도 초기 프로그래밍 기술 훈련 및 연습에 충분하답니다!

인디케이터 다듬기

MetaTrader 클라이언트 터미널에서 볼 수 있듯 MQL5의 인디케이터 개량 능력은 아주 표준적이고 간단합니다. 전역 수준 오퍼레이터로 이루어져 있죠.

//---- Indicator's author
#property copyright "2010, MetaQuotes Software Corp."
//---- Author's web-site link
#property link      "https://www.mql5.com"
//---- Indicator version number
#property version   "1.00"
//---- Drawing the indicator in the main window
#property indicator_chart_window
//---- One buffer is used for calculating and drawing the indicator
#property indicator_buffers 1
//---- Only one graphical plotting is used
#property indicator_plots   1
//---- Drawing the indicator as line
#property indicator_type1   DRAW_LINE
//---- Red is used as indicator's line color
#property indicator_color1  Red
//---- Indicator line is continuous curve
#property indicator_style1  STYLE_SOLID
//---- Indicator line thickness is equal to 1
#property indicator_width1  1
//---- Displaying indicator's label
#property indicator_label1  "SMA"

OnInit() 함수 호출도 있고요.

//---- Variable initialization for indicator's short name
   string shortname;
   StringConcatenate(shortname,"FATL(",FATLShift,")");
//--- Creating labels to display in Data Window
   PlotIndexSetString(0,PLOT_LABEL,shortname);
//--- Creating name to display in a separate window and in tool-tip
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
//--- Defining accuracy of displaying indicator's values
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//--- Prohibition of displaying blank values
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
StringConcatenate() 함수는 다음의 공식을 이용해 인디케이터의 이름 문자열을 집합시킵니다.
   shortname = shortname + "SMA(" + MAPeriod + "," + MAShift + ")";

인디케이터에 다른 인디케이터 적용하기를 읽어 보셨다면, OnCalculate()PlotIndexSetInteger() 함수 호출을 추가하는 것도 나쁘지 않겠죠?

//---- Calculating the 'first' starting number for the bars recalculation loop
   if(prev_calculated==0)        // Checking the first start of the indicator calculation
     {
      first=FATLPeriod-1+begin;  // Starting number for calculation of all bars
      //--- Increasing the start of data position by 'begin' bars, because the 
      //    calculations are based on data of another indicator
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+FATLPeriod);
     }
   else first=prev_calculated-1; // Starting number for calculation of new bars
라인을 추가했으니 당연히 인디케이터의 용량이 커지고 코드가 복잡해졌지만 이제 전보다 사용자 친화적인 인터페이스를 갖게 되었습니다.

인디케이터 템플릿을 이용한 새로운 인디케이터 생성 결과

지금까지 다룬 내용이 전체적으로 흥미롭기는 하지만 한 가지 특히 궁금한 게 있네요. 이미 두 클라이언트 터미널 모두에 인디케이터가 있는데 뭐하러 인디케이터 코드를 새로 만들죠? Moving Average.mq5 테크니컬 인디케이이터와 Custom Moving Average.mq5 커스텀 인디케이터 말이예요. 답은 간단합니다. 제 SMA 인디케이터 코드를 템플릿으로 활용해서 비슷한 인디케이터를 만드는 방법을 빠르게 배우기 위해서죠. 여러분의 머리도 좀 쉬어야 하잖아요! 예를 들어, Finware의 FATL을 이용해 MQL5로 디지털 필터 코드를 작성할 수 있습니다.

일반적으로 디지털 필터 계산 공식은 다음과 같습니다.

FILTER = SUM (K(i) * CLOSE (i), FilterPeriod)

좀 더 설명해 볼게요.

  • SUM—합계
  • K(i)—가중 계수
  • CLOSE (i)—현재 바의 종가
  • FilterPeriod—평균 필터에 필요한 바의 개수 

SMA 인디케이터 공식과 별반 다르지 않죠.

SMA = SUM ((1 / MAPeriod ) * CLOSE (i), MAPeriod)

차이점은 디지털 필터를 이용한 계산 기간이 정확하게 정해져 있으며 특정 필터나 K(i) 가중 계수에 대해서만 개별적으로 적용된다는 것이죠. 가중 계수와 디지털 필터 기간은 특화된 알고리즘을 이용해 계산됩니다. 해당 알고리즘 분석은 아직 복잡한 일이니까 이번에는 FATL 디지털 필터에 기존 값을 적용해 보도록 할게요. 디지털 필터에 관심이 많은 분들은 Digital Methods Generator 사이트(러시아어 웹사이트)를 방문해 보세요. 참고로 MQL4로의 FATL 인디케이터 변형 공식은 다음과 같습니다.

     FATL =  0.4360409450 * Close[bar + 0]
           + 0.3658689069 * Close[bar + 1]
           + 0.2460452079 * Close[bar + 2]
           + 0.1104506886 * Close[bar + 3]
           - 0.0054034585 * Close[bar + 4]
           - 0.0760367731 * Close[bar + 5]
           - 0.0933058722 * Close[bar + 6]
           - 0.0670110374 * Close[bar + 7]
           - 0.0190795053 * Close[bar + 8]
           + 0.0259609206 * Close[bar + 9]
           + 0.0502044896 * Close[bar + 10]
           + 0.0477818607 * Close[bar + 11]
           + 0.0249252327 * Close[bar + 12]
           - 0.0047706151 * Close[bar + 13]
           - 0.0272432537 * Close[bar + 14]
           - 0.0338917071 * Close[bar + 15]
           - 0.0244141482 * Close[bar + 16]
           - 0.0055774838 * Close[bar + 17]
           + 0.0128149838 * Close[bar + 18]
           + 0.0226522218 * Close[bar + 19]
           + 0.0208778257 * Close[bar + 20]
           + 0.0100299086 * Close[bar + 21]
           - 0.0036771622 * Close[bar + 22]
           - 0.0136744850 * Close[bar + 23]
           - 0.0160483392 * Close[bar + 24]
           - 0.0108597376 * Close[bar + 25]
           - 0.0016060704 * Close[bar + 26]
           + 0.0069480557 * Close[bar + 27]
           + 0.0110573605 * Close[bar + 28]
           + 0.0095711419 * Close[bar + 29]
           + 0.0040444064 * Close[bar + 30]
           - 0.0023824623 * Close[bar + 31]
           - 0.0067093714 * Close[bar + 32]
           - 0.0072003400 * Close[bar + 33]
           - 0.0047717710 * Close[bar + 34]
           + 0.0005541115 * Close[bar + 35]
           + 0.0007860160 * Close[bar + 36]
           + 0.0130129076 * Close[bar + 37]
           + 0.0040364019 * Close[bar + 38]; 

MQL5 인디케이터 버퍼의 바는 MQL4와 반대 방향으로 계산됩니다. 그러니까 이 공식을 MQL5 인디케이터에 사용하려면 괄호 속 증분 오퍼레이션을 감분 오퍼레이션으로 바꿔야 하죠. MQL5에는 Close[] 시계열이 없으므로 변수 또한 price[]로 변경하겠습니다. MetaEditor에서 다음 메뉴 명령을 사용하면 해당 작업을 자동화할 수 있습니다.

자주 쓰이는 Close [bar +price [bar -로 대체되어야 합니다.

해당 대화상자에서 '모두 바꾸기'를 클릭하세요. 이제 MQL5 FATL 인디케이터 계산에 필요한 공식이 만들어졌습니다.

     FATL =  0.4360409450 * price[bar - 0]
           + 0.3658689069 * price[bar - 1]
           + 0.2460452079 * price[bar - 2]
           + 0.1104506886 * price[bar - 3]
           - 0.0054034585 * price[bar - 4]
           - 0.0760367731 * price[bar - 5]
           - 0.0933058722 * price[bar - 6]
           - 0.0670110374 * price[bar - 7]
           - 0.0190795053 * price[bar - 8]
           + 0.0259609206 * price[bar - 9]
           + 0.0502044896 * price[bar - 10]
           + 0.0477818607 * price[bar - 11]
           + 0.0249252327 * price[bar - 12]
           - 0.0047706151 * price[bar - 13]
           - 0.0272432537 * price[bar - 14]
           - 0.0338917071 * price[bar - 15]
           - 0.0244141482 * price[bar - 16]
           - 0.0055774838 * price[bar - 17]
           + 0.0128149838 * price[bar - 18]
           + 0.0226522218 * price[bar - 19]
           + 0.0208778257 * price[bar - 20]
           + 0.0100299086 * price[bar - 21]
           - 0.0036771622 * price[bar - 22]
           - 0.0136744850 * price[bar - 23]
           - 0.0160483392 * price[bar - 24]
           - 0.0108597376 * price[bar - 25]
           - 0.0016060704 * price[bar - 26]
           + 0.0069480557 * price[bar - 27]
           + 0.0110573605 * price[bar - 28]
           + 0.0095711419 * price[bar - 29]
           + 0.0040444064 * price[bar - 30]
           - 0.0023824623 * price[bar - 31]
           - 0.0067093714 * price[bar - 32]
           - 0.0072003400 * price[bar - 33]
           - 0.0047717710 * price[bar - 34]
           + 0.0005541115 * price[bar - 35]
           + 0.0007860160 * price[bar - 36]
           + 0.0130129076 * price[bar - 37]
           + 0.0040364019 * price[bar - 38];

해당 알고리즘을 사용하는 인디케이터를 작성할 차례입니다. 우선 MetaEditor에서 SMA_1_en.mq5 인디케이터를 열어 FATL_en.mq5로 저장하세요. 인디케이터 템플릿이 준비되었으니 해당 템플릿 내 인디케이터 계산 알고리즘을 대체하고 변수를 수정해야 합니다. FATL 필터 계산에 필요한 공식의 블록 전체를 선택하고 클립보드로 복사하세요. 그리고 FATL.mq5 인디케이터 코드에서 버퍼의 마지막 초기화를 제외한 루프 연산자 내 코드를 모두 삭제하세요.

//---- Main loop of indicator calculation
   for(bar=first; bar<rates_total; bar++)
     {
     


      //---- Indicator buffer's cell initialization with FATL value
      ExtLineBuffer[bar]=FATL;
     }

삭제된 코드 대신에 클립보드에 복사해 놓은 FATL 디지털 필터 계산 알고리즘을 붙여 넣겠습니다. 이제 위에 설명된 방법을 이용해 SMA를 FATL로 대체합니다. 같은 방법으로 MAPeriod와 MAShift 입력 변수의 이름을 FATLPeriod와 FATLShft로 변경합니다. FATLPeriod 변수는 값이 39로 고정되어 있으므로 외부 변수에서 제외되어야 합니다. 같은 이유로 OnInit() 함수의 StringConcatenate() 오퍼레이터에서도 제거되어야 하고요. 이제 지역 변수 iii이 불필요해졌으니 삭제합니다. 끝으로, 인디케이터 선의 색깔을 파란색으로 바꾸고 굵기를 늘립니다.

이렇게 간단한 수정만 거치면 SMA_1_en.mq5 코드가 우리가 원하는 FATL_en.mq5 코드로 변하는 거죠.

//+------------------------------------------------------------------+
//|                                                      Fatl_en.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
//---- Indicator's author
#property copyright "2010, MetaQuotes Software Corp."
//---- Author's web-site link
#property link      "https://www.mql5.com"
//---- Indicator version number
#property version   "1.00"
//---- Drawing the indicator in the main window
#property indicator_chart_window
//---- One buffer is used for calculating and drawing the indicator
#property indicator_buffers 1
//---- Only one graphical plotting is used
#property indicator_plots   1
//---- Drawing the indicator as line
#property indicator_type1   DRAW_LINE
//---- Blue is used as indicator's line color
#property indicator_color1  Blue
//---- Indicator line is continuous curve
#property indicator_style1  STYLE_SOLID
//---- Indicator line thickness is equal to 2
#property indicator_width1  2
//---- Displaying indicator's label
#property indicator_label1  "FATL"

//---- Input parameters of indicator
input int FATLShift=0; // FATL horizontal shift in bars

//---- Declaring and initializing a variable to store the number of calculated bars
int FATLPeriod=39;

//---- Declaration of dynamic array, which will be 
//     used later as indicator buffer
double ExtLineBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
//---- Transformation of ExtLineBuffer dynamic array into indicator buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- Horizontal shift of indicator by FATLShift
   PlotIndexSetInteger(0,PLOT_SHIFT,FATLShift);
//---- Setting the position from which the drawing of indicator will start
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,FATLPeriod);
//---- Variable initialization for indicator's short name
   string shortname;
   StringConcatenate(shortname,"FATL(",FATLShift,")");
//--- Creating labels to display in Data Window
   PlotIndexSetString(0,PLOT_LABEL,shortname);
//--- Creating name to display in a separate window and in tool-tip
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
//--- Defining accuracy of displaying indicator's values
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//--- Prohibition of displaying blank values
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,     // amount of history in bars at the current tick
                const int prev_calculated, // amount of history in bars at the previous tick
                const int begin,           // beginning number of reliable count of bars
                const double &price[]      // price array for indicator calculation
                )
  {
//----+   
//---- Check if the number of bars is sufficient for calculation
   if(rates_total<FATLPeriod-1+begin)
      return(0);

//---- Declaring local variables
   int first,bar;
   double Sum,FATL;

//---- Calculating the 'first' starting number for the bars recalculation loop
   if(prev_calculated==0)        // Checking the first start of the indicator calculation
     {
      first=FATLPeriod-1+begin;  // Starting number for calculation of all bars
      //--- Increasing the start of data position by 'begin' bars, because the 
      //    calculations are based on data of another indicator
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+FATLPeriod);
     }
   else first=prev_calculated-1; // Starting number for calculation of new bars

//---- Main loop of indicator calculation
   for(bar=first; bar<rates_total; bar++)
     {
      //---- 
      FATL=0.4360409450*price[bar-0]
           + 0.3658689069 * price[bar - 1]
           + 0.2460452079 * price[bar - 2]
           + 0.1104506886 * price[bar - 3]
           - 0.0054034585 * price[bar - 4]
           - 0.0760367731 * price[bar - 5]
           - 0.0933058722 * price[bar - 6]
           - 0.0670110374 * price[bar - 7]
           - 0.0190795053 * price[bar - 8]
           + 0.0259609206 * price[bar - 9]
           + 0.0502044896 * price[bar - 10]
           + 0.0477818607 * price[bar - 11]
           + 0.0249252327 * price[bar - 12]
           - 0.0047706151 * price[bar - 13]
           - 0.0272432537 * price[bar - 14]
           - 0.0338917071 * price[bar - 15]
           - 0.0244141482 * price[bar - 16]
           - 0.0055774838 * price[bar - 17]
           + 0.0128149838 * price[bar - 18]
           + 0.0226522218 * price[bar - 19]
           + 0.0208778257 * price[bar - 20]
           + 0.0100299086 * price[bar - 21]
           - 0.0036771622 * price[bar - 22]
           - 0.0136744850 * price[bar - 23]
           - 0.0160483392 * price[bar - 24]
           - 0.0108597376 * price[bar - 25]
           - 0.0016060704 * price[bar - 26]
           + 0.0069480557 * price[bar - 27]
           + 0.0110573605 * price[bar - 28]
           + 0.0095711419 * price[bar - 29]
           + 0.0040444064 * price[bar - 30]
           - 0.0023824623 * price[bar - 31]
           - 0.0067093714 * price[bar - 32]
           - 0.0072003400 * price[bar - 33]
           - 0.0047717710 * price[bar - 34]
           + 0.0005541115 * price[bar - 35]
           + 0.0007860160 * price[bar - 36]
           + 0.0130129076 * price[bar - 37]
           + 0.0040364019 * price[bar - 38];

      //---- Indicator buffer's cell initialization with FATL value
      ExtLineBuffer[bar]=FATL;
     }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

인디케이터를 컴파일한 후에 클라이언트 터미널 차트에서 테스트하세요.

이제 FATL 인디케이터 코드 또한 다른 필터를 작성하기 위한 템플릿으로 사용될 수 있습니다. 게다가 문제가 훨씬 간단해졌죠. 우리 코드에서는 필터 계산 공식을 변환하고, FATL을 DIGFILTER로 바꾸고, 해당 디지털 필터의 차원에 맞게 DIGFILTERPeriod 변수를 초기화하기만 하면 됩니다.

클라이언트 터미널 내 디지털 필터 구축을 위한 공통 솔루션

T방금 본 인디케이터는 디지털 필터의 일반적인 문제를 해결법의 단일 변형입니다. 일반적인 솔루션이 되는 인디케이터가 있어서 하나의 인디케이터만으로 모든 디지털 필터를 구축할 수 있으면 너무 좋겠죠. 사실 이 문제는 이미 오래전에 세르게이 일류신(Sergei Ilyuhin)이 MetaTrader4 클라이언트 터미널에서 DF.dll 모듈을 이용하여 해결했습니다. 그러니 MetaTrader5에서도 쉽게 해결할 수 있겠죠. 해당 모듈에는 DigitalFilter() 함수가 사용됩니다.

DigitalFilter(int FType, int P1, int D1, int A1, int P2, int D2, int A2, double Ripple, int Delay, double& array[]); 

덕분에 디지털 필터 계수를 array[] 배열로 받을 수 있게 되었죠. 위 함수는 참조(배열 내 변수 형 선언 후 '&' 삽입)를 이용해 크기가 1500인 배열에 디지털 필터 계수를 작성합니다. 10가지 입력 변수 값을 받고 디지털 필터의 크기를 반환하죠. 그렇기 때문에 보편적인 디지털 필터를 구축하기에 적합합니다. 이제 문제는 전역 수준의 기존 인디케이터에 DLL을 가져오는 것과 인디케이터 초기화 블록에 계수 배열을 넣는 것, 그리고 해당 계수를 기반으로 OnCalculate() 함수의 계산을 실행하는 것인데요. DigitalFilter() 함수의 입력 변수는 인디케이터의 입력 변수가 되어야 합니다. 바로 해 보죠.

DF.dll 파일을 불러오는 건 전혀 어렵지 않습니다. 세 줄짜리 코드면 되거든요.

//---- DLL import
#import "DF.dll"
int DigitalFilter(int FType, int P1, int D1, int A1, int P2, int D2, int A2, double Ripple, int Delay, double& array[]); 
#import

다음으로 DigitalFilter() 함수의 모든 외부 변수를 해당 인디케이터의 입력 변수로 만들 겁니다.

//---- Input parameters of indicator
input FType_ FType=LPF;     //Filter Type
                            //0 - Low-Pass Filter (FATL / SATL / KGLP), 1 - High-Pass Filter (KGHP), 
                            //2 - Band-Pass Filter (RBCI / KGBP), 3 - Band-Stop Filter (KGBS)
input int    P1 = 28;       //Cut-off period 1, in bars
input int    D1 = 19;       //Transient process cut-off period 1, in bars
input int    A1 = 40;       //Fading in delay band 1, in dB
input int    P2 = 0;        //Cut-off period 2, in bars
input int    D2 = 0;        //Transient process cut-off period 2, in bars
input int    A2 = 0;        //Fading in delay band 2, in dB
input int    Delay=0;       //Delay, in bars
input double Ripple=0.08;   //Beats in bandwidth, in dB
input int    FILTERShift=0; //Moving Average horizontal shift, in bars

전역 수준에서 FILTERPeriod 변수를 초기화 없이 선언합니다.

//---- Declaring and initializing a variable to store the number of calculated bars
int FILTERPeriod;

전역 수준에서 필터 계수를 저장할 동적 배열을 선언합니다.

//---- Declaration of dynamic array, which will be 
//     used later as indicator buffer
double FILTERTable[];

이제 OnInit() 함수 블록을 살펴보겠습니다. DigitalFilter() 함수의 매개 변수로 FILTERTable[] 배열을 사용하는 건 별로 논리적이지 않습니다 . 최대 1500개의 요소를 갖는 배열을 만들어야 하는데 OnCalculate() 함수 블록은 100~200개의 요소만을 사용하니까요. 이 경우 OnInit() 함수 내부에서 선언된 Array[1500] 배열을 사용하는 편이 낫겠네요. 해당 배열에서 필요한 만큼의 데이터는 FILTERTable[] 배열에 작성됩니다. OnInit() 함수 종료 시 Array[] 배열은 소멸되며 필요한 데이터는 FILTERPeriod 디지털 필터의 길이와 동일한 크기를 갖는 FILTERTable[] 배열에 남게 됩니다. 다음은 동일한 목적을 수행하는 변형 코드입니다.

//---- Calculation of digital filter coefficients and determining the size of FILTERTable[] buffer
   double Array[1500];
   FILTERPeriod=DigitalFilter(FType,P1,D1,A1,P2,D2,A2,Ripple,Delay,Array);
//----  Changing the size of FILTERTable[] buffer for required number of digital filter coefficients
   if(FILTERPeriod<=0)
     {
      Print("Input parameters are incorrect. Indicator can't operate!");
      return;
     }
//---- Copying data from temporary array with size of 1500 to the main array with size of FILTERPeriod
   ArrayCopy(FILTERTable,Array,0,0,FILTERPeriod);

OnCalculate() 함수 내 필터 계산 코드는 꽤 간단합니다.

      //---- Digital filter calculation formula
      FILTER=0.0;
      for(iii = 0; iii<FILTERPeriod; iii++)
         FILTER+= FILTERTable[iii] * price[bar - iii];

해당 인디케이터 코드의 최종 버전은 DFilter_en.mq5 파일에 포함되어 있습니다. 인터페이스는 약간의 개선이 필요하죠. 인디케이터의 입력 변수가 0에서 3 사이의 값을 가지니까요.

input int FType = 0; //Тип фильтра
                     //0 - ФНЧ (FATL/SATL/KGLP), 1 - ФВЧ (KGHP), 2 - полосовой (RBCI/KGBP), 3 - режекторный (KGBS)

해당 값들은 숫자 형태일 때보다 필터명일 때 훨씬 이해하기 쉽습니다. 0-로우패스 필터(FATL/SATL/KGLP), 1-하이패스 필터(KGHP), 2-밴드패스 필터(RBCI/KGBP), 그리고 3-밴드스탑 필터(KGBS). 이런 경우를 대비해 MQL5는 열거형이라는 특수한 변수 형이 있죠. 우리의 경우 인디케이터의 입력 변수에 앞서 열거형을 선언하고 초기화해야 합니다.

//---- Declaration and initialization of digital filters types
enum FType_ //Filter Type
  {
   LPF, //Low-Pass Filter (FATL/SATL/KGLP)
   HPF, //High-Pass Filter (KGHP)
   BPF, //Band-Pass Filter (RBCI/KGBP)
   BSF, //Band-Stop Filter (KGBS)
  };

다음으로 인디케이터 외부 변수 선언에 사용된 변수 형을 대체해야 하는데요.

input FType_ FType = LPF; //Filter Type

인디케이터 대화상자 내 해당 매개 변수 값 설정 과정은 다음과 같은 모습입니다.

열거형 선언과 같이 명명된 상수 뒤로 주석이 한 줄 이어지며 상수는 입력 변수로 선택됩니다. 이제 범용 디지털 필터 소스 코드의 최종 버전이 나왔네요.

//+------------------------------------------------------------------+
//|                                                      ProjectName |
//|                                      Copyright 2010, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
/*
 * <<< DIGITAL FILTERS FOR METATRADER 5 >>> *
 *
 * DF.dll file should be placed in "\MetaTrader 5\MQL5\Libraries\" folder.
 * DF.dll requires three additional DLLs, containing a block of mathematical 
 * processing - bdsp.dll, lapack.dll, mkl_support.dll.
 * These DLLs must be installed in "C:\Windows\System32\" folder for 
 * Windows 32-bit operating systems or in "C:\Windows\SysWOW64\" folder 
 * for Windows 64-bit operating systems.
 *
 * Before using, make sure that:
 * 
 * 1. "Allow DLL import" option is enabled in Client Terminal settings 
 *    (Tools->Options->Expert Advisors tab).
 * 2. In "C:\Windows\System32\" or in "C:\Windows\SysWOW64\" folders the
 *    Bdsp.dll, lapack.dll and mkl_support.dll auxiliary math libraries are present.
 *
 * Description of input parameters:
 * 
 * Ftype  - Filter Type: 0 - Low-Pass Filter (FATL/SATL/KGLP), 1 - High-Pass Filter (KGHP),
 *          2 - Band-Pass Filter (RBCI / KGBP), 3 - Band-Stop Filter (KGBS)
 * P1     - Cut-off period P1, in bars
 * D1     - Transient process cut-off period D1, in bars
 * A1     - Fading in delay band A1, in dB
 * P2     - Cut-off period P2, in bars
 * D2     - Transient process cut-off period D2, in bars
 * A2     - Fading in delay band A2, in dB
 * Ripple - Beats in bandwidth, in dB
 * Delay  - Delay, in bars
 *
 * For Low-Pass Filter and HPF the values of P2, D2, A2 are ignored
 *
 * Conditions:
 * Low-Pass Filter:                       P1>D1
 * High-Pass Filter:                      P1<D1
 * Band-Pass Filter and Band-Stop Filter: D2>P2>P1>D1
 */
//+------------------------------------------------------------------+
//|      Digital Low Pass (FATL/SATL, KGLP) Filter    DFilter_en.mq5 | 
//|                    Digital Filter: Copyright (c) Sergey Ilyukhin |
//|                           Moscow, qpo@mail.ru  http://fx.qrz.ru/ |
//|                              MQL5 CODE: 2010,   Nikolay Kositsin |
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
//---- Indicator's author
#property copyright "2005, Sergey Ilyukhin, Moscow"
//---- Author's web-site link
#property link      "http://fx.qrz.ru/"
//---- Indicator version number
#property version   "1.00"
//---- Drawing the indicator in main window
#property indicator_chart_window
//---- One buffer is used for calculating and drawing the indicator
#property indicator_buffers 1
//---- Only one graphical plotting is used
#property indicator_plots   1
//---- Drawing the indicator as line
#property indicator_type1   DRAW_LINE
//---- Blue is used as indicator's line color
#property indicator_color1  DarkViolet
//---- Indicator line is continuous curve
#property indicator_style1  STYLE_SOLID
//---- Indicator line thickness is equal to 2
#property indicator_width1  2
//---- Displaying indicator's label
#property indicator_label1  "DFilter"
//---- Declaration and initialization of digital filters types
enum FType_ //Filter Type
  {
   LPF, //Low-Pass Filter (FATL/SATL/KGLP)
   HPF, //High-Pass Filter (KGHP)
   BPF, //Band-Pass Filter (RBCI/KGBP)
   BSF, //Band-Stop Filter (KGBS)
  };

//---- Input parameters of indicator
input FType_ FType=LPF;     //Filter Type
                            //0 - Low-Pass Filter (FATL / SATL / KGLP), 1 - High-Pass Filter (KGHP), 
                            //2 - Band-Pass Filter (RBCI / KGBP), 3 - Band-Stop Filter (KGBS)
input int    P1 = 28;       //Cut-off period 1, in bars
input int    D1 = 19;       //Transient process cut-off period 1, in bars
input int    A1 = 40;       //Fading in delay band 1, in dB
input int    P2 = 0;        //Cut-off period 2, in bars
input int    D2 = 0;        //Transient process cut-off period 2, in bars
input int    A2 = 0;        //Fading in delay band 2, in dB
input int    Delay=0;       //Delay, in bars
input double Ripple=0.08;   //Beats in bandwidth, in dB
input int    FILTERShift=0; //Moving Average horizontal shift, in bars

//---- DLL Import
#import "DF.dll"
int DigitalFilter(int FType,int P1,int D1,int A1,int P2,int D2,int A2,double Ripple,int Delay,double &array[]);
#import

//---- Declaring and initializing a variable to store the number of calculated bars
int FILTERPeriod;

//---- Declaration of dynamic array, which will be 
//     used later as indicator buffer
double ExtLineBuffer[];

//---- Declaring and initializing an array for the digital filter coefficients
double FILTERTable[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
//---- Transformation of ExtLineBuffer dynamic array into indicator buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- Horizontal shift of indicator by FILTERShift
   PlotIndexSetInteger(0,PLOT_SHIFT,FILTERShift);
//---- Setting the position from which the drawing of indicator will start
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,FILTERPeriod);
//---- Variable initialization for indicator's short name
   string shortname;
   StringConcatenate(shortname,"FILTER(",FILTERShift,")");
//---- Creating label to display in Data Window
   PlotIndexSetString(0,PLOT_LABEL,shortname);
//---- Creating name to display in a separate window and in tool-tip
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
//---- Defining accuracy of displaying indicator's values
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//---- Prohibition of empty values plotting
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
//---- Calculation of digital filter coefficients and determining the size of FILTERTable[] buffer
   double Array[1500];
   FILTERPeriod=DigitalFilter(FType,P1,D1,A1,P2,D2,A2,Ripple,Delay,Array);
//----  Changing the size of FILTERTable[] buffer for required number of digital filter coefficients
   if(FILTERPeriod<=0)
     {
      Print("Input parameters are incorrect. Indicator can't operate!");
      return;
     }
//---- Copying data from temporary array with size of 1500 to the main array with size of FILTERPeriod
   ArrayCopy(FILTERTable,Array,0,0,FILTERPeriod);
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,     // amount of history in bars at the current tick
                const int prev_calculated, // amount of history in bars at the previous tick
                const int begin,           // beginning number of reliable count of bars
                const double &price[]      // price array for indicator calculation
                )
  {
//----+   
//---- Check if the number of bars is sufficient for calculation
   if(rates_total<FILTERPeriod-1+begin)
      return(0);

//---- Declaring local variables
   int first,bar,iii;
   double Sum,FILTER;

//---- Calculating the 'first' starting number for the bars recalculation loop
   if(prev_calculated==0)         // Checking the first start of the indicator calculation
     {
      first=FILTERPeriod-1+begin; // Starting number for calculation of all bars
      //---- Increasing the start of data position by 'begin' bars, 
      //     because the calculations are based on data of another indicator
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+FILTERPeriod);
     }
   else first=prev_calculated-1;  // Starting number for calculation of new bars

//---- Main loop of indicator calculation
   for(bar=first; bar<rates_total; bar++)
     {
      //---- Digital filter calculation formula
      FILTER=0.0;
      for(iii = 0; iii<FILTERPeriod; iii++)
         FILTER+= FILTERTable[iii] * price[bar - iii];

      //---- Indicator buffer's cell initialization with FILTER value
      ExtLineBuffer[bar]=FILTER;
     }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+
클라이언트 터미널만을 이용한 MQL5 범용 디지털 필터의 구현으로 핀웨어(FinWare) 사의 디지털 필터에 대한 필요가 완전히 사라졌습니다. 이는 인디케이터의 활용에서 여러 가능성을 이끌어 내며 대단한 편리성을 가져다 주죠.

결론

여러가지를 손보다 보니 좀 복잡해진 것 같네요. 하지만 잘 들여다보면 모든 디테일 하나하나가 매우 논리적임을 알 수 있습니다. 아주 간단한 것에 대한 분석에서 시작해 좀 더 정교한 것으로의 의미 있고 신중한 전환을 이룬 것이죠.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/32

파일 첨부됨 |
dll.rar (1302.47 KB)
sma__en.mq5 (3.56 KB)
sma_1_en.mq5 (4.51 KB)
fatl_en.mq5 (6.08 KB)
dfilter_0_en.mq5 (8.17 KB)
dfilter_en.mq5 (8.42 KB)
MQL5: MetaTrader5로 상품선물거래위원회(CFTC) 보고서 분석하기 MQL5: MetaTrader5로 상품선물거래위원회(CFTC) 보고서 분석하기
이 글에서는 CTFC 보고서 분석에 필요한 도구를 개발해 보겠습니다. 우리가 해결할 문제는 다음과 같습니다. 중간 계산이나 변환을 거치지 않고 CFTC 보고서 내 데이터를 곧바로 활용할 수 있도록 해주는 인디케이터를 개발하는 것이죠. 그 외에도 여러 가지로 활용할 수 있습니다. 데이터 플로팅이라든지, 다른 인디케이터의 데이터로 활용하거나, 자동 분석 스크립트에서도 사용될 수 있고, 액스퍼트 어드바이저 매매 전략에서 사용될 수도 있죠.
WCF 서비스를 통해 MetaTrader5에서 .NET 애플리케이션으로 인용문 내보내기 WCF 서비스를 통해 MetaTrader5에서 .NET 애플리케이션으로 인용문 내보내기
MetaTrader5에서 다른 애플리케이션으로 인용문을 내보내고 싶으신가요? MQL5와 DLL이 함께라면 문제 없어요! 이 글은 MetaTrader5에서 .NET 애플리케이션으로 인용문을 내보내는 방법에 대한 설명입니다. 개인적으로는 .NET 플랫폼을 이용하는 게 훨씬 재밌고, 합리적이고, 간편하네요. 안타깝게도 MetaTrader5는 아직 .NET 형식을 지원하지 않기 때문에 예전과 같이 .NET 형식을 지원하는 win32 dll을 중간층으로 사용하겠습니다.
MQL5 이벤트 핸들링: 빠르게 MA 피리어드 바꾸기 MQL5 이벤트 핸들링: 빠르게 MA 피리어드 바꾸기
피리어드가 13인 단일 MA 인디케이터가 차트에 적용되었다고 상상해 봅시다. 피리어드를 20으로 바꾸고 싶은데, 인디케이터 속성 대화 상자에서 13을 20으로 바꾸고 싶지는 않네요. 맨날 쓰는 방법이니까 너무 지루하잖아요. 특히 인디케이터 코드를 열어서 수정하고 싶지가 않습니다. 버튼 하나만 눌러서 해결하고 싶은데요. 키보드의 위쪽 화살표가 딱이겠네요. 이 글에서는 그 방법을 찾아볼게요.
MQL5로 방출형 인디케이터 그리기 MQL5로 방출형 인디케이터 그리기
이 글에서는 새로운 시장 조사 접근법인 방출형 인디케이터에 대해 알아보겠습니다. 방출은 서로 다른 인디케이터의 교차점을 기반으로 계산됩니다. 각각의 틱 다음에 형형색색의 점이 나타나죠. 이 점들이 모여 성운, 구름, 궤도, 직선, 포물선 등의 형태를 갖는 클러스터를 형성합니다. 클러스터의 모양에 따라 시장 가격의 변화에 영향을 미치는, 눈에는 보이지 않는 원동력을 어느 정도 감지할 수 있죠.