English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5 Cookbook - MQL5의 다중 통화 Expert Advisor 및 대기 중인 주문 작업

MQL5 Cookbook - MQL5의 다중 통화 Expert Advisor 및 대기 중인 주문 작업

MetaTrader 5 | 12 10월 2021, 12:44
191 0
Anatoli Kazharski
Anatoli Kazharski

소개

이번에는 보류 중인 주문 Buy Stop 및 Sell Stop 작업을 기반으로 하는 거래 알고리즘을 사용하여 다중 통화 Expert Advisor를 만들 것입니다. 우리가 만들 패턴은 일중 거래/테스트를 위해 설계될 것입니다. 이 글에서는 다음 사항을 고려합니다.

  • 지정된 시간 범위에서 거래합니다. 거래 시작 및 종료 시간을 설정할 수 있는 기능을 만들어 보겠습니다. 예를 들어, 유럽 또는 미국 거래 세션의 시간이 될 수 있습니다. 확실히 Expert Advisor의 매개변수를 최적화할 때 가장 적합한 시간 범위를 찾을 수 있는 기회가 있을 것입니다.
  • 보류 중인 주문을 배치/수정/삭제합니다.
  • 거래 이벤트 처리: 마지막 포지션이 이익실현 또는 손절매에서 마감되었는지 확인하고 각 기호에 대한 거래 내역을 제어합니다.


Expert Advisor 개발

MQL5 Cookbook: Multi-Currency Expert Advisor - Simple, Neat and Quick Approach 글의 코드를 템플릿으로 사용할 것입니다. 패턴의 기본 구조는 그대로 유지되지만 몇 가지 중요한 변경 사항이 도입됩니다. Expert Advisor는 일중 거래를 위해 설계되었지만 필요에 따라 이 모드를 끌 수 있습니다. 이러한 경우 보류 중인 주문은 포지션이 마감된 경우 항상 즉시(새로운 바 이벤트에서) 배치됩니다.

Expert Advisor의 외부 매개변수부터 시작하겠습니다. 처음에는 포함 파일 Enums.mqh에 새 열거 ENUM_HOURS를 생성합니다. 이 열거형의 식별자 수는 하루의 시간 수와 같습니다.

//--- Hours Enumeration
enum ENUM_HOURS
  {
   h00 = 0,  // 00 : 00
   h01 = 1,  // 01 : 00
   h02 = 2,  // 02 : 00
   h03 = 3,  // 03 : 00
   h04 = 4,  // 04 : 00
   h05 = 5,  // 05 : 00
   h06 = 6,  // 06 : 00
   h07 = 7,  // 07 : 00
   h08 = 8,  // 08 : 00
   h09 = 9,  // 09 : 00
   h10 = 10, // 10 : 00
   h11 = 11, // 11 : 00
   h12 = 12, // 12 : 00
   h13 = 13, // 13 : 00
   h14 = 14, // 14 : 00
   h15 = 15, // 15 : 00
   h16 = 16, // 16 : 00
   h17 = 17, // 17 : 00
   h18 = 18, // 18 : 00
   h19 = 19, // 19 : 00
   h20 = 20, // 20 : 00
   h21 = 21, // 21 : 00
   h22 = 22, // 22 : 00
   h23 = 23  // 23 : 00
  };
-->

그런 다음 외부 매개변수 목록에서 시간 범위의 거래와 관련된 4개의 매개변수를 생성합니다.

  • TradeInTimeRange - 모드 활성화/비활성화. 이미 말씀드린 대로 Expert Advisor의 작업은 특정 시간 범위 내에서 뿐만 아니라 24시간 내내, 즉 연속 모드로 작동할 수 있도록 할 것입니다.
  • StartTrade - 거래 세션이 시작되는 시간입니다. 서버 시간이 이 값과 같으면 TradeInTimeRange 모드가 켜져 있는 경우 Expert Advisor가 주문을 보류합니다.
  • StopOpenOrders - 주문이 끝나는 시간입니다. 서버 시간이 이 값과 같을 때 Expert Advisor는 포지션이 마감되면 보류 중인 주문을 하는 것을 중단합니다.
  • EndTrade - 거래 세션이 중지되는 시간입니다. 서버 시간이 이 값과 같으면 Expert Advisor는 거래를 중지합니다. 지정된 기호에 대한 열린 포지션이 닫히고 보류 중인 주문이 삭제됩니다.

외부 매개변수 목록은 아래와 같이 표시됩니다. 주어진 예는 두 개의 기호에 대한 것입니다. PendingOrder 매개변수에서 현재 가격과의 거리를 포인트 단위로 설정합니다.

//--- External parameters of the Expert Advisor 
sinput long       MagicNumber       = 777;      // Magic number
sinput int        Deviation         = 10;       // Slippage
//---
sinput string delimeter_00=""; // --------------------------------
sinput string     Symbol_01            ="EURUSD";  // Symbol 1
input  bool       TradeInTimeRange_01  =true;      // |     Trading in a time range
input  ENUM_HOURS StartTrade_01        = h10;      // |     The hour of the beginning of a trading session
input  ENUM_HOURS StopOpenOrders_01    = h17;      // |     The hour  of the end of placing orders
input  ENUM_HOURS EndTrade_01          = h22;      // |     The hour of the end of a trading session
input  double     PendingOrder_01      = 50;       // |     Pending order
input  double     TakeProfit_01        = 100;      // |     Take Profit
input  double     StopLoss_01          = 50;       // |     Stop Loss
input  double     TrailingStop_01      = 10;       // |     Trailing Stop
input  bool       Reverse_01           = true;     // |     Position reversal
input  double     Lot_01               = 0.1;      // |     Lot
//---
sinput string delimeter_01=""; // --------------------------------
sinput string     Symbol_02            ="AUDUSD";  // Symbol 2
input  bool       TradeInTimeRange_02  =true;      // |     Trading in a time range
input  ENUM_HOURS StartTrade_02        = h10;      // |     The hour of the beginning of a trading session
input  ENUM_HOURS StopOpenOrders_02    = h17;      // |     The hour  of the end of placing orders
input  ENUM_HOURS EndTrade_02          = h22;      // |     The hour of the end of a trading session
input  double     PendingOrder_02      = 50;       // |     Pending order
input  double     TakeProfit_02        = 100;      // |     Take Profit
input  double     StopLoss_02          = 50;       // |     Stop Loss
input  double     TrailingStop_02      = 10;       // |     Trailing Stop
input  bool       Reverse_02           = true;     // |     Position reversal
input  double     Lot_02               = 0.1;      // |     Lot
-->

또한 외부 매개변수의 값으로 채워질 배열 목록에서 해당 변경을 수행해야 합니다.

//--- Arrays for storing external parameters
string     Symbols[NUMBER_OF_SYMBOLS];          // Symbol
bool       TradeInTimeRange[NUMBER_OF_SYMBOLS]; // Trading in a time range
ENUM_HOURS StartTrade[NUMBER_OF_SYMBOLS];       // The hour of the beginning of a trading session
ENUM_HOURS StopOpenOrders[NUMBER_OF_SYMBOLS];   // The hour  of the end of placing orders
ENUM_HOURS EndTrade[NUMBER_OF_SYMBOLS];         // The hour of the end of a trading session
double     PendingOrder[NUMBER_OF_SYMBOLS];     // Pending order
double     TakeProfit[NUMBER_OF_SYMBOLS];       // Take Profit
double     StopLoss[NUMBER_OF_SYMBOLS];         // Stop Loss
double     TrailingStop[NUMBER_OF_SYMBOLS];     // Trailing Stop
bool       Reverse[NUMBER_OF_SYMBOLS];          // Position Reversal
double     Lot[NUMBER_OF_SYMBOLS];              // Lot
-->

이제 취소 모드(Reverse 매개변수 값이 true임)에서 보류 주문 중 하나가 다음과 같을 때 반대 보류 주문이 삭제되고 새로 배치되도록 정렬합니다. 발동. 가격 수준(주문 가격, 손절매, 이익실현)을 변경하는 경우와 같이 보류 중인 주문의 볼륨을 변경할 수 없습니다. 따라서 우리는 그것을 삭제하고 필요한 볼륨으로 새로운 보류 주문을 해야 합니다.

또한 리버설 모드가 활성화되고 추적 손절매 수준이 동시에 설정되면 보류 중인 주문이 가격을 따릅니다. 그 위에 손절매가 설정되면 보류 중인 주문을 기반으로 가격 값이 계산되고 지정됩니다.

전역 범위에서 보류 중인 주문 주석에 대해 두 개의 문자열 변수를 생성해 보겠습니다.

//--- Pending order comments 
string comment_top_order    ="top_order";
string comment_bottom_order ="bottom_order";
-->

Expert Advisor 로딩 중 OnInit() 함수 초기화 시 외부 매개변수가 정확한지 확인합니다. 평가 기준은 다음과 같습니다. TradeInTimeRange 모드가 활성화된 경우, 거래 세션 시작 시간이 보류 중인 주문 종료 시간보다 한 시간 더 짧아지면 안 됩니다. 보류 중인 주문의 종료 시간은 거래 종료 시간보다 한 시간 더 짧아지면 안 됩니다. 이러한 검사를 수행할 CheckInputParameters() 함수를 작성해 보겠습니다.

//+------------------------------------------------------------------+
//| Checks external parameters                                       |
//+------------------------------------------------------------------+
bool CheckInputParameters()
  {
//--- Loop through the specified symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If there is no symbol and the TradeInTimeRange mode is disabled, move on to the following symbol.
      if(Symbols[s]=="" || !TradeInTimeRange[s])
         continue;
      //--- Check the accuracy of the start and the end of a trade session time
      if(StartTrade[s]>=EndTrade[s])
        {
         Print(Symbols[s],
               ": The hour of the beginning of a trade session("+IntegerToString(StartTrade[s])+") "
               "must be less than the hour of the end of a trade session"("+IntegerToString(EndTrade[s])+")!");
         return(false);
        }
      //--- A trading session is to start no later that one hour before the hour of placing pending orders.
      //    Pending orders are to be placed no later than one hour before the hour of the end  of a trading session.
      if(StopOpenOrders[s]>=EndTrade[s] ||
         StopOpenOrders[s]<=StartTrade[s])
        {
         Print(Symbols[s],
               ": The hour of the end of placing orders ("+IntegerToString(StopOpenOrders[s])+") "
               "is to be less than the hour of the end ("+IntegerToString(EndTrade[s])+") and "
               "greater than the hour of the beginning of a trading session  ("+IntegerToString(StartTrade[s])+")!");
         return(false);
        }
     }
//--- Parameters are correct
   return(true);
  }
-->

이 패턴을 구현하려면 거래 및 보류 주문을 위해 지정된 시간 범위 내에 머무르는지 확인하는 기능이 필요합니다. 이러한 함수의 이름은 IsInTradeTimeRange()IsInOpenOrdersTimeRange()입니다. 둘 다 동일하게 작동하며 유일한 차이점은 검사 범위의 상한입니다. 더 나아가 우리는 이러한 기능이 어디에 사용되는지 알게 될 것입니다.

//+------------------------------------------------------------------+
//| Checks if we are within the time range for trade                 |
//+------------------------------------------------------------------+
bool IsInTradeTimeRange(int symbol_number)
  {
//--- If TradeInTimeRange mode is enabled
   if(TradeInTimeRange[symbol_number])
     {
      //--- Structure of the date and time
      MqlDateTime last_date;
      //--- Get the last value of the date and time data set
      TimeTradeServer(last_date);
      //--- Outside of the allowed time range
      if(last_date.hour<StartTrade[symbol_number] ||
         last_date.hour>=EndTrade[symbol_number])
         return(false);
     }
//--- Within the allowed time range
   return(true);
  }
//+------------------------------------------------------------------+
//| Checks if we are within the time range for placing orders        |
//+------------------------------------------------------------------+
bool IsInOpenOrdersTimeRange(int symbol_number)
  {
//--- If the TradeInTimeRange mode if enabled
   if(TradeInTimeRange[symbol_number])
     {
      //--- Structure of the date and time
      MqlDateTime last_date; 
      //--- Get the last value of the date and time data set
      TimeTradeServer(last_date);
      //--- Outside the allowed time range
      if(last_date.hour<StartTrade[symbol_number] ||
         last_date.hour>=StopOpenOrders[symbol_number])
         return(false);
     }
//--- Within the allowed time range
   return(true);
  }
-->

이전 글에서는 포지션, 기호 및 거래 내역의 속성을 수신하는 기능을 이미 고려했습니다. 이 글에서는 보류 중인 주문의 속성을 가져오기 위해 유사한 함수가 필요합니다. 포함 파일 Enums.mqh에서 보류 중인 주문의 속성이 있는 열거형을 만들 것입니다.

//--- Enumeration of the properties of a pending order 
enum ENUM_ORDER_PROPERTIES
  {
   O_SYMBOL          = 0,
   O_MAGIC           = 1,
   O_COMMENT         = 2,
   O_PRICE_OPEN      = 3,
   O_PRICE_CURRENT   = 4,
   O_PRICE_STOPLIMIT = 5,
   O_VOLUME_INITIAL  = 6,
   O_VOLUME_CURRENT  = 7,
   O_SL              = 8,
   O_TP              = 9,
   O_TIME_SETUP      = 10,
   O_TIME_EXPIRATION = 11,
   O_TIME_SETUP_MSC  = 12,
   O_TYPE_TIME       = 13,
   O_TYPE            = 14,
   O_ALL             = 15
  };
-->

그런 다음 포함 파일 TradeFunctions.mqh에서 보류 중인 주문의 속성이 있는 구조를 작성한 다음 인스턴스화해야 합니다.

//-- Properties of a pending order
struct pending_order_properties
  {
   string            symbol;          // Symbol
   long              magic;           // Magic number
   string            comment;         // Comment
   double            price_open;      // Price specified in the order
   double            price_current;   // Current price of the order symbol
   double            price_stoplimit; // Limit order price for the Stop Limit order
   double            volume_initial;  // Initial order volume
   double            volume_current;  // Current order volume
   double            sl;              // Stop Loss level
   double            tp;              // Take Profit level
   datetime          time_setup;      // Order placement time
   datetime          time_expiration; // Order expiration time
   datetime          time_setup_msc;  // The time of placing an order for execution in milliseconds since 01.01.1970
   datetime          type_time;       // Order lifetime
   ENUM_ORDER_TYPE   type;            // Position type
  };
//--- Variable of the order features
pending_order_properties ord;
-->

보류 중인 주문의 속성 또는 모든 속성을 가져오기 위해 GetPendingOrderProperties() 함수를 작성할 것입니다. 보류 중인 주문을 선택한 후 이 기능을 사용하여 주문의 속성을 검색할 수 있습니다. 그 방법은 아래에서 자세히 설명하겠습니다.

//+------------------------------------------------------------------+
//| Retrieves the properties of the previously selected pending order|
//+------------------------------------------------------------------+
void GetPendingOrderProperties(ENUM_ORDER_PROPERTIES order_property)
  {
   switch(order_property)
     {
      case O_SYMBOL          : ord.symbol=OrderGetString(ORDER_SYMBOL);                              break;
      case O_MAGIC           : ord.magic=OrderGetInteger(ORDER_MAGIC);                               break;
      case O_COMMENT         : ord.comment=OrderGetString(ORDER_COMMENT);                            break;
      case O_PRICE_OPEN      : ord.price_open=OrderGetDouble(ORDER_PRICE_OPEN);                      break;
      case O_PRICE_CURRENT   : ord.price_current=OrderGetDouble(ORDER_PRICE_CURRENT);                break;
      case O_PRICE_STOPLIMIT : ord.price_stoplimit=OrderGetDouble(ORDER_PRICE_STOPLIMIT);            break;
      case O_VOLUME_INITIAL  : ord.volume_initial=OrderGetDouble(ORDER_VOLUME_INITIAL);              break;
      case O_VOLUME_CURRENT  : ord.volume_current=OrderGetDouble(ORDER_VOLUME_CURRENT);              break;
      case O_SL              : ord.sl=OrderGetDouble(ORDER_SL);                                      break;
      case O_TP              : ord.tp=OrderGetDouble(ORDER_TP);                                      break;
      case O_TIME_SETUP      : ord.time_setup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);           break;
      case O_TIME_EXPIRATION : ord.time_expiration=(datetime)OrderGetInteger(ORDER_TIME_EXPIRATION); break;
      case O_TIME_SETUP_MSC  : ord.time_setup_msc=(datetime)OrderGetInteger(ORDER_TIME_SETUP_MSC);   break;
      case O_TYPE_TIME       : ord.type_time=(datetime)OrderGetInteger(ORDER_TYPE_TIME);             break;
      case O_TYPE            : ord.type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);                break;
      case O_ALL             :
         ord.symbol=OrderGetString(ORDER_SYMBOL);
         ord.magic=OrderGetInteger(ORDER_MAGIC);
         ord.comment=OrderGetString(ORDER_COMMENT);
         ord.price_open=OrderGetDouble(ORDER_PRICE_OPEN);
         ord.price_current=OrderGetDouble(ORDER_PRICE_CURRENT);
         ord.price_stoplimit=OrderGetDouble(ORDER_PRICE_STOPLIMIT);
         ord.volume_initial=OrderGetDouble(ORDER_VOLUME_INITIAL);
         ord.volume_current=OrderGetDouble(ORDER_VOLUME_CURRENT);
         ord.sl=OrderGetDouble(ORDER_SL);
         ord.tp=OrderGetDouble(ORDER_TP);
         ord.time_setup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);
         ord.time_expiration=(datetime)OrderGetInteger(ORDER_TIME_EXPIRATION);
         ord.time_setup_msc=(datetime)OrderGetInteger(ORDER_TIME_SETUP_MSC);
         ord.type_time=(datetime)OrderGetInteger(ORDER_TYPE_TIME);
         ord.type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);                                      break;
         //---
     default: Print("Retrieved feature of the pending order was not taken into account in the enumeration "); return;
     }
  }
-->

이제 보류 중인 주문을 배치, 수정 및 삭제하는 기본 기능을 작성합니다. SetPendingOrder() 함수는 주문을 보류합니다. 보류 중인 주문이 제출되지 않으면 언급된 기능은 오류 코드 및 설명과 함께 저널에 항목을 작성합니다.

//+------------------------------------------------------------------+
//| Places a pending order                                           |
//+------------------------------------------------------------------+
void SetPendingOrder(int                  symbol_number,   // Symbol number
                     ENUM_ORDER_TYPE      order_type,      // Order type
                     double               lot,             // Volume
                     double               stoplimit_price, // Level of the StopLimit order 
                     double               price,           // Price
                     double               sl,              // Stop Loss
                     double               tp,              // Take Profit
                     ENUM_ORDER_TYPE_TIME type_time,       // Order Expiration
                     string               comment)         // Comment
//--- Set magic number in the trade structure
   trade.SetExpertMagicNumber(MagicNumber);
//--- If a pending order failed to be placed, print an error message  
   if(!trade.OrderOpen(Symbols[symbol_number],
                       order_type,lot,stoplimit_price,price,sl,tp,type_time,0,comment))
      Print("Error when placing a pending order: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }
-->

ModifyPendingOrder() 함수는 보류 중인 주문을 수정합니다. 주문의 가격뿐만 아니라 수량도 변경하여 함수의 마지막 매개변수로 전달할 수 있도록 정리할 것입니다. 전달된 볼륨 값이 0보다 크면 보류 중인 주문을 삭제하고 필요한 볼륨 값으로 새 주문을 배치해야 함을 의미합니다. 다른 모든 경우에는 가격 값을 변경하여 기존 주문을 수정하기만 하면 됩니다.

//+------------------------------------------------------------------+
//| Modifies a pending order                                         |
//+------------------------------------------------------------------+
void ModifyPendingOrder(int                  symbol_number,   //Symbol number
                        ulong                ticket,          // Order ticket
                        ENUM_ORDER_TYPE      type,            // Order type
                        double               price,           // Order price
                        double               sl,              // Stop Loss of the order
                        double               tp,              // Take Profit of the order
                        ENUM_ORDER_TYPE_TIME type_time,       // Order expiration
                        datetime             time_expiration, // Order expiration time
                        double               stoplimit_price, // Price
                        string               comment,         // Comment
                        double               volume)          // Volume
  {
//--- If the passed volume value is non-zero, delete the order and place it again
   if(volume>0)
     {
      //--- If the order failed to be deleted, exit
      if(!DeletePendingOrder(ticket))
         return;
      //--- Place a pending order
      SetPendingOrder(symbol_number,type,volume,0,price,sl,tp,type_time,comment);
      //--- Adjust Stop Loss of position as related to the order
      CorrectStopLossByOrder(symbol_number,price,type);
     }
//--- If the passed volume value is zero, modify the order
   else
     {
      //--- If the pending order failed to be modified, print a relevant message
      if(!trade.OrderModify(ticket,price,sl,tp,type_time,time_expiration,stoplimit_price))
         Print("Error when modifying the pending order price: ",
         GetLastError()," - ",ErrorDescription(GetLastError()));
      //--- Otherwise adjust Stop Loss of position as related to the order
      else
         CorrectStopLossByOrder(symbol_number,price,type);
     }
  }
-->

위의 코드에서 강조 표시된 두 개의 새로운 함수 DeletePendingOrder()CorrectStopLossByOrder()입니다. 첫 번째는 보류 중인 주문을 삭제하고 두 번째는 보류 중인 주문과 관련된 포지션의 손절매를 조정합니다.

//+------------------------------------------------------------------+
//| Deletes a pending order                                          | 
//+------------------------------------------------------------------+
bool DeletePendingOrder(ulong ticket)
  {

//--- If a pending order failed to get deleted, print a relevant message
   if(!trade.OrderDelete(ticket))
     {
      Print("Error when deleting a pending order: ",GetLastError()," - ",ErrorDescription(GetLastError()));
      return(false);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Modifies StopLoss of the position as related to the pending order|
//+------------------------------------------------------------------+
void CorrectStopLossByOrder(int             symbol_number, // Symbol number
                            double          price,         // Order Price
                            ENUM_ORDER_TYPE type)          // Order Type
  {
//--- If Stop Loss disabled, exit
   if(StopLoss[symbol_number]==0)
      return;
//--- If Stop Loss enabled
   double new_sl=0.0; // New Stop Loss value
//--- Get a Point value
   GetSymbolProperties(symbol_number,S_POINT);
//--- Number of decimal places
   GetSymbolProperties(symbol_number,S_DIGITS);
//--- Get Take Profit of position
   GetPositionProperties(symbol_number,P_TP);
//--- Calculate as related to the order type
   switch(type)
     {
      case ORDER_TYPE_BUY_STOP  :
         new_sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         break;
      case ORDER_TYPE_SELL_STOP :
         new_sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         break;
     }
//--- Modify the position
   if(!trade.PositionModify(Symbols[symbol_number],new_sl,pos.tp))
      Print("Error when modifying position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }
-->

보류 주문을 하기 전에 동일한 댓글이 있는 보류 주문이 이미 존재하는지 확인하는 것도 필요합니다. 이 글의 시작 부분에서 언급했듯이 "top_order"라는 주석이 있는 상위 매수 스탑 주문과 "bottom_order"라는 주석이 있는 매도 스탑 주문을 배치합니다. 이러한 확인을 용이하게 하기 위해 CheckPendingOrderByComment()라는 함수를 작성해 보겠습니다.

//+------------------------------------------------------------------+
//| Checks existence of a pending order by a comment                 |
//+------------------------------------------------------------------+
bool CheckPendingOrderByComment(int symbol_number,string comment)
  {
   int    total_orders  =0;  // Total number of pending orders
   string order_symbol  =""; // Order Symbol
   string order_comment =""; // Order Comment
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //---Select the order by the ticket
      if(OrderGetTicket(i)>0)
        {
         //--- Get the symbol name
         order_symbol=OrderGetString(ORDER_SYMBOL);
         //--- If the symbols are equal
         if(order_symbol==Symbols[symbol_number])
           {
            //--- Get the order comment
            order_comment=OrderGetString(ORDER_COMMENT);
            //--- If the comments are equal
            if(order_comment==comment)
               return(true);
           }
        }
     }
//--- Order with a specified comment not found
   return(false);
  }
-->

위의 코드는 OrdersTotal() 시스템 함수를 사용하여 총 주문 수를 얻을 수 있음을 보여줍니다. 그러나 지정된 기호에 대한 총 보류 주문 수를 얻기 위해 사용자 정의 함수를 작성할 것입니다. 이름을 OrdersTotalBySymbol()로 지정합니다.

//+------------------------------------------------------------------+
//| Returns the total number of orders for the specified symbol      |
//+------------------------------------------------------------------+
int OrdersTotalBySymbol(string symbol)
  {
   int   count        =0; // Order counter
   int   total_orders =0; // Total number of pending orders
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total number of orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If an order has been selected
      if(OrderGetTicket(i)>0)
        {
         //--- Get the order symbol
         GetOrderProperties(O_SYMBOL);
         //--- If the order symbol and the specified symbol are equal
         if(ord.symbol==symbol)
            //--- Increase the counter
            count++;
        }
     }
//--- Return the total number of orders
   return(count);
  }
-->

보류 중인 주문을 하기 전에 필요한 경우 손절매 및 이익실현 수준뿐만 아니라 가격도 계산해야 합니다. 반전 모드가 활성화된 경우 추적 손절매 수준을 다시 계산하고 변경하기 위해 별도의 사용자 정의 함수가 필요합니다.

보류 주문 가격을 계산하려면 CalculatePendingOrder() 함수를 작성해 보겠습니다.

//+------------------------------------------------------------------+
//| Calculates the pending order level(price)                        |
//+------------------------------------------------------------------+
double CalculatePendingOrder(int symbol_number,ENUM_ORDER_TYPE order_type)
  {
//--- For the calculated pending order value
   double price=0.0;
//--- If the value for SELL STOP order is to be calculated
   if(order_type==ORDER_TYPE_SELL_STOP)
     {
      //--- Calculate level
      price=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- Return calculated value if it is less than the lower limit of Stops level
      //    If the value is equal or greater, return the adjusted value
      return(price<symb.down_level ? price : symb.down_level-symb.offset);
     }
//--- If the value for BUY STOP order is to be calculated
   if(order_type==ORDER_TYPE_BUY_STOP)
     {
      //--- Calculate level
      price=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- Return the calculated value if it is greater than the upper limit of Stops level
      //    If the value is equal or less, return the adjusted value
      return(price>symb.up_level ? price : symb.up_level+symb.offset);
     }
//---
   return(0.0);
  }
-->

아래는 보류 중인 주문에서 손절매 및 이익실현 수준을 계산하기 위한 기능 코드입니다.

//+------------------------------------------------------------------+
//| Calculates Stop Loss level for a pending order                   |
//+------------------------------------------------------------------+
double CalculatePendingOrderStopLoss(int symbol_number,ENUM_ORDER_TYPE order_type,double price)
  {
//--- If Stop Loss is required
   if(StopLoss[symbol_number]>0)
     {
      double sl         =0.0; // For the Stop Loss calculated value
      double up_level   =0.0; // Upper limit of Stop Levels
      double down_level =0.0; // Lower limit of Stop Levels
      //--- If the value for BUY STOP order is to be calculated
      if(order_type==ORDER_TYPE_BUY_STOP)
        {
         //--- Define lower threshold
         down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits);
         //--- Calculate level
         sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is less than the lower limit of Stop level
         //    If the value is equal or greater, return the adjusted value
         return(sl<down_level ? sl : NormalizeDouble(down_level-symb.offset,symb.digits));
        }
      //--- If the value for the SELL STOP order is to be calculated
      if(order_type==ORDER_TYPE_SELL_STOP)
        {
         //--- Define the upper threshold
         up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is greater than the upper limit of the Stops level
         //    If the value is less or equal, return the adjusted value.
         return(sl>up_level ? sl : NormalizeDouble(up_level+symb.offset,symb.digits));
        }
     }
//---
   return(0.0);
  }
//+------------------------------------------------------------------+
//| Calculates the Take Profit level for a pending order             |
//+------------------------------------------------------------------+
double CalculatePendingOrderTakeProfit(int symbol_number,ENUM_ORDER_TYPE order_type,double price)
  {
//--- If Take Profit is required
   if(TakeProfit[symbol_number]>0)
     {
      double tp         =0.0; // For the calculated Take Profit value
      double up_level   =0.0; // Upper limit of Stop Levels
      double down_level =0.0; // Lower limit of Stop Levels
      //--- If the value for SELL STOP order is to be calculated
      if(order_type==ORDER_TYPE_SELL_STOP)
        {
         //--- Define lower threshold
         down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         tp=NormalizeDouble(price-CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is less than the below limit of the Stops level
         //    If the value is greater or equal, return the adjusted value
         return(tp<down_level ? tp : NormalizeDouble(down_level-symb.offset,symb.digits));
        }
      //--- If the value for the BUY STOP order is to be calculated
      if(order_type==ORDER_TYPE_BUY_STOP)
        {
         //--- Define the upper threshold
         up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         tp=NormalizeDouble(price+CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is greater than the upper limit of the Stops level
         //    If the value is less or equal, return the adjusted value
         return(tp>up_level ? tp : NormalizeDouble(up_level+symb.offset,symb.digits));
        }
     }
//---
   return(0.0);
  }
-->

취소된 보류 주문의 정류장 수준(가격)을 계산하고 끌어올리기 위해 다음 함수 CalculateReverseOrderTrailingStop()ModifyPendingOrderTrailingStop()을 작성할 것입니다. 아래에서 함수의 코드를 찾을 수 있습니다.

CalculateReverseOrderTrailingStop() 함수 코드:

//+----------------------------------------------------------------------------+
//| Calculates the Trailing Stop level for the reversed order                  |
//+----------------------------------------------------------------------------+
double CalculateReverseOrderTrailingStop(int symbol_number,ENUM_POSITION_TYPE position_type)
  {
//--- Variables for calculation
   double    level       =0.0;
   double    buy_point   =low[symbol_number].value[1];  // Low value for Buy
   double    sell_point  =high[symbol_number].value[1]; // High value for Sell
//--- Calculate the level for the BUY position
   if(position_type==POSITION_TYPE_BUY)
     {
      //--- Bar's low minus the specified number of points
      level=NormalizeDouble(buy_point-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //---  If the calculated level is lower than the lower limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level<symb.down_level)
         return(level);
      //--- If it is not lower, try to calculate based on the bid price
      else
        {
         level=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
         //--- If the calculated level is lower than the limit, return the current value of the level
         //    otherwise set the nearest possible value
         return(level<symb.down_level ? level : symb.down_level-symb.offset);
        }
     }
//--- Calculate the level for the SELL position
   if(position_type==POSITION_TYPE_SELL)
     {
      // Bar's high plus the specified number of points
      level=NormalizeDouble(sell_point+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- If the calculated level is higher than the upper limit of the Stops level, 
      //    then the calculation is complete, return the current value of the level
      if(level>symb.up_level)
         return(level);
      //--- If it is not higher, try to calculate based on the ask price
      else
        {
         level=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
         //--- If the calculated level is higher than the limit, return the current value of the level
         //    Otherwise set the nearest possible value
         return(level>symb.up_level ? level : symb.up_level+symb.offset);
        }
     }
//---
   return(0.0);
  }
-->

ModifyPendingOrderTrailingStop() 함수 코드:

//+------------------------------------------------------------------+
//| Modifying the Trailing Stop level for a pending order            |
//+------------------------------------------------------------------+
void ModifyPendingOrderTrailingStop(int symbol_number)
  {
//--- Exit, if the reverse position mode is disabled and Trailing Stop is not set
   if(!Reverse[symbol_number] || TrailingStop[symbol_number]==0)
      return;
//--- 
   double          new_level              =0.0;         // For calculating a new level for a pending order
   bool            condition              =false;       // For checking the modification condition
   int             total_orders           =0;           // Total number of pending orders
   ulong           order_ticket           =0;           // Order ticket
   string          opposite_order_comment ="";          // Opposite order comment
   ENUM_ORDER_TYPE opposite_order_type    =WRONG_VALUE; // Order type

//--- Get the flag of presence/absence of a position
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If a position is absent
   if(!pos.exists)
      return;
//--- Get a total number of pending orders
   total_orders=OrdersTotal();
//--- Get the symbol properties
   GetSymbolProperties(symbol_number,S_ALL);
//--- Get the position properties
   GetPositionProperties(symbol_number,P_ALL);
//--- Get the level for Stop Loss
   new_level=CalculateReverseOrderTrailingStop(symbol_number,pos.type);
//--- Loop through the orders from the last to the first one
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If the order selected
      if((order_ticket=OrderGetTicket(i))>0)
        {
         //--- Get the order symbol
         GetPendingOrderProperties(O_SYMBOL);
         //--- Get the order comment
         GetPendingOrderProperties(O_COMMENT);
         //--- Get the order price
         GetPendingOrderProperties(O_PRICE_OPEN);
         //--- Depending on the position type, check the relevant condition for the Trailing Stop modification
         switch(pos.type)
           {
            case POSITION_TYPE_BUY  :
               //---If the new order value is greater than the current value plus set step then condition fulfilled 
               condition=new_level>ord.price_open+CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point);
               //--- Define the type and comment of the reversed pending order for check.
               opposite_order_type    =ORDER_TYPE_SELL_STOP;
               opposite_order_comment =comment_bottom_order;
               break;
            case POSITION_TYPE_SELL :
               //--- If the new value for the order if less than the current value minus a set step then condition fulfilled
               condition=new_level<ord.price_open-CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point);
               //--- Define the type and comment of the reversed pending order for check
               opposite_order_type    =ORDER_TYPE_BUY_STOP;
               opposite_order_comment =comment_top_order;
               break;
           }
         //--- If condition fulfilled, the order symbol and positions are equal
         //    and order comment and the reversed order comment are equal
         if(condition && 
            ord.symbol==Symbols[symbol_number] && 
            ord.comment==opposite_order_comment)
           {
            double sl=0.0; // Stop Loss
            double tp=0.0; // Take Profit
            //--- Get Take Profit and Stop Loss levels
            sl=CalculatePendingOrderStopLoss(symbol_number,opposite_order_type,new_level);
            tp=CalculatePendingOrderTakeProfit(symbol_number,opposite_order_type,new_level);
            //--- Modify order
            ModifyPendingOrder(symbol_number,order_ticket,opposite_order_type,new_level,sl,tp,
                               ORDER_TIME_GTC,ord.time_expiration,ord.price_stoplimit,ord.comment,0);
            return;
           }
        }
     }
  }
-->

때때로 손절매 또는 이익실현에서 포지션이 마감되었는지 확인해야 할 수도 있습니다. 이 특별한 경우에 우리는 그러한 요구 사항을 접하게 될 것입니다. 따라서 마지막 거래 주석으로 이 이벤트를 식별하는 함수를 작성해 보겠습니다. 지정된 기호에 대한 마지막 거래 주석을 검색하기 위해 GetLastDealComment()라는 별도의 함수를 작성할 것입니다.

//+------------------------------------------------------------------+
//| Returns a the last deal comment for a specified symbol           |
//+------------------------------------------------------------------+
string GetLastDealComment(int symbol_number)
  {
   int    total_deals  =0;  // Total number of deals in the selected history
   string deal_symbol  =""; // Deal symbol 
   string deal_comment =""; // Deal comment
//--- If the deals history retrieved
   if(HistorySelect(0,TimeCurrent()))
     {
      //--- Receive the number of deals in the retrieved list
      total_deals=HistoryDealsTotal();
      //--- Loop though the total number of deals in the retrieved list from the last deal to the first one.
      for(int i=total_deals-1; i>=0; i--)
        {
         //--- Receive the deal comment
         deal_comment=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_COMMENT);
         //--- Receive the deal symbol
         deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL);
         //--- If the deal symbol and the current symbol are equal, stop the loop
         if(deal_symbol==Symbols[symbol_number])
            break;
        }
     }
//---
   return(deal_comment);
  }
-->

이제 지정된 기호의 마지막 포지션을 닫는 이유를 결정하는 함수를 쉽게 작성할 수 있습니다. 다음은 IsClosedByTakeProfit()IsClosedByStopLoss() 함수의 코드입니다.

//+------------------------------------------------------------------+
//| Returns the reason for closing position at Take Profit           |
//+------------------------------------------------------------------+
bool IsClosedByTakeProfit(int symbol_number)
  {
   string last_comment="";
//--- Get the last deal comment for the specified symbol
   last_comment=GetLastDealComment(symbol_number);
//--- If the comment contain a string "tp"
   if(StringFind(last_comment,"tp",0)>-1)
      return(true);
//--- If the comment does not contain a string "tp"
   return(false);
  }
//+------------------------------------------------------------------+
//| Returns the reason for closing position at Stop Loss             |
//+------------------------------------------------------------------+
bool IsClosedByStopLoss(int symbol_number)
  {
   string last_comment="";
//--- Get the last deal comment for the specified symbol
   last_comment=GetLastDealComment(symbol_number);
//--- If the comment contains the string "sl"
   if(StringFind(last_comment,"sl",0)>-1)
      return(true);
//--- If the comment does not contain the string "sl"
   return(false);
  }
-->

우리는 기록의 마지막 거래가 정말로 지정된 기호에 대한 거래인지 확인하기 위해 또 다른 검사를 수행할 것입니다. 마지막 거래 티켓을 기억에 남기고 싶습니다. 이를 달성하기 위해 전역 범위에 배열을 추가할 것입니다.

//--- Array for checking the ticket of the last deal for each symbol.
ulong last_deal_ticket[NUMBER_OF_SYMBOLS];
-->

마지막 거래 티켓을 확인하기 위한 IsLastDealTicket() 함수는 아래 코드와 같이 표시됩니다.

//+------------------------------------------------------------------+
//| Returns the event of the last deal for the specified symbol      |
//+------------------------------------------------------------------+
bool IsLastDealTicket(int symbol_number)
  {
   int    total_deals =0;  // Total number of deals in the selected history list
   string deal_symbol =""; // Deal symbol
   ulong  deal_ticket =0;  // Deal ticket
//--- If the deal history was received
   if(HistorySelect(0,TimeCurrent()))
     {
      //--- Get the total number of deals in the received list
      total_deals=HistoryDealsTotal();
      //--- Loop through the total number of deals from the last deal to the first one
      for(int i=total_deals-1; i>=0; i--)
        {
         //--- Get deal ticket
         deal_ticket=HistoryDealGetTicket(i);
         //--- Get deal symbol
         deal_symbol=HistoryDealGetString(deal_ticket,DEAL_SYMBOL);
         //--- If deal symbol and the current one are equal, stop the loop
         if(deal_symbol==Symbols[symbol_number])
           {
            //--- If the tickets are equal, exit
            if(deal_ticket==last_deal_ticket[symbol_number])
               return(false);
            //--- If the tickets are not equal report it
            else
              {
               //--- Save the last deal ticket
               last_deal_ticket[symbol_number]=deal_ticket;
               return(true);
              }
           }
        }
     }
//---
   return(false);
  }
-->

현재 시간이 지정된 거래 범위를 벗어나면 손실이든 이익이든 관계없이 해당 포지션은 강제로 청산됩니다. 포지션을 닫기 위한 ClosePosition() 함수를 작성해 보겠습니다.

//+------------------------------------------------------------------+
//| Closes position                                                  |
//+------------------------------------------------------------------+
void ClosePosition(int symbol_number)
  {
//--- Check if position exists  
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If there is no position, exit
   if(!pos.exists)
      return;
//--- Set the slippage value in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation));
//--- If the position was not closed, print the relevant message
   if(!trade.PositionClose(Symbols[symbol_number]))
      Print("Error when closing position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }
-->

거래 시간 범위를 벗어나 포지션이 청산되면 모든 보류 주문을 삭제해야 합니다. 우리가 작성하려고 하는 DeleteAllPendingOrders() 함수는 지정된 기호에 대한 모든 보류 주문을 삭제합니다.

//+------------------------------------------------------------------+
//| Deletes all pending orders                                       |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders(int symbol_number)
  {
   int   total_orders =0; // Total number of pending orders
   ulong order_ticket =0; // Order ticket
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total number of pending orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If the order selected
      if((order_ticket=OrderGetTicket(i))>0)
        {
         //--- Get the order symbol
         GetOrderProperties(O_SYMBOL);
         //--- If the order symbol and the current symbol are equal
         if(ord.symbol==Symbols[symbol_number])
            //--- Delete the order
            DeletePendingOrder(order_ticket);
        }
     }
  }
-->

이제 구조 체계에 필요한 모든 기능이 있습니다. 몇 가지 중요한 변경 사항과 보류 중인 주문 ManagePendingOrders() 관리를 위한 새로운 기능이 적용된 친숙한 함수 TradingBlock()을 살펴보겠습니다. 보류중인 주문과 관련된 현재 상황에 대한 완전한 통제가 수행됩니다.

현재 패턴에 대한 TradingBlock() 함수는 다음과 같습니다.

//+------------------------------------------------------------------+
//| Trade block                                                      |
//+------------------------------------------------------------------+
void TradingBlock(int symbol_number)
  {
   double          tp=0.0;                 // Take Profit
   double          sl=0.0;                 // Stop Loss
   double          lot=0.0;                // Volume for position calculation in case of reversed position
   double          order_price=0.0;        // Price for placing the order
   ENUM_ORDER_TYPE order_type=WRONG_VALUE; // Order type for opening position
//--- If outside of the time range for placing pending orders
   if(!IsInOpenOrdersTimeRange(symbol_number))
      return;
//--- Find out if there is an open position for the symbol
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If there is no position
   if(!pos.exists)
     {
      //--- Get symbol properties
      GetSymbolProperties(symbol_number,S_ALL);
      //--- Adjust the volume
      lot=CalculateLot(symbol_number,Lot[symbol_number]);
      //--- If there is no upper pending order
      if(!CheckPendingOrderByComment(symbol_number,comment_top_order))
        {
         //--- Get the price for placing a pending order
         order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_BUY_STOP);
         //--- Get Take Profit and Stop Loss levels
         sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_BUY_STOP,order_price);
         tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_BUY_STOP,order_price);
         //--- Place a pending order
         SetPendingOrder(symbol_number,ORDER_TYPE_BUY_STOP,lot,0,order_price,sl,tp,ORDER_TIME_GTC,comment_top_order);
        }
      //--- If there is no lower pending order
      if(!CheckPendingOrderByComment(symbol_number,comment_bottom_order))
        {
         //--- Get the price for placing the pending order
         order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_SELL_STOP);
         //--- Get Take Profit and Stop Loss levels
         sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_SELL_STOP,order_price);
         tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_SELL_STOP,order_price);
         //--- Place a pending order
         SetPendingOrder(symbol_number,ORDER_TYPE_SELL_STOP,lot,0,order_price,sl,tp,ORDER_TIME_GTC,comment_bottom_order);
        }
     }
  }
-->

보류 중인 주문을 관리하기 위한 ManagePendingOrders() 함수 코드::

//+------------------------------------------------------------------+
//| Manages pending orders                                           |
//+------------------------------------------------------------------+
void ManagePendingOrders()
  {
//--- Loop through the total number of symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading this symbol is forbidden, go to the following one
      if(Symbols[s]=="")
         continue;
      //--- Find out if there is an open position for the symbol
      pos.exists=PositionSelect(Symbols[s]);
      //--- If there is no position
      if(!pos.exists)
        {
         //--- If the last deal on current symbol and
         //    position  was exited on Take Profit or Stop Loss
         if(IsLastDealTicket(s) && 
            (IsClosedByStopLoss(s) || IsClosedByTakeProfit(s)))
            //--- Delete all pending orders for the symbol
            DeleteAllPendingOrders(s);
         //--- Go to the following symbol
         continue;
        }
      //--- If there is a position
      ulong           order_ticket           =0;           // Order ticket
      int             total_orders           =0;           // Total number of pending orders
      int             symbol_total_orders    =0;           // Number of pending orders for the specified symbol
      string          opposite_order_comment ="";          // Opposite order comment
      ENUM_ORDER_TYPE opposite_order_type    =WRONG_VALUE; // Order type
      //--- Get the total number of pending orders
      total_orders=OrdersTotal();
      //--- Get the total number of pending orders for the specified symbol
      symbol_total_orders=OrdersTotalBySymbol(Symbols[s]);
      //--- Get symbol properties
      GetSymbolProperties(s,S_ASK);
      GetSymbolProperties(s,S_BID);
      //--- Get the comment for the selected position
      GetPositionProperties(s,P_COMMENT);
      //--- If the position comment belongs to the upper order,
      //    then the lower order is to be deleted, modified/placed
      if(pos.comment==comment_top_order)
        {
         opposite_order_type    =ORDER_TYPE_SELL_STOP;
         opposite_order_comment =comment_bottom_order;
        }
      //--- If the position comment belongs to the lower order,
      //    then the upper order is to be deleted/modified/placed
      if(pos.comment==comment_bottom_order)
        {
         opposite_order_type    =ORDER_TYPE_BUY_STOP;
         opposite_order_comment =comment_top_order;
        }
      //--- If there are no pending orders for the specified symbol
      if(symbol_total_orders==0)
        {
         //--- If the position reversal is enabled, place a reversed order
         if(Reverse[s])
           {
            double tp=0.0;          // Take Profit
            double sl=0.0;          // Stop Loss
            double lot=0.0;         // Volume for position calculation in case of reversed positio
            double order_price=0.0; // Price for placing the order
            //--- Get the price for placing a pending order
            order_price=CalculatePendingOrder(s,opposite_order_type);
            //---Get Take Profit и Stop Loss levels
            sl=CalculatePendingOrderStopLoss(s,opposite_order_type,order_price);
            tp=CalculatePendingOrderTakeProfit(s,opposite_order_type,order_price);
            //--- Calculate double volume
            lot=CalculateLot(s,pos.volume*2);
            //--- Place the pending order
            SetPendingOrder(s,opposite_order_type,lot,0,order_price,sl,tp,ORDER_TIME_GTC,opposite_order_comment);
            //--- Adjust Stop Loss as related to the order
            CorrectStopLossByOrder(s,order_price,opposite_order_type);
           }
         return;
        }
      //--- If there are pending orders for this symbol, then depending on the circumstances delete or
      //    modify the reversed order
      if(symbol_total_orders>0)
        {
         //--- Loop through the total number of orders from the last one to the first one
         for(int i=total_orders-1; i>=0; i--)
           {
            //--- If the order chosen
            if((order_ticket=OrderGetTicket(i))>0)
              {
               //--- Get the order symbol
               GetPendingOrderProperties(O_SYMBOL);
               //--- Get the order comment
               GetPendingOrderProperties(O_COMMENT);
               //--- If order symbol and position symbol are equal,
               //    and order comment and the reversed order comment are equal
               if(ord.symbol==Symbols[s] && 
                  ord.comment==opposite_order_comment)
                 {
                  //--- If position reversal is disabled
                  if(!Reverse[s])
                     //--- Delete order
                     DeletePendingOrder(order_ticket);
                  //--- If position reversal is enabled
                  else
                    {
                     double lot=0.0;
                     //--- Get the current order properties
                     GetPendingOrderProperties(O_ALL);
                     //--- Get the current position volume
                     GetPositionProperties(s,P_VOLUME);
                     //--- If the order has been modified already, exit the loop.
                     if(ord.volume_initial>pos.volume)
                        break;
                     //--- Calculate double volume
                     lot=CalculateLot(s,pos.volume*2);
                     //--- Modify (delete and place again) the order
                     ModifyPendingOrder(s,order_ticket,opposite_order_type,
                                        ord.price_open,ord.sl,ord.tp,
                                        ORDER_TIME_GTC,ord.time_expiration,
                                        ord.price_stoplimit,opposite_order_comment,lot);
                    }
                 }
              }
           }
        }
     }
  }
-->

이제 메인 프로그램 파일을 약간만 조정하면 됩니다. 거래 이벤트 핸들러 OnTrade()을 추가합니다. 거래 이벤트에 대한 보류 중인 주문과 관련된 현재 상황의 평가가 이 기능에서 수행됩니다.

//+------------------------------------------------------------------+
//| Processing of trade events                                       |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Check the state of pending orders
   ManagePendingOrders();
  }
-->

ManagePendingOrders() 함수는 사용자 이벤트 핸들러 OnChartEvent()에서도 사용됩니다.

//+------------------------------------------------------------------+
//| User events and chart events handler                             |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // Event identifier
                  const long &lparam,   // Parameter of long event type
                  const double &dparam, // Parameter of double event type
                  const string &sparam) // Parameter of string event type
  {
//--- If it is a user event
   if(id>=CHARTEVENT_CUSTOM)
     {
      //--- Exit, if trade is prohibited
      if(CheckTradingPermission()>0)
         return;
      //--- If it is a tick event
      if(lparam==CHARTEVENT_TICK)
        {
         //--- Check the state of pending orders
         ManagePendingOrders();
         //--- Check signals and trade according to them
         CheckSignalsAndTrade();
         return;
        }
     }
  }
-->

CheckSignalsAndTrade() 함수에서도 일부 변경이 이루어졌습니다. 아래 코드에서 강조 표시된 것은 이 글에서 고려되는 새로운 기능을 특징으로 하는 문자열입니다.

//+------------------------------------------------------------------+
//| Checks signals and trades based on New Bar event                 |
//+------------------------------------------------------------------+
void CheckSignalsAndTrade()
  {
//--- Loop through all specified signals
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading this symbol is prohibited, exit
      if(Symbols[s]=="")
         continue;
      //--- If the bar is not new, move on to the following symbol
      if(!CheckNewBar(s))
         continue;
      //--- If there is a new bar
      else
        {
         //--- If outside the time range
         if(!IsInTradeTimeRange(s))
           {
            //--- Close position
            ClosePosition(s);
            //--- Delete all pending orders
            DeleteAllPendingOrders(s);
            //--- Move on to the following symbol
            continue;
           }
         //--- Get bars data
         GetBarsData(s);
         //--- Check conditions and trade
         TradingBlock(s);
         //--- If position reversal if enabled
         if(Reverse[s])
            //--- Pull up Stop Loss for pending order
            ModifyPendingOrderTrailingStop(s);
         //--- If position reversal is disabled
         else
         //--- Pull up Stop Loss
            ModifyTrailingStop(s);
        }
     }
  
-->

이제 모든 것이 준비되었으며 이 다중 통화 Expert Advisor의 매개변수를 최적화할 수 있습니다. 아래와 같이 전략 테스터를 설정해 보겠습니다.

그림 1 - 매개변수 최적화를 위한 테스터 설정.

그림 1 - 매개변수 최적화를 위한 테스터 설정.

먼저 통화 쌍 EURUSD에 대한 매개변수를 최적화한 다음 AUDUSD에 대한 매개변수를 최적화합니다. 아래 스크린샷은 EURUSD 최적화를 위해 선택할 매개변수를 보여줍니다.

그림 2 - 다중 통화 Expert Advisor 최적화를 위한 매개변수 설정

그림 2 - 다중 통화 Expert Advisor 최적화를 위한 매개변수 설정

통화 쌍 EURUSD의 매개변수가 최적화된 후 동일한 매개변수가 AUDUSD에 대해 최적화되어야 합니다. 아래는 함께 테스트한 두 기호의 결과입니다. 결과는 최대 회복 계수로 선택되었습니다. 테스트를 위해 랏 값은 두 기호 모두에 대해 1로 설정되었습니다.

그림 3 - 두 기호에 대한 테스트 결과.

그림 3 - 두 기호에 대한 테스트 결과.


결론

그것은 그것에 대해 꽤 많이입니다. 준비된 기능을 사용하면 거래 결정을 내리는 아이디어를 개발하는 데 집중할 수 있습니다. 이 경우 TradingBlock()ManagePendingOrders() 함수에서 변경 사항을 구현해야 합니다. 최근에 MQL5를 배우기 시작한 사람들은 더 많은 기호를 추가하고 거래 알고리즘 체계를 변경하는 연습을 하는 것이 좋습니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/755

파일 첨부됨 |
755.set (1.59 KB)
Renko 차트 표시 Renko 차트 표시
이 글에서는 Renko 차트 작성의 예와 MQL5에서 지표로 구현하는 방법을 설명합니다. 이 지표를 수정하면 기존 차트와 구별됩니다. 지표 창과 메인 차트 모두에서 구성할 수 있습니다. 또한 지그재그 표시기가 있습니다. 차트 구현의 몇 가지 예를 찾을 수 있습니다.
MQL5 Cookbook: 가격 다이버전스를 분석하기 위한 다중 기호 지표 개발 MQL5 Cookbook: 가격 다이버전스를 분석하기 위한 다중 기호 지표 개발
이 글에서는 지정된 기간의 가격 다이버전스를 분석하기 위해 다중 기호 지표의 개발을 고려할 것입니다. 핵심 주제는 다중 통화 표시기 프로그래밍에 대한 이전 글 "MQL5 Cookbook: MQL5의 다중기호 변동성 지표 개발"에서 이미 논의되었습니다. 따라서 이번에는 극적으로 변경된 새로운 기능에 대해서만 설명하겠습니다. 다중 통화 표시기 프로그래밍이 처음이라면 먼저 이전 글을 읽는 것이 좋습니다.
MQL5 Expert Advisor의 GSM 모뎀 작업 MQL5 Expert Advisor의 GSM 모뎀 작업
현재 모바일 터미널, 푸시 알림, ICQ 작업과 같이 거래 계정을 원격으로 편안하게 모니터링할 수 있는 방법이 많이 있습니다. 그러나 모두 인터넷 연결이 필요합니다. 이 글에서는 모바일 인터넷을 사용할 수 없는 경우에도 통화 및 문자 메시지를 통해 거래 단말기와 계속 연락할 수 있도록 하는 Expert Advisor를 만드는 과정에 대해 설명합니다.
MQL5 프로그래밍 기본: 목록 MQL5 프로그래밍 기본: 목록
거래 전략 개발을 위한 프로그래밍 언어의 새 버전인 MQL[MQL5]은 이전 버전[MQL4]에 비해 더 강력하고 효과적인 기능을 제공합니다. 이점은 본질적으로 객체 지향 프로그래밍 기능에 있습니다. 이 글에서는 노드 및 목록과 같은 복잡한 사용자 지정 데이터 유형을 사용할 가능성을 조사합니다. 또한 MQL5의 실제 프로그래밍에서 목록을 사용하는 예를 제공합니다.