"즉석에서" 사용자 패널에서 Expert Advisor 매개변수 변경
내용
소개
1. 문제 집중
2. Expert Advisor의 구조
3. 사용자 패널과의 상호 작용
결론
소개
복잡한 Expert Advisors를 개발할 때 외부 매개변수의 수가 매우 많을 수 있습니다. 그리고 설정을 수동으로 변경해야 하는 경우가 매우 많기 때문에 방대한 매개변수 목록이 주어지면 전체 프로세스에 시간이 많이 소요됩니다. 물론 미리 세트를 준비하고 저장할 수 있지만 경우에 따라 정확히 필요한 것이 아닐 수도 있죠. 항상 그렇듯이 MQL5가 유용합니다.
거래하는 동안 "즉석에서" Expert Advisor의 매개변수를 변경할 수 있는 사용자 패널을 만들어 보겠습니다. 이것은 수동 또는 반자동 모드에서 거래하는 사람들과 관련이 있을 수 있습니다. 변경 사항이 발생하면 매개변수가 파일에 기록되어 Expert Advisor가 해당 매개변수를 읽고 패널에 추가로 표시할 수 있습니다.
1. 초점 문제
설명을 위해 JMA 지표 방향으로 포지션을 여는 간단한 EA를 개발할 것입니다. EA는 현재 기호 및 시간 프레임에 완성된 바에 대해 작업합니다. 외부 매개변수에는 지표 기간, 손절매, 이익 실현, 리버스 및 랏이 포함됩니다. 이 예에서는 이러한 옵션으로 충분합니다.
패널을 켜고 끌 수 있는 두 개의 추가 매개변수(정보 패널 켜기/끄기)와 Expert Advisor 매개변수 설정 모드("On The Fly" 설정)를 활성화/비활성화할 수 있습니다. 매개변수의 수가 많은 경우 쉽고 빠른 액세스를 위해 목록의 맨 처음이나 끝에 추가 옵션을 배치하는 것이 항상 더 편리합니다.
그림 1. Expert Advisor의 매개변수가 있는 정보 패널
"On The Fly" 설정 모드는 기본적으로 비활성화되어 있습니다. 이 모드를 처음 활성화하면 Expert Advisor는 현재 가지고 있는 모든 매개변수를 저장하기 위해 파일을 생성합니다. 파일이 실수로 삭제된 경우에도 마찬가지입니다. Expert Advisor는 삭제를 감지하고 파일을 다시 생성합니다. "On Fly" 설정 모드가 비활성화되면 Expert Advisor는 외부 매개변수에 의해 안내됩니다.
이 모드가 활성화되면 Expert Advisor는 파일에서 매개변수를 읽고 정보 패널에서 매개변수를 클릭하기만 하면 필요한 값을 선택하거나 팝업 대화 상자에 새 값을 입력할 수 있습니다. 새 값을 선택할 때마다 파일 데이터가 업데이트됩니다.
2. Expert Advisor의 구조
프로그램이 작고 모든 기능이 하나의 파일에 쉽게 들어갈 수 있지만 적절하게 분류되면 모든 프로젝트 정보를 탐색하는 것이 훨씬 더 편리합니다. 따라서 기능을 유형별로 분류하고 처음부터 다른 파일에 넣어 나중에 마스터 파일에 포함시키는 것이 가장 좋습니다. 아래 그림은 OnTheFly Expert Advisor가 있는 공유 프로젝트 폴더와 모든 포함 파일을 보여줍니다. 포함 파일은 별도의 폴더(포함)에 있습니다.
그림 2. MetaEditor의 Navigator 창에 있는 프로젝트 파일
포함 파일이 마스터 파일과 동일한 폴더에 있는 경우 코드는 다음과 같습니다.
//+------------------------------------------------------------------+ //| CUSTOM LIBRARIES | //+------------------------------------------------------------------+ #include "Include/!OnChartEvent.mqh" #include "Include/CREATE_PANEL.mqh" #include "Include/FILE_OPERATIONS.mqh" #include "Include/ERRORS.mqh" #include "Include/ARRAYS.mqh" #include "Include/TRADE_SIGNALS.mqh" #include "Include/TRADE_FUNCTIONS.mqh" #include "Include/GET_STRING.mqh" #include "Include/GET_COLOR.mqh" #include "Include/ADD_FUNCTIONS.mqh"
파일을 포함하는 방법에 대한 자세한 내용은 MQL5 참조에서 찾을 수 있습니다.
외부 매개변수의 복제본인 전역 변수가 필요합니다. 해당 값은 Expert Advisor의 모드에 따라 외부 매개변수 또는 파일에서 할당됩니다. 이러한 변수는 전체 프로그램 코드에서 사용됩니다. 정보 패널에 값 표시, 거래 기능 등에서 말이죠.
// COPY OF EXTERNAL PARAMETERS int gPeriod_Ind = 0; double gTakeProfit = 0.0; double gStopLoss = 0.0; bool gReverse = false; double gLot = 0.0;
다른 모든 Expert Advisor와 마찬가지로 주요 기능은 OnInit, OnTick 및 OnDeinit입니다. 그리고 OnTimer 기능도 있을 것입니다. 매초마다 매개변수 파일의 존재를 확인하고 실수로 삭제된 경우 복원합니다. 사용자 패널과 상호 작용해야 하므로 OnChartEvent 함수도 사용됩니다. 이 함수는 다른 관련 함수와 함께 별도의 파일(!OnChartEvent.mqh)에 배치되었습니다.
마스터 파일의 핵심 코드는 다음과 같습니다.
#define szArrIP 5 // Size of the parameter array #define NAME_EXPERT MQL5InfoString(MQL5_PROGRAM_NAME) // Name of EA #define TRM_DP TerminalInfoString(TERMINAL_DATA_PATH) // Folder that contains the terminal data //+------------------------------------------------------------------+ //| STANDARD LIBRARIES | //+------------------------------------------------------------------+ #include <Trade/SymbolInfo.mqh> #include <Trade/Trade.mqh> //+------------------------------------------------------------------+ //| CUSTOM LIBRARIES | //+------------------------------------------------------------------+ #include "Include/!OnChartEvent.mqh" #include "Include/CREATE_PANEL.mqh" #include "Include/FILE_OPERATIONS.mqh" #include "Include/ERRORS.mqh" #include "Include/ARRAYS.mqh" #include "Include/TRADE_SIGNALS.mqh" #include "Include/TRADE_FUNCTIONS.mqh" #include "Include/GET_STRING.mqh" #include "Include/GET_COLOR.mqh" #include "Include/ADD_FUNCTIONS.mqh" //+------------------------------------------------------------------+ //| CREATING CLASS INSTANCES | //+------------------------------------------------------------------+ CSymbolInfo mysymbol; // CSymbolInfo class object CTrade mytrade; // CTrade class object //+------------------------------------------------------------------+ //| EXTERNAL PARAMETERS | //+------------------------------------------------------------------+ input int Period_Ind = 10; // Indicator Period input double TakeProfit = 100; // Take Profit (p) input double StopLoss = 30; // Stop Loss (p) input bool Reverse = false; // Reverse Position input double Lot = 0.1; // Lot //--- input string slash=""; // * * * * * * * * * * * * * * * * * * * sinput bool InfoPanel = true; // On/Off Info Panel sinput bool SettingOnTheFly = false; // "On The Fly" Setting //+------------------------------------------------------------------+ //| GLOBAL VARIABLES | //+------------------------------------------------------------------+ int hdlSI=INVALID_HANDLE; // Signal indicator handle double lcheck=0; // For the check of parameter values bool isPos=false; // Position availability //--- COPY OF EXTERNAL PARAMETERS int gPeriod_Ind = 0; double gTakeProfit = 0.0; double gStopLoss = 0.0; bool gReverse = false; double gLot = 0.0; //+------------------------------------------------------------------+ //| EXPERT ADVISOR INITIALIZATION | //+------------------------------------------------------------------+ void OnInit() { if(NotTest()) { EventSetTimer(1); } // If it's not the tester, set the timer //--- Init_arr_vparams(); // Initialization of the array of parameter values SetParameters(); // Set the parameters GetIndicatorsHandles(); // Get indicator handles NewBar(); // New bar initialization SetInfoPanel(); // Info panel } //+------------------------------------------------------------------+ //| CURRENT SYMBOL TICKS | //+------------------------------------------------------------------+ void OnTick() { // If the bar is not new, exit if(!NewBar()) { return; } else { TradingBlock(); } } //+------------------------------------------------------------------+ //| TIMER | //+------------------------------------------------------------------+ void OnTimer() { SetParameters(); SetInfoPanel(); } //+------------------------------------------------------------------+ //| EXPERT ADVISOR DEINITIALIZATION | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Get the deinitialization reason code if(NotTest()) { { Print(getUnitReasonText(reason)); } //--- // When deleting from the chart if(reason==REASON_REMOVE) { // Delete all objects created by the Expert Advisor DeleteAllExpertObjects(); //--- if(NotTest()) { EventKillTimer(); } // Stop the timer IndicatorRelease(hdlSI); // Delete the indicator handle } }
또한 마스터 파일에 몇 가지 기능을 더 포함했습니다.
- GetIndicatorsHandles – 지표 핸들을 가져옵니다.
- NewBar – 새 바 이벤트를 결정합니다.
- SetParameters – 모드에 따라 매개변수를 설정합니다.
- iZeroMemory – 일부 변수와 배열을 0으로 만듭니다.
//+------------------------------------------------------------------+ //| SETTING PARAMETERS IN TWO MODES | //+------------------------------------------------------------------+ // If this variable is set to false, the parameters in the file are read from the array // where they are saved for quick access after they have been read for the first time. // The variable is zeroed out upon the change in the value on the panel. bool flgRead=false; double arrParamIP[]; // Array where the parameters from the file are saved //--- void SetParameters() { // If currently in the tester or // in real time but with the "On The Fly" Setting mode disabled if(!NotTest() || (NotTest() && !SettingOnTheFly)) { // Zero out the variable and parameter array flgRead=false; ArrayResize(arrParamIP,0); //--- // Check the Indicator Period for correctness if(Period_Ind<=0) { lcheck=10; } else { lcheck=Period_Ind; } gPeriod_Ind=(int)lcheck; //--- gStopLoss=StopLoss; gTakeProfit=TakeProfit; gReverse=Reverse; //--- // Check the Lot for correctness if(Lot<=0) { lcheck=0.1; } else { lcheck=Lot; } gLot=lcheck; } else // If "On The Fly" Setting mode is enabled { // Check whether there is a file to write/read parameters to/from the file string lpath=""; //--- // If the folder exists if((lpath=CheckCreateGetPath())!="") { // Write or read the file WriteReadParameters(lpath); } } }
SetParameters 함수의 소스 코드는 간단하고 간단합니다. WriteReadParameters 함수를 자세히 살펴보겠습니다. 여기에서는 모든 것이 매우 간단합니다. 먼저 매개변수가 있는 파일이 있는지 확인합니다. 그렇다면 GetValuesParamsFromFile 함수를 사용하여 파일을 읽고 매개변수 값을 배열에 씁니다. 파일이 존재하지 않으면 현재 외부 매개변수가 기록된 상태로 생성됩니다.
아래는 위에서 설명한 작업 구현에 대한 자세한 설명이 포함된 코드입니다.
//+------------------------------------------------------------------+ //| WRITE DATA TO FILE | //+------------------------------------------------------------------+ void WriteReadParameters(string pth) { string nm_fl=pth+"ParametersOnTheFly.ini"; // File name and path //--- // Get the file handle to read the file int hFl=FileOpen(nm_fl,FILE_READ|FILE_ANSI); //--- if(hFl!=INVALID_HANDLE) // If the handle has been obtained, the file exists { // Get parameters from the file if(!flgRead) { // Set the array size ArrayResize(arrParamIP,szArrIP); //--- // Fill the array with values from the file flgRead=GetValuesParamsFromFile(hFl,arrParamIP); } //--- // If the array size is correct,... if(ArraySize(arrParamIP)==szArrIP) { // ...set the parameters to the variables //--- // Check the Indicator Period for correctness if((int)arrParamIP[0]<=0) { lcheck=10; } else { lcheck=(int)arrParamIP[0]; } gPeriod_Ind=(int)lcheck; //--- gTakeProfit=arrParamIP[1]; gStopLoss=arrParamIP[2]; gReverse=arrParamIP[3]; //--- // Check the Lot for correctness if(arrParamIP[4]<=0) { lcheck=0.1; } else { lcheck=arrParamIP[4]; } gLot=lcheck; } } else // If the file does not exist { iZeroMemory(); // Zero out variables //--- // When creating the file, write current parameters of the Expert Advisor //--- // Get the file handle to write to the file int hFl2=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,""); //--- if(hFl2!=INVALID_HANDLE) // If the handle has been obtained { string sep="="; //--- // Parameter names and values are obtained from arrays in the ARRAYS.mqh file for(int i=0; i<szArrIP; i++) { FileWrite(hFl2,arr_nmparams[i],sep,arr_vparams[i]); } //--- FileClose(hFl2); // Close the file //--- Print("File with parameters of the "+NAME_EXPERT+" Expert Advisor created successfully."); } } //--- FileClose(hFl); // Close the file }
WriteReadParameters 및 GetValuesParamsFromFile 함수는 FILE_OPERATIONS.mqh 파일에서 찾을 수 있습니다.
일부 기능은 이전 글 "다른 응용 프로그램을 위한 MetaTrader 5 지수를 준비하는 방법"에서 이미 설명했으므로 여기서는 다루지 않겠습니다. 거래 기능은 매우 간단하고 광범위하게 설명되어 있으므로 거래에 어려움을 겪지 않아야 합니다. 우리가 집중할 것은 이 글의 주요 주제입니다.
3. 사용자 패널과의 상호 작용
!OnChartEvent.mqh 파일에는 사용자 패널과의 상호 작용을 위한 기능이 포함되어 있습니다. 많은 함수에서 사용되는 변수와 배열은 맨 처음에 전역 범위에서 선언됩니다.
// Current value on the panel or // entered in the input box string currVal=""; bool flgDialogWin=false; // Flag for panel existence int szArrList=0,// Size of the option list array number=-1; // Parameter number in the panel list string nmMsgBx="", // Name of the dialog window nmValObj=""; // Name of the selected object //--- // Option list arrays in the dialog window string lenum[],lenmObj[]; //--- // colors of the dialog window elements color clrBrdBtn=clrWhite, clrBrdFonMsg=clrDimGray,clrFonMsg=C'15,15,15', clrChoice=clrWhiteSmoke,clrHdrBtn=clrBlack, clrFonHdrBtn=clrGainsboro,clrFonStr=C'22,39,38';
그 다음에는 이벤트를 처리하는 main 함수가 옵니다. 이 예에서는 두 가지 이벤트를 처리해야 합니다.
- CHARTEVENT_OBJECT_CLICK 이벤트 – 그래픽 개체에서 마우스 왼쪽 버튼을 클릭합니다.
- CHARTEVENT_OBJECT_EDIT 이벤트 – 그래픽 개체 편집에서 텍스트 편집 종료.
다른 MQL5 이벤트에 대한 자세한 내용은 MQL5 참조에서 확인할 수 있습니다.
"On The Fly" 설정 모드가 활성화된 경우(SettingOnTheFly) 항상 실시간으로 이벤트를 처리하기 위한 검사를 설정해 보겠습니다. 이벤트 처리는 ChartEvent_ObjectClick 및 ChartEvent_ObjectEndEdit와 같은 별도의 함수로 처리됩니다.
//+------------------------------------------------------------------+ //| USER EVENTS | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { // If the event is real time and the "On The Fly" setting mode is enabled if(NotTest() && SettingOnTheFly) { //+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_CLICK EVENT | //+------------------------------------------------------------------+ if(ChartEvent_ObjectClick(id,lparam,dparam,sparam)) { return; } //--- //+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_ENDEDIT EVENT | //+------------------------------------------------------------------+ if(ChartEvent_ObjectEndEdit(id,lparam,dparam,sparam)) { return; } } //--- return; }
목록에 속한 개체를 클릭하면 정보 패널에 대화 상자 창이 나타나 다른 값을 선택하거나 입력 상자에 새 값을 입력할 수 있습니다.
그림 3. 선택한 매개변수의 값을 수정하기 위한 대화 상자 창
어떻게 작동하는지 자세히 살펴보겠습니다. 그래픽 개체를 클릭하면 프로그램은 먼저 ChartEvent_ObjectClick 함수를 사용하여 그래픽 개체를 실제로 클릭했는지 여부를 이벤트 식별자로 확인합니다.
차트 중간에 대화창을 열려면 차트 크기를 알아야 합니다. ChartGetInteger 함수에서 CHART_WIDTH_IN_PIXELS 및 CHART_HEIGHT_IN_PIXELS 속성을 지정하여 얻을 수 있습니다. 그런 다음 프로그램은 DialogWindowInfoPanel로 전환됩니다. MQL5 참조에서 모든 차트 속성에 익숙해질 수 있습니다.
다음은 위의 작업을 구현하기 위한 코드입니다.
//+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_CLICK EVENT | //+------------------------------------------------------------------+ bool ChartEvent_ObjectClick(int id,long lparam,double dparam,string sparam) { // If there was an event of clicking on a graphical object if(id==CHARTEVENT_OBJECT_CLICK) // id==1 { Get_STV(); // Get all data on the symbol //--- string clickedChartObject=sparam; // Name of the clicked object //--- // Get the chart size width_chart=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS,0); height_chart=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,0); //--- DialogWindowInfoPanel(clickedChartObject); } //--- return(false); }
DialogWindowInfoPanel 함수를 사용하여 먼저 대화 창이 현재 열려 있는지 확인합니다. 창이 발견되지 않으면 GetNumberClickedObjIP 함수는 클릭이 정보 패널의 목록에 있는 개체와 관련되었는지 여부를 확인합니다. 클릭한 개체가 목록의 개체인 경우 함수는 개체 배열에서 관련 요소 번호를 반환합니다. 이 숫자를 사용하여 InitArraysAndDefault 함수는 대화 상자 창의 목록 배열 크기와 기본값을 결정합니다. 모든 작업이 성공하면 대화 상자 창이 나타납니다.
DialogWindowInfoPanel 함수가 대화 상자 창이 이미 열려 있다고 판단하면 프로그램은 대화 상자 창에서 개체를 클릭했는지 여부를 확인합니다. 예를 들어, 대화창을 열면 현재 패널에 표시된 값의 행이 선택된 것으로 나타납니다. 목록에서 다른 옵션을 클릭하면 프로그램은 클릭한 대화 상자 창 목록 옵션을 선택하는 SelectionOptionInDialogWindow 함수를 사용합니다.
현재 선택되어 있는 목록 옵션을 클릭하면 해당 개체가 편집 대상으로 식별되며 해당 상자를 클릭하면 새로운 값을 입력할 수 있도록 입력 상자가 나타납니다. SetEditObjInDialogWindow 함수는 입력 상자 설정을 담당합니다.
그리고 마지막으로 Apply 버튼을 클릭하면 프로그램에서 값이 수정되었는지 확인합니다. 있는 경우 새 값이 패널에 나타나고 파일에 기록됩니다.
대화창의 주요 기능 코드는 아래와 같습니다.
//+------------------------------------------------------------------+ //| DIALOG WINDOW OF THE INFO PANEL | //+------------------------------------------------------------------+ void DialogWindowInfoPanel(string clickObj) { // If there is currently no dialog window if(!flgDialogWin) { // Get the object number in the array // Exit if none of the parameters displayed on the panel has been clicked if((number=GetNumberClickedObjIP(clickObj))==-1) { return; } //--- // Initialization of default values //and determination of the list array size if(!InitArraysAndDefault()) { return; } //--- // Set the dialog window SetDialogWindow(); //--- flgDialogWin=true; // Mark the dialog window as open ChartRedraw(); } else // If the dialog window is open { // Set the input box for the modification of the value SetEditObjInDialogWindow(clickObj); //--- // If one of the buttons in the dialog box is clicked if(clickObj=="btnApply" || clickObj=="btnCancel") { // If the Apply button is clicked if(clickObj=="btnApply") { // Compare values on the panel with the ones on the list // If the value on the list is different from the one that is currently displayed on the panel // (which means it is different from the one in the file), // ...change the value on the panel and update the file if(currVal!=ObjectGetString(0,nmValObj,OBJPROP_TEXT)) { // Update the value on the panel ObjectSetString(0,nmValObj,OBJPROP_TEXT,currVal); ChartRedraw(); //--- // Read all data on the panel and write it to the file WriteNewData(); } } //--- DelDialogWindow(lenmObj); // Delete the dialog window iZeroMemory(); // Zero out the variables //--- // Update the data SetParameters(); GetHandlesIndicators(); SetInfoPanel(); //--- ChartRedraw(); } else // If neither Apply nor Cancel has been clicked { // Selection of the dialog window list option SelectionOptionInDialogWindow(clickObj); //--- ChartRedraw(); } } }
입력 상자에 새 값이 입력될 때마다 CHARTEVENT_OBJECT_EDIT 이벤트가 생성되고 프로그램이 ChartEvent_ObjectEndEdit 함수로 전환됩니다. 대화 상자 창의 값이 수정된 경우 입력된 값이 저장되고 정확성이 확인되고 목록의 개체에 할당됩니다. 아래 코드에서 더 자세히 볼 수 있습니다.
//+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_ENDEDIT EVENT | //+------------------------------------------------------------------+ bool ChartEvent_ObjectEndEdit(int id,long lparam,double dparam,string sparam) { if(id==CHARTEVENT_OBJECT_ENDEDIT) // id==3 { string editObject=sparam; // Name of the edited object //--- // If the value has been entered in the input box in the dialog window if(editObject=="editValIP") { // Get the entered value currVal=ObjectGetString(0,"editValIP",OBJPROP_TEXT); //--- // (0) Period Indicator if(number==0) { // Correct the value if it is wrong if(currVal=="0" || currVal=="" || SD(currVal)<=0) { currVal="1"; } //--- // Set the entered value ObjectSetString(0,"enumMB0",OBJPROP_TEXT,currVal); } //--- // (4) Lot if(number==4) { // Correct the value if it is wrong if(currVal=="0" || currVal=="" || SD(currVal)<=0) { currVal=DS(SS.vol_min,2); } //--- // Set the entered value ObjectSetString(0,"enumMB0",OBJPROP_TEXT,DS2(SD(currVal))); } //--- // (1) Take Profit (p) // (2) Stop Loss (p) if(number==1 || number==2) { // Correct the value if it is wrong if(currVal=="0" || currVal=="" || SD(currVal)<=0) { currVal="1"; } //--- // Set the entered value ObjectSetString(0,"enumMB1",OBJPROP_TEXT,currVal); } //--- DelObjbyName("editValIP"); ChartRedraw(); } } //--- return(false); }
Expert Advisor이 작동하는 모습은 아래 동영상에서 볼 수 있습니다.
결론
글 끝에 첨부된 압축 파일을 다운로드하여 자세히 알아볼 수 있습니다.
이 글이 MQL5를 배우기 시작한 사람들이 주어진 간단한 예를 사용하여 많은 질문에 대한 빠른 답변을 찾는 데 도움이 되기를 바랍니다. 제공된 코드 조각에서 의도적으로 몇 가지 검사를 생략했습니다.
예를 들어, 대화창이 열려 있을 때 차트 높이/너비를 변경하면 대화창이 자동으로 중앙에 위치하지 않습니다. 그리고 목록에서 다른 옵션을 선택하여 추가하면 해당 라인을 선택하는 개체가 상당히 이동하게 됩니다. 이건 당신의 숙제가 될 겁니다. 프로그래밍을 연습하는 것은 매우 중요하며 연습을 많이 할수록 더 좋습니다.
행운을 빕니다!
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/572