English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
20 MQL5에서의 매매 신호들

20 MQL5에서의 매매 신호들

MetaTrader 5 | 5 7월 2021, 10:25
119 0
Sergey Gritsay
Sergey Gritsay

시작하며

트레이더들은 가격변동의 법칙을 찾고자 하며, 좋은 매매 타이밍을 찾아낼 법칙을 만들고자 합니다. 완전 자동 시스템을 만들기 위해서는 그런 순간, 매매 신호, 이 오는 것을 인식하게 하는 법을 배워야 합니다.

신호는 트레이더들에게 잠재적으로 포지션 진입을 할 만한 포인트를 알려주지만, 전부 그대로 할 필요는 없습니다. 추가적인 기준을 통해 대부분의 신호도 걸러낼 수 있지만, 우리에게 중요한 것은 아닙니다. 본 문서의 주제는 가장 인기 좋은 매매 신호를 어떻게 해야 MQL5로 짤 것인가 입니다.


1. 우리가 아는 신호엔 무엇이 있는가?

시장에 진입하는 순간을 판단하는 메소드는 여러 타입으로 나뉠 수 있습니다:

  • 이동평균의 교차
  • 범위 돌파
  • 스토캐스틱 레이트 기반 과매도/과매수 영역 이탈
  • 채널 경계 바운스
  • 채널 경계 돌파
  • 트렌드의 변화


2. 어떻게 해야 더 낫게 코딩하는가?

코드를 쓰는 방법은 다양합니다; OnStart(), OnTick() 그리고 다른 함수들 안에서 쓰일 수 있습니다. 이들에 대한 자세한 설명은 이 홈페이지의 도움말 혹은 MetaEditor에 내장된 유저 가이드에서 확인할 수 있습니다; 하지만 이 메소드는 효과적이지 않으며 때로는 같은 코드를 몇번이고 써야할 수도 있습니다. 이렇게 하면 다른 방법을 사용할 수 있습니다. 프로그램 코드의 모든 부분에서 호출할 수 있는 커스텀 함수를 사용할 수 있습니다.

만든 함수를 더욱 편하게 사용하기 위하여 이 함수들을 외부 include 파일로 묶고 SignalTrade라고 명명합시다; 이 파일은 ...\MQL5\Include 폴더에 저장됩니다. 이 모듈을 모든 프로그램에 쉽게 연결할 수 있게 해줍니다. 

모든 신호가 다르게 보이지만, 그들은 많은 공통점을 가지고 있습니다. 신호를 생성하는 기능에서 수신할 수 있는 세 가지 변형 모델이 있습니다.

  • 매수 신호
  • 매도 신호
  • 신호 없음

그러면 이 신호에 해당하는 리스트를 만들어 보겠습니다.

  •  1 - 매수 신호
  • -1 - 매도 신호
  •  0 - 신호 없음

신호를 리턴하는 함수 프로토타입을 써봅시다. 우리의 기능을 하나 또는 다른 작업이 수행될 여러 부분으로 나누세요. 데이터를 저장하는데에 필요한 변수들은 함수 시작 부분에서 선언되고 초기화되었습니다. 생성된 인디케이터에서 필요한 정보를 로드 및 확인하는 작업이 추가로 수행됩니다. 정보 확인은 데이터 및 프로그램 전체에 대한 예상치 못한 결과를 방지하기 위해 필수적입니다. 

정보가 로드되고 확인되면 신호 형성으로 이동합니다. 신호가 형성되는 즉시 함수를 종료하고 획득한 신호를 위에서 언급한 값 중 하나로 반환합니다.

int TradeSignal()
  {
   //--- zero means absence of signal
   int sig=0;
   
   //--- check the handles of indicators

   //--- if all the handles are invalid, create them

   //--- if the handles are valid, copy the values from indicators

   //--- check the copied data

   //--- in case of an error of copying, exit from the function

   //--- perform the indexation of the array as a timeseries

   //--- in case of an indexation error, exit from the function

   //--- check conditions and set the value for sig
 
   //--- return the trade signal
   return(sig);
  }


3. 20 매매 신호 예시

매매 신호를 얻기 위해 각기 다른 인디케이터를 사용할 것입니다. MQL5에서는 인디케이터들은 특별한 함수를 통해 호출되는데, 예를 들면 iMA, iAC, iMACD, iIchimoku, 등이 있습니다.; 이들은 대응되는 기술적 인디케이터의 복사본을 클라이언트 터미널의 글로벌 캐시에 생성합니다. 패러미터가 같은 인디케이터 복사본이 이미 있는 경우, 새 사본은 작성되지 않지만 기존 사본에 대한 링크 카운터는 증가합니다.

이러한 함수들은 해당되는 인디케이터 사본의 핸들을 반환합니다. 또한 이 핸들을 사용하면 해당 인디케이터로 계산한 데이터를 얻을 수 있습니다. 해당 버퍼의 데이터(기술적 인디케이터는 내부 버퍼에 계산된 데이터를 포함하며, 그 수는 인디케이터 유형에 따라 1에서 5까지 달라질 수 있음)는 CopyBuffer() 함수를 사용하여 MQL5 프로그램으로 복사할 수 있습니다.

인디케이터의 값을 계산하는 데 시간이 필요하기 때문에 인디케이터 생성 직후엔 인디케이터의 데이터를 사용할 수 없습니다. 따라서 가장 좋은 방법은 OnInit() 내에 핸들을 생성하는 것입니다. iCustom() 함수는 해당 커스텀 인디케이터를 생성하고, 생성에 성공하면 인디케이터의 핸들을 반환합니다. 커스텀 인디케이터에는 최대 512개의 인디케이터 버퍼가 포함될 수 있으며, CopyBuffer() 함수와 가져온 핸들을 사용하여 내용물을 확보할 수도 있습니다.

각각의 신호에 대응해서 우리의 프로토타입 TradeSignal()을 기반으로한 함수를 만들고 이렇게 명명합시다: TradeSignal_01() - TradeSignal_20(). 이동 평균의 교차점에 기초한 신호의 예제를 사용하여 신호를 형성하기 위한 함수의 구조를 자세히 살펴본 후, 다른 신호에 대한 함수를 비슷한 방식으로 작성하겠습니다.

3.1. 이동 평균의 교차점

1번 그림. 두 이동평균의 교차점

1번 그림. 두 이동평균의 교차점

TradeSignal_01() 함수를 이용하여 우리는 두 이동평균들(МА)의 교차점에 대한 신호를 얻습니다: 빠른 것은 8 주기, 느린 것은 16 주기 입니다.

함수에서는 교차점 사실만 수정하고 해당 신호의 값을 함수에 반환합니다. 우리 룰과 프로토타입에 의하면 함수는 이와 같이 보일 겁니다:

int TradeSignal_01()
  {
//--- zero means that there is no signal
   int sig=0;

//--- check the handles of indicators
   if(h_ma1==INVALID_HANDLE)//--- if the handle is invalid
     {
      //--- create is again                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- exit from the function
      return(0);
     }
   else //--- if the handle is valid
     {
      //--- copy value of the indicator to the array
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- if the array of data is less than required
         //--- exit from the function
         return(0);
      //--- set the indexation in the array as in a timeseries                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- in case of an indexation error, exit from the function
         return(0);
     }

   if(h_ma2==INVALID_HANDLE)//--- if the handle is invalid
     {
      //--- create it again                                                      
      h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
      //--- exit from the function
      return(0);
     }
   else //--- if the handle is valid 
     {
      //--- copy values of the indicator to the array
      if(CopyBuffer(h_ma2,0,0,2,ma2_buffer)<2) //--- if there is less data than required
         //--- exit from the function
         return(0);
      //--- set the indexation in the array as in a timeseries                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- in case of an indexation error, exit from the function
         return(0);
     }

//--- check the condition and set a value for the sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

이제 코드의 모든 부분을 좀 더 자세히 살펴보겠습니다. 함수 시작 시 신호 유형을 저장할 로컬 변수를 선언하고 신호 없음을 의미하는 0으로 초기화합니다.

int sig=0;

또한 핸들의 유효성을 확인합니다. 만약 핸들이 유효하지 못하다면 핸들을 만들고 함수를 종료합니다. 왜냐하면 인디케이터의 계산에는 시간이 좀 걸리기 때문입니다. 인디케이터 버퍼에서 데이터를 복사하는 오류를 방지하기 위해 구현됩니다.

   if(h_ma1==INVALID_HANDLE)//--- if the handle is invalid
     {
      //--- create it again                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- exit from the function
      return(0);
     }

만약 핸들이 유효하다면 어레이에 데이터를 복사합니다. 상황을 분석하려면, 빠른 MA의 마지막 세 개 바와 느린 MA의 두 바의 데이터를 복사하는 것으로 충분합니다. 이를 위해 이하의 함수를 쓰십시오:

int  CopyBuffer(
   int       indicator_handle,     // handle of an indicator
   int       buffer_num,           // number of buffer of an indicator
   int       start_pos,            // where to start from 
   int       count,                // amount to be copied
   double    buffer[]              // an array to copy data to
   );

확인해야할 것: 필요한 것보다 데이터가 적으면 복사 오류가 발생했으며, 데이터를 저장해야 하는 어레이를 참조하면 오류가 발생합니다. 이를 피하기 위해 함수를 종료합니다. 또한 타임시리즈 같은 어레이의 인덱스화가 필요합니다; 이하의 함수를 그를 위해 쓸 수 있습니다:

bool  ArraySetAsSeries(
   void  array[],     // array by a link
   bool  set          // true means that the indexation order is reversed
   );

어레이 인덱스화 중에 오류가 발생하면 함수를 종료하십시오. 그렇지 않으면 잘못된 결과를 얻을 수 있습니다.

else //--- if the handle is valid 
     {
      //--- copy values of the indicator to the array
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- if there is less data than required
         //--- exit from the function
         return(0);
      //--- set the indexation in the array like in a timeseries                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- in case of an indexation error, exit from the function
         return(0);
     }

이제 인디케이터가 생성되고 필요한 모든 정보가 확보되면 주요 단계인 신호 형성 단계로 넘어갑시다.

현재 막대에서 신호 깜박임을 방지하려면 닫힌 첫번째 및 두번째의 클로즈된 바만 분석하십시오.

구매 신호를 만듭니다. 이를 위해 2번째 바에서의 빠른 MA 값을 취하여 1번째 바에서의 느린 MA 값과 비교하고, 1번째 바에서의 빠른 MA의 값을 1번째 바에서의 느린 MA 값과 비교합니다. 만약 2번째 바에서의 빠른 MA 값이 1번째 바에서의 느린 MA 값보다 작고, 1번째 막대에서의 빠른 MA 값이 1번째 막대에서의 느린 MA 값보다 크면, 이것은 빠른 MA가 느린 MA의 값을 위쪽으로 교차했다는 것을 의미합니다. 즉, 매수하라는 신호입니다. 만약 우리 조건이 참이라면 sig 변수에 1을 씁니다.

매도 신호 또한 비슷한 방식으로 형성됩니다. 만약 2번째 바에서의 빠른 MA 값이 1번째 바에서의 느린 MA 값보다 크고, 1번째 막대에서의 빠른 MA 값이 1번째 막대의 느린 MA값보다 작다면, 그것은 빠른 MA에 의한 느린 MA의 하향 횡단을 의미합니다. 만약 우리 조건이 참이라면 sig 변수에 -1을 씁니다. 만약 양쪽 조건이 모두 거짓 이라면 이것은 신호가 없다는 의미이므로 sig 변수에 0을 씁니다. 이제 신호가 형성되었으니 TradeSignal_01()함수에 신호 타입을 리턴합니다.

//--- check the condition and set a value for the sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);

3.2. MACD의 주 선과 신호 선의 교차점

2번 그림. MACD의 주 선과 신호 선의 교차점

TradeSignal_02() 함수를 통해 우리는 신호의 교차점과 MACD의 주요 라인에 대한 신호를 얻을 것입니다. 만약 신호 선이 위에서 아래로 주 선을 가로지르면 매수 신호입니다. 만약 신호 선이 아래에서 위로 주 선을 가로지르면 매도 신호입니다. 다른 케이스들은 모두 신호 없음으로 간주합니다.

int TradeSignal_02()
  {
   int sig=0;

   if(h_macd==INVALID_HANDLE)
     {
      h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_macd,0,0,2,macd1_buffer)<2)
         return(0);
      if(CopyBuffer(h_macd,1,0,3,macd2_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(macd1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(macd2_buffer,true))
         return(0);
     }

//--- check the condition and set a value for sig
   if(macd2_buffer[2]>macd1_buffer[1] && macd2_buffer[1]<macd1_buffer[1])
      sig=1;
   else if(macd2_buffer[2]<macd1_buffer[1] && macd2_buffer[1]>macd1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.3. 프라이스 채널 범위의 돌파

3번 그림. 프라이스 채널 상하한 경계 돌파

TradeSignal_03() 함수는 프라이스 채널의 상한 또는 하한 경계 돌파에 대한 신호를 수신합니다.

가격이 프라이스 채널 상단의 경계를 뚫고 가격이 이 경계선 위에 고정되면 매수하라는 신호탄입니다. 가격이 프라이스 채널 하단의 경계를 뚫고 가격이 이 경계선 밑에 고정되면 매도하라는 신호입니다. 다른 케이스들은 신호 없음 처리됩니다

이전의 두 함수들과 달리 이 경우엔 종가를 보관하기 위한 어레이가 필요합니다. 이하의 함수들을 사용하십시오:

int  CopyClose(
   string           symbol_name,       // symbol name
   ENUM_TIMEFRAMES  timeframe,          // period
   int              start_pos,         // where to start from 
   int              count,             // amount to be copied
   double           close_array[]      // array for copying close prices to
   );
int TradeSignal_03()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,3,pc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_pc,1,0,3,pc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the conditions and set a value for sig
   if(Close[1]>pc1_buffer[2])
      sig=1;
   else if(Close[1]<pc2_buffer[2])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.4. ADX 적응 채널 범위 돌파

4번 그림. ADX 적응 채널의 상하한 경계 돌파

TradeSignal_04() 함수를 통하여 우리는 ADX 적응 채널의 상한 또는 하한 경계 돌파에 대한 신호를 받아올 것입니다..

가격이 적응 채널 ADX의 상단 경계를 뚫고 종가가 이 경계선 위에 고정되면 매수하라는 신호입니다. 가격이 적응 채널 ADX의 하단 경계를 뚫고 종가가 이 경계선 밑에 고정되면 매도하라는 신호입니다. 다른 케이스들은 신호 없음 처리됩니다

int TradeSignal_04()
  {
   int sig=0;

   if(h_acadx==INVALID_HANDLE)
     {
      h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_acadx,0,0,2,acadx1_buffer)<2)
         return(0);
      if(CopyBuffer(h_acadx,1,0,2,acadx2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(acadx1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(acadx2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for the sig
   if(Close[1]>acadx1_buffer[1])
      sig=1;
   else if(Close[1]<acadx2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.5. 스토캐스틱의 과매수/과매도 영역 이탈

5번 그림. 스토캐스틱에 의한 과매수/과매도 교차.

TradeSignal_05()를 이용하여 과매수/과매도 영역을 이탈하는 것에 관한 신호를 받습니다; 해당 영역의 레벨은 20에서 80까지의 값을 가집니다.

오실레이터 (%K 혹은 %D) 가 특정 레벨 (일반적으로 20) 아래로 떨어졌다가 위로 치솟으면 매수합니다. 오실레이터 (%K 혹은 %D) 가 특정 레벨 (일반적으로 80) 위로 올라갔다가 아로 주저앉으면 매도합니다.

int TradeSignal_05()
  {
   int sig=0;

   if(h_stoh==INVALID_HANDLE)
     {
      h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_stoh,0,0,3,stoh_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(stoh_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(stoh_buffer[2]<20 && stoh_buffer[1]>20)
      sig=1;
   else if(stoh_buffer[2]>80 && stoh_buffer[1]<80)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.6. RSI의 과매수/과매도 영역 이탈

6번 그림. RSI 인디케이터에 의한 과매수 및 과매도 레벨 교차

TradeSignal_06() 함수를 이용하여 우리는 과매수/과매도 영역을 이탈하는 것에 관한 신호를 받습니다; 해당 영역의 레벨은 30에서 70까지의 값을 가집니다.

만약 RSI 가 특정 레벨 (일반적으로 30) 아래로 떨어졌다가 위로 올라가면 매수합니다. 마찬가지로, RSI가 특정 레벨 (일반적으로 70) 위로 솟구쳤다가 아래로 떨어지면 매도합니다.

int TradeSignal_06()
  {
   int sig=0;

   if(h_rsi==INVALID_HANDLE)
     {
      h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_rsi,0,0,3,rsi_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(rsi_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(rsi_buffer[2]<30 && rsi_buffer[1]>30)
      sig=1;
   else if(rsi_buffer[2]>70 && rsi_buffer[1]<70)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
 
3.7. CCI 과매수/과매도 영역 이탈

7번 그림. CCI 인디케이터에 의한 과매수 및 과매도 레벨 교차

TradeSignal_07()을 이용하여 우리는 과매수/과매도영역을 이탈하는 것에 관한 신호를 받습니다; 해당 영역의 레벨은 -100에서 100까지의 값을 가집니다.

만약 CCI 가 -100레벨 밑으로 하강했다가 다시 그 위로 상승하면 매수합니다. 마찬가지로, CCI 100레벨 위로 솟구쳤다가 다시 그 아래로 하강하면 매도합니다.

int TradeSignal_07()
  {
   int sig=0;

   if(h_cci==INVALID_HANDLE)
     {
      h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_cci,0,0,3,cci_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(cci_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(cci_buffer[2]<-100 && cci_buffer[1]>-100)
      sig=1;
   else if(cci_buffer[2]>100 && cci_buffer[1]<100)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.8. 윌리엄스 %의 과매수/과매도 영역 이탈

8번 그림. 윌리엄스 %인디케이터에 의한 과매수 및 과매도 교차

TradeSignal_08()을 이용하여 우리는 과매수/과매도영역에서의 Williams % 인디케이터 이탈에 관한 신호를 받습니다; 해당 영역의 레벨은 -20에서 -80까지의 값을 가집니다.

만약 윌리엄스 % 가 -80레벨 밑으로 하강했다가 다시 그 위로 상승하면 매수합니다. 마찬가지로 윌리엄스 %가 -20레벨 위로 상승했다가 다시 그 아래로 하강하면 매도합니다.

int TradeSignal_08()
  {
   int sig=0;

   if(h_wpr==INVALID_HANDLE)
     {
      h_wpr=iWPR(Symbol(),Period(),14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_wpr,0,0,3,wpr_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(wpr_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(wpr_buffer[2]<-80 && wpr_buffer[1]>-80)
      sig=1;
   else if(wpr_buffer[2]>-20 && wpr_buffer[1]<-20)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }
3.9. 볼린저 채널 경계에서의 바운스

9번 그림. 볼린저 채널의 경계에서의 매매가 바운스

TradeSignal_09()함수를 이용하여 우리는 볼린저 채널의 경계에서 매매가가 바운스 할 때 신호를 받습니다.

만약 매매가가 볼린저 채널의 위쪽 경계에 닿거나 돌파하고 다시 아래로 꺾이면 매도하라는 신호입니다. 만약 매매가가 볼린저 채널의 아래쪽 경계에 닿거나 돌파하면 매수하라는 신호입니다.

int TradeSignal_09()
  {
   int sig=0;

   if(h_bb==INVALID_HANDLE)
     {
      h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_bb,1,0,2,bb1_buffer)<2)
         return(0);
      if(CopyBuffer(h_bb,2,0,2,bb2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(bb1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(bb2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<=bb2_buffer[1] && Close[1]>bb2_buffer[1])
      sig=1;
   else if(Close[2]>=bb1_buffer[1] && Close[1]<bb1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.10. 표준편차 채널 경계에서의 바운스

10번 그림. 표준편차 채널의 경계에서의 매매가 바운스

TradeSignal_10()함수를 이용하여 우리는 표준편차 채널의 경계에서 매매가가 바운스 할 때 신호를 받습니다..

만약 매매가가 표준편차 채널의 위쪽 경계에 닿거나 돌파하고 다시 아래로 꺾이면 매도하라는 신호입니다. 만약 매매가가 표준편차 채널의 아래쪽 경계에 닿거나 돌파하면 매수하라는 신호입니다.

int TradeSignal_10()
  {
   int sig=0;

   if(h_sdc==INVALID_HANDLE)
     {
      h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sdc,0,0,2,sdc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sdc,1,0,2,sdc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sdc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sdc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<=sdc2_buffer[1] && Close[1]>sdc2_buffer[1])
      sig=1;
   else if(Close[2]>=sdc1_buffer[1] && Close[1]<sdc1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.11. 프라이스 채널 경계에서의 바운스

11번 그림. 프라이스 채널의 경계에서의 매매가 바운스

TradeSignal_11()함수를 이용하여 우리는 프라이스 채널의 경계에서 매매가가 바운스 할 때 신호를 받습니다.

만약 매매가가 프라이스 채널의 상단 경계에 닿거나 이를 돌파하고 다시 아래로 꺾이면 매도하라는 신호입니다. 만약 매매가가 프라이스 채널의 하단 경계에 닿거나 이를 돌파하면 매수하라는 신호입니다.

int TradeSignal_11()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,4,pc1_buffer)<4)
         return(0);
      if(CopyBuffer(h_pc,1,0,4,pc2_buffer)<4)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[1]>pc2_buffer[2] && Close[2]<=pc2_buffer[3])
      sig=1;
   else if(Close[1]<pc1_buffer[2] && Close[2]>=pc1_buffer[3])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.12. 엔벨로프 채널 경계에서의 바운스

12번 그림. 엔벨로프 채널의 경계에서의 매매가 바운스

TradeSignal_12()함수를 이용하여 우리는 엔벨로프 채널의 경계에서 매매가가 바운스 할 때 신호를 받습니다.

만약 매매가가 엔벨로프 채널의 상단 경계에 닿거나 이를 돌파하고 다시 아래로 꺾이면 매도하라는 신호입니다. 만약 매매가가 엔벨로프 채널의 하단 경계에 닿거나 이를 돌파하면 매수하라는 신호입니다.

int TradeSignal_12()
  {
   int sig=0;

   if(h_env==INVALID_HANDLE)
     {
      h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_env,0,0,2,env1_buffer)<2)
         return(0);
      if(CopyBuffer(h_env,1,0,2,env2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(env1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(env2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<=env2_buffer[1] && Close[1]>env2_buffer[1])
      sig=1;
   else if(Close[2]>=env1_buffer[1] && Close[1]<env1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.13. 돈치안 채널(Donchain Channel)의 돌파

13번 그림. 돈치안 채널의 경계 돌파

TradeSignal_13() 함수를 통해 매매가가 돈치안 채널의 경계를 돌파했을 때 신호를 받습니다.

만약 매매가가 돈치안 채널 상단 경계를 돌파하고 종가가 경계 위쪽에 형성된다면 이는 매수하라는 신호입니다. 만약 매매가가 돈치안 채널 하단 경계를 돌파하고 종가가 경계 아래쪽에 형성된다면 이는 매도하라는 신호입니다.

int TradeSignal_13()
  {
   int sig=0;

   if(h_dc==INVALID_HANDLE)
     {
      h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_dc,0,0,3,dc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_dc,1,0,3,dc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(dc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(dc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[1]>dc1_buffer[2])
      sig=1;
   else if(Close[1]<dc2_buffer[2])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.14. 실버 채널의 돌파

14번 그림. 실버 채널의 경계 돌파

TradeSignal_14() 함수를 통해 매매가가 실버 채널의 경계를 돌파했을 때 신호를 받습니다. 실버 채널 인디케이터는 8개의 경계를 그려 보조 및 저항 레벨 표시로 활용가능합니다. 신호를 받기 위해 중앙의 2개 경계를 사용할 것입니다. 

만약 매매가가 실버 채널 상단 경계를 돌파하고 종가가 이 경계 위쪽에 형성된다면 이는 매수하라는 신호입니다. 만약 매매가가 실버 채널 하단 경계를 돌파하고 종가가 이 경계 아래쪽에 형성된다면 이는 매도하라는 신호입니다.

int TradeSignal_14()
  {
   int sig=0;

   if(h_sc==INVALID_HANDLE)
     {
      h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sc,0,0,2,sc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sc,1,0,2,sc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[2]<sc1_buffer[1] && Close[1]>sc1_buffer[1])
      sig=1;
   else if(Close[2]>sc2_buffer[1] && Close[1]<sc2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.15. 갤러거 채널(Gallagher Channel)의 돌파

15번 그림. 갤러거 채널의 경계 돌파

TradeSignal_15() 채널에서 매매가가 갤러거 채널의 경계를 돌파했을 때 신호를 받습니다. 갤러거 채널의 인디케이터는 10일간 최소값과 최대값으로 그려집니다. 

만약 매매가가 갤러거 채널의 상단 경계를 돌파하고 종가가 이 경계 위에 형성된다면 이는 매수하라는 신호입니다. 만약 매매가가 갤러거 채널 하단 경계를 돌파하고 종가가 이 경계 아래쪽에 형성된다면 이는 매도하라는 신호입니다.

int TradeSignal_15()
  {
   int sig=0;

   if(h_gc==INVALID_HANDLE)
     {
      h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
      return(0);
     }
   else
     {
      if(CopyBuffer(h_gc,0,0,3,gc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_gc,1,0,3,gc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(gc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(gc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(Close[1]>gc1_buffer[2])
      sig=1;
   else if(Close[1]<gc2_buffer[2])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.16. NRTR에 의한 트렌드 변화

16번 그림. NRTR 인디케이터를 이용한 트렌드 변화 확인

TradeSignal_16() 함수에서 우리는 NRTR 트렌드가 변할 때 신호를 받습니다.

NRTR 인디케이터가 상승 트렌드를 보여준다면 이는 매수 신호입니다. NRTR 인디케이터가 하강 트렌드를 보여준다면 이는 매수 신호입니다.

int TradeSignal_16()
  {
   int sig=0;

   if(h_nrtr==INVALID_HANDLE)
     {
      h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_nrtr,0,0,2,nrtr1_buffer)<2)
         return(0);
      if(CopyBuffer(h_nrtr,1,0,2,nrtr2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(nrtr1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(nrtr2_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(nrtr1_buffer[1]>0)
      sig=1;
   else if(nrtr2_buffer[1]>0)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.17. 앨리게이터(Alligator)에 의한 트렌드 변화

17번 그림. 앨리게이터에 의한 트렌드 변화

TradeSignal_17() 함수에서 우리는 앨리게이터 트렌드가 변화할 때에 신호를 받을 것입니다.

만약 턱(Jaw), 이빨(Teeth), 입술(Lips)이 모두 닫히고 구부러졌다면 앨리게이터는 자러 갈 것이거나 이미 잠 든 상태입니다. 앨리게이터가 자고있는 중에는 점점 굶주리게 됩니다; 더 오래 잠들수록 일어났을 때의 허기가 커지게 됩니다. 앨리게이터가 일어나서 제일 먼저 하는 일은 입을 열고 하품하는 것입니다. 그 뒤로는 음식, 고기나 곰이나 황소, 의 냄새를 맡기 시작하는데 그 뒤로 바로 사냥을 시작합니다. 앨리게이터가 충분히 사냥을 마친 후에는 음식-매매가 (잔고선집중(Balance Lines Converge))에 흥미를 잃게되는데; 이때가 바로 이익을 실현할 때입니다.

int TradeSignal_17()
  {
   int sig=0;

   if(h_al==INVALID_HANDLE)
     {
      h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_al,0,0,2,al1_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,1,0,2,al2_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,2,0,2,al3_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(al1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al3_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(al3_buffer[1]>al2_buffer[1] && al2_buffer[1]>al1_buffer[1])
      sig=1;
   else if(al3_buffer[1]<al2_buffer[1] && al2_buffer[1]<al1_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.18. AMA에 의한 트렌드 변화

18번 그림. AMA에 의한 트렌드 변화A

TradeSignal_18()함수를 통해 우리는 AMA 트렌드가 변화할 때 신호를 받습니다.

만약 AMA 인디케이터가 위를 향한다면 이는 매수 신호입니다. 만약 AMA가 아래를 향한다면 이는 매도 신호입니다.

int TradeSignal_18()
  {
   int sig=0;

   if(h_ama==INVALID_HANDLE)
     {
      h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ama,0,0,3,ama_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(ama_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(ama_buffer[2]<ama_buffer[1])
      sig=1;
   else if(ama_buffer[2]>ama_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.19. 어썸 오실레이터(Awesome Oscillator) 색의 변화

19번 그림. 어썸 오실레이터 인디케이터를 이용한 트렌드 변화 인식

TradeSignal_19() 함수에서 우리는 어썸 오실레이터 히스토그램의 색이 변할 때에 신호를 받습니다.

MQL5의 기능중 하나는 인디케이터들을 위한 버퍼를 만들 수 있는 가능성인데 이 안에 #property indicator_colorN 속성에 설정된 선들의 색의 인덱스를 저장할 수 있습니다. 어썸 오실레이터 히스토그램의 색이 녹색이라면 이는 매수 신호입니다. 어썸 오실레이터 히스토그램의 색이 빨갛게 변한다면 이는 매도 신호입니다.

int TradeSignal_19()
  {
   int sig=0;

   if(h_ao==INVALID_HANDLE)
     {
      h_ao=iAO(Symbol(),Period());
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ao,1,0,20,ao_buffer)<20)
         return(0);
      if(!ArraySetAsSeries(ao_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(ao_buffer[1]==0)
      sig=1;
   else if(ao_buffer[1]==1)
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }

3.20. 일목(Ichimoku)에 의한 트렌드 변화

20번 그림. 일목 인디케이터를 이용한 트렌드 변화 확인

TradeSignal_20() 함수에서 우리는 일목 트렌드가 변화했을 때 신호를 받습니다. 이를 위해 우리는 전환선들과 기준선들의 교차점을 분석할 것입니다.

전환선이 기준선을 아래에서 위로 교차할 때에 매수 신호가 발생합니다. 위에서 아래로의 교차는 매도 신호입니다.

int TradeSignal_20()
  {
   int sig=0;

   if(h_ich==INVALID_HANDLE)
     {
      h_ich=iIchimoku(Symbol(),Period(),9,26,52);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ich,0,0,2,ich1_buffer)<2)
         return(0);
      if(CopyBuffer(h_ich,1,0,2,ich2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(ich1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(ich2_buffer,true))
         return(0);
     }
//--- check the condition and set a value for sig
   if(ich1_buffer[1]>ich2_buffer[1])
      sig=1;
   else if(ich1_buffer[1]<ich2_buffer[1])
      sig=-1;
   else sig=0;

//--- return the trade signal
   return(sig);
  }


4. 인디케이터로 만들기

이제는 미리 만들어진 인디케이터 블록들을 가지고 있기 때문에, 선택된 모든 방법에 기초하여 신호를 보여주는 인디케이터를 쓰기 시작할 수 있습니다. 생성된 템플릿을 사용하면 모든 인디케이터로부터의 신호 수신을 구현할 수 있습니다. 신호 조건을 올바르게 공식화하고 코드에 추가하기에 충분합니다.

유연성이 떨어지는 내장 패러미터를 사용하여 인디케이터를 써 보겠습니다 인디케이터의 신호는 차트 오른쪽에 화살표(위로 화살표 - 매수, 아래로 화살표 - 매도, 교차 - 신호 없음) 형태로 그려집니다. 기본 Wingdings 폰트를 사용하여 화살표를 그려봅시다. 또한 심볼 차트에 신호에 대한 정보를 표시하기 위한 몇 가지 다른 함수를 만들어야 합니다. 새로운 기능을 추가하여 프로그램 작성 시 사용할 수 있는 라이브러리로 별도 블록으로 결합하겠습니다. 이 라이브러리를 LibFunctions라고 명명합시다.

앞으로 쓸 인디케이터의 헤더에 신호 생성을 위한 함수, 시그널을 시각적으로 보여주기 위한 임포트 함수와 파일의 연결을 작성하고나서 글로벌 스케이프에 인디케이터로부터 받은 신호의 타입을 저장할 변수를 선언하십시오.

//--- Connect necessary libraries of functions
#include <SignalTrade.mqh>
//--- Import of functions from the LibFunctions library
#import "LibFunctions.ex5"
void SetLabel(string nm,string tx,ENUM_BASE_CORNER cn,ENUM_ANCHOR_POINT cr,int xd,int yd,string fn,int fs,double yg,color ct);
string arrow(int sig);
color Colorarrow(int sig);
#import
//+------------------------------------------------------------------+
//| Declare variables for storing signals of indicators              |
//+------------------------------------------------------------------+
int SignalMA;
int SignalMACD;
int SignalPC;
int SignalACADX;
int SignalST;
int SignalRSI;
int SignalCCI;
int SignalWPR;
int SignalBB;
int SignalSDC;
int SignalPC2;
int SignalENV;
int SignalDC;
int SignalSC;
int SignalGC;
int SignalNRTR;
int SignalAL;
int SignalAMA;
int SignalAO;
int SignalICH;

예전에 언급한 적 있는 내용이지만 인디케이터는 인디케이터를 가리키는 포인터 (핸들)이 생성될 때에 딱 한번만 터미널에 로딩됩니다; 그 때문에 프로그램 실행시에 딱 한 번 호출되는 OnInit() 함수에 그 생성전반을 넣는 것입니다.

int OnInit()
  {
//--- create indicator handles
   h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
   h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
   h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
   h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
   h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
   h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
   h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
   h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
   h_wpr=iWPR(Symbol(),Period(),14);
   h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
   h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
   h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
   h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
   h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
   h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
   h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
   h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
   h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
   h_ao=iAO(Symbol(),Period());
   h_ich=iIchimoku(Symbol(),Period(),9,26,52);
   return(0);
  }

OnCalculate() 함수에서 모든 주요 계산이 처리되었고; 거기에 인디케이터의 나머지 코드를 둘 것입니다.

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[])
  {
//---assign the signal value to the variable
   SignalMA    = TradeSignal_01();
   SignalMACD  = TradeSignal_02();
   SignalPC    = TradeSignal_03();
   SignalACADX = TradeSignal_04();
   SignalST    = TradeSignal_05();
   SignalRSI   = TradeSignal_06();
   SignalCCI   = TradeSignal_07();
   SignalWPR   = TradeSignal_08();
   SignalBB    = TradeSignal_09();
   SignalSDC   = TradeSignal_10();
   SignalPC2   = TradeSignal_11();
   SignalENV   = TradeSignal_12();
   SignalDC    = TradeSignal_13();
   SignalSC    = TradeSignal_14();
   SignalGC    = TradeSignal_15();
   SignalNRTR  = TradeSignal_16();
   SignalAL    = TradeSignal_17();
   SignalAMA   = TradeSignal_18();
   SignalAO    = TradeSignal_19();
   SignalICH   = TradeSignal_20();

//--- draw graphical objects on the chart in the upper left corner
   int size=((int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS)/22);
   int i=0;
   int x=10;
   int y=0;
   int fz=size-4;

   y+=size;
   SetLabel("arrow"+(string)i,arrow(SignalMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMA));
   x+=size;
   SetLabel("label"+(string)i,"Moving Average",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalMACD),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMACD));
   x+=size;
   SetLabel("label"+(string)i,"MACD",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalACADX),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalACADX));
   x+=size;
   SetLabel("label"+(string)i,"Adaptive Channel ADX",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalST),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalST));
   x+=size;
   SetLabel("label"+(string)i,"Stochastic Oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalRSI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalRSI));
   x+=size;
   SetLabel("label"+(string)i,"RSI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalCCI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalCCI));
   x+=size;
   SetLabel("label"+(string)i,"CCI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalWPR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalWPR));
   x+=size;
   SetLabel("label"+(string)i,"WPR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalBB),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalBB));
   x+=size;
   SetLabel("label"+(string)i,"Bollinger Bands",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSDC));
   x+=size;
   SetLabel("label"+(string)i,"StDevChannel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC2),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC2));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell 2",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalENV),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalENV));
   x+=size;
   SetLabel("label"+(string)i,"Envelopes",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalDC));
   x+=size;
   SetLabel("label"+(string)i,"Donchian Channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSC));
   x+=size;
   SetLabel("label"+(string)i,"Silver-channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalGC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalGC));
   x+=size;
   SetLabel("label"+(string)i,"Galaher Channel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalNRTR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalNRTR));
   x+=size;
   SetLabel("label"+(string)i,"NRTR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAL),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAL));
   x+=size;
   SetLabel("label"+(string)i,"Alligator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAMA));
   x+=size;
   SetLabel("label"+(string)i,"AMA",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAO),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAO));
   x+=size;
   SetLabel("label"+(string)i,"Awesome oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalICH),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalICH));
   x+=size;
   SetLabel("label"+(string)i,"Ichimoku Kinko Hyo",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);

   return(rates_total);
  }

인디케이터가 준비됐네요. 다 하고나면 차트에 다음 그림이 나올 겁니다



5. Expert Advisor로 만들기

유사한 방법으로 차트에 지표의 신호를 보여주는 Expert Advisor를 작성할 수 있습니다. 그래픽 제어 요소를 갖춘 정보 시스템을 구현해 보겠습니다. 필요한 인디케이터 선택하고 그래픽 인터페이스를 통해 표시기를 설정할 수 있습니다.


여기서는 그래픽 인터페이스를 구현하는 방법에 대해서는 다루지 않을 것입니다; 해당 정보는 MQL5으로 매매를 위한 액티브 제어판 만들기 문서를 참조해 주십시오.

인디케이터 세팅을 그래픽 인터페이스를 통해서 변경하기 위하여 우리의 SignalTrade.mqh library 라이브러리를 개선한 후 SignalTradeExp.mqh 라고 명명합시다.

먼저 인디케이터 세팅을 저장할 추가적인 변수들이 필요합니다.

//--- input parameters Moving Average
int                periodma1=8;
int                periodma2=16;
ENUM_MA_METHOD     MAmethod=MODE_SMA;
ENUM_APPLIED_PRICE MAprice=PRICE_CLOSE;
//--- input parameters MACD
int                FastMACD=12;
int                SlowMACD=26;
int                MACDSMA=9;
ENUM_APPLIED_PRICE MACDprice=PRICE_CLOSE;
//--- input parameters Price Channel
int                PCPeriod=22;
//--- input parameters Adaptive Channel ADX
int                ADXPeriod=14;
//--- input parameters Stochastic Oscillator
int                SOPeriodK=5;
int                SOPeriodD=3;
int                SOslowing=3;
ENUM_MA_METHOD     SOmethod=MODE_SMA;
ENUM_STO_PRICE     SOpricefield=STO_LOWHIGH;
//--- input parameters RSI
int                RSIPeriod=14;
ENUM_APPLIED_PRICE RSIprice=PRICE_CLOSE;
//--- input parameters CCI
int                CCIPeriod=14;
ENUM_APPLIED_PRICE CCIprice=PRICE_TYPICAL;
//--- input parameters WPR
int                WPRPeriod=14;
//--- input parameters Bollinger Bands
int                BBPeriod=20;
double             BBdeviation=2.0;
ENUM_APPLIED_PRICE BBprice=PRICE_CLOSE;
//--- input parameters Standard Deviation Channel
int                SDCPeriod=14;
double             SDCdeviation=2.0;
ENUM_APPLIED_PRICE SDCprice=PRICE_CLOSE;
ENUM_MA_METHOD     SDCmethod=MODE_SMA;
//--- input parameters Price Channel 2
int                PC2Period=22;
//--- input parameters Envelopes
int                ENVPeriod=14;
double             ENVdeviation=0.1;
ENUM_APPLIED_PRICE ENVprice=PRICE_CLOSE;
ENUM_MA_METHOD     ENVmethod=MODE_SMA;
//--- input parameters Donchian Channels
int                DCPeriod=24;
int                DCExtremes=3;
int                DCMargins=-2;
//--- input parameters Silver-channels
int                SCPeriod=26;
double             SCSilvCh=38.2;
double             SCSkyCh=23.6;
double             SCFutCh=61.8;
//--- input parameters NRTR
int                NRTRPeriod   =  40;
double             NRTRK        =  2.0;
//--- input parameters Alligator
int                ALjawperiod=13;
int                ALteethperiod=8;
int                ALlipsperiod=5;
ENUM_MA_METHOD     ALmethod=MODE_SMMA;
ENUM_APPLIED_PRICE ALprice=PRICE_MEDIAN;
//--- input parameters AMA
int                AMAperiod=9;
int                AMAfastperiod=2;
int                AMAslowperiod=30;
ENUM_APPLIED_PRICE AMAprice=PRICE_CLOSE;
//--- input parameters Ichimoku Kinko Hyo
int                IKHtenkansen=9;
int                IKHkijunsen=26;
int                IKHsenkouspanb=52;

인디케이터의 상수 값을 변수로 대체하십시오. 나머지는 그대로 두십시오.

h_ma1=iMA(Symbol(),Period(),periodma1,0,MAmethod,MAprice);

중요한 점은 컴퓨터 메모리의 효율적인 사용입니다. 설정을 변경할 때는 이전 설정으로 인디케이터 복사본을 언로드하고 새 것을 로드해야 합니다. 이것은 이하의 함수를 통해 할 수 있습니다:

bool  IndicatorRelease(
   int       indicator_handle,     // indicator handle
   );
   if(id==CHARTEVENT_OBJECT_ENDEDIT && sparam=="PIPSetEditMA2")
     {
      periodma2=(int)ObjectGetString(0,"PIPSetEditMA2",OBJPROP_TEXT);
      ObjectSetString(0,"PIPSetEditMA2",OBJPROP_TEXT,(string)periodma2);
      //--- unload old copy of the indicator
      IndicatorRelease(h_ma2);
      //--- create new copy of the indicator
      h_ma2=iMA(Symbol(),Period(),periodma2,0,MAmethod,MAprice);
      ChartRedraw();
     }

마치며

지금까지 우리는 어떻게 인디케이터에서 정보를 읽고, 이를 Expert Advisor에 넘기는지에 대해 알아봤습니다. 그런 식으로 모든 인디케이터에서 신호를 받아올 수 있죠.

노트

  • SignalTrade.mq5, AdaptiveChannelADX.mq5, Donchian Channels.mq5, NRTR.mq5, Price Channel.mq5, PriceChannelGalaher.mq5, Silver-channels.mq5, StandardDeviationChannel.mq5 인디케이터 파일들은 ...\MQL5\Indicators 폴더에 복사되어야합니다.
  • SignalTrade.mqh 및 SignalTradeExp.mqh include 파일들은 ...\MQL5\Include 폴더에 복사되어야합니다.
  • LibFunctions.mq5 함수 라이브러리는 ...\MQL5\Libraries 폴더에 복사되어야합니다.
  • ExpSignalTrade.mq5 Expert Advisor는 ...\MQL5\Experts에 복사되어야합니다.

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

파일 첨부됨 |
indicators.zip (8.9 KB)
expsignaltrade.mq5 (165.84 KB)
signaltrade.mq5 (10.57 KB)
signaltrade.mqh (20.16 KB)
signaltradeexp.mqh (23.26 KB)
매매봇 프로토타입 매매봇 프로토타입
본 문서는 매매 시스템의 알고리즘과 요소들을 만드는 원리를 요약하고 체계화합니다. 본 문서는 익스퍼트 알고리즘 디자인을 다룹니다. 예시로서 빠르고 손쉬운 매매 시스템에 쓰일 수 있는 CExpertAdvisor 클래스가 사용될 것입니다.
다른 인디케이터 기반으로 인디케이터를 쓰는 방법에 관하여 다른 인디케이터 기반으로 인디케이터를 쓰는 방법에 관하여
MQL5은 인디케이터를 백지로부터 만들어갈 수 있게도 해주지만, 클라이언트 터미널에 이미 빌트인 된 것이나 커스텀 인디케이터 등 이미 존재하는 다른 인디케이터 기반으로 만들 수 있는 옵션 또한 제공합니다. 고르고 나면 여기서도 두가지 선택지가 있습니다 - 새 계산이나 그래픽 스타일을 추가하는 방식으로 인디케이터를 개선하는 것, 그리고 iCustom() 이나 IndicatorCreate() 함수를 써서 클라이언트 터미널에 내장된 것이나 커스텀 인디케이터를 쓰는 것.
MetaTrader 5에서의 거래 이벤트 MetaTrader 5에서의 거래 이벤트
거래 계정의 현재 상태를 모니터링한다는 것은 오픈 포지션과 주문을 통제한다는 것을 의미합니다. 거래 신호가 진정한 거래가 되기 전, 이는 거래 서버로의 요청으로써 클라이언트 터미널에서 전송되어야 하며, 이 때 거래 서버는 처리 대기 중인 주문 대기열에 배치될 것입니다. 거래 서버에 의한 수락하거나, 만료 시 삭제하거나, 거래 기준으로 거래를 수행하는 등과 관련된 이러한 모든 조치에는 거래 이벤트가 뒤따르게 되고, 이 때 거래 서버는 터미널에 이런 사항에 대해 알려주게 됩니다.
Expert Advisor에서의 자금 관리용 함수들 Expert Advisor에서의 자금 관리용 함수들
거래 전략의 개발은 주로 시장 진입과 퇴출을 위한 패턴을 찾는 것뿐만 아니라 포지션을 유지하는 것에 초점을 맞추고 있습니다. 만약 일부 패턴을 자동 트레이딩을 위한 공식으로 만들 수 있다면, 투자자는 자동 투자 모드에서 오픈 포지션을 보장하기 위해서 안전한 수준의 모기지 자금뿐만 아니라 포지션의 양, 마진의 크기를 계산해야하는 문제에 직면하게 됩니다. 이 글에서 우리는 그러한 계산을 할 수 있는 간단한 예시를 보이기 위해 MQL5 언어를 사용할 것입니다.