English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
MQL5를 사용하여 맞춤형 실제 강도 지수 지표를 만드는 방법

MQL5를 사용하여 맞춤형 실제 강도 지수 지표를 만드는 방법

MetaTrader 5트레이딩 | 10 4월 2025, 06:50
356 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

소개

기술적 지표는 가격 움직임만으로는 파악하기 어려운 추가적인 인사이트를 우리에게 줄 수 있으므로 적절히 활용하면 매우 유용할 수 있습니다. 우리가 사용할 수 있는 기술 지표는 이미 많이 있습니다. 하지만 때로는 특정 인사이트를 표시하거나 나타내도록 맞춤형을 만들어야 하거나 좋은 아이디어를 바탕으로 새로운 지표를 만들어야 하는 경우도 있습니다. MQL5에는 이러한 맞춤형 지표를 만들어 MetaTrader 5 거래 플랫폼에서 사용할 수 있는 방법이 있습니다. 이 글에서 저는 실제 강도 지수 지표를 처음부터 만드는 방법을 알려드리겠습니다. 우리는 이 지표의 세부 사항을 알아보고 코드에서 이를 계산하는 방법을 살펴볼 것입니다. 뿐만 아니라 Expert Advisor를 만들고 우리의 트레이딩 전략에 따라 이 맞춤형 지표를 트레이딩 시스템에서 사용하는 방법을 배워볼 것입니다. 다음과 같은 주제를 통해 이 모든 것을 다뤄보겠습니다:

여러분이 이들 주제에서 다루는 내용을 이해하면 실제 강도 지수 지표를 사용하고 해석하는 방법을 잘 이해할 수 있을 것입니다. 그리고 이 지표를 계산하고 MetaTrader 5에서 사용할 MQL5 언어의 맞춤형 지표로 코딩 할 수 있습니다. 여러분은 다른 트레이딩 시스템이나 EA에서도 지표를 구현할 수 있을 것입니다. 여러분의 코딩 실력을 키우고 싶다면 직접 코딩을 해보는 것을 추천합니다. 이는 학습 과정이나 로드맵에서 매우 중요한 과정입니다. MetaTrader 5 거래 터미널에 내장된 IDE에서 MQL5 코드를 작성하기 위해 우리는 MetaTrader5를 사용할 것입니다. 플랫폼이 없거나 다운로드 및 사용 방법을 모르는 경우 저의 이전 글 'MetaEditor에서 MQL5 코드 작성하기' 란 내용을 참고하세요.

면책: 모든 정보는 정보 제공의 목적으로만 '있는 그대로' 제공되며 거래의 목적이나 조언을 위해 준비된 것이 아닙니다. 여기의 정보는 어떤 종류의 결과도 보장하지 않습니다. 귀하의 거래 계정에서 이들 자료를 사용하기로 한 경우 귀하는 이에 대한 위험을 감수해야 하며 이에 대한 책임은 오직 귀하에게만 있습니다.


실제 강도 지수(TSI) 정의

이 부분에서는 실제 강도 지수(TSI)를 제대로 이해하기 위해 이 기술 지표를 파악해 보겠습니다. 이 지표는 윌리엄 블라우가 가격 움직임의 모멘텀을 측정하는 기술적 지표로 개발했습니다. 즉 이 지표는 상품의 강세 또는 약세 여부를 측정하는 것으로 0선을 중심으로 진동하므로 모멘텀 오실레이터 지표입니다. 신호선을 함께 사용하면 이 신호선 간의 크로스오버를 기반으로 추가 매수 또는 매도 신호를 얻을 수 있습니다. 또한 TSI 라인과 제로 레벨과의 크로스오버를 기준으로 신호를 얻을 수도 있습니다. 0선 이상이면 강세 모멘텀, 0선 미만이면 약세 모멘텀을 의미합니다. 과매수 및 과매도 영역을 감지하고 강세 및 약세 다이버전스를 감지하는 데 사용할 수도 있습니다. 우리는 지표의 신뢰의 가중치를 높이기 위해 신호를 확인해야 한다는 것을 알고 있습니다. 그러므로 더 나은 통찰력을 얻으려면 가격의 움직임과 동일한 맥락에서 사용해야 하며 다른 기술 도구와 함께 사용하는 것도 좋습니다.

이제 이 지표를 계산하는 방법을 살펴보겠습니다. 계산은 다음과 같은 여러 단계로 수행됩니다:

이중 평활 모멘텀을 계산합니다:

  • 현재 가격에서 이전 가격을 빼서 모멘텀(가격 변동)을 계산합니다.
  • 계산된 모멘텀의 25주기 EMA를 구하여 첫 번째 평활화를 계산합니다.
  • 첫 번째 평활화의 13주기 EMA(계산된 모멘텀의 25주기 EMA)를 구하여 두 번째 평활화를 계산합니다.

이중 평활화 된 절대 모멘텀을 계산합니다:

  • 절대 현재 가격에서 절대 이전 가격을 빼서 절대 모멘텀을 계산합니다.
  • 계산된 절대 모멘텀의 25주기 EMA를 구하여 첫 번째 평활화를 계산합니다.
  • 첫 번째 평활화의 13주기 EMA(계산된 절대 모멘텀의 25주기 EMA)를 구하여 두 번째 평활화를 계산합니다,

TSI = 100*((이중 평활 모멘텀 / 이중 평활 절대 모멘텀) 계산

이 계산을 통해 우리는 가격 움직임의 모멘텀을 측정합니다. 앞서 언급한 것처럼 지표는 과매수 및 과매도 영역을 감지하는 0을 중심으로 진동하는 선으로 나타납니다.


맞춤형 단순 TSI 지표

MQL5 프로그래밍 언어에는 미리 정의된 기술 지표가 많이 있으며 미리 정의된 함수를 사용하여 시스템에서 이들을 사용할 수 있습니다. 이미 이 시리즈의 이전 글에서 우리는 이러한 인기 있는 보조지표에 기반한 트레이딩 시스템을 설계하는 방법에 대해 알아보았습니다. 여러분 모두 이전의 글을 확인하여 유용한 정보를 찾을 수 있을 것입니다. 이제 문제는 플랫폼이 제공하는 기본 지표 패키지에 존재하지 않는 지표를 어떻게 만들 수 있는지, 또는 존재하더라도 원하는 우리가 신호나 트리거를 얻기 위해 어떻게 맞춤형으로 지표를 만들 수 있는지 입니다. 간단한 답변은 이럴 경우 우리가 기본 프로그래밍 언어를 사용하여 맞춤형 지표를 만드는 것입니다.

이제 우리는 MQL5를 사용하여 맞춤형 실제 강도 지수 지표를 만드는 방법을 배워 보겠습니다. 그런 다음 다른 시스템이나 EA에서 그 기능을 사용할 것입니다. 이 맞춤형 지표를 만드는 단계는 다음과 같습니다.

#property를 사용하여 추가 파라미터를 생성하고 그 옆에 식별자 값을 지정할 것입니다. 다음과 같이 말입니다:

  • (indicator_separate_window) - 지표를 별도의 창에 표시합니다.
  • (indicator_buffers) - 지표의 버퍼 수를 지정합니다, 우리는 (8)을 지정합니다.
  • (indicator_plots) - 지표의 그래픽 시리즈 수를 지정합니다, 우리는 (1)을 지정합니다.
  • (indicator_label1) - 그래픽 시리즈 번호에 대한 레이블을 설정합니다, 우리는 (TSI)를 지정합니다.
  • (indicator_type1) - ENUM_DRAW_TYPE 값에서 값을 지정하여 그래픽 플로팅의 유형을 지정합니다, 우리는 (DRAW_LINE)을 지정합니다.
  • (indicator_color1) - 표시되는 지표 라인의 색상을 지정합니다, 우리는 (clrBlue)를 지정합니다.
  • (indicator_style1) - 지표 라인의 스타일을 지정합니다, 우리는 (STYLE_SOLID)를 지정합니다.
  • (indicator_width1) - 지표의 선 두께를 지정합닏, 우리는 (3)을 지정합니다.
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_plots   1
#property indicator_label1  "TSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  3

우리는 MovingAverage.mqh 파일을 포함하여 계산에 해당 컴포넌트를 사용해야 합니다. 이 파일은 #include 명령을 사용하여 Include 파일에 존재합니다. 파일 이름을 파일 이름과 동일하게 작성해야 합니다.

#include <MovingAverages.mqh>

프로그램에 지정된 기본값을 업데이트해야 하는 경우 입력 클래스를 사용하여 사용자가 이 값을 입력하여 지표 계산에 사용할 평활 기간의 두 가지 입력을 설정해야 합니다. 그런 다음 부호가 없는 정수인 uint를 사용하여 선언해야 하는 변수(InpSmPeriod1, InpSmPeriod2)의 데이터 유형을 결정합니다. 그런 다음 기본값으로 InpSmPeriod1에는 (25)를, InpSmPeriod2에는 (13)을 할당합니다. 

input uint     InpSmPeriod1   =  25;    // Smoothing period 1
input uint     InpSmPeriod2   =  13;   // Smoothing period 2

평활화 기간으로 사용할 두 개의 정수 변수 만들기(smperiod1,smperiod2).

int            smperiod1;
int            smperiod2;

지표 버퍼로 사용할 7개의 배열 만들기

double         indBuff[];
double         momBuff[];
double         momSmBuff1[];
double         momSmBuff2[];
double         absMomBuff[];
double         absMomSmBuff1[];
double         absMomSmBuff2[];

OnInit () 함수 내에서 우리는 다음 단계를 수행할 것입니다:

InpSmPeriod1과 InpSmPeriod2의 사용자 입력값이 2보다 작으면 2 값을 반환하고 , 다른 값이 있으면 InpSmPeriod1과 InpSmPeriod2 값을 반환하여 (smperiod1,smperiod2) 의 변수를 선언합니다.

   smperiod1=int(InpSmPeriod1<2 ? 2 : InpSmPeriod1);
   smperiod2=int(InpSmPeriod2<2 ? 2 : InpSmPeriod2);

지표 버퍼를 배열로 바인딩하는 (SetIndexBuffer) 함수를 사용하여 지표 버퍼를 바인딩합니다. 매개변수는 다음과 같습니다:

index: 프로그램에서 지표 버퍼의 개수와 0~7로 시작하는 숫자를 설정합니다.

buffer[]: 맞춤형 지표에 선언된 배열을 지정합니다.

data_type: 지표 배열에 저장된 데이터의 유형을 지정합니다.

   SetIndexBuffer(0,indBuff,INDICATOR_DATA);
   SetIndexBuffer(2,momBuff,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3,momSmBuff1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(4,momSmBuff2,INDICATOR_CALCULATIONS);
   SetIndexBuffer(5,absMomBuff,INDICATOR_CALCULATIONS);
   SetIndexBuffer(6,absMomSmBuff1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(7,absMomSmBuff2,INDICATOR_CALCULATIONS);

해당 지표의 속성 값을 설정하는 경우 이 지표 속성(prop 값)은 속성 식별자만 지정하여 호출하는 변형과 함께 (IndicatorSetString) 함수를 사용하여 나오는 문자열 유형이어야 합니다. 이 단계에서는 지표의 간단한 이름을 설정하고 지표 창의 왼쪽 상단에 표시할 기간을 결정합니다. 매개변수:

  • prop_id: (ENUM_CUSTOMIND_PROPERTY_STRING) 열거형 중 하나인 지표의 속성의 식별자를 지정합니다. 우리의 프로그램에서는 (INDICATOR_SHORTNAME)이 됩니다.
  • prop_value: 문자열 데이터 유형이 될 속성 값을 지정합니다. "True Strength Index ("+(string)smperiod1+","+(string)smperiod2+")"가 됩니다.
IndicatorSetString(INDICATOR_SHORTNAME,"True Strength Index ("+(string)smperiod1+","+(string)smperiod2+")");

지표의 다른 값을 정수 데이터 유형으로 설정하여 속성 식별자만 지정하여 변형된 호출로 (IndicatorSetInteger)를 사용하여 자릿수에 따라 지표의 값을 정규화할 수 있습니다. 매개 변수 

  • prop_id: (ENUM_CUSTOMIND_PROPERTY_INTEGER) 열거형 중 하나인 지표 속성의 식별자를 지정합니다. 우리의 프로그램에서는 (INDICATOR_DIGITS)가 됩니다.
  • prop_value: 정수 데이터 유형이 될 속성의 값을 지정합니다. 우리의 경우 Digits()가 됩니다.
IndicatorSetInteger(INDICATOR_DIGITS,Digits());

(ArraySetAsSeries)를 사용하여 배열에 AS_SERIES 플래그를 설정합니다.

   ArraySetAsSeries(indBuff,true);
   ArraySetAsSeries(momBuff,true);
   ArraySetAsSeries(momSmBuff1,true);
   ArraySetAsSeries(momSmBuff2,true);
   ArraySetAsSeries(absMomBuff,true);
   ArraySetAsSeries(absMomSmBuff1,true);
   ArraySetAsSeries(absMomSmBuff2,true);

OnCalculate 함수 뒤에

  • OnCalculate 부분에서 AS_SERIES 플래그를 닫힌 배열로 설정한 다음 rates_total이 2보다 작은지 확인하여 0을 반환합니다.
  • (rates_total-prev_calculated)와 같은 정수 변수(limit)를 생성합니다.
  • limit 변수가 1보다 큰지 확인하고 다음 단계를 수행해야 합니다:
  • (rates_total - 2)의 결과로 limit 변수를 업데이트합니다.
  • (ArrayInitialize) 함수를 사용하여 미리 설정된 값으로 더블 타입의 숫자 배열을 초기화합니다. 이 매개변수들은 array[]로 초기화될 숫자 배열을 지정하고 다른 매개변수는 value로 새로 설정될 값을 지정합니다.
   ArraySetAsSeries(close,true);
   if(rates_total<2)
      return 0;

   int limit=rates_total-prev_calculated;
   if(limit>1)
     {
      limit=rates_total-2;
      ArrayInitialize(indBuff,EMPTY_VALUE);
      ArrayInitialize(momBuff,0);
      ArrayInitialize(momSmBuff1,0);
      ArrayInitialize(momSmBuff2,0);
      ArrayInitialize(absMomBuff,0);
      ArrayInitialize(absMomSmBuff1,0);
      ArrayInitialize(absMomSmBuff2,0);
     }

momBuff[i], absMomBuff[i]를 업데이트하는 루프를 생성합니다. 이 단계에서 사용된 새로운 함수들은 다음과 같습니다:

  • (for) 루프 연산과 이 연산의 세 가지 표현식 및 실행 연산자.
  • IsStopped()은 mql5 프로그램이 강제 종료되었는지 확인합니다.
  • MathAbs은 절대값(modulus)을 반환할 수 있으며 동일한 결과를 얻기 위해 febs() 함수를 사용할 수 있습니다.
   for(int i=limit; i>=0 && !IsStopped(); i--)
     {
      momBuff[i]=close[i]-close[i+1];
      absMomBuff[i]=MathAbs(momBuff[i]);
     }

(MovingAverage) Include 파일에서 ExponentialMAOnBuffer 함수를 사용하여 다음을 확인합니다.

   if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,momBuff,momSmBuff1)==0)
      return 0;
   if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,absMomBuff,absMomSmBuff1)==0)
      return 0;
   if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,momSmBuff1,momSmBuff2)==0)
      return 0;
   if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,absMomSmBuff1,absMomSmBuff2)==0)
      return 0;

for 함수를 사용하여 indBuff[i] 변수를 업데이트하는 다른 루프를 생성합니다.

   for(int i=limit; i>=0 && !IsStopped(); i--)
      indBuff[i]=(absMomSmBuff2[i]!=0 ? 100.0*momSmBuff2[i]/absMomSmBuff2[i] : 0);

프로그램 마지막에 수익률(rates_total) 함수가 있습니다.

   return(rates_total);

이제 TSI 맞춤형 지표를 만들기 위한 코드를 완성했습니다. 여러분은 더 많은 맞춤을 하기 위해 코드의 기본 설정을 편집할 수도 있습니다. 다음은 이렇게 생성된 지표의 한 블록에 포함된 전체 코드입니다:

//+------------------------------------------------------------------+
//|                                                   simple TSI.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_plots   1
#property indicator_label1  "TSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  3
#include <MovingAverages.mqh>
input uint     InpSmPeriod1   =  25;    // Smoothing period 1
input uint     InpSmPeriod2   =  13;   // Smoothing period 2
int            smperiod1;
int            smperiod2;
double         indBuff[];
double         momBuff[];
double         momSmBuff1[];
double         momSmBuff2[];
double         absMomBuff[];
double         absMomSmBuff1[];
double         absMomSmBuff2[];
int OnInit()
  {
   smperiod1=int(InpSmPeriod1<2 ? 2 : InpSmPeriod1);
   smperiod2=int(InpSmPeriod2<2 ? 2 : InpSmPeriod2);
   SetIndexBuffer(0,indBuff,INDICATOR_DATA);
   SetIndexBuffer(2,momBuff,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3,momSmBuff1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(4,momSmBuff2,INDICATOR_CALCULATIONS);
   SetIndexBuffer(5,absMomBuff,INDICATOR_CALCULATIONS);
   SetIndexBuffer(6,absMomSmBuff1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(7,absMomSmBuff2,INDICATOR_CALCULATIONS);
   IndicatorSetString(INDICATOR_SHORTNAME,"True Strength Index ("+(string)smperiod1+","+(string)smperiod2+")");
   IndicatorSetInteger(INDICATOR_DIGITS,Digits());
   ArraySetAsSeries(indBuff,true);
   ArraySetAsSeries(momBuff,true);
   ArraySetAsSeries(momSmBuff1,true);
   ArraySetAsSeries(momSmBuff2,true);
   ArraySetAsSeries(absMomBuff,true);
   ArraySetAsSeries(absMomSmBuff1,true);
   ArraySetAsSeries(absMomSmBuff2,true);
   return(INIT_SUCCEEDED);
  }
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[])
  {
   ArraySetAsSeries(close,true);
   if(rates_total<2)
      return 0;

   int limit=rates_total-prev_calculated;
   if(limit>1)
     {
      limit=rates_total-2;
      ArrayInitialize(indBuff,EMPTY_VALUE);
      ArrayInitialize(momBuff,0);
      ArrayInitialize(momSmBuff1,0);
      ArrayInitialize(momSmBuff2,0);
      ArrayInitialize(absMomBuff,0);
      ArrayInitialize(absMomSmBuff1,0);
      ArrayInitialize(absMomSmBuff2,0);
     }
   for(int i=limit; i>=0 && !IsStopped(); i--)
     {
      momBuff[i]=close[i]-close[i+1];
      absMomBuff[i]=MathAbs(momBuff[i]);
     }
   if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,momBuff,momSmBuff1)==0)
      return 0;
   if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,absMomBuff,absMomSmBuff1)==0)
      return 0;
   if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,momSmBuff1,momSmBuff2)==0)
      return 0;
   if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,absMomSmBuff1,absMomSmBuff2)==0)
      return 0;
   for(int i=limit; i>=0 && !IsStopped(); i--)
      indBuff[i]=(absMomSmBuff2[i]!=0 ? 100.0*momSmBuff2[i]/absMomSmBuff2[i] : 0);
   return(rates_total);
  }

이 코드를 오류 없이 컴파일한 후 내비게이터의 지표 폴더에서 선택해 원하는 차트에 끌어다 놓으면 다음과 같이 지표 창과 입력창이 표시됩니다:

간단한 TSI 입력

사용자가 결정할 수 있는 두 가지 입력값이 있으며 기본값은 평활화 기간 1의 경우 25, 평활화 기간 2의 경우 13입니다. 하지만 앞서 언급했듯이 사용자는 자신의 선호에 따라 이를 바꿀 수 있습니다.

 간단한 TSI 색상

이전 그림의 색상 탭에서 사용자가 TSI 라인의 색상, 너비 및 스타일을 선택할 수 있는 것을 볼 수 있습니다. 앞서 설명한 대로 기본 설정 입력과 지표 스타일을 결정하면 차트에서 다음과 같은 지표를 찾을 수 있습니다:

 simpleTSI 첨부

이전 차트에서 볼 수 있듯이 가격 아래 별도의 창에 TSI 지표선이 있고 이 선이 0을 중심으로 진동하는 것을 볼 수 있습니다. 그리고 지표의 레이블, 평활화 기간, 지표의 값이 있습니다.


맞춤형 TSI EA

이 부분에서는 자동화 시스템에서 맞춤형 지표를 사용하여 특정 조건이 트리거 되면 특정 신호나 동작이 작동하도록 하는 아주 간단한 방법을 배워보겠습니다. 먼저 TSI 현재 값으로 차트에 코멘트를 생성하는 간단한 EA를 만들고 이후 더 복잡한 명령을 실행하는 EA를 개발할 것입니다.

EA를 만드는 데 필요한 단계는 다음과 같습니다:

  • (TSI)의 정수 변수를 생성합니다.
  • 지표의 핸들과 해당 매개 변수를 반환하는 iCustom 함수를 사용하여 TSI를 정의하는 방법은 다음과 같습니다:
    • symbol: 심볼 이름을 지정합니다. 우리는 현재 심볼에 적용할 _Symbol을 사용합니다.
    • period: 기간을 지정합니다. 우리는 _period를 사용하여 현재 기간에 적용합니다.
    • name: 맞춤형 지표의 경로를 지정합니다.
  • EA가 제거되면 OnDeinit(const int reason) 부분에 "TSI 시스템 제거됨"이라는 텍스트를 출력합니다.
  • tsiVal[] 배열을 생성합니다.
  • 첫 번째 위치로 호출하는 변형과 필요한 요소 수와 함께 CopyBuffer 함수를 사용하여 TSI 지표의 버퍼 데이터를 가져옵니다. 매개변수는 다음과 같습니다:
    • indicator_handle: 지표가 반환하는 지표 핸들을 지정합니다. 우리는 (TSI)를 사용합니다.
    • buffer_num: 지표 버퍼 번호를 지정합니다. 우리는 (0)을 사용합니다.
    • start_pos: 복사할 시작 위치를 지정합니다. 우리는 (0)을 사용합니다.
    • count: 복사할 데이터 개수를 지정합니다. 우리는 (1)을 사용합니다.
    • buffer[]: 복사할 배열을 지정합니다. 우리는 (tsiVal)을 사용합니다.
  • 코멘트 함수를 사용하여 (DoubleToString) 함수를 사용하여 문자열로 변환된 현재 TSI 값과 자릿수를 지정하는 숫자를 지정합니다. 우리는 (_Digits)를 사용하겠습니다.

전체 코드는 다음과 같습니다.

//+------------------------------------------------------------------+
//|                                           customSimpleTSI-EA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
int TSI;
int OnInit()
  {
   TSI=iCustom(_Symbol,_Period,"My Files\\TSI\\simpleTSI");
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("TSI System Removed");
  }
void OnTick()
  {
   double tsiVal[];
   CopyBuffer(TSI,0,0,1,tsiVal);
   Comment("TSI Value ",DoubleToString(tsiVal[0],_Digits));
  }

이 코드를 오류와 함께 컴파일하고 차트에 첨부됩니다. 다음은 테스트에서 나온 신호의 예시입니다:

 iCustom TSI 전자 신호

왼쪽 상단 차트에 현재 TSI 값이 나타난 코멘트가 있는 것을 볼 수 있습니다. 신호가 지표를 기반으로 한 것과 동일한지 확인할 수 있습니다. 이를 위해 다음과 같이할 수 있습니다:

iCustom TSI 연결됨 - 동일한 신호

오른쪽 상단에는 EA가 첨부되어 있고 지표의 신호는 현재 TSI 값과 함께 왼쪽 상단에 표시되어 있습니다. TSI 지표는 가격 아래에 별도의 창으로 차트에 삽입되어 있으며 왼쪽 모서리에 있는 선 위의 값은 EA의 신호와 동일합니다.


TSI 시스템 EA

이 부분에서는 생성된 맞춤형 TSI 지표를 기반으로 EA를 개발하여 특정 전략에 따라 신호를 받도록 하겠습니다. 지금부터 설명할 전략은 교육의 목적으로만 사용된다는 점에 유의하세요. 어떤 경우든 최적화가 필요합니다. 따라서 여러분은 실제 계정에서 사용하기 전에 테스트를 해서 유용한지 확인해야 합니다.

우리는 전략에 따라 매수 및 매도 신호를 얻기 위해 두 이동평균과 결합된 맞춤형 TSI 지표를 사용할 것입니다. 이는 다음과 같습니다:

우리는 맞춤형 TSI 지표와 함께 두 개의 단순 이동 평균 중 하나는 주기가 10인 빠른 이동 평균이고 다른 하나는 주기가 20인 느린 이동 평균을 사용할 것입니다. 시스템은 이전 빠른 MA 값이 이전 느린 MA보다 작고 동시에 현재 빠른 MA가 현재 느린 MA보다 크면 이는 강세 MA 크로스오버가 있음을 의미하며 현재 TSI 값이 0보다 큰지 확인하고 차트에 코멘트로 매수 신호를 보내야 합니다. 이전 빠른 이평선 값이 이전 느린 이평선보다 크고 동시에 현재 빠른 이평선이 현재 느린 이평선보다 작다면 이는 약세 이평선 크로스오버를 의미하며 현재 TSI 값이 0보다 작은지 확인하여 차트에 코멘트로 매도 신호를 보내야 합니다.

간단히 하면,

fastMA[1]<slowMA[1] && fastMA[0]>slowMA[0] && tsiVal[0]>0 ==> 매수 신호
fastMA[1]>slowMA[1] && fastMA[0]<slowMA[0] && tsiVal[0]<0 ==> 매도 신호

다음은 이러한 유형의 시스템을 만드는 전체 코드입니다:

//+------------------------------------------------------------------+
//|                                                TSI System EA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
input ENUM_MA_METHOD inpMAType = MODE_SMA; //Moving Average Type
input ENUM_APPLIED_PRICE inpPriceType = PRICE_CLOSE; //Price type
input int inpFastMAPeriod = 10; // Fast moving average period
input int inpSlowMAPeriod = 20; //Slow moving average period
int tsi;
double fastMAarray[], slowMAarray[];
int OnInit()
  {
   tsi=iCustom(_Symbol,_Period,"My Files\\TSI\\simpleTSI");
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("TSI System Removed");
  }
void OnTick()
  {
   double tsiVal[];
   CopyBuffer(tsi,0,0,1,tsiVal);
   int fastMA =iMA(_Symbol,_Period,inpFastMAPeriod,0,inpMAType,inpPriceType);
   int slowMA =iMA(_Symbol,_Period,inpSlowMAPeriod,0,inpMAType,inpPriceType);
   ArraySetAsSeries(fastMAarray,true);
   ArraySetAsSeries(slowMAarray,true);
   CopyBuffer(fastMA,0,0,3,fastMAarray);
   CopyBuffer(slowMA,0,0,3,slowMAarray);
   if(fastMAarray[1]<slowMAarray[1]&&fastMAarray[0]>slowMAarray[0])
     {
      if(tsiVal[0]>0)
        {

         Comment("Buy Signal",
                 "\nTSI Value ",DoubleToString(tsiVal[0],_Digits),
                 "\nfastMA ",DoubleToString(fastMAarray[0],_Digits),
                 "\nslowMA ",DoubleToString(slowMAarray[0],_Digits));
        }
     }
   if(fastMAarray[1]>slowMAarray[1]&&fastMAarray[0]<slowMAarray[0]&&tsiVal[0]<0)
     {
      if(tsiVal[0]<0)
        {
         Comment("Sell Signal",
                 "\nTSI Value ",DoubleToString(tsiVal[0],_Digits),
                 "\nfastMA ",DoubleToString(fastMAarray[0],_Digits),
                 "\nslowMA ",DoubleToString(slowMAarray[0],_Digits));
        }
     }
  }

이 코드의 차이점은 다음과 같습니다:

이동평균 유형, 적용 가격 유형, 빠른 이동평균 기간, 느린 이동평균 기간에 대해 사용자가 4개의 입력을 생성하고 기본값을 할당합니다.

input ENUM_MA_METHOD inpMAType = MODE_SMA; //Moving Average Type
input ENUM_APPLIED_PRICE inpPriceType = PRICE_CLOSE; //Price type
input int inpFastMAPeriod = 10; // Fast moving average period
input int inpSlowMAPeriod = 20; //Slow moving average period

빠른 MA 배열과 느린 MA 이렇게 두 개의 배열을 생성합니다.

double fastMAarray[], slowMAarray[];

이동 평균의 핸들과 해당 매개 변수를 반환하는 사전 정의된 iMA 함수를 사용하여 두 이동 평균을 정의하면 다음과 같습니다:

  • symbol: 현재 심볼에 적용하기 위해 _Symbol을 사용합니다.
  • period: 현재 기간에 적용하기 위해 _period를 사용합니다.
  • ma_period: 빠른 MA와 느린 MA에 사용자가 입력한 값을 사용합니다.
  • ma_shift: 시프트가 필요 없으므로 (0)을 사용합니다.
  • ma_method: MA 유형입니다.
  • applied _price: 가격 유형입니다.
   int fastMA =iMA(_Symbol,_Period,inpFastMAPeriod,0,inpMAType,inpPriceType);
   int slowMA =iMA(_Symbol,_Period,inpSlowMAPeriod,0,inpMAType,inpPriceType);

느린 MA와 빠른 MA에 대해 ArraySetAsSeries 함수를 사용하여 AS_SERIES 플래그를 설정합니다.

   ArraySetAsSeries(fastMAarray,true);
   ArraySetAsSeries(slowMAarray,true);

CopyBuffer 함수를 사용하여 두 이동 평균의 버퍼에서 데이터 가져오기

   CopyBuffer(fastMA,0,0,3,fastMAarray);
   CopyBuffer(slowMA,0,0,3,slowMAarray);

전략의 조건 정의하기,

매수 신호인 경우:

이전 fastMA가 이전 slowMA보다 작고 현재 fastMA가 현재 slowMA보다 크면서 동시에 현재 tsiVal이 0보다 큰 경우 EA는 다음 순서로 차트에 매수 신호 코멘트를 반환해야 합니다:

  • 매수 신호.
  • TSI 값
  • fastMA 값
  • slowMA 값
   if(fastMAarray[1]<slowMAarray[1]&&fastMAarray[0]>slowMAarray[0])
     {
      if(tsiVal[0]>0)
        {

         Comment("Buy Signal",
                 "\nTSI Value ",DoubleToString(tsiVal[0],_Digits),
                 "\nfastMA ",DoubleToString(fastMAarray[0],_Digits),
                 "\nslowMA ",DoubleToString(slowMAarray[0],_Digits));
        }
     }

매도 신호인 경우:

이전 빠른MA가 이전 느린MA보다 크고 현재 빠른MA가 현재 느린MA보다 작고 동시에 현재 tsiVal이 0보다 작은 경우 EA는 다음과 같은 순서로 차트에 매도 신호를 코멘트로 반환해야 합니다:

  • 매도 신호
  • TSI 값
  • fastMA 값
  • slowMA 값
   if(fastMAarray[1]>slowMAarray[1]&&fastMAarray[0]<slowMAarray[0]&&tsiVal[0]<0)
     {
      if(tsiVal[0]<0)
        {
         Comment("Sell Signal",
                 "\nTSI Value ",DoubleToString(tsiVal[0],_Digits),
                 "\nfastMA ",DoubleToString(fastMAarray[0],_Digits),
                 "\nslowMA ",DoubleToString(slowMAarray[0],_Digits));
        }
     }

신호를 받기 위해 이 코드를 오류 없이 컴파일하고 드래그 앤 드롭 하여 실행하면 입력 탭에서 다음과 같은 창을 찾을 수 있습니다:

TSI 시스템 EA 입력

MA 유형, 가격 유형, 빠른 MA 기간 및 느린 MA 기간의 네 가지 입력이 있는 것을 볼 수 있습니다. 설정을 마치고 확인을 누르면 EA가 차트에 첨부되고 신호가 다음과 같이 표시되는 것을 확인할 수 있습니다:

매수 신호의 경우

 TSI System EA - 매수 신호

이전 차트에서 볼 수 있듯이 다음과 같은 전략 조건에 따라 왼쪽 상단 모서리에 코멘트로 매수 신호가 있습니다:

  • 매수 신호.
  • TSI 값
  • fastMA 값
  • slowMA 값

매도 신호인 경우:

 TSI 시스템 EA - 매도 신호

이전 차트에서 볼 수 있듯이 전략 조건에 따라 왼쪽 상단 모서리에 다음과 같은 매도 신호가 코멘트로 표시됩니다:

  • 매도 신호
  • TSI 값
  • fastMA 값
  • slowMA 값

결론

이 글에서 우리는 여러분이 설정하고 여러분의 선호에 따른 자신만의 실제 강도 지수 기술 지표를 만드는 방법에 대해 알아봤습니다. 이 지표가 제공하는 정보와 인사이트는 트레이딩에 매우 유용할 수 있습니다. 또한 우리는 간단한 트레이딩 시스템에서 이 맞춤형 지표를 사용하여 차트에 코멘트로 TSI 지표의 현재 값을 생성하는 방법도 배웠습니다. 또한 자동매매 시스템에서 이 지표를 사용하는 방법도 살펴봤는데 이 경우 이동평균과 같은 다른 기술 도구와 결합된 TSI 데이터를 활용하는 EA를 만들었습니다. 이 맞춤형 TSI와 두 이동 평균의 조합은 특정 전략에 따라 매수 및 매도 신호를 생성했으며 이는 TSI 시스템 EA란 주제에서 자세히 살펴 보았습니다.

이 글이 여러분의 트레이딩과 프로그래밍 학습에 도움이 되길 바랍니다. 지표에 대한 다른 기사를 읽고 가장 인기 있는 기술 지표를 기반으로 트레이딩 시스템을 만드는 방법을 배우려면 이동평균, 볼린저 밴드, RSI, MACD, 스토캐스틱, 포물선형 SAR, ATR 등과 같은 인기 지표를 다룬 이전의 기사를 참조하시기 바랍니다.

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

파일 첨부됨 |
simpleTSI.mq5 (7.46 KB)
iCustomTSI_ea.mq5 (0.81 KB)
TSI_System_EA.mq5 (2.09 KB)
MQL5 전략 테스터를 이해하고 효과적으로 사용하기 MQL5 전략 테스터를 이해하고 효과적으로 사용하기
MQL5 프로그래머나 개발자는 중요하고 가치 있는 도구를 마스터해야 합니다. 이러한 도구 중 하나가 전략 테스터입니다. 이 글은 MQL5의 전략 테스터를 이해하고 효과적으로 사용하기 위한 실용적인 가이드입니다.
패턴 검색에서 무자비 대입 방식(5부): 새로운 시각 패턴 검색에서 무자비 대입 방식(5부): 새로운 시각
이 글에서는 제가 꽤 오랜만에 찾은 알고리즘 트레이딩에 관한 완전히 색다른 접근법을 보여드리겠습니다. 물론 이 모든 것은 여러 문제를 동시에 해결할 수 있도록 여러 가지 변경을 거친 저의 무자비 대입 프로그램과 관련이 있습니다. 그럼에도 불구하고 이 기사는 더 일반적이고 가능한 한 간단한 것으로 밝혀 졌기 때문에 무자비 대입에 대해 아무것도 모르는 사람들에게도 적합합니다.
모집단 최적화 알고리즘: 묘목 파종 및 성장(SSG) 모집단 최적화 알고리즘: 묘목 파종 및 성장(SSG)
묘목 파종 및 성장(SSG) 알고리즘은 다양한 조건에서 뛰어난 생존 능력을 발휘하는 지구상에서 가장 탄력적인 유기체 중 하나로부터 영감을 받은 것입니다.
MQL5 - 여러분도 이 언어의 마스터가 될 수 있습니다. MQL5 - 여러분도 이 언어의 마스터가 될 수 있습니다.
이 글은 제가 MQL5 언어에 어떻게 첫발을 내딛게 되었는지 알려드리는 일종의 인터뷰 형식의 글입니다. 훌륭한 MQL5 프로그래머가 되는 방법을 알려드리겠습니다. 저는 이 위업을 달성하는 데 필요한 기반을 설명할 것입니다. 유일한 전제 조건은 배우고자 하는 의지입니다.