English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
초보자를 위한 MQL5 Expert Advisor 코드 작성 가이드

초보자를 위한 MQL5 Expert Advisor 코드 작성 가이드

MetaTrader 5트레이딩 시스템 | 5 7월 2021, 10:19
1 310 0
Samuel Olowoyo
Samuel Olowoyo

시작하며

이 글은 새로운 MQL5 언어로 간단한 Expert Advisor를 쓰는 방법을 배우고자 하는 초보자를 대상으로 합니다 먼저 우리가 EA(Expert advisor)에 기대하는 부분을 정의한 후 EA가 어떤 식으로 그걸 진행하게 할 것인지를 생각해 보겠습니다.


1. 매매 전략

EA가 할 것:

  • 특정 지표를 모니터링하고, 특정 조건이 충족될 경우(또는 특정 조건이 충족될 경우), 충족된 현재 조건에 따라 매매(단기/매도 또는 장기/매수) 거래합니다.

위의 내용을 매매 전략이라고 합니다. EA를 작성하기 전에 먼저 EA를 어떤 식으로 자동화시킬 것인지를 생각해야 합니다. 따라서 이 경우, 위의 문구를 수정하여 우리가 EA로 개발하고자 하는 전략을 반영하도록 합시다.

  • 기간을 8로 두고, Moving Average(이동 평균)라는 인디케이터를 사용하겠습니다(어떤 기간이든 선택할 수 있지만, 우리의 전략상 8을 사용합니다).

  • 우리는 EA가 이동 평균-8(편의상 앞으로 MA-8라고 지칭하겠습니다)이 올라가고 가격이 그의 거의 바로 위에 있을 때 롱(매수)을, MA-8이 하락하고 가격이 거의 바로 아래에 있을 때 숏우리는 이사가 평균-8(논의를 위해 MA-8이라고 부르겠다)이 상승하고 그 위쪽에 근접하고 MA-8이 하락하고 그 아래쪽에 근접할 때 숏(매도)하도록 하고 싶습니다.
  • 같은 8 주기의 평균방향이동(Average Directional Movement, ADX)이라는 또 다른 지표도 활용해 시장 동향 파악에 도움을 줄 예정입니다. 우리는 오직 시장에 추세가 발생했을 때만 거래를 하고 시장이 비추세 상태일 때(추세가 없는)는 쉬고 싶기 때문에 이렇게 하는겁니다. 이를 위해 당사는 위의 조건이 충족되고 ADX 값이 22보다 클 때만 매매(매도 또는 매수)를 실시합니다. ADX가 22보다 크지만 감소중이거나, ADX가 22보다 작으면 B 조건이 충족되었더라도 매매하지 않습니다.
  • 또한 30ppi의 Stop loss를 설정하여 스스로를 보호하고 수익 목표를 위해 100ppi의 수익을 목표로 합니다.
  • 또한 우리는 EA가 새로운 바가 형성되었을 때에만 매수/매도 기회를 엿보기를 원하고, 매수 조건이 충족되었는데도 오픈하지 않았을 때에 매수 포지션을 오픈하길 원하며, 매도 조건이 만족되었는데 오픈하지 않았을 때에 매도 포지션을 오픈하길 원합니다.

자, 이제 전략을 수립했습니다; 직접 코드를 쓸 때가 왔죠.


2. Expert Advisor의 코드 작성하기

2.1 MQL5 위자드

MetaQuotes 언어 에디터 5를 기동시키세요. 그런 후 Ctrl+N을 누르거나 메뉴 바의 New버튼을 누르십시오.

1번 그림. 새 MQL5 문서 시작하기

1번 그림. 새 MQL5 문서 시작하기

 MQL5 Wizard 창에서 Expert Advisor를 선택하고 그림 2와 같이 "Next"를 클릭합니다.

2번 그림. 문서 타입 선택하기

2번 그림. 프로그램 타입 선택하기

다음 창에서 이름 상자에 EA에 부여할 이름을 입력합니다. 저는 My_First_EA라고 쳤습니다. 그런 다음 작성자 상자에 이름을 입력하고 링크 상자에 홈페이지 주소 또는 이메일 주소를 입력할 수 있습니다(있는 경우에만).

3번 그림. Expert Advisor의 일반 속성

3번 그림. Expert Advisor의 일반 속성

우리는 어떤 값이 우리에게 가장 좋은 결과를 줄 수 있는지 보기 위해 우리의 EA에 대한 몇몇 패러미터를 변경할 수 있기를 원하기 때문에, 우리는 "추가" 버튼을 클릭하여 그것들을 추가할 것입니다.

4번 그림. EA 입력 패러미터 세팅하기

4번 그림. EA 입력 패러미터 세팅하기

우리의 EA에서는 Stop Loss, Take Profit, ADX Period 및 Moving Average Period 설정을 실험할 수 있기를 원하므로 이 시점에서 이들을 정의하겠습니다.

Name 섹션 아래를 더블 클릭하고 패러미터의 이름을 입력한 다음 Type 아래를 더블 클릭합니다. 그 다음 Initial value 섹션 아래를 더블 클릭해서 패러미터의 초기값을 입력하세요.

작업이 완료되면 다음과 같이 표시됩니다:

5번 그림. EA 입력 패러미터의 자료 타입

5번 그림. EA 입력 패러미터의 자료 타입

위에서 보시다시피 모든 패러미터에 대해 정수(int) 자료 타입을 선택했습니다. 자료 타입에 대하여 간단히 짚고 넘어가겠습니다.

  • char: The char 타입은 1 바이트 (8비트)의 메모리를 차지하며 2^8=256 값의 이진법적 표현을 제공합니다. char 타입은 양수 음수 모두 존재할 수 있습니다. 값의 범위는 -128 to 127 입니다.
  • uchar : ucharchar 타입과 마찬가지로 1바이트 메모리를 차지하는 정수 타입이지만 이와는 다르게 uchar는 양수값으로만 쓰이도록 의도되어있습니다. 최소값은 0이며 최대값은 255입니다. uchar 타입 맨 앞의 u 자는 unsigned의 약자입니다.
  • short: short 타입의 사이즈는 2바이트 (16비트) 로 따라서 2의 16승 까지의 값을 나타낼 수 있습니다. 2^16 = 65 536. short 타입은 양수 음수 모두 표현할 수 있기때문에 표현 가능한 값의 범주는 -32 768 에서 32 767 입니다.
  • ushort: 언사인드 short 타입은 ushort으로, 마찬가지로 2바이트 크기입니다. 최소값은 0, 최대값은 65 535.
  • int : int 타입의 사이즈는 4바이트 (32비트). 최소값은 -2 147 483 648, 최대값은 2 147 483 647.
  • uint :  언사인드 integer 타입으로 uint. 4바이트의 메모리를 차지하며, 0에서 4 294 967 295까지 표현가능합니다.
  • long : long 타입의 사이즈는 8 바이트 (64 비트). 최소값은 -9 223 372 036 854 775 808, 최대값은 9 223 372 036 854 775 807.
  • ulong : ulong 타입 역시 8 바이트를 차지하며 0 에서 18 446 744 073 709 551 615의 값을 보유할 수 있습니다.

다양한 데이터 유형에 대한 위의 설명에서 언사인드 정수 유형은 음수 값을 저장하도록 설계되지 않았으며 음수 값을 설정하려고 하면 예기치 않은 결과가 발생할 수 있습니다. 예를 들어 음수 값을 저장하려면 언사인드 타입 (예: uchar, uint, ushort, ulong) 내에는 저장할 수 없습니다.

EA로 돌아와서. 자료 타입들을 봤으니 char 이나 uchar 를 써야한다는 것을 느꼈을 겁니다. 우리가 패러미터 안에 넣으려는 것은 각각 127   이나 255 보다 작기 때문입니다. 메모리 관리를 잘하려면 이렇게 하는 것이 가장 좋습니다. 그러나 본 가이드에서는 혼란을 피하기 위해 우리는 여전히 int 를 쓰겠습니다.

필요한 파라미터를 모두 설정했으면 Finished 버튼을 클릭하면 다음 그림에 표시된 대로 MetaQuotes Editor에서 코드의 골격을 만들 수 있습니다.


더 잘 이해하기 위해 코드를 여러 부분으로 나눕시다.

코드의 상단 부분(Header)은 EA의 속성이 정의된 곳입니다. 3번 그림에서 MQL5 위자드에 넣은 값들을 보게 될겁니다. 

코드의 이 부분에서는 설명 (EA의 간단한 텍스트 설명)과 같은 추가 파라미터를 정의하고 상수를 선언하며 추가 파일 또는 가져오기 기능을 포함할 수 있습니다. 


문이 # 기호로 시작될 때, 전처리기 지시어라고 불리며, 세미콜론 ';'로 끝나지 않습니다. 전처리기 지시어의 다른 예시들은 이하가 있는데:

#define : 

The #define 지시어는 상수 선언에 사용됩니다. 이 구조로 적을 수 있습니다

#define identifier token_string

이 코드는 코드 내의 모든 identifier 값을 token_string 값으로 대체한다는 의미입니다.

예시 :

#define ABC               100
#define COMPANY_NAME      "MetaQuotes Software Corp."

이 코드는 모든 COMPANY_NAME을  "MetaQuotes Software Corp." 로 교체하거나 모든 ABC를 char (혹은 integer) 100 으로 바꿉니다.

전처리기 지침에 대한 자세한 내용은 MQL5 Manual에서 확인할 수 있습니다. 이제 토론을 계속하겠습니다.

코드 헤더의 두 번째 부분은 입력 패러미터 섹션입니다.

 

우리는 모든 패러미터를 지정하며, 이는 본 섹션의 EA에서 사용됩니다. 여기에는 EA에서 작성할 모든 함수에 사용되는 모든 변수가 포함됩니다.

이 수준에서 선언된 변수를 글로벌 변수 라고 부르는데, 이는 변수는 이를 필요로 할 수 도 있는 EA 내의 모든 함수가 접근 가능하기 때문입니다. 입력 패러미터는 EA 외부에서만 바꿀 수 있는 패러미터입니다. 또한 우리는 EA 과정에서 다룰 다른 변수를 선언할 수 있지만, 이 섹션에서는 EA 이외에는 사용할 수 없습니다.

다음은 EA 초기화 기능입니다. 이것은 EA가 시작되거나 차트에 첨부될 때 호출되는 첫 번째 함수이며, 한 번만 호출됩니다. 


이 섹션은 EA가 제대로 작동하는지 확인하기 위해 몇 가지 중요한 점검을 하기에 가장 좋은 포인트입니다.

차트에 EA가 작동하기에 충분한 바가 있는지 여부 등을 결정할 수 있습니다.

또한 지표(ADX 및 이동 평균 지표)에 사용할 핸들을 얻기에 가장 좋은 장소이기도 합니다.

 
 OnDeinit 함수는 EA가 차트에서제거되었을 때 호출됩니다.

EA의 경우 이 섹션의 초기화 중에 지표용으로 생성된 핸들을 해방합니다.


본 함수는 NewTick 이벤트를 처리하는데, 이는 심볼에 대한 새로운 견적을 받았을 때 생성됩니다. 

만약 Expert Advisors의 사용이 클라이언트 터미널에서 금지되어 있다면 ("자동 매매" 버튼) Expert Advisors는 매매를 할 수 없다는 점에 유의하셔야합니다.

6번 그림. 자동매매가 활성화됨

6번 그림. 자동매매가 활성화됨

앞서 개발된 거래 전략을 구현하는 대부분의 코드는 이 절에 작성될 것입니다.

우리의 EA에 대한 코드의 다양한 부분을 살펴보았으니, 이제는 디테일을 추가해보도록 합시다.

2.2 입력 패러미터 섹션

//--- input parameters
input int      StopLoss=30;      // Stop Loss
input int      TakeProfit=100;   // Take Profit
input int      ADX_Period=8;     // ADX Period
input int      MA_Period=8;      // Moving Average Period
input int      EA_Magic=12345;   // EA Magic Number
input double   Adx_Min=22.0;     // Minimum ADX Value
input double   Lot=0.1;          // Lots to Trade
//--- Other parameters
int adxHandle; // handle for our ADX indicator
int maHandle;  // handle for our Moving Average indicator
double plsDI[],minDI[],adxVal[]; // Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars
double maVal[]; // Dynamic array to hold the values of Moving Average for each bars
double p_close; // Variable to store the close value of a bar
int STP, TKP;   // To be used for Stop Loss & Take Profit values

보시다시피 패러미터가 더 추가되었습니다. 새로운 패러미터에 대해 계속 논의하기 전에 지금 볼 수 있는 것에 대해 이야기하겠습니다. 두 정방향 슬래쉬 ‘//’ 로 코드 안에 코멘트를 쓸 수 있게 해줍니다. 코멘트를 사용하면 코드에서 변수가 무엇의 약자인지, 또는 해당 시점에서 무엇을 하고 있는지 알 수 있습니다. 또한 코드를 더 잘 이해할 수 있습니다. 코멘트를 쓰는 데는 두 가지 기본적인 방법이 있습니다.

// 다른 패러미터 …

이건 한 줄 코멘트 입니다

/*

  이건 여러 줄 코멘트입니다

*/

이건 여러 줄 코멘트입니다. 여러 줄 코멘트는 /* 로 시작해 */로 끝납니다.

컴파일러는 코드를 컴파일할 때 모든 코멘트를 무시합니다.

입력 패러미터에 대해 단일 라인 주석을 사용하는 것은 EA 사용자가 해당 패러미터의 약자를 이해하도록 하는 좋은 방법입니다. EA 입력 속성에서 사용자는 패러미터 자체를 볼 수 없지만 다음과 같은 주석을 볼 수 있습니다.

7번 그림. Expert Advisor 입력 패러미터

7번 그림. Expert Advisor 입력 패러미터

자 이제 코드로 돌아가서…

EA에 추가적인 패러미터를 추가하기로 결정했습니다. EA_Magic은 EA의 모든 주문에 대한 마법의 숫자입니다.  최소 ADX 값 (Adx_Min)은 double 자료 타입으로 선언됩니다. double은 정수 부분, 소수점 및 분수 부분을 포함하는 부동 소수점 상수를 저장하기위해 쓰입니다.

예시:

double mysum = 123.5678;

double b7 = 0.09876;

랏 (Lot)은 우리가 거래하고자 하는 금융상품의 수량을 나타냅니다. 그런 다음 사용할 다른 매개 변수를 선언했습니다.

adxHandle은 ADX 인디케이터 핸들을 저장하는데 쓰이고, maHandle은 이동 평균 인디케이터를 저장하는데 쓰입니다. plsDI[], minDI[], adxVal[]은 차트 상의 각 바의 +DI, -DI 그리고 주요 ADX (ADX 인디케이터의)를 저장하는데 쓰이는 동적 어레이입니다. maVal[] 은 차트 상 각 바의 이동 평균 인디케이터를 보존하는 동적 어레이입니다.

그런데 동적 어레이란 무엇입니까? 동적 어레이는 차원이 없이 선언된 배열입니다. 즉, 대괄호 쌍에는 값이 지정되지 않습니다. 반면에 정적 배열은 선언 지점에서 그 치수를 정의됩니다.

예시:

double allbars[20]; //이건 20개의 요소를 받습니다

p_close 는 매수/매도 거래에서 지켜볼 바의 종가를 저장하는 변수 입니다.

STP TKP 는 EA의 Stop Loss 및 Take Profit 값을 저장하는 데 사용됩니다.

2.3. EA 초기화 섹션

int OnInit()
  {
//--- Get handle for ADX indicator
   adxHandle=iADX(NULL,0,ADX_Period);
//--- Get the handle for Moving Average indicator
   maHandle=iMA(_Symbol,_Period,MA_Period,0,MODE_EMA,PRICE_CLOSE);
//--- What if handle returns Invalid Handle
   if(adxHandle<0 || maHandle<0)
     {
      Alert("Error Creating Handles for indicators - error: ",GetLastError(),"!!");
     }

여기서는 각 인디케이터 기능을 사용하여 인디케이터의 핸들을 가져옵니다.

ADX 인디케이터 핸들iADX 함수를 이용하여 얻어집니다. 이건 차트 심볼(NULL 역시 현 차트의 현 심볼을 의미함), 차트 기간/타임프레임 (0 역시 현 차트의 현 타임프레임을 의미함), ADX 평균 기간을 인덱스 (앞쪽 입력 패러미터 섹션에서 정의한) 계산에서 패러미터나 인수로서 씁니다  

int  iADX(
   string           symbol,         // 심볼 이름
   ENUM_TIMEFRAMES  period,         // 기간
   int              adx_period      // 평균 기간
   );

이동 평균 인디케이터 핸들iMA 함수를 쓰는 걸로 얻어집니다. 여기에는 다음과 같은 인수가 있습니다.

  • 차트 심볼 (현 차트의 현 심볼에서 _symbol, symbol() or NULL 를 통해 얻어질 수 있는),
  • 차트기간/타임프레임 (현 차트의 현 타임프레임에서 _period, period(), or 0를 통해 얻어질 수 있는),
  • 이동 평균 평균화 기간 (앞서 입력 패러미터 섹션에서 정의한),
  • 가격 차트에 비례하는 인디케이터 이동 (이곳의 이동은 0),
  • 이동 평균 평활 타입 (다음의 평균 방법중 하나 일 수 있음: Simple Averaging-MODE_SMA, Exponential Averaging-MODE_EMA, Smoothed Averaging-MODE_SMMA 혹은 Linear-Weighted Averaging-MODE_LWMA), 및
  • 평균화에 쓰인 단가 (여기선 종가를 씀).

int  iMA(
   string               symbol,            // 심볼 이름
   ENUM_TIMEFRAMES      period,            // 기간
   int                  ma_period,         // 평균 기간
   int                  ma_shift,          // 수평 이동
   ENUM_MA_METHOD       ma_method,         // 평활 타입
   ENUM_APPLIED_PRICE   applied_price      // 핸들이나 단가의 타입
   );

이러한 인디케이터 함수에 대한 자세한 내용은 MQL5 매뉴얼을 참조해주세요. 그를 통해 각 인디케이터 사용 방법을 더 잘 이해할 수 있죠

함수가 핸들을 성공적으로 반환하지 못한 경우, 우리는 다시 오류를 확인하려고 합니다. 그러면 INFLIZED_HANDLEX_HANDLE 오류가 발생합니다. 경고 함수를사용하여 GetlastError 함수를 사용하여 오류를 표시합니다.

//--- Let us handle currency pairs with 5 or 3 digit prices instead of 4
   STP = StopLoss;
   TKP = TakeProfit;
   if(_Digits==5 || _Digits==3)
     {
      STP = STP*10;
      TKP = TKP*10;
     }

우리는 Stop Loss(손실 중지) 및 Take Profit(이익 획득) 값을 변수에 저장하기로 결정합니다. STPTKP 는 우리가 앞쪽에서 선언해둔 것입니다. 어째서 이렇게 하는가?

입력 패러미터에 저장된 값이 읽기 전용이기 때문에 수정할 수 없습니다. 그래서 우리는 우리의 EA가 모든 브로커들과 잘 작동하도록 하고 싶습니다. Digits 혹은 Digits() 는 현재 차트 기호의 가격 정확도를 결정하는 소수 자릿수를 반환합니다. 5자리 또는 3자리 가격표의 경우, 손실 중지와 이익 회수율을 모두 10으로 곱합니다.

2.4. EA 개별화 섹션

 

이 함수는 EA가 비활성화되거나 차트에서 제거될 때마다 호출되므로 초기화 프로세스 중에 생성된 모든 인디케이터 핸들을 여기에 릴리스합니다. 두 개의 핸들을 만들었습니다. 하나는 ADX 표시기용 핸들이고 다른 하나는 이동 평균 인디케이터용 핸들입니다.

우리는 이를 달성하기 위해 IndicatorRelease() 함수를 쓸 것입니다. 단 하나의 인수만을 받습니다 (인디케이터 핸들)

bool  IndicatorRelease(
   int       indicator_handle,     // 인디케이터 핸들
   );

이 함수는 인디케이터 핸들을 제거하고 사용하지 않은 경우 인디케이터의 계산 블록을 해제합니다..

2.5 EA 온틱 섹션

여기서 우리가 해야 할 첫 번째 일은 현재 차트에 충분한 바가 있는지 확인하는 것입니다. 우리는 Bars 함수를 이용하여 모든 차트 이력의 총 바 수를 얻을 수 있습니다. 두개의 인수를 받는데, the 심볼 (_Symbol 혹은 Symbol()을 통해 얻을 수 있는). 이 둘은 현 차트나 우리의 EA가 부착된 곳의 현 심볼을 리턴해줍니다) 그리고 현 차트의 기간 혹은 타임프레임 ( Period 혹은 Period()를 통해 얻어지는. 이 두 가지는 EA가 연결된 현재 차트의 시간을 반환합니다).

사용 가능한 총 바가 60개 미만이면 차트에 충분한 바가 있을 때까지 EA가 완화되기를 바래야죠. Alert 함수는 별도의 창에 메시지를 표시합니다. 쉼표로 구분된 값을 매개 변수/인수로 사용합니다. 이 경우 문자열 값이 하나만 있습니다. 리턴은 EA의 초기화를 종료합니다.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Do we have enough bars to work with
   if(Bars(_Symbol,_Period)<60) // if total bars is less than 60 bars
     {
      Alert("We have less than 60 bars, EA will now exit!!");
      return;
     }
// We will use the static Old_Time variable to serve the bar time.
// At each OnTick execution we will check the current bar time with the saved one.
// If the bar time isn't equal to the saved time, it indicates that we have a new tick.
   static datetime Old_Time;
   datetime New_Time[1];
   bool IsNewBar=false;

// copying the last bar time to the element New_Time[0]
   int copied=CopyTime(_Symbol,_Period,0,1,New_Time);
   if(copied>0) // ok, the data has been copied successfully
     {
      if(Old_Time!=New_Time[0]) // if old time isn't equal to new bar time
        {
         IsNewBar=true;   // if it isn't a first call, the new bar has appeared
         if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);
         Old_Time=New_Time[0];            // saving bar time
        }
     }
   else
     {
      Alert("Error in copying historical times data, error =",GetLastError());
      ResetLastError();
      return;
     }

//--- EA should only check for new trade if we have a new bar
   if(IsNewBar==false)
     {
      return;
     }
 
//--- Do we have enough bars to work with
   int Mybars=Bars(_Symbol,_Period);
   if(Mybars<60) // if total bars is less than 60 bars
     {
      Alert("We have less than 60 bars, EA will now exit!!");
      return;
     }

//--- Define some MQL5 Structures we will use for our trade
   MqlTick latest_price;     // To be used for getting recent/latest price quotes
   MqlTradeRequest mrequest;  // To be used for sending our trade requests
   MqlTradeResult mresult;    // To be used to get our trade results
   MqlRates mrate[];         // To be used to store the prices, volumes and spread of each bar
   ZeroMemory(mrequest);     // Initialization of mrequest structure

Expert Advisor는 새 바의 시작 부분에서 거래 업무를 수행하므로 새로운 바 식별에 대한 문제를 해결해야 합니다. 다른 말로, 리는 EA가 모든 체크에서 Long/Short 설정을 검사하지 않고, 우리는 EA가 Long/Short 위치를 새 막대가 있을 때만 확인하기를 원합니다. 

정적 datetime 변수 Old_Time를 선언해, Bar 타임을 저장하는걸로 시작합니다 우리는 OnTick 함수의 다음 호출까지 메모리에 값을 유지하기를 원하기 때문에 그것을 정적이라고 선언했습니다. 그런 다음 이 값을 새 (현재) 막대 시간을 보유하기 위한 하나의 요소의 배열인 New_Time 변수(마찬가지로 datetime 데이터 타입인)와 비교할 수 있다. 우리는 또한 bool 자료 타입 변수IsNewBar를 선언하고 값을 false로 설정합니다. 왜냐하면 우리는 새로운 막대가 생겼을 때만 그것의 값이 TRUE이길 원하기 때문입니다.

현재 막대의 시간을 얻기 위해 Copy Time 함수를 사용합니다. 막대 시간을 하나의 요소로 배열 New_Time에 복사합니다. 성공적이면 새 막대의 시간을 이전 막대 시간과 비교합니다. 시간이 같지 않으면 새 막대가 생겼다는 뜻이며 변수 IsNewBar를 TRUE로 설정하고 현재 막대 시간의 값을 변수 Old_Time에 저장합니다.

IsNewBar 변수는 우리에게 새 막대가 생겼음을 나타낸다. 만약 이게 FALSE라면, OnTick 함수의 실행을 멈춥니다.

한번 코드를 보죠

if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);

디버그 모드 실행을 확인하고, 디버그 모드가 실행될 때 바 시간에 대한 메시지를 인쇄합니다. 이러면 계속 살펴보는겁니다.

다음으로 우리가 여기서 할 것은 이제 우리가 다룰 막대가 충분한지 확인하는 것입니다. 어째서 같은걸 반복하는가? EA가 제대로 작동하는지 확인하기 위함이죠 유의할 점은 EA가 차트에 연결될 때 OnInit 함수가 한 번만 호출되지만, 새로운 체크(가격 시세)가 있을 때마다 OnTick 기능이 호출된다는 점입니다.

아까랑은 다르게 했다는 것을 느끼셨을지도 모르겠네요. 여기서는 우리는 익스프레션으로부터 얻은 역사의 총 막대들을 저장하기로 결정합니다.

int Mybars=Bars(_Symbol,_Period);

OnTick 함수 내에서 선언된 새 변수 Mybars안에 말이죠. 이 유형의 변수는 코드의 입력 패러미터 섹션에서 선언한 변수와 달리 로컬 변수입니다. 코드의 입력 패러미터 섹션에 선언된 변수는 모든 함수에서 사용할 수 있지만, 이를 필요로 할 수 있는 코드 내에서, 단일 함수 내에서 선언된 변수는 제한적이며 해당 함수에서만 사용할 수 있습니다. 해당 함수 밖에서는 사용할 수 없습니다.

다음으로, 우리는 우리의 EA의 이 섹션에서 사용될 MQL5 구조 타입들의 몇 가지 변수를 선언했습니다. MQL5는 내장 빌트인 구조가 꽤 많아서 EA 개발자들이 쉽게 사용할 수 있습니다. 구조들을 한번 들여다 봅시다.

MqlTick

이는 심볼의 가장 최근 가격을 저장하는데에 쓰는 구조입니다.

struct MqlTick
  {
   datetime     time;          // 가장 최근 가격 업데이트
   double       bid;           // 현 입찰가
   double       ask;           // 현 매도가
   double       last;          // 가장 최근 거래가 (마지막)
   ulong        volume;        // 가장 최근 거래량
  };

MqlTick 타입 중 하나로 선언된 변수는 Ask, Bid, Last Volume 를 얻는데에 손쉽게 사용될 수 있습니다. SymbolInfoTick() 함수를 호출하면 되죠.

따라서 매도 매수가를 얻는데에 쓰기 위해 latest_priceMqlTick 타입으로 선언했습니다.

MqlTradeRequest

본 구조는 매매 운영을 위한 모든 거래 요청을 위해 쓰입니다. 이 구조는 매매를 위한 모든 필드를 구조 내에 저장합니다.

struct MqlTradeRequest
  {
   ENUM_TRADE_REQUEST_ACTIONS    action;       // 매매 타입
   ulong                         magic;        // Expert Advisor ID (매직넘버)
   ulong                         order;        // 주문 티켓
   string                        symbol;       // 매매 심볼
   double                        volume;       // 랏의 요청 볼륨
   double                        price;        // 가격
   double                        stoplimit;    // 주문의 StopLimit 레벨
   double                        sl;           // 주문의 Stop Loss 레벨
   double                        tp;           // 주문의 Take Profit 레벨
   ulong                         deviation;    // 요청된 가격에서 가능한 최대 편차
   ENUM_ORDER_TYPE               type;          // 주문 타입
   ENUM_ORDER_TYPE_FILLING       type_filling;  // 주문 실행 타입
   ENUM_ORDER_TYPE_TIME          type_time;     // 주문 실행 타임
   datetime                      expiration;    // 주문 만료 타입 (ORDER_TIME_SPECIFIED 타입 오더)
   string                        comment;       // 주문 코멘트
  };

MqlTradeRequest 타입으로 선언된 모든 변수를 사용하여 거래 업무를 위한 주문을 보낼 수 있습니다. 여기서mrequestMqlTradeRequest 타입으로 선언했습니다.

MqlTradeResult

모든 거래 작업의 결과는 MqlTradeResult 유형의 특수하게 미리 정의된 구조로 반환됩니다. MqlTradeResult 타입으로 선언된 변수는 어떤 것이던 주문 요청 결과에 액세스 가능합니다.

struct MqlTradeResult
  {
   uint     retcode;          // 작업 반환 코드
   ulong    deal;             // 딜 티켓(실행된 경우)
   ulong    order;            // 주문 티켓(주문된 경우)
   double   volume;           // 딜 수량, 브로커가 확인한 양
   double   price;            // 딜 가격, 브로커가 확인한 수치
   double   bid;              // 현 매수가
   double   ask;              // 현 매도가
   string   comment;          // 브로커 운영 코멘트 (기본값으로는 운영 설명)
  };

여기서는 mresultMqlTradeResult 타입으로 선언했습니다.

MqlRates

가격(오픈, 클로즈, 고가, 저가), 시간, 각 막대의 볼륨 및 심볼에 대한 스프레드가 이 구조에 저장됩니다.  MqlRates 타입으로 선언된 어레이는 어떤 것이던지 가격, 볼륨, 스프레드 이력을 저장하는데에 쓰일 수 있습니다.

struct MqlRates
  {
   datetime time;         // 기간 시작 시간
   double   open;         // 오픈 가격e
   double   high;         // 기간 내 최고가
   double   low;          // 기간 내 최저가
   double   close;        // 종가
   long     tick_volume;  // 틱 볼륨
   int      spread;       // 스프레드
   long     real_volume;  // 거래량
  };

이들 정보를 저장할 mrate[]  어레이를 선언했습니다.

/*
     Let's make sure our arrays values for the Rates, ADX Values and MA values 
     is store serially similar to the timeseries array
*/
// the rates arrays
   ArraySetAsSeries(mrate,true);
// the ADX DI+values array
   ArraySetAsSeries(plsDI,true);
// the ADX DI-values array
   ArraySetAsSeries(minDI,true);
// the ADX values arrays
   ArraySetAsSeries(adxVal,true);
// the MA-8 values arrays
   ArraySetAsSeries(maVal,true);

다음에는 막대 세부 정보를 시리즈로 저장하는 데 사용할 모든 어레이를 설정하기로 합니다. 이는 어레이로 인덱싱 될 값들이 시계열, 즉, 0, 1, 2, 3, 처럼 되도록입니다. (막대 색인과 일치하도록. ArraySetAsSeries() 함수를 씁니다.

bool  ArraySetAsSeries(
   
void  array[],     // 레퍼런스 어레이
   bool  set          // true일 경우 인덱싱 역순
   );

이 작업은 코드의 초기화 섹션에서 한 번 수행될 수도 있습니다. 하지만, 설명을 충실히 하기 위해서 이 시점에서 그것을 보이기로 했습니다.

//--- Get the last price quote using the MQL5 MqlTick Structure
   if(!SymbolInfoTick(_Symbol,latest_price))
     {
      Alert("Error getting the latest price quote - error:",GetLastError(),"!!");
      return;
     }

SymbolInfoTick 함수를 써서 최신가를 뽑아냅니다. 이 함수는 두 인수를 받습니다 - 파트 심볼MqlTick 구조 값 (latest_price). 마찬가지로 오류가 나면 보고합니다.

//--- Get the details of the latest 3 bars
   if(CopyRates(_Symbol,_Period,0,3,mrate)<0)
     {
      Alert("Error copying rates/history data - error:",GetLastError(),"!!");
      return;
     }

다음으로 우리는 CopyRates 함수를 사용하여 최신 세 개의 막대에 대한 정보를 Mqlrates 타입 어레이로 복사했습니다. CopyRates 함수는 MqlRates 구조의 이력 데이터를 받아오는데 쓰는데, 이는 정해진 심볼-기간을 특정 량만 구해 MqlRates 타입 어레이에 보관합니다. 

int  CopyRates(
   string           symbol_name,       // 심볼 이름
   ENUM_TIMEFRAMES  timeframe,         // 기간
   int              start_pos,         // 시작점
   int              count,             // 복사할 데이터 카운트
   MqlRates         rates_array[]      // 복사할 타겟 어레이
   );

심볼명은 ‘_symbol’를 통해 얻을 수 있으며, 현 기간/타임프레임은 ‘_period’를 통해 얻을 수 있습니다. 시작점의 경우 우리는 현재 바인 Bar 0에서 시작할 것이고, 딱 3개의 바를 셀게요; Bars 0, 1, 그리고 2. 결과값은 mrate[].어레이에 저장될 것입니다

mrate[] 어레이는 이제 바 0,1,2의 모든 가격, 시간, 볼륨과 스프레드 정보를 담고 있습니다.  따라서 이들 바에 대한 정보를 얻고자한다면 이하를 활용합니다:

mrate[bar_number].bar_property

예를 들자면, 각 바에서 이하의 정보를 얻을 수 있습니다:

mrate[1].time   // 바 1 시작시간
mrate[1].open   // 바 1 오픈 가격
mrate[0].high   // 바 0 (현재 바) 고가, 등

이어서, CopyBuffer 함수를 이용하여 모든 인디케이터 값을 우리가 선언한 동적 배열로 복사하였습니다.

int  CopyBuffer(
   int       indicator_handle,     // 인디케이터 핸들
   int       buffer_num,           // 인디케이터 버퍼 번호
   int       start_pos,            // 스타트 포지션
   int       count,                // 복사할 양
   double    buffer[]              // 복사할 목표 어레이
   );

저 인디케이터 핸들은 우리가 OnInit 섹션에서 만든 핸들입니다. 버퍼 번호와 관련하여 ADX 인디케이터에는 세 가지 버퍼가 있습니다.

  • 0 - MAIN_LINE,
  • 1 - PLUSDI_LINE,
  • 2 - MINUSDI_LINE.

이동평균 인디케이터는 오직 하나 (1)의 버퍼만 있습니다:

  • 0 – MAIN_LINE.

현 바 (0) 에서 이전 두 개의 바로 복사합니다. 따라서 복사할 레코드는 3개 (바 0, 1, 2). buffer[]가 앞서 선언한 목표 동적 어레이입니다 – adxVal, plsDI, minDI 그리고 maVal.

여기서 다시 볼 수 있듯이 복사 프로세스에서 발생할 수 있는 모든 오류를 캡처하려고 합니다. 오류가 발생하면 더 이상 진행할 필요가 없습니다.

CopyBuffer()CopyRates() 함수가 성공적으로 복사된 레코드의 수를 리턴하고, 오류 시에는 -1을 돌려보내는 것을 주의깊게 잘 볼 필요가 있습니다. 그렇기 때문에 여기서는 오류 검사 함수에서 0보다 작은 값을 확인합니다.

//--- Copy the new values of our indicators to buffers (arrays) using the handle
   if(CopyBuffer(adxHandle,0,0,3,adxVal)<0 || CopyBuffer(adxHandle,1,0,3,plsDI)<0
      || CopyBuffer(adxHandle,2,0,3,minDI)<0)
     {
      Alert("Error copying ADX indicator Buffers - error:",GetLastError(),"!!");
      return;
     }
   if(CopyBuffer(maHandle,0,0,3,maVal)<0)
     {
      Alert("Error copying Moving Average indicator buffer - error:",GetLastError());
      return;
     }

이 시점에서는 이미 구매 또는 판매 포지션이가 열려 있는지 확인하고 싶습니다. 즉, 하나의 매도나 매수 거래가 열려있는지 확인하려고 한다는 것 입니다. 이미 매수가 있는 경우 새 매수가 열리지 않고 이미 매도가 열려 있는 경우 새 매도를 열지 않으려합니다.

이를 이루기 위해 우린 먼저 만약 매수나 매도 포지션이 열려있을 경우 TRUE 값으로 세팅 될 2개의 bool 자료 타입 변수를 선언합니다(Buy_openedSell_opened).

//--- we have no errors, so continue
//--- Do we have positions opened already?
    bool Buy_opened=false;  // variable to hold the result of Buy opened position
    bool Sell_opened=false; // variable to hold the result of Sell opened position
    
    if (PositionSelect(_Symbol) ==true)  // we have an opened position
    {
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
         {
            Buy_opened = true;  //It is a Buy
         }
         else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
         {
            Sell_opened = true; // It is a Sell
         }
    }

우리는 거래 함수 PositionSelect 를 사용하여 오픈 포지션이 있는지 확인합니다. 이 함수는 포지션이 이미 열려 있는 경우 TRUE를 반환하고, 없는 경우 FALSE를 반환합니다.

bool  PositionSelect(
   string  symbol      // Symbol name 
 );

주 인수/패러미터로서 우리가 확인하고자 하는 심볼(통화 페어)가 필요하죠. 여기서는 현재 심볼(통화 페어)를 체크하는 것이기에 _symbol을 이용하겠습니다.

이 식이 TRUE로 반환되면 열린 포지션이 매수인지 매도인지 확인하려고 합니다. PositionGetInteger  함수를 쓰겠습니다. POSITION_TYPE 변환자랑 같이 쓰면 열린 포지션의 타입을 반환합니다. 포지션 타입 식별자를 반환하게 되는데 이는 POSITION_TYPE_BUY 혹은 POSITION_TYPE_SELL 중 하나가 됩니다

long  PositionGetInteger(
   ENUM_POSITION_PROPERTY  property_id      // Property identifier
   );

이 경우엔 우리는 어떤 포지션을 열어두었는지 판단하는데에 썼습니다. 만약 반환되어온 값이 매도라면 TRUE 값을 Sell_opened 에 보관하며, 만약 매수라면 TRUE 값을 Buy_opened에 보관하는 식입니다. 나중에 코드에서 매수 또는 매도 조건을 확인할 때 이 두 변수를 사용할 수 있습니다.

이제 매수/매도 설정에 사용할 바의 근접 가격을 저장할 때입니다. 이전에 변수를 선언한 것을 기억하세요.

// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1

   p_close=mrate[1].close;  // bar 1 close price

이 작업을 마쳤으므로 이제 다음 단계로 진행하겠습니다.

/*
    1. Check for a long/Buy Setup : MA-8 increasing upwards, 
    previous price close above it, ADX > 22, +DI > -DI
*/
//--- Declare bool type variables to hold our Buy Conditions
   bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]); // MA-8 Increasing upwards
   bool Buy_Condition_2 = (p_close > maVal[1]);         // previuos price closed above MA-8
   bool Buy_Condition_3 = (adxVal[0]>Adx_Min);          // Current ADX value greater than minimum value (22)
   bool Buy_Condition_4 = (plsDI[0]>minDI[0]);          // +DI greater than -DI

//--- Putting all together   
   if(Buy_Condition_1 && Buy_Condition_2)
     {
      if(Buy_Condition_3 && Buy_Condition_4)
        {
         // any opened Buy position?
         if (Buy_opened) 
         {
            Alert("We already have a Buy Position!!!"); 
            return;    // Don't open a new Buy Position
         }
         mrequest.action = TRADE_ACTION_DEAL;                                // immediate order execution
         mrequest.price = NormalizeDouble(latest_price.ask,_Digits);          // latest ask price
         mrequest.sl = NormalizeDouble(latest_price.ask - STP*_Point,_Digits); // Stop Loss
         mrequest.tp = NormalizeDouble(latest_price.ask + TKP*_Point,_Digits); // Take Profit
         mrequest.symbol = _Symbol;                                         // currency pair
         mrequest.volume = Lot;                                            // number of lots to trade
         mrequest.magic = EA_Magic;                                        // Order Magic Number
         mrequest.type = ORDER_TYPE_BUY;                                     // Buy Order
         mrequest.type_filling = ORDER_FILLING_FOK;                          // Order execution type
         mrequest.deviation=100;                                            // Deviation from current price
         //--- send order
         OrderSend(mrequest,mresult);

이제 매수 기회를 확인할 시간입니다.

위 식이 아까 우리가 준비한 전략을 나타내는 것이므로 분석해봅시다. 우리는 주문을 하기 전에 반드시 충족되어야 하는 각각의 조건에 대해 bool 타입의 변수를 선언하고 있습니다. bool 타입 변수는 TRUEFALSE. 만 포함할 수 있습니다. 따라서 우리의 매수 전략은 4개의 조건으로 나뉩니다.  만약 이들 중 어떠한 조건이라도 만족되면TRUE 값이 우리의 bool 타입 변수에 저장될 것이고, 아닌 경우 FALSE 가 저장될 것입니다. 하나씩 살펴보도록 하죠.

bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]);

여기에 Bars 0, 1 그리고 2MA-8 값들이 있습니다 . 만약 현 바의 MA-8 값이 이전의 Bar 1의 것 보다 크고 Bar 1MA-8 값이Bar 2의 것 보다 크다면 이는 MA-8우상향중이란 의미입니다. 이는 우리의 매수 조건 중 하나를 만족합니다.

bool Buy_Condition_2 = (p_close > maVal[1]); 

이 식은 Bar 1 종가가 같은 기간 (바 1 기간)의 MA-8 값보다 높은지 확인하는 것입니다. 가격이 더 높으면 우리의 두 번째 조건도 만족스러웠으니 다른 조건도 확인할 수 있습니다. 다만 우리가 방금 고려했던 두 가지 조건이 충족되지 않았다면 다른 조건을 확인할 필요가 없을 것입니다. 그렇기 때문에 우리는 다음 식을 이 두 초기 조건(표현)에 포함하기로 결정합니다.

bool Buy_Condition_3 = (adxVal[0]>Adx_Min);

이제 ADX의 현재 값(bar 0의 ADX 값)이 입력 매개 변수에 선언된 최소 ADX 값보다 큰지 확인하려고 합니다. 이 식이 참일 경우, 즉 ADX의 현재 값이 최소 요구 값보다 큽니다. 또한 plusDI값이 minusDI 값보다 큰지 확인하여야 합니다. 이게 다음 식에서 달성되는 부분입니다

bool Buy_Condition_4 = (plsDI[0]>minDI[0]);

이러한 조건이 모두 충족되면 즉, 해당 조건이 충족되면 이미 매수 포지션이 있는 경우 새 매수 포지션이 열리지 않도록 해야 합니다. 이제 코드에서 앞서 선언한 Buy_opened 변수의 값을 확인할 때입니다.

// any opened Buy position?
if (Buy_opened) 
   {
      Alert("We already have a Buy Position!!!"); 
      return;    // Don't open a new Buy Position
   }

Buy_opened가 TRUE이면 다른 매수 포지션을 열지 않아야하니 알림을 표시하고 EA가 다음 틱을 기다리도록 합니다. 하지만, 만약 Buy_opened이 FALSE 인 경우, 우리는 앞서 주문을 보낼 때 선언한 MqlTradeRequest 타입 변수 (mrequest)를 이용하여 기록을 준비합니다.

  • 여기에서의 액션, 즉 거래 타입을 말하는건데, 이는 TRADE_ACTION_DEAL입니다. 왜냐하면 즉각 실행될 매매 주문을 넣기 때문입니다. 만약 주문을 변경할 경우TRADE_ACTION_MODIFY를 사용합니다. 주문을 삭제할 경우TRADE_ACTION_REMOVE.  우리는 MqlTick 타입 latest_price 변수를 통해 최신 매도 매도가를 체크합니다.  주문의 손절매 단가는 StopLoss를 매도가에서 빼는걸로 하며, 이익실현 단가는 매도가격에 TakeProfit을 더하는걸로 얻어집니다. 또한 매도가, StopLoss, TakeProfit 값에 NormalizeDouble 함수를 썼다는 것을 눈치채셨을지도 모르겠습니다. 거래 서버에 보내기 전에 항상 통화 쌍의 자릿수로 이러한 가격을 정규화하는 것이 좋습니다. 
  • 심볼은 현재 심볼입니다 (_Symbol 혹은 Symbol()). 주문 타입은 우리가 주문하는 주문의 타입을 말하는 것으로, 여기서는 매수주문 ORDER_TYPE_BUY. 매도 주문이라면ORDER_TYPE_SELL가 됩니다.
  • order type_filling은 주문 실행 타입입니다; ORDER_FILLING_FOK 는t주문 가격보다 같거나 더 좋은 가격으로 지정된 볼륨에서만 실행될 수 있다는 의미를 담고 있습니다. 주문 심볼에 주문 볼륨이 충분하지 않으면 주문이 실행되지 않습니다.

OrderSend() 함수는 2개의 인수를 받습니다; MqlTradeRequest 타입 변수와 MqlTradeResult 타입 변수.

bool  OrderSend(
   MqlTradeRequest&  request      // query structure
   MqlTradeResult&   result       // structure of the answer
   );

보시다시피 MqlTradeRequest 타입 변수와 MqlTradeResult 타입 변수를 OrderSend를 써서 주문하는데에 썼습니다.

         // get the result code
         if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed
           {
            Alert("A Buy order has been successfully placed with Ticket#:",mresult.order,"!!");
           }
         else
           {
            Alert("The Buy order request could not be completed -error:",GetLastError());
            ResetLastError();           
            return;
           }

이제 주문을 했으니 MqlTradeResult 타입 변수를 써서 주문의 결과를 체크하겠습니다. 만약 주문이 성공적으로 처리됐으면 알려주었으면 하며, 만약 잘 안되었어도 알고싶습니다. MqlTradeResult 타입 변수 ‘mresult’ 를 통하여 Operation 반환 코드에 액세스 가능하며, 주문이 완료되었다면 주문 티켓 번호 에도 가능합니다.

반환 코드 10009OrderSend 요청이 성공적으로 완료되었다는 의미이며, 10008은 주문 요청이 이루어졌다는 의미입니다. 그렇기 때문에 우리는 이 두 가지 반환 코드 중 어떤 것이든 확인하였습니다. 만약 이 중 하나라도 가지고 있다면, 주문이 완료되었거나 주문이 완료되었다고 확신해도 됩니다.

매도 기회를 확인하려면 ADX를 제외하고 구매 기회에 대해 수행한 작업의 반대되는 최소값보다 커야 합니다.

/*
    2. Check for a Short/Sell Setup : MA-8 decreasing downwards, 
    previous price close below it, ADX > 22, -DI > +DI
*/
//--- Declare bool type variables to hold our Sell Conditions
   bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);  // MA-8 decreasing downwards
   bool Sell_Condition_2 = (p_close <maVal[1]);                         // Previous price closed below MA-8
   bool Sell_Condition_3 = (adxVal[0]>Adx_Min);                         // Current ADX value greater than minimum (22)
   bool Sell_Condition_4 = (plsDI[0]<minDI[0]);                         // -DI greater than +DI
   
 //--- Putting all together
   if(Sell_Condition_1 && Sell_Condition_2)
       {
         if(Sell_Condition_3 && Sell_Condition_4)
           {
            // any opened Sell position?
            if (Sell_opened) 
            {
                Alert("We already have a Sell position!!!"); 
                return;    // Don't open a new Sell Position
            }
            mrequest.action = TRADE_ACTION_DEAL;                                 // immediate order execution
            mrequest.price = NormalizeDouble(latest_price.bid,_Digits);          // latest Bid price
            mrequest.sl = NormalizeDouble(latest_price.bid + STP*_Point,_Digits); // Stop Loss
            mrequest.tp = NormalizeDouble(latest_price.bid - TKP*_Point,_Digits); // Take Profit
            mrequest.symbol = _Symbol;                                         // currency pair
            mrequest.volume = Lot;                                            // number of lots to trade
            mrequest.magic = EA_Magic;                                        // Order Magic Number
            mrequest.type= ORDER_TYPE_SELL;                                     // Sell Order
            mrequest.type_filling = ORDER_FILLING_FOK;                          // Order execution type
            mrequest.deviation=100;                                           // Deviation from current price
            //--- send order
            OrderSend(mrequest,mresult);

매수 섹션에서 했던 것처럼, 주문 요청이 완료되기 전에 만족해야하는 각각의 조건을 위해 bool 타입 변수를 선언해야합니다. bool 타입 변수는 TRUE 혹은 FALSE. 만을 담을 수 있기에 우리 매도 전략은 4개의 조건으로 나뉩니다.  만약 이들 중 어떠한 조건이라도 만족되면TRUE 값이 우리의 bool 타입 변수에 저장될 것이고, 아닌 경우 FALSE 가 저장될 것입니다. 매수 쪽에서 했던 것 처럼 하나씩 찬찬히 살펴봅시다

   bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);

여기서 우리는 Bars 0, 12MA-8 값들을 보겠습니다. 만약 현 바의 MA-8 값이 이전 Bar 1의 값보다 낮고, Bar 1MA-8 값이Bar 2 것보다 낮다면, MA-8 값은 하락하고있다는 뜻입니다. 이는 매도 설정 대한 조건 중 하나를 충족합니다.

   bool Sell_Condition_2 = (p_close <maVal[1]); 

이 식은 바 1 종가가 같은 기간(바 1 기간)의 MA-8 값보다 낮은지 확인하는 것입니다. 가격이 더 낮으면 우리의 두 번째 조건도 만족시키게 되며, 따라서 다른 조건도 확인할 수 있습니다. 다만 우리가 방금 고려했던 두 가지 조건이 충족되지 않았다면 다른 조건을 확인할 필요가 없을 것입니다. 그렇기 때문에 우리는 다음 식을 이 두 초기 조건(표현)에 포함하기로 결정합니다.

   bool Sell_Condition_3 = (adxVal[0]>Adx_Min); 

이제 ADX의 현재 값(bar 0의 ADX 값)이 입력 매개 변수에 선언된 최소 ADX 값보다 큰지 확인하려고 합니다. 이 식이 참일 경우, 즉 ADX의 현재 값이 최소 요구 값보다 큽니다. 또한 MinusDI 값이 plusDI 보다 큰지 확인하려고 합니다. 이게 다음 식에서 달성되는 부분입니다

bool Sell_Condition_4 = (plsDI[0]<minDI[0]);

이러한 조건이 충족되면, 즉 우리는 새로운 매수 포지션이 열려있을 경우 새로이 열지 않길 원합니다. 이제 코드에서 앞서 선언한 Buy_opened 변수의 값을 확인할 때입니다.

// any opened Sell position?
            if (Sell_opened) 
            {
                Alert("We already have a Sell position!!!"); 
                return;    // Don't open a new Sell Position
            }

Sell_opened가 참일 경우 다른 매도 포지션를 열 생각이 없으므로, 이를 알려준 다음 EA가 다음 틱을 기다리도록 경고를 표시합니다. 그러나 Sell_opened가 FALSE인 경우, 을 선택하면 매수 주문 때처럼 매도 주문을 요청합니다.

여기서 가장 큰 차이점은 손절매 가격과 이익 실현 가격을 계산하는 방법입니다. 또한 우리는 판매하기 때문에 매수호가으로 판매합니다. 그래서 우리는 최신 입찰 가격을 얻기 위해 MqlTick 타입의 변수 latest_price 을 사용했습니다. 앞서 설명한 바와 같이 여기에 있는 다른 타입은 ORDER_TYPE_SELL입니다.

또한 여기에서 NormalizeDouble 함수를 매수호가, StopLoss 및 TakeProfit 값에 활용했는데, 거래 서버에 보내기 전에 항상 통화 쌍 수를 정상화하는 것이 좋습니다. 

매수 주문과 마찬가지로 매도 주문의 성공 여부도 확인해야 합니다. 그래서 매수 주문과 같은 식을 사용했습니다.

         if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed
           {
            Alert("A Sell order has been successfully placed with Ticket#:",mresult.order,"!!");
           }
         else
           {
            Alert("The Sell order request could not be completed -error:",GetLastError());
            ResetLastError();
            return;
           }
        }


3. Expert Advisor 디버깅 및 테스팅

이 시점에서, 우리는 우리의 전략이 효과가 있는지 아닌지를 알기 위해 우리의 EA를 테스트해야 합니다. EA 코드에 한 두 가지 오류가 있을 수 있어도 이상하지 않기 때문이기도 하죠. 이는 다음 스텝에서 밝혀질 것입니다.

3.1 디버깅

코드를 디버깅하면 코드가 어떻게 한 줄 한 줄씩 실행되는지 알 수 있고(브레이크포인트를 설정하는 경우) 코드에서 오류나 버그를 발견하고 코드를 실제 거래에서 사용하기 전에 신속하게 수정할 수 있습니다.

지금부터 Expert Advisor를 디버깅하는 단계별 프로세스를 살펴보겠습니다. 처음에는 브레이크포인트 있이, 다음엔 브레이크포인트 없이 다룰 예정입니다. 이렇게 하려면 우선 편집기를 닫지 않았는지 확인합니다. 우선 EA 테스트에 사용할 차트를 선택하겠습니다. 에디터 메뉴 바에서 도구를 클릭하고 아래 보이는대로 옵션을 누르세요:

8번 그림. 디버깅 옵션 세팅하기

8번 그림. 디버깅 옵션 세팅하기 

옵션 창이 나타나면 사용할 통화 쌍과 기간/시간 프레임을 선택하고 확인 버튼을 클릭합니다.

9번 그림. 디버거 옵션 윈도우

디버거를 실행하기 전에, 브레이크포인트를 세팅합시다. 브레이크포인트가 있으면 특정 위치나 줄에서 행동이나 퍼포먼스를 확인할 수 있죠. 모든 코드를 한 번에 실행하는 대신, 브레이크포인트가 나타날 때마다 디버거가 중지되어 네트워크 작업을 기다립니다. 이를 통해 우리는 코드를 분석하고 설정된 모든 중단점에 도달할 때 코드를 모니터링하고 동작을 모니터링할 수 있을 것이다. 몇몇 변수값을 확인할 수 있고전부 원하던대로 가고있는지 볼 수 있습니다.

브레이크포인트를 삽입하려면 코드의 브레이크포인트를 설정할 라인으로 이동하십시오. 왼쪽의 코드 줄 테두리 근처의 회색 필드에 두 번 클릭하면 흰색 정사각형이 있는 작고 동그란 파란색 단추가 표시됩니다. 또는 다른 방법으로, 브레이크포인트를 표시할 코드 줄의 아무 곳에나 마우스 커서를 놓고 F9을 누릅니다. 브레이크포인트을 제거하려면 F9를 다시 누르거나 두 번 클릭합니다.

10번 그림. 브레이크포인트 세팅하기

10번 그림. 브레이크포인트 세팅하기

코드를 위해 5개의 다른 라인에 브레이크포인트를 설정할 예정입니다.  

설명을 위해 그것들에 1 에서 5 라벨도 붙일 것입니다.

계속하려면 아래 그림과 같이 7개의 코드 라인에서 브레이크포인트를 설정하십시오. 브레이크포인트 1 은 방금 위에서 만든 것입니다.

11번 그림. 추가적인 브레이크포인트 세팅하기

11번 그림. 추가적인 브레이크포인트 세팅하기

브레이크포인트 설정을 완료하면 코드 디버깅을 시작할 수 있습니다.

디버거를 시작하려면 F5를 누르거나 MetaEditor의 도구 모음에서 녹색 단추를 클릭합니다.

 12번 그림. 디버거 시작하기

12번 그림. 디버거 시작하기

편집자가 가장 먼저 하는 일은 코드를 컴파일하는 것인데, 해당 지점에 오류가 있으면 코드를 표시하고 오류가 없으면 코드가 성공적으로 컴파일되었음을 알려줍니다.

13번 그림. 컴파일 보고서

13번 그림. 컴파일 보고서

코드가 성공적으로 컴파일되었다고 해서 코드에 오류가 없을 수 있는 것은 아닙니다. 코드 작성 방법에 따라 런타임 오류가 발생할 수 있습니다. 예를 들어, 약간의 실수로 인해 우리의 표현식 중 하나라도 제대로 평가되지 않는다면, 코드는 정확하게 컴파일되지만 올바르게 실행되지 않을 수 있지요. 말이 너무 많았네요, 직접 보여드리겠습니다…

디버거가 코드 컴파일을 완료하면 거래 터미널로 이동하고 MetaEditor 옵션 설정에서 지정한 차트에 EA를 첨부합니다. 동시에 EA의 입력 패러미터 섹션이 표시됩니다. 아직 아무것도 조정하지 않았으니 확인 버튼만 클릭하면 됩니다.

14번 그림. 디버깅을 위한 Expert Advisors 입력 패러미터

14번 그림. 디버깅을 위한 Expert Advisors 입력 패러미터

이제 차트의 오른쪽 상단 모서리에 EA가 분명하게 표시됩니다.

일단 EA가 OnTick() 를 시작시키면 브레이크포인트 1에 도달하는 즉시 멈출겁니다.

15번 그림. 디버거가 첫 브레이크포인트에서 멈춤

15번 그림. 디버거가 첫 브레이크포인트에서 멈춤

해당 코드 라인에서 녹색 화살표가 표시됩니다. 이전 코드 라인이 실행되었음을 알려드립니다. 현재 라인을 실행할 준비가 되었습니다.

계속하기 전에 몇 가지 설명을 드리겠습니다. 편집기의 도구모음을 보면 이전에 회색으로 표시되었을 곡선 화살표가 있는 세 개의 버튼이 활성화되었음을 확인할 수 있습니다. 이것은 우리가 지금 디버거를 실행하고 있기 때문입니다. 이러한 버튼/명령은 코드를 단계별로 진행하는 데 사용됩니다(단계 시작, 단계 이동 또는 단계 종료).

16번 그림. Step into 커맨드

16번 그림. Step into 커맨드

Step Into 해당 코드 라인 안에서 한 스텝에서 다음 스텝으로 넘어가면서 어떠한 함수 호출값 으로 들어가는걸 말합니다 버튼을 클릭하거나 F11를 눌러 명령을 실행합니다. (우리는 이 명령을 코드의 단계별 디버깅에서 사용할 것입니다.)

17번 그림. Step over 커맨드

17번 그림. Step over 커맨드

Step over는, 반대로 해당 코드 라인 안에서 어떤 함수 호출값으로도 들어가지 않습니다. 버튼을 클릭하거나 F10를 눌러 명령을 실행합니다.

18번 그림. Step out 커맨드

18번 그림. Step out 커맨드

한 단계 높은 프로그램 단계를 실행하려면 이 버튼을 클릭하거나 Shift+F11을 누릅니다.

또한 에디터의 하단에는 공구함 창이 표시됩니다. 이 창의 디버그 탭에는 다음과 같은 헤더들이 있습니다.

  • File : 호출된 파일명을 표시합니다
  • Function : 파일에서 현재 불린 함수를 표시합니다
  • Line : 해당함수가 있는 파일에서 코드 라인 수를 표시합니다.
  • Expression : 코드 관리중에 알고싶은 식/변수 이름을 적을 수 있는 곳입니다. 
  • Value : Expression 쪽에 입력한 식/변수의 값을 표시합니다.
  • Type : 현재 관리중인 식/변수의 데이터 타입을 표시합니다.

디버깅 과정으로 돌아가서…

다음으로 모니터링에 관심이 있는 코드에서 변수/표현을 입력하는 것입니다. 코드에서 실제로 중요한 변수/표현만 모니터링해야 합니다. 본례에서는 다음을 모니터링합니다.

  • Old_Time (옛 바 시간)
  • New_Time[0] (현재 바 시간)
  • IsNewBar (새 바를 나타내는 플래그)
  • Mybars (이력의 총 바 수) – Our EA depends on it

ADX 값이나, MA-8 값 등의 다른 값도 추가할 수 있습니다.

식/변수를추가하려면, Expression 영역 아래를 더블클릭하거나 Expression 영역 아래에 우클릭 하고 Add 를 윗 그림처럼 고르면 됩니다.

관리하거나 살펴볼 식/변수를 치세요.

19번 그림. 식 관리 창

19번 그림. 식 관리 창

필요한 모든 변수/식을 친다…

20번 그림. 관리할 변수나 식을 추가하기

20번 그림. 관리할 변수나 식을 추가하기

변수가 아직 선언되지 않은 경우 이 변수의 유형은 "Unknown identifier"(정적 변수 제외)입니다.

자 이제 넘어가봅시다…

21번 그림. 실전 Step into 커맨드

21번 그림. 실전 Step into 커맨드

Step into 버튼을 누르거나 F11키를 누르고  무슨 일이 벌어지나 보시기 바랍니다. 계속 이 버튼이나 F11를 눌러 브레이크포인트 2에 도달할때 누르고, 아래 보이는 것처럼 브레이크포인트 4에 도달할때까지 반복한 후 식 관리 창을 보세요.


22번 그림. 변수나 식 지켜보기

22번 그림. 변수나 식 지켜보기

23번 그림. 변수나 식 지켜보기

23번 그림. 변수나 식 지켜보기

24번 그림. 변수나 식 지켜보기

24번 그림. 변수나 식 지켜보기

새로운 틱이 있으면, OnTick() 함수의 주먹 코드라인으로 돌아옵니다. 이제 변수/표현의 모든 값이 새로운 눈금이기 때문에 재설정됩니다. 단, 정적 변수로 선언된 값이 있을 경우 제외됩니다. 우리의 경우엔 하나의 정적 변수 Old_Time이 있습니다.

25번 그림. NewTick 이벤트 변수들의 값들

25번 그림. NewTick 이벤트 변수들의 값들

프로세스를 다시 검토하려면, 계속  F11 키를 누르고 식 관리 창에서 변수를 계속 모니터링하십시오. 디버거를 중지한 다음 모든 브레이크포인트를 제거할 수 있습니다.

보이는 대로 디버그 모드에서는 "새로운 바가 생겼습니다..." 라고 나옵니다.

26번 그림. 디버그 모드에서 Expert Advisor가 메세지를출력합니다

26번 그림. 디버그 모드에서 Expert Advisor가 메세지를출력합니다

디버깅 프로세스를 이번에는 브레이크포인트 없이 실행해보세요. 매 체크표마다 계속 지켜보면, 매수/매도 조건 중 하나라도 충족될 시 거래가 성립될 것이고, 주문이 성공했는지 아니면 그렇지 않은지를 알려주기 위해 코드를 작성했으니까 알림을 보게 될 것입니다.

27번 그림. 디버깅 중 Expert Advisor가 매매를 주문함

27번 그림. 디버깅 중 Expert Advisor가 매매를 주문함

커피 휴식 잠시하면서 EA가 일하도록 좀 시간을 줘도 될 것 같습니다. 돌아와서 돈을 좀 벌었으면 (농담입니다) MetaEditor에서 중지(빨간색) 버튼을 클릭하여 디버깅을 중지합니다.

28번 그림. 디버거 멈추기

28번 그림. 디버거 멈추기

우리가 여기서 실제로 한 것은 우리의 EA가 새로운 바의 오픈 시에만 거래를 확인하고 우리의 EA가 실제로 작동한다는 것을 확인하는 것입니다. 우리의 EA 코드를 수정할 수 있는 여지가 아직 많이 있습니다.

이 시점에서 트레이딩 터미널이 인터넷에 연결되어 있어야 합니다. 그렇지 않으면 터미널이 트레이딩할 수 없기 때문에 디버깅이 작동하지 않습니다.

3.2 EA 전략 테스트하기

현 단계에선 트레이딩 터미널에 같이 깔려있는 Strategy Tester를 테스트해볼 것입니다.   Strategy Tester를 시작하려면, CONTROL+R 를 누르거나 Terminal Menu Bar의 View를 클릭해서 아래 보이는 대로 Strategy Tester를 누르세요

26번 그림. 전략 테스팅(Strategy Testing) 실행하기

26번 그림. 전략 테스팅(Strategy Testing) 실행하기

테스트기(Strategy Tester)가 터미널 하단에 표시되어 있습니다. 모든 테스터 설정을 보려면 테스터를 확장/크기 조정해야 합니다. 이렇게 하려면 빨간색 화살표로 표시된 지점으로 마우스 포인터를 이동합니다(아래 그림 참조).

27번 그림. Strategy Tester 창

27번 그림. Strategy Tester 창

마우스 포인터가 더블 엔드 화살표로 바뀌며, 마우스를 누른 상태에서 선을 위로 끕니다. 설정 탭의 모든 내용이 표시되면 중지하시면 됩니다.

28번 그림. Strategy Tester 세팅 탭

28번 그림. Strategy Tester 세팅 탭

  1. 테스트할 EA를 선택
  2. 테스트할 통화 쌍 선택
  3. 테스트할 기간/타임프레임 선택
  4. 사용자 지정 기간을 선택하고 날짜를 5로 설정
  5. 테스트에 쓸 사용자 지정 기간의 날짜 설정
  6. 정상적으로 실행중입니다
  7. 테스트에 사용할 입금액을 USD로 선택하십시오.
  8. 최적화를 사용 안 함으로 설정(지금 최적화하는 것이 아니라 테스트만 하려고 함)
  9. 테스트를 시작할 준비가 되면 이 버튼을 클릭하십시오.

시작 단추 를 클릭하기 전에 테스터의 다른 탭을 살펴보겠습니다.

에이전트 탭

테스트에서 테스터가 쓸 프로세서 컴퓨터의 프로세서 타입에 따라 다릅니다 제 것은 싱글 코어 컴퓨터였습니다

29번 그림. Strategy Tester 에이전트 탭

29번 그림. Strategy Tester 에이전트 탭

Once the agent, you will see something similar to the figure below

30번 그림. 테스트 도중의 Strategy Tester 에이전트 탭

30번 그림. 테스트 도중의 Strategy Tester 에이전트 탭

저널 탭

테스트 기간 중의 모든 이벤트가 표시되는 곳입니다.

31번 그림. Strategy Tester 저널 탭이 매매 행위를 보여주는 중

31번 그림. Strategy Tester 저널 탭이 매매 행위를 보여주는 중

입력 탭

EA를 위한 패러미터를 표기하는 곳입니다.

32번 그림. Strategy Tester 입력 탭

32번 그림. Strategy Tester 입력 탭

EA를 최적화하고 있다면 동그라미 친 영역에서 값을 설정해야 합니다.

  • Start는 테스터가 시작할 때 쓸 값입니다.
  • Step은 선택한 값의 증가율이고,
  • Stop은 도달했을 때 멈출 값입니다.

그러나 우리의 경우 EA를 최적화하고 있지 않기 때문에 현재로서는 이를 만질 필요가 없습니다.  

모든 것이 설정되면 이제 설정탭으로 돌아가서 [시작] 단추를 클릭하십시오. 그러면 테스터가 작업을 시작합니다. 지금부터는 원한다면 가서 커피를 한 잔 더 마시셔도 되고, 만약 저랑 비슷한 타입이라면 전부 다 보셔도 됩니다. 그 후 저널 탭을 좀 보세요.  

그래프 탭

저널 탭에서 주문에 대한 메시지가 전송되기 시작하면 방금 만든 그래프라는 새 탭으로 이동할 수 있습니다. 그래프 탭으로 전환하면 거래 결과에 따라 그래프가 계속 증가하거나 감소하는 것을 볼 수 있습니다.

33번 그림. Expert Advisor 테스트를 위한 그래프 결과

33번 그림. Expert Advisor 테스트를 위한 그래프 결과

결과 탭

테스트가 끝나면 Results라고 된 또다른 탭을 볼 수 있게 됩니다. Results 탭으로 넘어가보면 방금 시행한 테스트의 요약을 볼 수 있습니다.

 34번 그림. Strategy Tester 결과 탭이 테스트 결과 요약을 보여주는 중

 34번 그림. Strategy Tester 결과 탭이 테스트 결과 요약을 보여주는 중

총 매출총이익, 순이익, 총 매출총손실거래 등을 확인할 수 있습니다. 본 테스트를 위해 선택한 기간 사이에 USD 1,450.0 정도를 가지고 있었다는게 정말 흥미롭네요 최소한 약간의 소득을 올렸습니다.

한가지 확실히 짚고 넘어가죠. Strategy Tester에 표시되는 EA 패러미터의 설정이 EA의 입력 패러미터의 초기 설정과 다르다는 것을 알게 됩니다. 방금 전 EA를 최대한 활용하기 위해 어떤 입력 패러미터건 바꿀 수 있다는 것을 보여드렸습니다. 이동 평균과 ADX에 각각 8의 기간을 사용하는 대신 이동평균의 기간은 10, ADX는 14로 변경하였습니다. 손절매도 30에서 35로 바꿨습니다. 마지막으로 2시간 타임프레임을 쓰기로 했습니다. 잊지마세요, 이건 Strategy Tester 입니다.

테스트 리포트를 보고싶다면 결과 탭 아무데나를 우클릭하면 메뉴를 볼 수 있습니다. 이 메뉴에서, ‘리포트로 저장하기’를 누르세요.

35번 그림. 테스트 결과 저장하기

35번 그림. 테스트 결과 저장하기

저장 대화창이 나타날 건데요, 보고서 이름을 입력하고(원하는 경우 기본 이름을 그대로 두고) 저장 버튼을 클릭합니다. 전체 보고서는 HTML 형식으로 저장됩니다.

테스트가 시행된 차트를 보려면 차트 열기 를 누르면 차트가 나타날 것입니다.

36번 그림. 차트가 테스트를 보여주는 중  

36번 그림. 차트가 테스트를 보여주는 중

이게 끝입니다, 이걸로 우리는 EA를 멋지게 쓰고 테스트했으며 결과를 보는 일만 남았습니다. strategy tester 세팅 탭으로 돌아가 다른 타임프레임/기간 으로 테스팅 해도 됩니다.

과제

다른 통화 쌍, 다른 시간 프레임, 다른 정지 손실, 다른 이익 획득 및 EA의 성능을 테스트해 보십시오. 새 이동 평균 및 ADX 값도 시도할 수 있습니다. 제가 앞에서 말했듯이, 그것이 Strategy Tester의 정수입니다. 저에게 결과를 공유해주시길 바랍니다.

마치며

이 단계별 가이드에서는 개발된 거래 전략을 기반으로 간단한 Expert Advisor 를 만드는 데에 필요한 기본 단계를 살펴볼 수 있었습니다. 또한 디버거를 사용하여 EA에서 오류를 확인하는 방법도 살펴보았습니다. 또한 Strategy Tester를 사용하여 EA의 성능을 테스트하는 방법도 논의했습니다. 이를 통해 우리는 새로운 MQL5 언어의 힘과 안정성을 볼수 있었죠. EA는 아직 완벽하거나 완전하지 않으며 실제 매매에 쓰이기 위해서는 앞으로도 계속 조정될 수 있습니다.

EA는 아직 완벽하거나 완전하지 않으며 실제 매매에 쓰이기 위해서는 앞으로도 계속 조정될 수 있습니다.아직 배울 것이 더 있습니다. MQL5 매뉴얼과 함께 기사를 다시 읽어보시기 바랍니다. 그리고 이 글에서 배운 모든 것을 시도해 보세요. 저는 당신이 머지않아 훌륭한 EA 개발자가 될 것이라고 확신합니다.

즐거운 코딩 되시길


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

파일 첨부됨 |
my_first_ea.mq5 (11.86 KB)
구글 차트 API와 표준 라이브러리 클래스를 이용하여 정보 보드 만들기 구글 차트 API와 표준 라이브러리 클래스를 이용하여 정보 보드 만들기
MQL5 프로그래밍 언어는 주로 자동화된 거래 시스템과 복잡한 기술 분석 수단을 만드는 것을 목표로 합니다. 하지만 이것 외에도, 그것은 우리가 시장 상황을 추적하기 위한 흥미로운 정보 시스템을 만들 수 있게 해주며, 거래자와의 반품 연결을 제공합니다. 본 문서는 MQL5 표준 라이브러리 구성요소를 다룰 것이며, 목표 달성을 위한 실전 사례 또한 적어두었습니다. 또한 차트 작성에 쓰인 구글 차트 API의 실제 예시 또한 실려있습니다.
MetaTrader5가 주는 새로운 기회 MetaTrader5가 주는 새로운 기회
전 세계 투자자들에게 사랑 받은 MetaTrader4는 그 자체로 정말 완벽해 보였죠. 빠른 처리 속도, 안정성, 다양한 인디케이터 작성 방법, 엑스퍼트 어드바이저, 거래 시스템, 그리고 선택 가능한 브로커의 수까지, 다른 터미널과는 비교가 불가능할 정도였으니까요. 하지만 시간이 흘러 이제 우리는 MetaTrader4와 MetaTrader5 사이의 선택의 길에 놓여 있는데요. 이 글에서는 MetaTrader5의 주요 차별점에 대해 이야기하도록 하겠습니다.
Expert Advisor에서의 자금 관리용 함수들 Expert Advisor에서의 자금 관리용 함수들
거래 전략의 개발은 주로 시장 진입과 퇴출을 위한 패턴을 찾는 것뿐만 아니라 포지션을 유지하는 것에 초점을 맞추고 있습니다. 만약 일부 패턴을 자동 트레이딩을 위한 공식으로 만들 수 있다면, 투자자는 자동 투자 모드에서 오픈 포지션을 보장하기 위해서 안전한 수준의 모기지 자금뿐만 아니라 포지션의 양, 마진의 크기를 계산해야하는 문제에 직면하게 됩니다. 이 글에서 우리는 그러한 계산을 할 수 있는 간단한 예시를 보이기 위해 MQL5 언어를 사용할 것입니다.
MQL5로 틱 인디케이터 만들기 MQL5로 틱 인디케이터 만들기
이 글에서는 가격을 틱 차트로 나타내는 틱 인디케이터와 특정 개수의 틱을 이용해 캔들을 그리는 캔들 인디케이터 두 가지의 작성 방법을 다룰 겁니다. 두 인디케이터 모두 가격 정보를 파일로 만들어 인디케이터 재가동 시 저장된 데이터(다른 프로그램에서도 이용 가능)를 이용합니다.