MQL5 Cookbook: 지표를 사용하여 Expert Advisor의 거래 조건 설정
소개
이 글에서 Expert Advisor는 포지션 개방 조건을 확인하는 데 사용할 값의 지표로 향상됩니다. 재미를 더하기 위해 외부 매개변수에 드롭다운 목록을 만들어 세 가지 거래 지표 중 하나를 선택할 수 있습니다.
만일을 대비하여: MQL5 Cookbook 시리즈의 이전 글 전체에서 작업한 Expert Advisor를 계속 수정합니다. Expert Advisor의 마지막 버전은 "MQL5 Cookbook: History of Deals And Function Library for Get Position Properties"라는 제목의 글에서 다운로드할 수 있습니다.
또한 이 글에서는 거래 작업을 수행할 수 있는지 여부를 확인하기 위해 만들 기능을 제공합니다. 포지션 개시 기능은 Expert Advisor가 거래 모드(즉시 실행 및 시장 실행)를 결정할 수 있도록 수정됩니다.
Expert Advisor의 코드는 이전 글의 모든 개선 사항 및 개선 사항에 따라 이미 1,500줄을 초과하므로 새로운 기능이 추가될 때마다 점점 더 편리해집니다. 따라서 논리적 솔루션은 이를 별도의 라이브러리 파일로 여러 범주로 나누는 것입니다. 목표가 설정되었으니 이제 시작하겠습니다.
Expert Advisor 개발
이전 글의 Expert Advisor(*.mq5) 소스 코드를 별도의 폴더 TestIndicatorConditions에 넣습니다. 이 폴더에서 Include 하위 폴더를 만들어야 합니다. 이것은 포함 파일(*.mqh)을 생성할 폴더입니다. MQL5 마법사(Ctrl+N)를 사용하여 생성하거나 표준 텍스트 파일(*.txt)로 필요한 디렉토리에 수동으로 생성하고 나중에 *.mqh로 이름을 변경할 수 있습니다.
다음은 생성된 모든 포함 파일의 이름과 주석입니다.
- Enums.mqh에는 모든 열거가 포함됩니다.
- InfoPanel.mqh는 정보 패널 설정, 그래픽 개체 생성 및 삭제 기능을 제공합니다.
- Errors.mqh는 오류 코드 및 초기화 해제 이유를 반환하는 모든 함수를 다룹니다.
- TradeSignals.mqh는 배열을 가격, 지표 값, 신호 블록으로 채우는 기능을 제공합니다.
- TradeFunctions.mqh에는 거래 기능이 포함됩니다.
- ToString.mqh는 숫자 값을 문자열 값으로 변환하는 함수를 다룹니다.
- Auxiliary.mqh는 다른 보조 기능에 사용됩니다.
이러한 라이브러리를 기본 파일에 포함하려면 #include 지시문을 사용합니다. Expert Advisor의 메인 파일과 포함 파일 폴더(Include)가 같은 폴더에 있기 때문에 파일을 포함하는 코드는 다음과 같습니다.
//--- Include custom libraries #include "Include\Enums.mqh" #include "Include\InfoPanel.mqh" #include "Include\Errors.mqh" #include "Include\TradeSignals.mqh" #include "Include\TradeFunctions.mqh" #include "Include\ToString.mqh" #include "Include\Auxiliary.mqh"
그런 다음 Expert Advisor의 기본 파일에서 소스 코드의 일부를 열어 수정하고 이동할 수 있습니다.
코드를 올바르게 탐색하기 위해 인접 헤더 파일에 대한 참조와 Expert Advisor의 기본 파일에 대한 참조가 각 헤더 파일에 추가됩니다. 예를 들어, 거래 기능 라이브러리인 TradeFunctions.mqh의 경우 다음과 같습니다.
//--- Connection with the main file of the Expert Advisor #include "..\TestIndicatorConditions.mq5" //--- Include custom libraries #include "Enums.mqh" #include "InfoPanel.mqh" #include "Errors.mqh" #include "TradeSignals.mqh" #include "ToString.mqh" #include "Auxiliary.mqh"
동일한 중첩 수준에 있는 파일의 경우 단순히 이름을 지정하는 것으로 충분합니다. 한 단계 위로 이동하려면 경로에서 백슬래시 앞에 두 개의 점을 넣어야 합니다.
Enums.mqh 파일에 지표에 대한 열거를 추가해 보겠습니다. 설명을 위해 이 Expert Advisor에서는 두 개의 표준 지표(이동 평균 및 상품 채널 지수)와 하나의 맞춤 지표(MultiRange_PCH)를 사용합니다. 열거는 다음과 같습니다.
//--- Indicators enum ENUM_INDICATORS { MA = 0, // Moving Average CCI = 1, // CCI PCH = 2 // Price Channel };
외부 매개변수는 다음과 같이 수정됩니다.
//--- External parameters of the Expert Advisor sinput long MagicNumber=777; // Magic number sinput int Deviation=10; // Slippage input ENUM_INDICATORS Indicator=MA; // Indicator input int IndicatorPeriod=5; // Indicator period input int IndicatorSegments=2; // Number of one direction indicator segments input double Lot=0.1; // Lot input double VolumeIncrease=0.1; // Position volume increase input double VolumeIncreaseStep=10; // Step for volume increase input double StopLoss=50; // Stop Loss input double TakeProfit=100; // Take Profit input double TrailingStop=10; // Trailing Stop input bool Reverse=true; // Position reversal sinput bool ShowInfoPanel=true; // Display of the info panel
위에서 언급했듯이 Indicator 매개변수의 드롭다운 목록에서 세 가지 지표 중 하나를 선택할 수 있습니다.
지표 기간을 설정할 수 있는 모든 지표에 적용할 수 있는 매개변수는 IndicatorPeriod뿐입니다. Expert Advisor 이전 버전의 NumberOfBars 매개변수는 IndicatorSegments로 이름이 바뀌었으며 이제 주어진 지표가 포지션 오픈 조건을 만족하기 위해 위/아래로 움직여야 하는 바 수를 나타냅니다.
또한 포인트의 볼륨 증가 단계를 설정하는 데 사용할 수 있는 다른 외부 매개변수 VolumeIncreaseStep을 추가했습니다.
GetBarsData() 사용자 정의 함수에서 조정되는 데 사용되는 AllowedNumberOfBars 변수(현재 AllowedNumberOfSegments) 값. 이제 별도의 함수에 배치되고 초기화 시에만 호출됩니다.
포지션 오픈 조건은 이제 지표 값을 사용하여 확인되므로 할당되는 값은 항상 2배 커집니다. 즉, IndicatorSegments 외부 변수에 1 값이 할당되면 AllowedNumberOfSegments 변수에는 3 값이 할당됩니다. BUY)의 경우 완료된 바의 지표 값은 이전 바의 지표 값보다 커야 합니다. 이를 위해 마지막 세 개의 지표 값을 가져와야 합니다.
다음은 CorrectInputParameters() 함수 코드입니다.
//+------------------------------------------------------------------+ //| Adjusting input parameters | //+------------------------------------------------------------------+ void CorrectInputParameters() { //--- Adjust the number of bars for the position opening condition if(AllowedNumberOfSegments<=0) { if(IndicatorSegments<=1) AllowedNumberOfSegments=3; // At least three bars are required if(IndicatorSegments>=5) AllowedNumberOfSegments=5; // but no more than 7 else AllowedNumberOfSegments=IndicatorSegments+1; // and always greater by two } }
지표를 다루기 전에 거래가 허용되는지 확인하는 함수인 CheckTradingPermission()을 만들어 보겠습니다. 함수에 나열된 이유로 거래가 허용되지 않으면 0 값이 반환됩니다. 이것은 다음 시도가 다음 바에서 이루어져야 함을 의미합니다.
//+------------------------------------------------------------------+ //| Checking if trading is allowed | //+------------------------------------------------------------------+ bool CheckTradingPermission() { //--- For real-time mode if(IsRealtime()) { //--- Checking server connection if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return(1); //--- Permission to trade at the running program level if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED)) return(2); //--- Permission to trade at the terminal level if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return(3); //--- Permission to trade for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) return(4); //--- Permission to trade automatically for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT)) return(5); } //--- return(0); }
이제 글의 요점으로 내려가 보겠습니다. 지표의 값에 액세스하려면 먼저 핸들을 가져와야 합니다. 이것은 지표의 짧은 이름과 그 앞에 오는 'i' 기호로 이루어진 이름을 가진 특수 기능을 사용하여 수행됩니다.
예를 들어, 이동 평균 표시에는 해당 기능 iMA()이 있습니다. MetaTrader 5 터미널의 모든 표준 지표 핸들은 이 기능을 사용하여 얻을 수 있습니다. 전체 목록은 기술 지표라는 MQL5 참조 섹션에서 확인할 수 있습니다. 사용자 지정 지표의 핸들을 가져와야 하는 경우 iCustom() 함수를 사용하세요.
Indicator 매개변수에서 선택한 지표에 따라 해당 지표의 핸들 값이 indicator_handle 전역 변수에 할당되는 GetIndicatorHandle() 함수를 구현합니다. 기능 코드는 거래 신호 기능 라이브러리(\Include\TradeSignals.mqh 파일)에서 찾을 수 있으며 지표 핸들이 있는 변수는 Expert Advisor의 기본 파일에 있습니다.
//+------------------------------------------------------------------+ //| Getting the indicator handle | //+------------------------------------------------------------------+ void GetIndicatorHandle() { //--- If the Moving Average indicator is selected if(Indicator==MA) indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); //--- If the CCI indicator is selected if(Indicator==CCI) indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE); //--- If the MultiRange_PCH indicator is selected if(Indicator==PCH) indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod); //--- If the indicator handle could not be obtained if(indicator_handle==INVALID_HANDLE) Print("Failed to get the indicator handle!"); }
또한 얻은 지표 핸들을 사용하여 값을 가져올 수 있는 GetDataIndicators() 함수를 만듭니다. 이는 CopyTime(), CopyClose(), 를 사용하여 바 값을 가져오는 것과 유사한 방식으로 CopyBuffer() 함수를 사용하여 수행됩니다. CopyOpen(), CopyHigh() 및 CopyLow() 함수는 "MQL5 Cookbook: MetaTrader 5 전략 테스터"의 포지션 속성 분석"이라는 글에서 고려됩니다..
지표는 여러 버퍼(값 행)를 가질 수 있으므로 버퍼 인덱스는 두 번째 매개변수로 CopyBuffer() 함수에 전달됩니다. 표준 지표의 버퍼 색인은 MQL5 참조에서 찾을 수 있습니다. 사용자 지정 지표의 경우 소스 코드를 사용할 수 있는 경우 코드에서 버퍼 색인을 찾을 수 있습니다. 코드가 없으면 Strategy Tester의 시각화 모드에서 조건이 어떻게 충족되는지 관찰하여 실험을 통해 인덱스를 찾아야 합니다.
그 전에 Expert Advisor의 기본 파일에서 지표 버퍼 값에 대한 동적 배열을 생성해야 합니다.
//--- Arrays for indicator values double indicator_buffer1[]; double indicator_buffer2[];
GetIndicatorsData()의 코드는 다음과 같습니다.
//+------------------------------------------------------------------+ //| Getting indicator values | //+------------------------------------------------------------------+ bool GetIndicatorsData() { //--- If the indicator handle has been obtained if(indicator_handle!=INVALID_HANDLE) { //--- For the Moving Average or CCI indicator if(Indicator==MA || Indicator==CCI) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- For the MultiRange_PCH indicator if(Indicator==PCH) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); ArraySetAsSeries(indicator_buffer2,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments || CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 or indicator_buffer2 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- return(true); } //--- If the indicator handle has not been obtained, retry else GetIndicatorHandle(); //--- return(false); }
GetTradingSignal() 함수가 크게 변경되었습니다. 포지션이 없는 경우와 포지션이 있는 경우 조건이 다릅니다. 이동 평균 및 CCI 지표의 조건은 동일합니다. MultiRange_PCH의 경우 별도의 블록에 배열됩니다. 코드를 보다 쉽게 읽을 수 있도록 하고 반복을 방지하기 위해 포지션 열기 또는 반전 신호를 반환하는 보조 함수 Get Signal()을 만듭니다. 단, 해당 위치가 존재하고 외부 매개 변수에 의해 관련 작업이 허용됩니다.
다음은 GetSignal() 함수의 코드입니다.
//+------------------------------------------------------------------+ //| Checking the condition and returning a signal | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetSignal() { //--- Check conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]<indicator_buffer1[2]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5] && indicator_buffer1[5]<indicator_buffer1[6]) return(ORDER_TYPE_SELL); //--- A Buy signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]>indicator_buffer1[2]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5] && indicator_buffer1[5]>indicator_buffer1[6]) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- A Buy signal if(close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); } //--- No signal return(WRONG_VALUE); }
GetTradingSignal() 함수 코드는 이제 다음과 같습니다.
//+------------------------------------------------------------------+ //| Determining trading signals | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetTradingSignal() { //--- If there is no position if(!pos.exists) { //--- A Sell signal if(GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- A Buy signal if(GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); } //--- If the position exists if(pos.exists) { //--- Get the position type GetPositionProperties(P_TYPE); //--- Get the last deal price GetPositionProperties(P_PRICE_LAST_DEAL); //--- Block that checks conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } } //--- No signal return(WRONG_VALUE); }
이제 심볼 속성의 일부인 Instant Execution 및 Market Execution 모드를 살펴보고 OpenPosition() 코드를 수정하기만 하면 됩니다. 그에 따른 포지션 열기 기능. 이름이 자명한 모드는 MQL5 참조에서도 찾을 수 있습니다.
- 즉시 실행
- 시장 실행
시장 실행 모드를 취급할 때 손절매 및 이익 실현 수준으로 포지션을 열 수 없음을 기억하세요. 먼저 위치를 연 다음 레벨을 설정하여 수정합니다.
기호 속성의 구조에 실행 모드를 추가해 보겠습니다.
//--- Symbol properties struct symbol_properties { int digits; // Number of decimal places in the price int spread; // Spread in points int stops_level; // Stops level double point; // Point value double ask; // Ask price double bid; // Bid price double volume_min; // Minimum volume for a deal double volume_max; // Maximum volume for a deal double volume_limit; // Maximum permissible volume for a position and orders in one direction double volume_step; // Minimum volume change step for a deal double offset; // Offset from the maximum possible price for a transaction double up_level; // Upper Stop level price double down_level; // Lower Stop level price ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // Execution mode };
따라서 ENUM_SYMBOL_PROPERTIES 열거를 수정해야 합니다.
//--- Enumeration of position properties enum ENUM_SYMBOL_PROPERTIES { S_DIGITS = 0, S_SPREAD = 1, S_STOPSLEVEL = 2, S_POINT = 3, S_ASK = 4, S_BID = 5, S_VOLUME_MIN = 6, S_VOLUME_MAX = 7, S_VOLUME_LIMIT = 8, S_VOLUME_STEP = 9, S_FILTER = 10, S_UP_LEVEL = 11, S_DOWN_LEVEL = 12, S_EXECUTION_MODE = 13, S_ALL = 14 };
및 GetSymbolProperties() 함수:
case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //--- case S_ALL : symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD); symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits); symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits); symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT); symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits); symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits); symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits); symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //---
결과적으로 OpenPosition() 함수 코드는 이제 다음과 같습니다.
//+------------------------------------------------------------------+ //| Opening a position | //+------------------------------------------------------------------+ void OpenPosition(double lot, ENUM_ORDER_TYPE order_type, double price, double sl, double tp, string comment) { //--- Set the magic number in the trading structure trade.SetExpertMagicNumber(MagicNumber); //--- Set the slippage in points trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); //--- The Instant Execution mode // A position can be opened with the Stop Loss and Take Profit levels set if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } //--- The Market Execution mode // First open a position and only then set the Stop Loss and Take Profit levels // *** Starting with build 803, Stop Loss and Take Profit can be set upon position opening *** if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET) { //--- If there is no position, first open a position and then set Stop Loss and Take Profit if(!pos.exists) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); //--- Get the flag of presence/absence of the position pos.exists=PositionSelect(_Symbol); //--- If the position exists if(pos.exists) { //--- Set Stop Loss and Take Profit if(!trade.PositionModify(_Symbol,sl,tp)) Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } //--- If the position exists, increase its volume and leave the Stop Loss and Take Profit levels unchanged else { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } }
이벤트 처리 기능에 마지막으로 매우 중요한 터치를 추가해야 합니다.
- OnInit
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Adjust the input parameters CorrectInputParameters(); //--- Get indicator handles GetIndicatorHandle(); //--- Initialize the new bar CheckNewBar(); //--- Get the properties GetPositionProperties(P_ALL); //--- Set the info panel SetInfoPanel(); //--- return(0); }
- OnDeinit
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Print the deinitialization reason to the journal Print(GetDeinitReasonText(reason)); //--- When deleting from the chart if(reason==REASON_REMOVE) { //--- Delete all objects relating to the info panel from the chart DeleteInfoPanel(); //--- Delete the indicator handle IndicatorRelease(indicator_handle); } }
- OnTick
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- If the bar is not new, exit if(!CheckNewBar()) { if(IsVisualMode() || IsRealtime()) { //--- Get the properties and update the values on the panel GetPositionProperties(P_ALL); //--- Set/update the info panel SetInfoPanel(); } return; } //--- If there is a new bar else { //--- If trading is allowed if(CheckTradingPermission()==0) { if(!GetIndicatorsData()) return; GetBarsData(); // Get bar data TradingBlock(); // Check the conditions and trade ModifyTrailingStop(); // Modify the Trailing Stop level } } //--- Get the properties GetPositionProperties(P_ALL); //--- Update the info panel SetInfoPanel(); }
이제 모든 기능이 준비되었으므로 매개변수를 최적화할 수 있습니다. 메인 프로그램 파일에서 코드를 컴파일해야 한다는 것을 명심하세요.
매개변수 최적화 및 Expert Advisor 테스트
Strategy Tester는 아래와 같이 설정해야 합니다.
그림 1. 전략 테스터 설정.
또한 최적화를 위해 Expert Advisor의 매개변수를 설정합니다(설정이 포함된 첨부된 *.set 파일 참조).
그림 2. Expert Advisor의 설정.
최적화는 듀얼 코어 프로세서에서 약 40분이 걸렸습니다. 최적화 차트를 사용하면 이익 영역의 결과를 기반으로 거래 시스템의 품질을 부분적으로 평가할 수 있습니다.
그림 3. 최적화 차트.
최대 회복 계수 테스트 결과는 다음과 같습니다.
그림 4. 최대 회복 계수 테스트 결과.
결론
글에 첨부된 것은 Expert Advisor의 소스 코드와 함께 다운로드 가능한 아카이브입니다. 글에 첨부된 것은 Expert Advisor의 소스 코드와 함께 다운로드 가능한 아카이브입니다. Expert Advisor의 올바른 작동을 보장하려면 MultiRange_PCH 지표를 다운로드하여 <Metatrader 5 터미널>\MQL5\Indicators에 배치해야 합니다.
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/645