English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5 이벤트 핸들링: 빠르게 MA 피리어드 바꾸기

MQL5 이벤트 핸들링: 빠르게 MA 피리어드 바꾸기

MetaTrader 5지표 | 5 7월 2021, 13:06
78 0
Sceptic Philozoff
Sceptic Philozoff

개요

본문은 MetaTrader사가 개발한 MetaTrader5 플랫폼의 MQL5가 자랑하는 새로운 기능 중 한가지에 대한 글입니다. 어쩌면 너무 늦게 올리는 것인지도 모르지만(2009년 9월이나 10월에 썼어야 해요), 아직 해당 주제에 대한 비슷한 글이 보이지 않네요. 게다가 그 동안은 인디케이터 내 이벤트 핸들링이 불가능했고요.

차트에 간단한 가격 인디케이터(이 경우 MA)가 있다고 생각하고 어떻게 하면 평활화 피리어드를 바꿀 수 있는지 고민해 봅시다. MT4에서는 다음과 같은 옵션이 있었죠.

  • MetaEditor에서는 MA 피리어드를 규정하는 입력 변수(extern)를 수정하고 소스 파일을 컴파일하면 끝이었죠.
  • MetaEditor를 열지 않고도 터미널에서 인디케이터 속성 대화 상자를 열어 해당 입력 변수를 수정할 수도 있죠.
  • Win32 API 라이브러리에서 필요한 함수를 찾아 키보드를 사용하도록 인디케이터 코드를 수정할 수도 있고요.

하지만 우리 모두 가장 덜 피곤한 방법을 원하잖아요? 이제 사용자 지정 이벤트 핸들링을 지원하는 MT5 플랫폼 덕분에 위의 과정 없이 간단하게 키 하나로 인디케이터 매개 변수를 수정할 수 있게 됐습니다. 이 글은 문제 해결에 대한 기술적 구현 방법을 설명합니다.

태스크 지정 및 문제점

사용되는 인디케이터 소스 코드는 클라이언트 터미널에 포함되어 있습니다. 원본 소스 코드 파일(Custom Moving Average.mq5)은 본문 하단에서 찾아볼 수 있습니다.

이번에는 소스 코드를 분석하거나 MQL4와 비교해서 어떤 변화가 있었는지는 알아보지 않겠습니다. 물론 크게 바뀐 부분도 있지만 잘 살펴야 보이는 부분도 있습니다. 기본 연산 재구조에 대한 설명은 포럼이나 온라인 도움말에서 검색할 수 있습니다.

어쨌든 MQL4 인디케이터의 주요 부분은 변경되지 않았습니다. 문제 해결을 위한 코드 수정의 최소 80%가 '블랙박스'로 알려진 인디케이터의 계산 함수를 기반으로 이루어졌습니다.

우리가 원하는 결과는 다음과 비슷한 형태일 겁니다. 해당 인디케이터가 차트에 적용되었다고 생각하고, 시프트 값이 0이고 피리어드가 10인 지수 이동 평균(EMA)이 있다고 가정합니다. 우리의 목표는 SMA 의 평활화 피리어드를 3 만큼(13) 증가시키고 5바 만큼 오른쪽으로 이동시키는 것입니다. 아마 다음의 과정으로 진행되겠죠.

  • 키를 몇 번 눌러 지수 MA를 SMA로 변경(MA 타입 변경)
  • 위쪽 화살표를 세 번 눌러 SMA 피리어드를 3 만큼 증가
  • 위쪽 화살표를 5번 눌러 MA를 오른쪽으로 5바 이동

가장 뻔한 방법은 인디케이터에 OnChartEvent() 함수를 추가해 키스트로크 이벤트 핸들러를 작성하는 겁니다. https://www.mql5.com/en/forum/53/page1/#comment_2655의 MetaTrader4 클라이언트 터미널 변경 사항 목록 내 빌드 245와 246을 따르면,

MetaTrader5 클라이언트 터미널 빌드 245와 246

MQL5: 엑스퍼트 어드바이저와 유사한 방식으로 커스텀 인디케이터를 이용해 이벤트 핸들링하기

자, 이제 인디케이터에 이벤트 핸들러를 추가하는 건 문제가 아니죠. 하지만 코드를 약간 수정하기는 해야 합니다.

우선, MQL5에서는 인디케이터의 외부 매개 변수에 변경 사항이 생겼습니다. 더이상 코드에서 수정할 수가 없게 된 것이죠. 클라이언트 터미널의 속성 대화 상자를 통해서만 변경 가능합니다. 하지만 급하게 변경이 필요한 경우에는 외부 매개 변수 값을 그냥 인디케이터의 전역 변수로 복사하면 전역 변수가 외부 매개 변수 값인 것처럼 계산이 실행됩니다. 그러나 이 경우 사용자들에게 혼란을 야기할 수 있어 외부 변수의 실행 가능성이 낮아집니다. 이제 이런 매개 변수들은 필요하지도 않습니다.

따라서 인디케이터에 외부 (입력) 변수가 없죠. 외부 변수의 역할을 하는 변수들을 지금부터 터미널 전역 변수 또는 줄여서 TGV로 부르겠습니다. 인디케이터의 외부 변수 역할을 하는 TGV를 확인하고 싶다면 터미널에서 F3 키를 누르기만 하면 됩니다. 제가 아는 가장 간단한 인디케이터 변수 제어 방법입니다.

두 번째로(이게 중요) 인디케이터 외부 변수에 변경 사항이 발생할 때마다 모든 과거 값을 찾아 처음부터 재조정해야 합니다. 다시 말해, 인디케이터 생성 초기에 주로 이루어지는 계산을 다시 수행해야 하는 거죠. 인디케이터의 연산 최적화 상태는 유지되지만 그 효과가 약간 감소됩니다.

다음은 1차로 수정된 인디케이터 코드의 일부입니다. 전체 코드는 본문 하단에서 찾아볼 수 있습니다.

 

'기본' 버전: 표준 인디케이터 소스 코드 변경 사항 설명

외부 매개 변수는 더이상 외부 변수가 아닌 전역 변수가 되었습니다.

모든 외부 변수의 입력 수정자 또한 사라졌습니다. 일반적으로는 전역 변수로의 전환이 힘들겠지만, 전통적인 방법을 따르기로 했습니다.

int              MA_Period   = 13;
int              MA_Shift    =  0;
ENUM_MA_METHOD   MA_Method   =  0;
int              Updated     =  0;     /// Indicates whether the indicator has updated after changing it's values

첫 세 가지 옵션은 피리어드, 오프셋, 그리고 MA 유형이었죠. 새로 추가된 네 번째 옵션은 MA 변수 변경 시 연산 최적화를 담당합니다. 몇 줄 내려가서 설명하겠습니다. 

가상 키 코드

가상 키 코드를 입력하세요.

#define KEY_UP             38
#define KEY_DOWN           40
#define KEY_NUMLOCK_DOWN   98
#define KEY_NUMLOCK_UP    104
#define KEY_TAB             9

'위쪽 화살표'와 '아래쪽 화살표'에 설정할 코드인데요. 탭 키와 숫자판의 8과 2에도 적용될 겁니다. 사실 이미 동일한 코드(VK_XXX 상수 형식 이름)가 <MT5dir>\MQL5\Include\VirtualKeys.mqh 파일에 포함되어 있지만 그냥 그대로 두기로 했습니다.


함수 코드 수정 및 LWMA 계산

CalculateLWMA() 함수를 약간 수정했습니다. 기존 함수에서는 정적 수정자를 이용해 weightsum 변수를 선언했었는데요. 이는 해당 함수의 첫 번째 호출 시 미리 계산 값을 구해놓으려는 의도였습니다. 이 변수는 코드에 여전히 남아있습니다. 다음은 해당 함수의 원래 코드입니다.weightsum 변수와 계산에 관련된 부분은 주석으로 표시했습니다.

void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int              i,limit;
   static int     weightsum;                       // <-- using weightsum
   double               sum;
//--- first calculation or number of bars was changed
   if(prev_calculated==0)                          // <-- using weightsum
     {
      weightsum=0;                                 // <-- using  weightsum
      limit=InpMAPeriod+begin;                     // <-- using weightsum
      //--- set empty value for first limit bars
      for(i=0;i<limit;i++) ExtLineBuffer[i]=0.0;
      //--- calculate first visible value
      double firstValue=0;
      for(i=begin;i<limit;i++)                     // <-- using weightsum
        {
         int k=i-begin+1;                          // <-- using weightsum
         weightsum+=k;                             // <-- using weightsum
         firstValue+=k*price[i];
        }
      firstValue/=(double)weightsum;
      ExtLineBuffer[limit-1]=firstValue;
     }
   else limit=prev_calculated-1;
//--- main loop
   for(i=limit;i<rates_total;i++)
     {
      sum=0;
      for(int j=0;j<InpMAPeriod;j++) sum+=(InpMAPeriod-j)*price[i-j];
      ExtLineBuffer[i]=sum/weightsum;              // <-- using weightsum
      }
//---
  }

이전까지는 기존 함수도 잘 작동했습니다만, 인디케이터와 어드바이저를 동시에 실행(본문 하단 참조)하니까 해당 MA에 문제가 발생하더라고요. 가장 큰 문제는 위에서 이미 설명한 정적 변수 weightsum 때문이었습니다. 이 변수가 MA 매개 변수에 변동이 생길 때마다 증가해서 매번 다시 계산을 해야 했어요.

직접 그리고 즉각적으로 weightsum 값(1에서 MA 피리어드까지의 정수의 합, 등차수열 계산식 이용 가능)을 계산하는 가장 쉬운 방법이죠. 동시에 정적 상태를 거부하는 방법이기도 하고요. 이번에는 정적 수정자를 사용해 weightsum 값을 선언하는 대신, 수정자를 사용하지 않고 '올바른' 값으로 초기화를 시켜 '변수 누적' 초기 루프를 제거합니다.

int weightsum = MA_Period *( MA_Period + 1 ) / 2;

이제 전부 제대로 작동하는군요.


핸들러로서의 OnCalculate() 함수

OnCalculate() 함수를 꽤 많이 수정했기 때문에 전체 코드를 가져왔습니다.

int OnCalculate(const int rates_total,
                const int prev_calculated,            /// Mathemat: full recalculation!
                const int begin,                      /// Mathemat: full recalculation!
                const double &price[])
  {
//--- check for bars count
   if(rates_total<MA_Period-1+begin)
      return(0);// not enough bars for calculation
//--- first calculation or number of bars was changed
   if(prev_calculated==0)
      ArrayInitialize(LineBuffer,0);
//--- sets first bar from what index will be draw
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MA_Period-1+begin);

//--- calculation (Mthmt - optimized by Mathemat)

   if( GlobalVariableGet( "Updated" ) == 1 )
   {
      if(MA_Method==MODE_EMA)  CalculateEMA(       rates_total,prev_calculated,begin,price);
      if(MA_Method==MODE_LWMA) CalculateLWMA_Mthmt(rates_total,prev_calculated,begin,price);
      if(MA_Method==MODE_SMMA) CalculateSmoothedMA(rates_total,prev_calculated,begin,price);
      if(MA_Method==MODE_SMA)  CalculateSimpleMA(  rates_total,prev_calculated,begin,price);
   }
   else
   {
      OnInit( );                 /// Mthmt
      if(MA_Method==MODE_EMA)  CalculateEMA(       rates_total,0,0,price);
      if(MA_Method==MODE_LWMA) CalculateLWMA_Mthmt(rates_total,0,0,price);
      if(MA_Method==MODE_SMMA) CalculateSmoothedMA(rates_total,0,0,price);
      if(MA_Method==MODE_SMA)  CalculateSimpleMA(  rates_total,0,0,price);
      GlobalVariableSet( "Updated", 1 );
      Updated = 1;
   }
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

가장 큰 변화는 '밑바닥부터' 인디케이터를 계산해야 된다는 생각과 관련됩니다. 사용자가 키보드 조작을 통해 MA 피리어드를 13에서 14로 바꿨다면 이전의 모든 최적화 결과는 쓸모가 없어지므로 당연히 MA를 다시 계산해야 하죠. 이런 경우는 새로 추가된 변수가 0값(단축키 입력으로 TGV는 변했지만 아직 인디케이터를 다시 그리는 틱이 수신되지 않음)을 가질 때 발생합니다.

추가적으로, 지금까지는 OnInit() 함수를 반드시 호출해야 했죠. 선 위에 커서가 닿을 때 나타나는 인디케이터 별명을 변경하기 위해서요. 초기 MA 계산 후 새로 추가된 TGV는 1로 설정되어 최적화된 인디케이터 연산을 가능하게 합니다. 더이상 인디케이터 변수를 바꿀 필요가 없을 때까지 말입니다.


OnChartEvent() 핸들러

다음은 간단한 OnChartEvent() 핸들러 코드입니다.

void OnChartEvent( const int          id,
                   const long    &lparam,
                   const double  &dparam,
                   const string  &sparam )
{
   if( id == CHARTEVENT_KEYDOWN )
      switch( lparam )
      {
         case( KEY_TAB          ):  changeTerminalGlobalVar( "MA_Method",  1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         
         case( KEY_UP           ):  changeTerminalGlobalVar( "MA_Period",  1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         case( KEY_DOWN         ):  changeTerminalGlobalVar( "MA_Period", -1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         
         case( KEY_NUMLOCK_UP   ):  changeTerminalGlobalVar( "MA_Shift",   1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         case( KEY_NUMLOCK_DOWN ):  changeTerminalGlobalVar( "MA_Shift",  -1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
      }
      
      return;
}//+------------------------------------------------------------------+      

핸들러가 작동하는 방식은 다음과 같은데요. 단축키를 누르면 가상 코드가 생성되고, changeTerminalGlobalVar() 보조 함수가 실행되며 특정 TGV를 올바르게 수정합니다. 새로 추가된 플래그 값이 0으로 재설정되고, 플래그는 OnCalculate() 함수를 실행해 '밑바닥부터' 인디케이터를 다시 그릴 틱을 기다립니다.


TGV를 '올바르게' 수정하는 보조 함수

마지막은 OnChartEvent() 핸들러에서 사용된 changeTerminalGlobalVar() 함수의 코드입니다.

void changeTerminalGlobalVar( string name, int dir = 0 )
{
   int var = GlobalVariableGet( name );
   int newparam = var + dir;
   if( name == "MA_Period" )
   {
      if( newparam > 0 )       /// Possible period is valid for MA
      {
         GlobalVariableSet( name, newparam ); 
         MA_Period = newparam;     /// Don't forget to change the global variable    
      }   
      else                       /// we do not change the period, because MA period is equal to 1 minimum
      {   
         GlobalVariableSet( name, 1 );     
         MA_Period = 1;     /// Don't forget to change the global variable 
      }   
   }   
      
   if( name == "MA_Method" )    /// Here when you call the 'dir' it is always equal to 1, the dir value is not important    
   {
      newparam = ( var + 1 ) % 4;
      GlobalVariableSet( name, newparam );  
      MA_Method = newparam;
   }      

   if( name == "MA_Shift" )
   {
      GlobalVariableSet( name, newparam );     
      MA_Shift = newparam;
   }      

   ChartRedraw( );
   return;
}//+------------------------------------------------------------------+

이 함수의 주요 목적은 '물리적 한계'를 고려한 새 MA 매개 변수의 올바른 연산입니다. MA 피리어드를 1 미만으로 설정할 수 없으므로 시프트 값은 랜덤으로 정해집니다. 하지만 MA 형식은 ENUM_MA_METHOD 열거형의 멤버와 상응하는 0부터 3까지의 숫자로 나타납니다.

 

확인합니다. 작동은 하지만 'C급'이네요. 어떻게 해야 할까요?

음, 인디케이터를 차트에 적용하고 MA 매개 변수를 바꾸기 위해 핫키를 몇 번 눌러 볼게요. 모두가 정상적으로 작동하지만 한 가지 걸리는 게 있습니다. TGV는 즉각적으로 변하는 반면(F3 키로 TGV를 호출해 확인) MA는 새로운 틱이 도착해야 다시 그려지네요. 미국 시장을 기준으로 하면 지연이 눈에 띄지 않을 수도 있습니다. 하지만 밤처럼 조용한 시기라면 몇 분 동안 기다려야 될 수도 있죠. 어떻게 된 걸까요?

뭐... 작성하는 대로 결과가 나오는 거죠. 빌드 245 이전에는 '엔트리 포인트'가 OnCalculate() 함수 단 하나였습니다. 물론 인디케이터의 초기 연산, 초기화 그리고 컴플리션을 수행하는 OnInit() 함수나 OnDeinit() 함수에 대해 이야기하는 건 아닙니다. 이제 새로운 Timer 이벤트와 ChartEvent 이벤트에 연결된 여러 개의 엔트리 포인트가 있습니다.

그러나 새로운 핸들러들은 고유의 기능만을 수행하며 OnCalculate() 핸들러와는 형식상으로 연결되어 있지 않습니다. 그렇다면, 홀로 떨어진 OnChartEvent() 핸들러를 이용해 '제대로' 된(MA가 즉각적으로 다시 그려지는) 인디케이터를 어떻게 작성할까요?

여러 가지 구현 방법이 있습니다.

  • '마트료시카 인형'(OnChartEvent() 내부에서 OnCalculate() 호출): OnCalculate() 함수 호출을 핸들러에 삽입하고 매개 변수를 미리 설정합니다. OnChartEvent() 핸들러는 최소 하나 이상의 MA 변수의 변화를 의미하므로 모든 과거 데이터에 영향을 미치게 됩니다(연산 최적화 없이 '밑바닥부터' 다시 계산해야 하죠).
  • 그래픽 버퍼를 수정하는 OnCalculate() 함수의 첫 부분으로 컨트롤을 이동하는 '인공 틱'. MT5 관련 설명에서 보셨겠지만, '딱 맞는' 메소드는 없습니다(제가 잘 모를 수도 있고요). 궁금하시면 API나 PostMessageA 등을 검색해 보세요. 해당 변형은 여기서 설명하지 않겠습니다. 아직 우리가 모르는 기능들이 나중에 바뀔 수 있다는 보장은 없으니까요. 전 그렇게 될 거라고 생각해요.

 

'마트료시카 인형'이 잘 작동하는군요!

사실 가장 중요한 부분은 이미 다루었던 거네요. 다음은 매우 간단한 함수 코드입니다. OnChartEvent() 핸들러
return 연산자 바로 앞에 호출을 삽입할 수 있죠.

int OnCalculate_Void()
{
   const int rates_total = Bars( _Symbol, PERIOD_CURRENT );
   CopyClose( _Symbol, PERIOD_CURRENT, 0, rates_total, _price );
   OnCalculate( rates_total, 0, 0, _price );
   return( 1 );
}//+------------------------------------------------------------------+

컴파일된 인디케이터를 차트에 적용하고 나면, 코드가 빠르게, 그리고 틱과는 상관 없이 작동하는 것을 볼 수 있습니다.

단점은 종가가 price[] 배열에 복사된다는 것입니다. 원하는 경우 인디케이터 속성 대화 상자 내 '세팅' 탭에서 '적용' 필드를 설정해 원하는 함수로 CopyClose() 함수를 대체할 수 있습니다. 현재 가격이 기본(시가, 고가, 저가, 종가)인 경우 이미 이에 상응하는 CopyXXXX() 함수가 있죠. 조금 더 복잡한 계산이 필요한 가격의 경우(중간 가격, 가중 가격 등) 배열을 다른 방식으로 계산해야 합니다.

우리가 배열 전체 기록을 복사하는 CopyClose() 함수를 필요로 하지 않는다고는 말할 수 없겠네요. 과거 데이터가 너무 많지 않은 이상 해당 함수는 꽤 빠르게 작동합니다. 1999년 이전의 기록(약 70만 개의 바)으로 EURUSD H1에서 인디케이터를 시험해 보니 인디케이터 연산 시 속도가 느려지지는 않네요. 상당한 양의 과거 기록에 대한 속도 저하는 CopyXXXX() 함수 때문이 아닌 기록 초반의 복잡한 인디케이터 재계산(필수) 때문일 수 있습니다.


시행 결과 및 결론

단일 인디케이터 파일과 '인디케이터+어드바이저' 조합 중에 뭐가 더 나을까요?

그렇게 간단한 질문은 아닙니다. 단일 인디케이터 파일은 이벤트 핸들러를 포함한 모든 함수가 한 곳에 집중되어 있다는 장점을 가지죠.

반대로 3~4개의 인디케이터가 엑스퍼트 어드바이저의 차트에 적용되었다고 생각해 봅시다. 흔치 않은 상황은 아닙니다. 게다가 각각의 인디케이터가 표준 OnCalculate() 함수와 더불어 서로 다른 이벤트 핸들러를 가지고 있다고 생각해 보세요. 이 정신 없는 상황에서 이벤트 처리 시 혼동을 최소화하려면 모든 이벤트 핸들로를 한 곳으로 모으는 것이 좋겠죠. 엑스퍼트 어드바이저로 말입니다.

오랫동안 소프트웨어 개발자들은 인디케이터에서 이벤트를 처리할 수 있게 할지 말지를 고민해 왔습니다. 비공개 베타 버전 09.09.09이 나온지 정확히 5개월이 되었네요. 베타 버전에서는 인디케이터가 '순수 연산 엔티티'로 간주되어 연산 속도를 저하시키는 다른 모든 기능을 제한했었죠. '순수'의 개념이 설 자리가 없을 겁니다. 이제 프로그래머들의 판타지가 펼쳐질 테니까요. 하지만 '순수'와 '불순'의 한계 사이의 균형은 유지되어야 합니다.

2009년 9월과 10월 사이 MT5 베타 버전의 빌드 개수가 200개도 채 되지 않았을 때, 저는 '엑스퍼트 어드바이저+인디케이터' 조합으로 코드를 작성하고 디버깅도 해보았는데요. 빠른 MA 매개 변수 변환은 가능했지만 C급 정도였다고나 할까요. 틱이 도착한 후에야 새로고침되었거든요. 당시에는 이 조합만이 유일한 해결책이었는데 이제는 아무도 관심을 갖지 않겠죠.

당시에는 어떻게 인디케이터 기능을 조금 더 향상시킬 수 있을지 생각하지 못했어요. 현재 최신 버전에 반영되어 있는 것처럼 말입니다. 하지만 이제는 필요한 분들께 보다 간편한 해결책을 제공할 수 있어 기쁩니다.


저희 작업 현장을 촬영한 짧은 비디오를 첨부합니다. MA 곡선의 변화(피리어드의 증가 및 감소)는 어떤 면에서는 눈부시기까지 합니다. 이게 바로 마트료시카 인형이죠.


물론 이런 묘책은 인디케이터 연산에 시간이 별로 걸리지 않을 때만 유용하게 쓰입니다. 이 인디케이터에 포함된 단일 MA가 이에 해당하죠.


한 가지 아쉬운 점

이전에 인디케이터 외부 변수였던 것들이 이제는 터미널 전역 변수(TGV)라는 걸 기억하세요. F3 키를 누르면 볼 수 있습니다. 전역 변수 대화 상자를 열어 TGV 중 한 가지(예: MA 피리어드)를 수정했다고 가정해 봅시다. 차트 내 인디케이터에 즉각적으로 해당 변화가 반영되기를 바라시겠죠.

현재 터미널에는 사용자에 의해 개발된 TGV 편집용 이벤트가 없습니다(예: CHARTEVENT_GLOBALVAR_ENDEDIT). 또한, 전역 변수 대화 상자에서 TGV 수정 기능을 끌 수도 없고요. 따라서 틱이 없으면 아무런 이벤트도 확인할 수 없죠. 실제로는 어떤 상황이 발생할까요?

키보드를 사용하지 않고는 바로 다음 틱의 업데이트조차 '틀리게' 될 겁니다. 새로 추가된 변수에 0값이 지정되지 않았으므로 '최적화된' 연산(수정된 TGV의 전 값에 상응)만 실행되게 됩니다. 이런 경우를 대비해 한 가지 조언을 드리겠습니다. TGV를 수정한 후에는 TGV 수정용 핫키를 최소 한 번 이상 눌러주세요. 그러면 새로 추가된변수 값이 이 되어 인디케이터 전체에 대한 재연산이 이루어집니다.

모든 사용자 및 개발자는 이 점을 기억해야 합니다.


소스 코드 및 동영상 첨부 파일

소스 코드 파일도 첨부합니다. 파일 설명

  1. Custom Moving Average.mq5-MT5에 포함된 MA 소스 코드 파일
  2. MyMA_ind_with_ChartEvent.mq5-최초('C급') 구현: 틱 도착 후 인디케이터 업데이트 진행
  3. MyMA_ind_with_ChartEvent_Matryoshka.mq5-두 번째('B급') 버전: 틱 도착과 상관 없이 인디케이터 업데이트 진행

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

그래픽 컨트롤 옵션이 있는 인디케이터 만들기 그래픽 컨트롤 옵션이 있는 인디케이터 만들기
시장 분위기가 무엇인지 안다면 MACD 인디케이터(이동 평균 수렴 확산 지수)도 아실 겁니다. 컴퓨터 분석이 가능해지면서 투자자들이 사용하기 시작한 가격 변동을 파악할 수 있는 아주 뛰어난 분석 도구죠. 이 글에서는 MACD 지표를 어떤 식으로 변형할 수 있는지 알아보고 그래픽 설정 변경이 가능한 하나의 인디케이터로 변형된 MACD 지표를 구현해 보겠습니다.
MQL5: MetaTrader5로 상품선물거래위원회(CFTC) 보고서 분석하기 MQL5: MetaTrader5로 상품선물거래위원회(CFTC) 보고서 분석하기
이 글에서는 CTFC 보고서 분석에 필요한 도구를 개발해 보겠습니다. 우리가 해결할 문제는 다음과 같습니다. 중간 계산이나 변환을 거치지 않고 CFTC 보고서 내 데이터를 곧바로 활용할 수 있도록 해주는 인디케이터를 개발하는 것이죠. 그 외에도 여러 가지로 활용할 수 있습니다. 데이터 플로팅이라든지, 다른 인디케이터의 데이터로 활용하거나, 자동 분석 스크립트에서도 사용될 수 있고, 액스퍼트 어드바이저 매매 전략에서 사용될 수도 있죠.
MetaTrader5와 MATLAB의 상호 작용 MetaTrader5와 MATLAB의 상호 작용
이 글은 MetaTrader5와 MATLAB 패키지 사이의 상호 작용에 대한 설명입니다. 데이터 변환 메커니즘과 MATLAB 데스크톱과 상호 작용이 가능한 범용 라이브러리 개발 과정에 대해 살펴볼 겁니다. MATLAB 환경에서 생성된 DLL의 사용법도 알아보겠습니다. 이 글은 C++와 MQL5를 이미 알고 있는 숙련된 프로그래머들을 위해 작성되었습니다.
초보자를 위한 실용적인 MQL5 디지털 필터 구현 초보자를 위한 실용적인 MQL5 디지털 필터 구현
자동 매매 시스템 관련 포럼에서 자주 언급되는 것 중 하나가 디지털 필터입니다. 그러니 MQL5에서 사용할 수 있는 디지털 필터 표준 코드를 꼭 제공해 드려야죠. 이 글에서는 '뉴비들을 위한 MQL5 커스텀 인디케이터'에 있는 간단한 SMA 인디케이터 코드를 조금 더 복잡하지만 보편적으로 사용할 수 있는 디지털 필터로 변환하는 법을 알아보겠습니다. 본문의 내용은 직전 글과 이어집니다. 프로그래밍 오류 수정법과 텍스트 변환 방법에 대한 설명 역시 포함되어 있습니다.