애플리케이션을 통한 MQL5 함수의 이해
소개
프로그래밍 세계에는 그 중요성에 따라 많이 사용하고 듣는 매우 인기 있는 용어가 있는데 그것은 바로 함수입니다. 이 기사에서는 매우 기능적이고 고품질의 소프트웨어를 만드는 방법을 배우기 위해 함수에 대해 자세히 알아볼 것입니다. 우리는 함수가 무엇인지, 함수를 사용해야 하는 이유와 애플리케이션에서 함수를 사용하는 방법에 대해 알아볼 것입니다. 그 후 모든 트레이딩 시스템에서 사용할 수 있는 몇 가지 간단한 함수를 예로 들어 이 글을 통해 배울 내용을 적용해 보겠습니다. 이 흥미로운 주제를 다루기 위해 이 글에서 다룰 주요 아이디어는 다음과 같습니다:
함수 정의
이 부분에서는 프로그래밍에서 함수라는 것과 그 유형 그리고 함수를 사용해야 하는 이유에 대해 알아볼 것입니다. 함수는 의미 있는 이름으로 선언된 코드 블록으로서 호출되어 특정한 작업을 수행하며 소프트웨어의 다른 부분에서도 사용될 수 있습니다. 소프트웨어의 여러 부분 또는 많은 소프트웨어에서 소프트웨어가 수행해야 하는 특정한 작업이 있을 경우 이 작업을 수행하는 함수나 코드 블록을 만든 다음 전체 코드를 다시 작성하지 않고 해당 부분에서만 호출하므로 함수는 이 글을 통해 보게 될 것처럼 지저분한 코드가 많지 않고 코드를 추상화 하는 방법이라고 할 수 있습니다. 이러한 함수에는 크게 내장 함수와 사용자 정의 함수의 두 가지 유형이 있습니다. 내장 함수는 프로그래밍 언어 자체에서 이미 만들어진 함수이며 사용자 정의 함수는 필요에 따라 사용자가 직접 만들 수 있는 함수입니다. 이 글에서는 사용자 정의 함수에 중점을 두겠습니다. 우리는 이 유형에 대해 자세히 알아보고 이러한 유형의 기능을 사용해야 하는 이유와 그 중요성 또는 그 사용시의 특징을 살펴 보겠습니다.
주식이 최대 하락폭에 도달한 경우 모든 미체결 주문을 청산하는 작업을 소프트웨어의 여러 부분에서 수행해야 한다고 가정해 보겠습니다. 여기서 함수를 만들고 이 작업을 수행하는 데 필요한 모든 코드 또는 로직을 포함한 다음 다른 부분에서 이 함수를 호출하는 것이 좋습니다. 이 작업을 수행하기 위해 여러 부분에서 동일한 코드를 작성하고 반복하는 것은 좋지 않거나 부담스러울 수 있습니다.
만약 여러분이 이러한 유형의 함수를 왜 사용해야 하는지 묻는다면 이 질문에 대한 답을 통해 사용자 정의 함수 사용의 특징을 알아볼 수 있을 것입니다. 그 내용은 다음과 같습니다:
- DRY의 개념을 적용하는 데 도움이 됩니다(반복하지 말기): 사용자 정의 함수를 사용하면 같은 코드를 계속 반복하지 않고 작업을 수행할 수 있는 함수를 만든 다음 소프트웨어의 적절한 부분에서 이 함수를 호출할 수 있습니다.
- 재사용 가능성: 함수를 만든 후에는 언제든지 재사용할 수 있습니다.
- 분할과 정복의 개념을 적용하는 데 도움이 됩니다: 소프트웨어를 만들 때 문제를 해결하기 위해 복잡한 코드를 만들 수 있지만 큰 문제를 작은 문제로 나누고 각각의 문제를 함수를 통해 해결하면 큰 문제를 해결하려는 목표를 달성하는 데 매우 유용할 수 있습니다.
- 코드의 가독성과 이해도를 높이는 데 도움이 됩니다: 함수를 사용하면 함수를 통해 특정 문제를 처리하고 특정 작업을 수행하기 때문에 코드가 더 체계적으로 정리되어 가독성이 높아집니다.
- 추상화 개념을 적용하는 데 도움이 됩니다: 함수를 사용하면 코드를 추상화 할 수 있습니다. 함수를 사용하지 않으면 함수를 사용하는 것보다 더 많은 코드를 작성해야 할 수 있습니다.
- 캡슐화 개념을 적용하는 데 도움이 됩니다: 함수를 사용하면 함수를 사용하지 않을 때보다 코드와 데이터를 더 안전하게 보호하고 관리할 수 있습니다.
- 디버깅 프로세스가 개선됩니다: 함수를 사용하면 오류를 훨씬 쉽게 탐색하고 해결할 수 있어 오류를 개선하는 데 도움이 됩니다.
앞에서 함수를 사용할 경우의 특징에 대해 살펴본 내용에 따르면 소프트웨어에서 이러한 사용자 정의 함수를 사용하면 얼마나 많은 이점이 있는지 쉽게 알 수 있습니다.
함수 구조
이 부분에서는 함수와 함수의 구조에 대해 다음 두 단계를 통해 자세히 알아보겠습니다:
- 함수 선언 또는 정의
- 함수 호출
먼저 새 함수를 정의하거나 선언해야 하므로 우리는 다음과 같은 구조와 비슷한 작업을 수행해야 합니다:
returnedDataType functionName(param1, param2)
{
bodyOfFunction
} - (returnedDataType): 함수가 실행된 후 반환해야 하는 데이터 유형입니다.
- (functionName): 함수의 이름이며 함수가 수행하는 작업에 따라 이 이름을 지정합니다.
- (param1, param2): 여기에 변수를 지정하지 않을 수 있으므로 필요한 경우 변수나 입력 힌트를 추가합니다.
- (bodyOfFunction): 작업을 수행할 모든 코드를 지정합니다.
이를 간단한 예제에 적용해 보겠습니다. 두 값을 더하는 작업을 수행하는 간단한 함수를 만들어야 하는 경우 다음 코드 블록을 통해 할 수 있습니다:
//addition function // returned data type is an integer - the name of the function is add - parameters or arguments are two int variables val1 and val2 int add(int val1, int val2) { //body of function that we need the function to perform when calling it //create a result new variable to be assigned by the result of val1 and val2 addition int result = val1+val2; //Print result in the experts tab Print(result); //returning value return result; }
함수를 정의한 후에는 두 번째 단계인 함수를 호출하는 단계를 수행해야 합니다. 우리는 소프트웨어 코드의 원하는 부분에서 함수 이름을 호출하고 원하는 매개 변수를 지정하면 됩니다. 예제로 돌아가서 함수를 호출하려면 다음과 같이 하면 됩니다:
//calling our defined function by its name and specifying arguments add(5,15);
함수를 호출하면 다음 그림과 같이 함수와 지정된 인수에 따라 EA 탭에서 20이란 값의 결과를 얻을 수 있습니다.
![]()
앞의 예는 함수를 사용하여 수행할 수 있는 작업의 예에 불과하지만 우리가 함수에서 사용할 수 있는 많은 특성을 보여줍니다. 다음은 그 중 일부입니다.
인수가 있는 함수
지난 add 함수 예제에서 val1과 val2라는 두 개의 정수 변수를 사용했는데 이러한 변수는 함수에서 인수로 간주됩니다. 이러한 인수는 정수, 문자열, ...등과 같은 데이터 유형일 수 있습니다. 이들은 지난 예제에서 보았던 것과 같은 정수 변수였습니다. 우리는 다음과 같은 문자열 인수의 또 다른 예를 볼 수 있습니다:
//sayHello function // returned data type is string - name of function is sayHello - parameters or arguments are two string variables greeting and name string sayHello(string greeting, string name) { //body of function that we need the function to perform when calling it //create a result new variable to be assigned by the result of greeting and name addition string result = greeting+name; //Print result in the experts tab Print(result); //returning value return result; }
이 함수는 앞서 설명한 방식대로 호출할 수 있으며 실행 후 결과는 다음과 같습니다:
![]()
이들은 또한 우리가 함수의 본문을 실행하기 위한 매개변수나 인수로서 우리는 함수에서 필요한 데이터 유형에 따라 데이터 유형을 혼합하여 사용할 수도 있습니다. 이러한 인수는 필요에 따라 숫자가 될 수도 있습니다.
인수가 없는 함수
매개 변수나 인수를 지정하지 않고 함수를 선언하거나 정의할 수도 있으며 의미 있는 함수명을 정한 다음 인수를 비워두고 작업을 수행할 함수 본문을 채워서 완성한 후 다음과 같이 인수를 지정하지 않고 함수를 호출할 수도 있습니다:
//sayHello function // returned data type is a string - the name of the function is sayHello - no parameters string sayHello() { //body of the function that we need the function to perform when calling it //create a result new variable to be assigned by the result of greeting and name addition string greeting= "Hello, "; string name= "World!"; string result = greeting+name; //Print the result in the experts' tab Print(result); //returning value return result; }
함수를 호출하면 다음과 같이 됩니다:
sayHello();
결과는 앞서 인수가 있는 함수에서 살펴본 것과 동일합니다. 왜냐하면 함수의 본문이 동일하기 때문입니다.
기본값이 있는 함수
우리는 함수를 정의하고 매개변수의 초기값 또는 기본값을 지정하지만 원하는 값으로 이들을 변경하거나 업데이트할 수 있습니다. 동일한 예를 적용할 때 다음과 같이 할 수 있습니다.
//defining function with default values string sayHello(string greeting= "Hello, ", string name="World!") { string result = greeting+name; Print(result); return result; }
그런 다음 우리는 기본값과 기본값을 업데이트할 경우의 차이를 식별하기 위해 함수를 두 번 호출 할수도 있습니다. 만약 우리가 기본값을 업데이트할 것이라면 우리는 매개 변수를 지정할 수 있지만 지정하지 않으면 함수는 다음과 같이 기본값을 반환합니다.
sayHello(); sayHello("Hi, ", "Developer!");
결과는 다음과 같습니다:

매개 변수 전달
함수 값으로 전달할 수 있으며 이러한 값은 앞서 언급했듯이 int, 문자열, 배열 등 모든 유형의 데이터가 될 수 있습니다. 매개변수를 값으로 전달하면 함수의 원래 변수는 동일하거나 변경되지 않은 상태로 유지됩니다. 왜냐하면 매개변수 값을 함수에 전달한 것이기 때문입니다. 원래의 변수를 업데이트해야 하는 경우 참조로 함수에 매개변수를 전달할 수도 있습니다.
다음은 참조 전달에 대해 언급한 내용을 이해하기 위한 간단한 예시입니다.
//passing by reference void updateNums(int &val1, int &val2) { val1*=2; val2/=2; }
그런 다음 새로운 변수를 만든 다음 그 값을 출력하고 이 새 변수를 매개변수로 사용하여 함수를 호출하고 호출 후 그 값을 출력하여 차이를 알아봅니다:
//new variables int firstNum = 10; int secondNum = 20; //before calling function Print("before calling: "); Print(firstNum, " - " ,secondNum, "\n"); // calling updateNums(firstNum, secondNum); // after calling Print("after calling: "); Print(firstNum, " - " ,secondNum, "\n");
따라서 두 번의 인쇄 결과는 첫 번째는 새로운 변수 10과 20의 값이 될 것이고 호출 후에는 함수 본문에 따라 20과 10으로 업데이트한 후의 값을 찾을 수 있습니다. 결과는 다음과 같음을 알 수 있습니다:

반환 연산자
값을 반환하는 함수가 있는 경우 반환 연산자가 있어야 합니다. 함수 작업에 따라 함수에 반환 연산자가 두 개 이상 있을 수 있지만 함수가 값을 반환하는 경우 함수의 마지막 줄에 반환 연산자가 하나 이상 반드시 있어야 합니다. 이 반환 연산자는 모든 유형이 될 수 있지만 배열은 될 수 없습니다. 그러나 배열에서 요소를 반환할 수 있습니다. 함수가 배열을 반환하도록 하려면 앞서 설명한 것과 같이 참조로 배열을 함수에 전달하면 됩니다.
다음은 앞서 살펴본 예제 중 반환 연산자가 있는 예제입니다.
string sayHello(string greeting= "Hello, ", string name="World!") { string result = greeting+name; Print(result); return result; }
Void 유형 함수
값을 반환하지 않는 함수가 있는 경우 이 유형은 값을 반환하지 않는 void 유형 함수를 사용합니다. 이 유형의 함수에 매개 변수를 전달할 수 있으며 반환 연산자가 필요하지는 않습니다. 다음은 이러한 유형의 함수에 대한 예입니다.
void add(int val1, int val2) { int result= val1+val2; }
함수 오버로딩
함수를 정의할 때 동일한 작업을 수행하기 위해 동일한 이름으로 여러 함수를 정의해야 하지만 다른 매개변수를 사용해야 하는 경우가 있습니다. 예를 들어 덧셈 작업이 있는데 이 두 개의 값에 이 덧샘 작업을 수행해야 하고 또한 세 개의 값에 이 덧샘 작업을 수행해야 하는 경우 같은 이름의 두 개의 함수를 만들지만 작업에 따라 매개 변수를 변경합니다. 다시말해 오버로딩 함수는 동일한 작업을 수행하지만 다른 매개 변수를 사용하는 함수입니다.
이러한 서로 다른 매개변수는 서로 다른 데이터 유형의 매개변수이거나 동일한 데이터 유형의 숫자 또는 둘 다일 수 있습니다. 다음은 데이터 유형은 같지만 매개변수 개수가 다른 오버로딩 함수의 예시입니다:
void overloadingFun(int val1, int val2) { int result=val1+val2; } void overloadingFun(int val1, int val2, int val3) { int result=val1+val2+val3; }
보시다시피 함수는 같지만 매개 변수가 다릅니다. 함수를 호출할 때 우리가 함수 이름을 입력하고 이 두 가지 함수가 나타나면 우리에게 필요한 작업에 따라 필요한 것을 선택할 수 있습니다. 다음은 데이터 유형에 따라 매개변수가 다른 오버로딩 함수의 예시입니다:
void overloadingFun(int val1, int val2) { int result=val1+val2; } void overloadingFun(string message, int val1, int val2) { int result=message+val1+val2; }
함수를 호출할 때 매개변수에 따라 필요한 함수를 선택할 수도 있습니다.
함수 응용 프로그램
이 부분에서는 코딩 과정을 더 쉽게 만들기 위해 사용하는 사용자 정의 함수의 이점을 살펴보고 간단한 애플리케이션을 만들어 보겠습니다. 이러한 애플리케이션을 만든 후에는 소프트웨어의 다른 부분에서 호출하거나 다른 소프트웨어에 포함시켜 호출할 수 있습니다.
뉴스 알림 앱
경제 뉴스의 발표에 거래하는 것은 매우 위험하며 뉴스 중에는 거래하지 말라는 전문가의 조언이 많다는 것을 모두 알고 있습니다. 경제 달력은 거시 경제 뉴스와 지표에 대한 설명, 날짜, 시간 및 중요도, 이러한 경제 이벤트의 발표와 함께 해당 내용을 업데이트하고 그에 따라 거래할 수 있도록 도와주는 달력입니다. MetaTrader 5 거래 터미널에도 이 캘린더가 있습니다. 도구 상자 창에서 탭을 찾을 수 있으며 중요도, 통화 및 국가 측면에서 보고자 하는 항목을 선택할 수 있습니다. 경제 달력으로 작업하기 위한 기본 내장 함수도 있으며 다음 링크를 통해 MQL5 문서에서 모두 확인할 수 있습니다:
우리는 뉴스 중 거래를 피하기 위해 뉴스 경제 달력을 수동으로 확인하거나 뉴스가 다가올 때 알려주는 앱을 만들어 거래를 중지해야 합니다. 이 작업은 이러한 알람이 계속 작동하도록 하는 작업이므로 모든 거래 시스템이나 소프트웨어의 많은 부분에서 필요합니다. 우리는 이를 위한 함수를 생성한 다음 쉽게 호출할 수 있습니다. 다음 단계를 통해 이를 수행할 것입니다:
이 앱은 EA가 될 것이며 글로벌 범위에서 함수 이름에 (isNewsComing)이라는 부울 유형을 만들 것입니다. parameters ()를 추가하지는 않을 것입니다.
bool isNewsComing() 함수의 본문은 값 이름으로 배열을 만들고 그 유형은 실제 값과 같은 뉴스 릴리스의 값인 (MqlCalendarValue)가 됩니다.
MqlCalendarValue values[]; (iTime)을 사용하여 하루의 시작 시간을 정의하여 (startTime)이라는 이름의 새로운 날짜 시간 변수를 선언한 후 바의 시작 시간을 반환하고 (endTime)에 새로운 datetime 변수를 선언 한 후 정의된 시작 시간과 초 수와 동일한 하루의 종료 시간을 반환하는 (PeriodSecond) 함수를 사용하여 현재 날짜를 정의해야 합니다.
datetime startTime=iTime(_Symbol,PERIOD_D1,0); datetime endTime=startTime+PeriodSeconds(PERIOD_D1);
시작 시간과 종료 시간을 정의하여 시간 범위를 결정하고 현재 국가 및 통화별로 정렬하는 CalendarValueHistory 함수를 사용하여 하루의 모든 이벤트 값의 배열을 가져오고 매개 변수는 값, 시작 시간, 종료 시간, 국가 및 통화에 대한 배열입니다.
CalendarValueHistory(values,startTime,endTime,NULL,NULL);
우리는 루프를 생성할 것입니다. 이 루프는 생성된 (i) int 변수에 대해 (0) 값으로 시작하여 (i)를 한 값씩 증가시며 (i)가 값의 배열의 크기보다 작으면 루프를 계속 반복합니다.
for(int i=0; i<ArraySize(values); i++)
for 루프의 본문은 이벤트 변수를 생성하고 있으며, 그 유형은 이벤트 설명을 위한 (MqlCalendarEvent)이며 (CalendarEventById)에서 사용할 수 있습니다.
MqlCalendarEvent event; (CalendarEventById)와 (CalendarEventById) 매개 변수를 사용하여 ID로 이벤트 설명을 가져옵니다. 매개 변수는 event_id와 이벤트입니다.
CalendarEventById(values[i].event_id,event); 국가 변수를 생성하면 그 유형은 국가 설명을 위한 (MqlCalendarCountry)가 되며 (CalendarCountryById)와 함께 사용할 수 있습니다.
MqlCalendarCountry country; (CalendarCountryById) 함수를 사용하여 ID로 국가 설명을 가져오고 매개 변수는 country_id 및 국가입니다.
CalendarCountryById(event.country_id,country); 현재 심볼 또는 통화 뉴스를 기준으로 이벤트를 필터링하도록 조건을 설정합니다. 뉴스의 중요도는 중간 또는 높은 경우입니다.
if(StringFind(_Symbol,country.currency)<0) continue; if(event.importance==CALENDAR_IMPORTANCE_NONE) continue; if(event.importance==CALENDAR_IMPORTANCE_LOW) continue;
알림 시간 범위를 뉴스 시간 30초 전으로 설정합니다.
if(TimeCurrent()>=values[i].time-30*PeriodSeconds(PERIOD_M1) && TimeCurrent()<values[i].time+30*PeriodSeconds(PERIOD_M1))
그런 다음 시스템 트레이딩 탭에 이벤트 이름과 ( 출시 예정!)이라는 텍스트가 포함된 메시지를 인쇄해야 합니다. 거래 중지...)
Print(event.name, " is coming! Stop Trading...");
반환되는 값은 참입니다.
return true;
조건이 거짓이면 루프를 완료하고 함수를 종료하기 위해 거짓을 반환합니다.
return false;
그런 다음 OnTick 함수에서 함수를 호출하고 참을 반환하면 (뉴스가 나옵니다...!)라는 메시지를 인쇄해야 합니다.
if(isNewsComing()) { Print("News is comming...!"); }
이제 우리는 함수를 만들고 호출했습니다. 필요에 따라 소프트웨어의 모든 부분에서 사용할 수 있습니다. 다음은 전체 코드를 다시 쉽게 읽을 수 있도록 한 전체 블록 코드이며 모든 애플리케이션의 소스 코드 파일이 문서에 첨부되어 있습니다.
//+------------------------------------------------------------------+ //| News Alert Function | //+------------------------------------------------------------------+ void OnTick() { if(isNewsComing()) { Print("News is comming...!"); } } //+------------------------------------------------------------------+ bool isNewsComing() { MqlCalendarValue values[]; datetime startTime=iTime(_Symbol,PERIOD_D1,0); datetime endTime=startTime+PeriodSeconds(PERIOD_D1); CalendarValueHistory(values,startTime,endTime,NULL,NULL); for(int i=0; i<ArraySize(values); i++) { MqlCalendarEvent event; CalendarEventById(values[i].event_id,event); MqlCalendarCountry country; CalendarCountryById(event.country_id,country); if(StringFind(_Symbol,country.currency)<0) continue; if(event.importance==CALENDAR_IMPORTANCE_NONE) continue; if(event.importance==CALENDAR_IMPORTANCE_LOW) continue; if(TimeCurrent()>=values[i].time-30*PeriodSeconds(PERIOD_M1) && TimeCurrent()<values[i].time+30*PeriodSeconds(PERIOD_M1)) { Print(event.name, " is coming! Stop Trading..."); return true; } } return false; } //+------------------------------------------------------------------+
Lotsize calc 앱
우리는 위험률과 최대 손실을 핍 단위로 결정한 후 최적의 랏 크기를 계산할 수 있는 앱을 만들고 위험률과 진입 가격, 손절 가격을 결정한 후 최적의 랏 크기를 계산하는 오버로드 함수를 만들 것입니다. 다음 단계와 같이 우리는 이 앱을 스크립트로 만들 것입니다:
OptimalLotSize라는 이름의 함수를 더블로 생성하고 매개 변수는 다음과 같이 두 개로 설정합니다. 첫 번째 함수의 경우 최대 위험 비율의 더블 변수, 최대 손실의 더블 변수(핍 단위).
double OptimalLotSize(double maxRiskPrc, double maxLossInPips)
그런 다음 이러한 매개 변수에 대해 수행해야 할 작업을 지정합니다. 먼저 계정의 식별자인 적절한 계정 속성 값을 반환하는 (AccountInfoDouble) 함수를 사용하여 계정 예탁 자산 평가 총액 값을 (ENUM_ACCOUNT_INFO_DOUBLE)로 정의하고 값을 통해 알림을 만듭니다.
double accEquity = AccountInfoDouble(ACCOUNT_EQUITY); Alert("accEquity: ", accEquity);
지정된 심볼의 해당 속성 심볼명의 변형을 반환하는 (SymbolInfoDouble) 함수를 사용하여 심볼 계약 크기를 정의하고 현재 심볼을 반환하는 (_Symbol), 이 반환된 값으로 알림이 필요하면 (SYMBOL_TRADE_CONTRACT_SIZE)가 될 prop_id를 (ENUM_SYMBOL_INFO_DOUBLE) 값 중 하나로 반환합니다.
double lotSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_CONTRACT_SIZE); Alert("lotSize: ", lotSize);
핍 값 계산 및 해당 값으로 알림 받기
double tickValue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); Alert("tickValue: ", tickValue);
정의된 계좌 예탁 자산 평가 총액에서 손실의 최대값을 계산하고 이 값으로 알림을 받습니다.
double maxLossDollar = accEquity * maxRiskPrc; Alert("maxLossDollar: ", maxLossDollar);
계산된 최대 손실 값을 기준으로 시세 통화로 최대 값을 계산하고 이 값으로 알림을 반환합니다.
double maxLossInQuoteCurr = maxLossDollar / tickValue; Alert("maxLossInQuoteCurr: ", maxLossInQuoteCurr);
최적의 랏 크기를 계산하고 해당 값이 포함된 알림을 반환합니다.
double OptimalLotSize = NormalizeDouble(maxLossInQuoteCurr / (maxLossInPips * 0.0001)/ lotSize,2); Alert("OptimalLotSize: ", OptimalLotSize);
반환 연산자는 더블 유형 값으로 OptimalLotSize를 반환합니다.
return OptimalLotSize; 그 후 최대 위험 비율, 진입 가격 및 손절매 가격에 대한 세 가지 더 유형의 매개 변수를 전달하여 오버로딩 함수를 생성합니다.
double OptimalLotSize(double maxRiskPrc, double entryPrice, double stopLoss)
진입 가격과 손절매 가격의 입력 파라미터를 기준으로 최대 손실을 핍 단위의 절대값으로 정의한 다음 0.0001로 나눕니다.
double maxLossInPips = MathAbs(entryPrice - stopLoss)/0.0001;
반환 연산자는 최대 위험 비율과 최대 손실(핍)을 매개변수로 하는 OptimalLotSize 함수가 됩니다.
return OptimalLotSize(maxRiskPrc,maxLossInPips); 그런 다음 다음과 같이 필요에 따라 OnStart() 부분의 두 함수 중 하나를 호출할 수 있습니다.
OptimalLotSize(0.01, 1.12303, 1.11920);
다음은 이러한 유형의 앱을 만들기 위해 이러한 유형의 함수를 생성하는 전체 코드입니다.
//+------------------------------------------------------------------+ //| lotSize Calc Function | //+------------------------------------------------------------------+ void OnStart() { OptimalLotSize(0.01, 1.12303, 1.11920); } //+------------------------------------------------------------------+ double OptimalLotSize(double maxRiskPrc, double maxLossInPips) { double accEquity = AccountInfoDouble(ACCOUNT_EQUITY); Alert("accEquity: ", accEquity); double lotSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_CONTRACT_SIZE); Alert("lotSize: ", lotSize); double tickValue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); Alert("tickValue: ", tickValue); double maxLossDollar = accEquity * maxRiskPrc; Alert("maxLossDollar: ", maxLossDollar); double maxLossInQuoteCurr = maxLossDollar / tickValue; Alert("maxLossInQuoteCurr: ", maxLossInQuoteCurr); double OptimalLotSize = NormalizeDouble(maxLossInQuoteCurr / (maxLossInPips * 0.0001)/ lotSize,2); Alert("OptimalLotSize: ", OptimalLotSize); return OptimalLotSize; } //+------------------------------------------------------------------+ double OptimalLotSize(double maxRiskPrc, double entryPrice, double stopLoss) { double maxLossInPips = MathAbs(entryPrice - stopLoss)/0.0001; return OptimalLotSize(maxRiskPrc,maxLossInPips); } //+------------------------------------------------------------------+
이 스크립트를 실행하면 다음과 같은 경고가 표시됩니다.

앞서 언급한 애플리케이션과 마찬가지로 코드를 다시 작성하지 않고도 다른 소프트웨어 부분에서 쉽게 사용하고 호출하여 작업을 수행할 수 있는 lotSize Calc 함수가 있습니다.
모든 앱 닫기
여기서 이 작업을 수행하기 위해 소프트웨어의 적절한 부분에서 사용하거나 호출할 수 있는 함수를 만들어 미체결 주문과 지정가 주문을 청산할 수 있는 스크립트를 만들어야 합니다. 다음 단계를 통해 만들수 있습니다:
(Trade.mqh) 파일에 모든 거래 함수를 포함시키기 위해 사전 처리 또는 #include를 사용하여 코드에 Trade 클래스를 포함시킵니다.
#include <Trade/Trade.mqh> 소프트웨어에서 사용할 CTrade 클래스 유형으로 객체 생성하기
CTrade trade;
전역 범위에서도 인수가 없는 void closeAll 함수를 만들어야 합니다.
void closeAll() 함수의 본문에서 미결 주문을 확인하기 위해 for 루프를 생성합니다.
for(int i=PositionsTotal()-1; i>=0; i--)
루프의 본문, ulong posTicket 변수를 생성하고 여기에 보유한 주문의 티켓을 할당합니다.
ulong posTicket=PositionGetTicket(i);
trade.PositionClose(posTicket)를 사용하여 보유한 거래를 청산합니다.
trade.PositionClose(posTicket);
이러한 주문을 감지하는 또 다른 for 루프를 생성하고 해당 티켓을 posTicket의 ulong 변수에 할당하고 감지된 티켓별로 펜딩 주문을 삭제합니다.
for(int i=OrdersTotal()-1; i>=0; i--) { ulong posTicket=OrderGetTicket(i); trade.OrderDelete(posTicket); }
그런 다음 OnStart() 부분에서 이 함수를 호출할 수 있습니다.
closeAll();
이 스크립트를 실행하면 모든 주문을 청산하고 삭제합니다. 다음은 하나의 블록에 이 closeAllApp을 생성하는 전체 코드입니다:
//+------------------------------------------------------------------+ //| closeAll Function | //+------------------------------------------------------------------+ #include <Trade/Trade.mqh> CTrade trade; //+------------------------------------------------------------------+ void OnStart() { closeAll(); } //+------------------------------------------------------------------+ void closeAll() { //close all open positions for(int i=PositionsTotal()-1; i>=0; i--) { ulong posTicket=PositionGetTicket(i); trade.PositionClose(posTicket); } //delete all pending orders for(int i=OrdersTotal()-1; i>=0; i--) { ulong posTicket=OrderGetTicket(i); trade.OrderDelete(posTicket); } } //+------------------------------------------------------------------+
위에서 언급한 애플리케이션은 사용자 정의 함수로 만들 수 있는 애플리케이션의 예시일 뿐이며 트레일링 스톱 및 거래 관리 애플리케이션, 드로다운 안전화 도구 등 필요에 따라 다른 애플리케이션이나 기능을 개발하거나 만들 수 있습니다.
결론
이 기사에서 살펴본 바와 같이 여러분은 소프트웨어에서 함수를 사용하는 것이 중요하다는 것을 알게 되셨을 것입니다. 다음과 같이 함수를 사용할 때 얻을 수 있는 이점이 있기 때문일 것입니다:
- 프로그래밍에 DRY(반복하지 않기) 개념을 적용하는 데 도움이 됩니다.
- 큰 문제를 작은 문제로 나누어 처리하면 큰 문제를 처리하는 데 도움이 됩니다.
- 코드 가독성 높이기.
- 재사용 가능성.
- 코드 추상화하기.
- 코드 캡슐화하기.
- 디버깅 개선.
또한 인수가 있는 함수, 인수가 없는 함수, 기본값이 있는 함수 등 함수의 구조와 모든 특성을 알아보고 내장 함수와 사용자 정의 함수 등을 만들고 정의하는 방법을 잘 이해하셨을 것입니다. 모든 데이터 유형을 사용하여 함수를 정의하고 이를 기반으로 반환 연산자를 처리할 수 있으며 동일한 작업을 수행하기 위해 이름은 같지만 인수가 다른 여러 오버로드 함수를 만드는 방법에 대해 알아보기도 했습니다.
함수를 만드는 애플리케이션을 예제로 공유하여 두 가지 다른 함수를 만들면서 주제를 더 깊이 이해하는 데 많은 도움이 되었을 것입니다:
- New Alert App: 중요한 뉴스가 있을 때 알림을 받기 위해 소프트웨어의 어느 부분에서나 사용하거나 호출할 수 있습니다. newsAlertApp의 소스 코드가 첨부되어 있습니다.
- Lot size Calc App: 정의된 위험 비율, 진입가, 손절가를 기준으로 거래를 하기 위한 최적의 랏 크기를 반환하기 위해 소프트웨어의 어느 부분에서나 사용하거나 호출할 수 있습니다. 또는 정의된 위험 비율과 핍 단위의 최대 손실에 기반하여 작동하기도 합니다. 다시말해 우리는 이 앱에 오버로딩 함수를 만든 것입니다. lotSizeCalcApp 소스 코드가 첨부되어 있습니다.
- Close All App: 진입한 모든 주문과 펜딩 주문을 모두 클로즈 하기 위해 사용하거나 호출합니다. closeAllApp 소스 코드가 첨부되어 있습니다.
함수의 세계는 매우 흥미롭습니다. 여러분은 유용한 코드를 쉽고 원활하고 효과적으로 만들 수 있도록 함수에 대해 주의 깊게 살펴보아야 합니다. 이 글이 여러분의 코딩 기술을 개발하고 트레이딩 경력을 향상시키는 데 도움이 되었기를 바라며 트레이딩을 향상하기 위한 유용한 도구를 만드는 데 도움이 되었기를 바랍니다. 프로그래밍에 대한 더 많은 기사를 읽어 보시거나 이동평균, RSI, MACD, 스토캐스틱, 볼린저 밴드, 포물선 사... 등과 같은 가장 인기 있는 기술 지표를 기반으로 트레이딩 시스템을 만드는 방법에 대해 알아보고 싶으시다면 이 글을 참고하시기 바랍니다. 제 글을 확인하시면 이에 대한 글을 찾아보실 수 있으며 그 글도 유용하게 활용되기를 바랍니다.
MetaQuotes 소프트웨어 사를 통해 영어가 번역됨
원본 기고글: https://www.mql5.com/en/articles/12970
경고: 이 자료들에 대한 모든 권한은 MetaQuotes(MetaQuotes Ltd.)에 있습니다. 이 자료들의 전부 또는 일부에 대한 복제 및 재출력은 금지됩니다.
이 글은 사이트 사용자가 작성했으며 개인의 견해를 반영합니다. Metaquotes Ltd는 제시된 정보의 정확성 또는 설명 된 솔루션, 전략 또는 권장 사항의 사용으로 인한 결과에 대해 책임을 지지 않습니다.
파이썬으로 트레이딩 로봇 개발하기(3부): 모델 기반 트레이딩 알고리즘 구현하기
MQL5 Algo Forge 시작하기
Python과 MQL5로 로봇 개발하기(2부): 모델 선택, 생성 및 훈련, Python 사용자 지정 테스터
그것들(김의 기능)은 오랫동안 MQL4에서 MQL5로 포팅되어 왔습니다.
제 무지를 용서해 주시고 비꼬는 것이 아니라 무엇이 포팅 되었습니까? MQL5로 다시 작성되었나요? 한 곳에 모아져 있고 어렵지 않다면 MQL5로 포팅 된 Kim의 기능에 대한 링크를 제공해 주시기 바랍니다. 사이트에서 Kim의 포팅된 함수를 검색해 보았지만 아무것도 찾지 못했습니다.
감사합니다, 블라디미르.
MQL5로 포팅된 Kim의 함수에 대한 링크를 제공합니다.
https://www.mql5.com/ru/forum/107476
트레이딩, 자동매매 시스템 및 트레이딩 전략 테스트 포럼
라이브러리: MT4Orders
fxsaber, 2019.01.13 17:23 PM.
MT4에서 Kim의 기능은 꽤 유명해서 그의 사이트에서 모든 소스를 다운로드하고 MT5에서 간단한 "변환기"를 작성했습니다.https://www.mql5.com/ru/forum/107476
감사합니다!
안부, 블라디미르.