English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5 Cookbook: 사용자 지정 정보 패널의 포지션 속성

MQL5 Cookbook: 사용자 지정 정보 패널의 포지션 속성

MetaTrader 5 | 2 9월 2021, 16:56
55 0
Anatoli Kazharski
Anatoli Kazharski

소개

이번에는 현재 기호에 대한 포지션 속성을 가져와 수동 거래 중에 사용자 지정 정보 패널에 표시하는 간단한 Expert Advisor를 만들 것입니다. 정보 패널은 그래픽 개체를 사용하여 생성되며 표시된 정보는 틱마다 새로 고쳐집니다. 이것은 "MQL5 Cookbook: Get Position Properties" 시리즈의 이전 글에서 설명한 스크립트를 수동으로 실행해야 하는 모든 시간보다 훨씬 편리할 것입니다.

 

Expert Advisor 개발

그래픽 개체부터 시작하겠습니다. 정보 패널을 만들려면 배경, 헤더, 이름 및 포지션 속성 값에 대한 개체가 필요합니다. 배경과 헤더에는 가격과 함께 움직이지 않는 직사각형이 필요합니다. 직사각형은 사각형 레이블 또는 편집과 같은 그래픽 개체를 사용하여 만들 수 있으며 개체 속성의 이름과 값은 텍스트 레이블을 사용하여 만듭니다.

코드를 진행하기 전에 먼저 정보 패널의 레이아웃을 준비합니다. 그 편의성은 설정 창에서 속성을 빠르게 변경하고 정보 패널의 모양을 사용자 지정할 수 있다는 사실에 있습니다.

모든 개체에는 선택한 개체의 상황에 맞는 메뉴에서 열 수 있는 설정 창이 있습니다. 설정 창은 필요한 개체를 선택하고 속성을 클릭하여 개체 목록(Ctrl+B)에서 열 수도 있습니다. 정보 패널 레이아웃은 아래와 같습니다. 코드를 작성할 때 크기와 좌표를 추정하는 데에도 사용할 수 있습니다. 정보 패널의 코드가 준비되면 Expert Advisor가 레이아웃 개체를 '볼' 수 없으므로 차트에서 제거하지 않으므로 수동으로 레이아웃 개체를 삭제해야 합니다.

그림 1. 정보 패널의 레이아웃을 준비합니다.

그림 1. 정보 패널의 레이아웃을 준비합니다.

이제 Expert Advisor용 템플릿을 만들어야 합니다. 이것은 스크립트만큼 빠르게 수행할 수 있습니다. MQL5 마법사에서는 기본적으로 Expert Advisor(템플릿) 옵션이 선택됩니다. 이번에는 필요하지 않으므로 옵션을 변경하지 않고 다음 단계를 진행합니다. 그런 다음 마침을 클릭하면 아래와 같은 템플릿이 표시됩니다.

//+------------------------------------------------------------------+
//|                                      PositionPropertiesPanel.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Expert Advisor 템플릿이 스크립트 템플릿과 다르다는 것을 바로 알 수 있습니다. 프로그램 속성(#property) 외에도 OnInit(), OnDeinit() 및 OnTick()의 세 가지 주요 기능이 있습니다.

OnInit() 함수는 프로그램을 로드할 때, 외부 매개변수를 변경할 때, 프로그램을 컴파일할 때 호출됩니다. 단, 기호 또는 기간을 변경할 때 프로그램은 바로 그 때 차트에 추가됩니다. 필요한 경우 나중에 사용할 수 있도록 이 함수의 특정 변수 또는 배열을 초기화할 수 있습니다.

OnDeinit() 함수는 차트에서 프로그램을 삭제하거나 계정, 기호 또는 기간을 변경할 때 호출됩니다. 가능한 모든 초기화 해제 이유는 MQL5 참조에 나와 있습니다. 이 Expert Advisor는 초기화 해제 이유 식별자(OnDeinit() 함수 매개변수)를 텍스트로 변환하는 사용자 정의 함수 GetDeinitReasonText()를 사용합니다.

마지막으로 OnTick() 함수입니다. Expert Advisor가 현재 작동 중인 차트의 기호에 새 틱이 있을 때마다 호출됩니다.

이제 Expert Advisor에서 사용할 모든 상수, 변수 및 배열을 준비하겠습니다. 우리는 그것들을 프로그램의 맨 처음에 배치할 것입니다. 먼저 프로그램 전체에서 값이 변경되지 않은 상태로 유지되는 변수를 정의합니다.

//---
#define INFOPANEL_SIZE 14 // Size of the array for info panel objects
#define EXPERT_NAME MQL5InfoString(MQL5_PROGRAM_NAME) // Name of the Expert Advisor
//---

다음은 포지션 속성에 대한 전역 변수입니다.

//--- GLOBAL VARIABLES
bool                 pos_open=false;         // Flag of presence/absence of an open position
string               pos_symbol="";          // Symbol
long                 pos_magic=0;            // Magic number
string               pos_comment="";         // Comment
double               pos_swap=0.0;           // Swap
double               pos_commission=0.0;     // Commission
double               pos_price=0.0;          // Position price
double               pos_cprice=0.0;         // Current price of the position
double               pos_profit=0.0;         // Profit/Loss of the position
double               pos_volume=0.0;         // Position volume
double               pos_sl=0.0;             // Stop Loss of the position
double               pos_tp=0.0;             // Take Profit of the position
datetime             pos_time=NULL;          // Position opening time
long                 pos_id=0;               // Position identifier
ENUM_POSITION_TYPE   pos_type=WRONG_VALUE;   // Position type

변수 다음에 그래픽 개체 이름의 배열을 선언합니다. 이러한 개체는 차트에 포지션 속성과 해당 값을 표시합니다. 이를 위해 두 개의 문자열 배열을 만들고 해당 요소를 값으로 즉시 초기화합니다. 대괄호 안에는 프로그램의 맨 처음에 선언된 INFOPANEL_SIZE 상수 값을 사용합니다. 즉, 각 배열에는 14개의 요소가 있습니다.

// Array of names of objects that display names of position properties
string positionPropertyNames[INFOPANEL_SIZE]=
  {
   "name_pos_symbol",
   "name_pos_magic",
   "name_pos_comment",
   "name_pos_swap",
   "name_pos_commission",
   "name_pos_price",
   "name_pos_cprice",
   "name_pos_profit",
   "name_pos_volume",
   "name_pos_sl",
   "name_pos_tp",
   "name_pos_time",
   "name_pos_id",
   "name_pos_type"
  };
//---
// Array of names of objects that display values of position properties
string positionPropertyValues[INFOPANEL_SIZE]=
  {
   "value_pos_symbol",
   "value_pos_magic",
   "value_pos_comment",
   "value_pos_swap",
   "value_pos_commission",
   "value_pos_price",
   "value_pos_cprice",
   "value_pos_profit",
   "value_pos_volume",
   "value_pos_sl",
   "value_pos_tp",
   "value_pos_time",
   "value_pos_id",
   "value_pos_type"
  };
//---

이러한 이름을 사용하여 차트에서 필요한 개체를 프로그래밍 방식으로 찾고 표시된 텍스트, 색상, 크기 등과 같은 속성을 설정하거나 변경할 수 있습니다. 또한 이러한 이름은 차트에서 생성된 후 개체 목록(Ctrl+B) 창에 표시됩니다. 그러나 MQL5 프로그램에 의해 생성된 개체는 기본적으로 숨겨져 있으므로 거기에서 볼 수 없습니다. 표시되도록 하려면 개체 목록 창에서 모두 나열을 클릭해야 합니다. 이 기능은 프로그래밍 방식으로 만든 개체와 수동으로 만든 개체를 구분하는 데 도움이 되며 매우 편리합니다.

또한 Expert Advisor가 그래픽 개체를 생성하는 데 사용할 사용자 정의 기능이 필요합니다. 그래픽 개체 생성을 위해 MQL5에서 제공하는 기능은 ObjectCreate()입니다. 하지만 개체 속성도 설정해야 하기 때문에 개체 자체는 두 번 이상 생성해야 할 수도 있으므로 한 줄의 코드로 구현할 수 있는 보다 편리하고 간결한 방법을 생각하는 것이 좋습니다

정보 패널 배경과 헤더를 생성하기 위해 그래픽 개체 편집을 사용할 것입니다. CreateEdit() 함수를 작성해 보겠습니다.

//+------------------------------------------------------------------+
//| CREATING THE EDIT OBJECT                                         |
//+------------------------------------------------------------------+
void CreateEdit(long             chart_id,         // chart id
                int              sub_window,       // (sub)window number
                string           name,             // object name
                string           text,             // displayed text
                ENUM_BASE_CORNER corner,           // chart corner
                string           font_name,        // font
                int              font_size,        // font size
                color            font_color,       // font color
                int              x_size,           // width
                int              y_size,           // height
                int              x_distance,       // X-coordinate
                int              y_distance,       // Y-coordinate
                long             z_order,          // Z-order
                color            background_color, // background color
                bool             read_only)        // Read Only flag
  {
// If the object has been created successfully,...
   if(ObjectCreate(chart_id,name,OBJ_EDIT,sub_window,0,0))
     {
      // ...set its properties
      ObjectSetString(chart_id,name,OBJPROP_TEXT,text);                 // displayed text
      ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner);            // set the chart corner
      ObjectSetString(chart_id,name,OBJPROP_FONT,font_name);            // set the font
      ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size);       // set the font size
      ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color);         // font color
      ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,background_color); // background color
      ObjectSetInteger(chart_id,name,OBJPROP_XSIZE,x_size);             // width
      ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,y_size);             // height
      ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance);     // set the X coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance);     // set the Y coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);         // cannot select the object if FALSE
      ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order);           // Z-order of the object
      ObjectSetInteger(chart_id,name,OBJPROP_READONLY,read_only);       // Read Only
      ObjectSetInteger(chart_id,name,OBJPROP_ALIGN,ALIGN_LEFT);         // align left
      ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n");              // no tooltip if "\n"
     }
  }

이제 한 줄의 코드를 사용하여 그래픽 개체 편집(OBJ_EDIT)을 만들 수 있습니다. 차트에 정보 패널을 설정하는 함수를 만들 때 예를 들어 설명하겠습니다.

이제 포지션 속성 및 해당 값 목록을 표시하는 데 사용할 텍스트 레이블 개체를 계속 진행하고 유사한 방식으로 CreateLabel() 함수를 만듭니다.

//+------------------------------------------------------------------+
//| CREATING THE LABEL OBJECT                                        |
//+------------------------------------------------------------------+
void CreateLabel(long               chart_id,   // chart id
                 int                sub_window, // (sub)window number
                 string             name,       // object name
                 string             text,       // displayed text
                 ENUM_ANCHOR_POINT  anchor,     // anchor point
                 ENUM_BASE_CORNER   corner,     // chart corner
                 string             font_name,  // font
                 int                font_size,  // font size
                 color              font_color, // font color
                 int                x_distance, // X-coordinate
                 int                y_distance, // Y-coordinate
                 long               z_order)    // Z-order
  {
// If the object has been created successfully,...
   if(ObjectCreate(chart_id,name,OBJ_LABEL,sub_window,0,0))
     {
      // ...set its properties
      ObjectSetString(chart_id,name,OBJPROP_TEXT,text);              // displayed text
      ObjectSetString(chart_id,name,OBJPROP_FONT,font_name);         // set the font
      ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color);      // set the font color
      ObjectSetInteger(chart_id,name,OBJPROP_ANCHOR,anchor);         // set the anchor point
      ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner);         // set the chart corner
      ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size);    // set the font size
      ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance);  // set the X-coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance);  // set the Y-coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);      // cannot select the object if FALSE
      ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order);        // Z-order of the object
      ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n");           // no tooltip if "\n"
     }
  }

또한 MQL5 참조에서 기능 설명을 살펴보는 것이 좋습니다.

차트에서 삭제되면 Expert Advisor는 이전에 차트에 추가한 모든 개체를 차례로 삭제해야 합니다. 이렇게 하려면 개체 이름을 DeleteObjectByName() 함수에 전달하기만 하면 됩니다. 그런 다음 지정된 이름으로 개체를 검색하고 개체를 검색하는 기본 제공 ObjectFind() 함수와 ObjectDelete( ) 개체를 삭제하는 함수입니다.

//+------------------------------------------------------------------+
//| DELETING THE OBJECT BY NAME                                      |
//+------------------------------------------------------------------+
void DeleteObjectByname(string name)
  {
   int  sub_window=0;      // Returns the number of the subwindow where the object is located
   bool res       =false;  // Result following an attempt to delete the object
//--- Find the object by name
   sub_window=ObjectFind(ChartID(),name);
//---
   if(sub_window>=0) // If it has been found,..
     {
      res=ObjectDelete(ChartID(),name); // ...delete it
      //---
      // If an error occurred when deleting the object,..
      if(!res) // ...print the relevant message
        {
         Print("Error deleting the object: ("+IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
        }
     }
  }

또한 DeleteObjectByName() 함수에서 개체를 삭제할 때 오류 검사도 구현합니다. 오류가 발생하면 오류 코드와 설명이 포함된 관련 메시지가 나타납니다. 위의 코드에서 볼 수 있듯이 오류 코드를 텍스트 설명으로 변환하는 추가 사용자 정의 함수인 ErrorDescription() 함수를 사용합니다. 오류 코드가 많기 때문에 이 기능의 일부만 사용하여 위의 예를 보여 드리겠습니다(아래 코드 참조). 코드의 전체 버전은 이 글에 첨부된 소스 코드 파일에서 찾을 수 있습니다.

//+------------------------------------------------------------------+
//| RETURNING THE ERROR DESCRIPTION                                  |
//+------------------------------------------------------------------+
string ErrorDescription(int error_code)
  {
   string error_string="";
//---
   switch(error_code)
     {
      //--- Trade server return codes

      case 10004: error_string="Requote";                                                                break;
      case 10006: error_string="Request rejected";                                                       break;
      case 10007: error_string="Request canceled by trader";                                             break;
      case 10008: error_string="Order placed";                                                           break;
      case 10009: error_string="Request executed";                                                       break;
      case 10010: error_string="Request executed partially";                                             break;
      case 10011: error_string="Request processing error";                                               break;
      case 10012: error_string="Request timed out";                                                      break;
      case 10013: error_string="Invalid request";                                                        break;
      case 10014: error_string="Invalid request volume";                                                 break;
      case 10015: error_string="Invalid request price";                                                  break;
      case 10016: error_string="Invalid Stop orders in the request";                                     break;
      case 10017: error_string="Trading forbidden";                                                      break;
      case 10018: error_string="Market is closed";                                                       break;
      case 10019: error_string="Insufficient funds";                                                     break;
      case 10020: error_string="Prices changed";                                                         break;
      case 10021: error_string="No quotes to process the request";                                       break;
      case 10022: error_string="Invalid order expiration in the request";                                break;
      case 10023: error_string="Order status changed";                                                   break;
      case 10024: error_string="Too many requests";                                                      break;
      case 10025: error_string="No changes in the request";                                              break;
      case 10026: error_string="Automated trading is disabled by trader";                                break;
      case 10027: error_string="Automated trading is disabled by the client terminal";                   break;
      case 10028: error_string="Request blocked for processing";                                         break;
      case 10029: error_string="Order or position frozen";                                               break;
      case 10030: error_string="The specified type of order execution by balance is not supported";      break;
      case 10031: error_string="No connection with trade server";                                        break;
      case 10032: error_string="Transaction is allowed for live accounts only";                          break;
      case 10033: error_string="You have reached the maximum number of pending orders";                  break;
      case 10034: error_string="You have reached the maximum order and position volume for this symbol"; break;

      ...

     }
//---
   return(error_string);
  }

이전 글에서 포지션 속성을 가져오는 GetPositionProperties() 함수를 다루었습니다. 이번에는 함수 구조가 조금 더 복잡해집니다. 글로벌 변수 pos_open에 저장되어 있는 열린 포지션의 유무 플래그와 함께 현재 열려 있는 포지션을 확인합니다. 이 정보는 매번 PositionSelect() 함수를 호출하지 않고도 다른 함수에서 필요할 수 있습니다.

그런 다음 열린 포지션이 있으면 해당 속성을 가져오고 그렇지 않으면 모든 변수가 0이 됩니다. 이제 간단한 ZeroPositionProperties() 함수를 작성해 보겠습니다.

//+------------------------------------------------------------------+
//| ZEROING OUT VARIABLES FOR POSITION PROPERTIES                    |
//+------------------------------------------------------------------+
void ZeroPositionProperties()
  {
   pos_symbol     ="";
   pos_comment    ="";
   pos_magic      =0;
   pos_price      =0.0;
   pos_cprice     =0.0;
   pos_sl         =0.0;
   pos_tp         =0.0;
   pos_type       =WRONG_VALUE;
   pos_volume     =0.0;
   pos_commission =0.0;
   pos_swap       =0.0;
   pos_profit     =0.0;
   pos_time       =NULL;
   pos_id         =0;
  }

또한 GetPositionProperties() 함수의 끝에서 차트의 정보 패널을 그리거나 업데이트하는 사용자 정의 SetInfoPanel() 함수를 호출합니다.

//+------------------------------------------------------------------+
//| GETTING POSITION PROPERTIES                                      |
//+------------------------------------------------------------------+
void GetPositionProperties()
  {
// Check if there is an open position
   pos_open=PositionSelect(_Symbol);
//---
   if(pos_open) // If an open position exists, get its properties
     {
      pos_symbol     =PositionGetString(POSITION_SYMBOL);
      pos_comment    =PositionGetString(POSITION_COMMENT);
      pos_magic      =PositionGetInteger(POSITION_MAGIC);
      pos_price      =PositionGetDouble(POSITION_PRICE_OPEN);
      pos_cprice     =PositionGetDouble(POSITION_PRICE_CURRENT);
      pos_sl         =PositionGetDouble(POSITION_SL);
      pos_tp         =PositionGetDouble(POSITION_TP);
      pos_type       =(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      pos_volume     =PositionGetDouble(POSITION_VOLUME);
      pos_commission =PositionGetDouble(POSITION_COMMISSION);
      pos_swap       =PositionGetDouble(POSITION_SWAP);
      pos_profit     =PositionGetDouble(POSITION_PROFIT);
      pos_time       =(datetime)PositionGetInteger(POSITION_TIME);
      pos_id         =PositionGetInteger(POSITION_IDENTIFIER);
     }
   else // If there is no open position, zero out variables for position properties
      ZeroPositionProperties();
//---
   SetInfoPanel(); // Set/update the info panel
  }

이제 SetInfoPanel() 함수를 작성해 보겠습니다. 아래는 이 코드입니다.

//+------------------------------------------------------------------+
//| SETTING THE INFO PANEL                                           |
//|------------------------------------------------------------------+
void SetInfoPanel()
  {
   int               y_bg=18;             // Y-coordinate for the background and header
   int               y_property=32;       // Y-coordinate for the list of properties and their values
   int               line_height=12;      // Line height
//---
   int               font_size=8;         // Font size
   string            font_name="Calibri"; // Font
   color             font_color=clrWhite; // Font color
//---
   ENUM_ANCHOR_POINT anchor=ANCHOR_RIGHT_UPPER; // Anchor point in the top right corner
   ENUM_BASE_CORNER  corner=CORNER_RIGHT_UPPER; // Origin of coordinates in the top right corner of the chart
//--- X-coordinates
   int               x_first_column=120// First column (names of properties)
   int               x_second_column=10;  // Second column (values of properties)
//--- Array of Y-coordinates for the names of position properties and their values
   int               y_prop_array[INFOPANEL_SIZE]={0};
//--- Fill the array with coordinates for each line on the info panel
   y_prop_array[0]=y_property;
   y_prop_array[1]=y_property+line_height;
   y_prop_array[2]=y_property+line_height*2;
   y_prop_array[3]=y_property+line_height*3;
   y_prop_array[4]=y_property+line_height*4;
   y_prop_array[5]=y_property+line_height*5;
   y_prop_array[6]=y_property+line_height*6;
   y_prop_array[7]=y_property+line_height*7;
   y_prop_array[8]=y_property+line_height*8;
   y_prop_array[9]=y_property+line_height*9;
   y_prop_array[10]=y_property+line_height*10;
   y_prop_array[11]=y_property+line_height*11;
   y_prop_array[12]=y_property+line_height*12;
   y_prop_array[13]=y_property+line_height*13;
//--- Background of the info panel
   CreateEdit(0,0,"InfoPanelBackground","",corner,font_name,8,clrWhite,230,190,231,y_bg,0,C'15,15,15',true);
//--- Header of the info panel
   CreateEdit(0,0,"InfoPanelHeader","POSITION PROPERTIES",corner,font_name,8,clrWhite,230,14,231,y_bg,1,clrFireBrick,true);
//--- List of the names of position properties and their values
//    Property name
   CreateLabel(0,0,pos_prop_names[0],"Symbol :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[0],2);
//    Property value
   CreateLabel(0,0,pos_prop_values[0],GetValInfoPanel(0),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[0],2);
//---
   CreateLabel(0,0,pos_prop_names[1],"Magic Number :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[1],2);
   CreateLabel(0,0,pos_prop_values[1],GetValInfoPanel(1),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[1],2);
//---
   CreateLabel(0,0,pos_prop_names[2],"Comment :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[2],2);
   CreateLabel(0,0,pos_prop_values[2],GetValInfoPanel(2),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[2],2);
//---
   CreateLabel(0,0,pos_prop_names[3],"Swap :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[3],2);
   CreateLabel(0,0,pos_prop_values[3],GetValInfoPanel(3),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[3],2);
//---
   CreateLabel(0,0,pos_prop_names[4],"Commission :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[4],2);
   CreateLabel(0,0,pos_prop_values[4],GetValInfoPanel(4),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[4],2);
//---
   CreateLabel(0,0,pos_prop_names[5],"Open Price :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[5],2);
   CreateLabel(0,0,pos_prop_values[5],GetValInfoPanel(5),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[5],2);
//---
   CreateLabel(0,0,pos_prop_names[6],"Current Price :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[6],2);
   CreateLabel(0,0,pos_prop_values[6],GetValInfoPanel(6),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[6],2);
//---
   CreateLabel(0,0,pos_prop_names[7],"Profit :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[7],2);
   CreateLabel(0,0,pos_prop_values[7],GetValInfoPanel(7),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[7],2);
//---
   CreateLabel(0,0,pos_prop_names[8],"Volume :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[8],2);
   CreateLabel(0,0,pos_prop_values[8],GetValInfoPanel(8),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[8],2);
//---
   CreateLabel(0,0,pos_prop_names[9],"Stop Loss :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[9],2);
   CreateLabel(0,0,pos_prop_values[9],GetValInfoPanel(9),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[9],2);
//---
   CreateLabel(0,0,pos_prop_names[10],"Take Profit :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[10],2);
   CreateLabel(0,0,pos_prop_values[10],GetValInfoPanel(10),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[10],2);
//---
   CreateLabel(0,0,pos_prop_names[11],"Time :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[11],2);
   CreateLabel(0,0,pos_prop_values[11],GetValInfoPanel(11),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[11],2);
//---
   CreateLabel(0,0,pos_prop_names[12],"Identifier :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[12],2);
   CreateLabel(0,0,pos_prop_values[12],GetValInfoPanel(12),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[12],2);
//---
   CreateLabel(0,0,pos_prop_names[13],"Type :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[13],2);
   CreateLabel(0,0,pos_prop_values[13],GetValInfoPanel(13),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[13],2);
//---
   ChartRedraw(); // Redraw the chart
  }

SetInfoPanel() 함수를 자세히 살펴보겠습니다. 그래픽 개체의 속성과 관련된 변수(좌표, 색상, 글꼴, 표시되는 텍스트 등)는 함수 시작 부분에 선언됩니다. 정보 패널의 포지션 속성 목록에 대한 Y 좌표 배열을 채우는 과정에 주의하세요. 초보자도 알기 쉽게 구현되어 있습니다. 그러나 루프를 사용할 때는 몇 줄의 코드로 줄일 수 있습니다. 다음과 같이 작성할 수 있습니다.

//--- Fill the array with coordinates for each line on the info panel
   for(int i=0; i<INFOPANEL_SIZE; i++)
     {
      if(i==0) y_prop_array[i]=y_property;
      else     y_prop_array[i]=y_property+line_height*i;
     }

그런 다음 패널에 표시되어야 하는 개체의 모든 속성은 이전에 생성된 CreateLabel() 및 CreateEdit() 함수의 매개변수에 지정되어야 합니다 (한 번에 하나의 개체를 요함). 전체 목록은 루프를 사용하여 몇 줄의 코드로 구현할 수도 있습니다. 이렇게 하려면 차트에서 포지션 속성 이름의 텍스트를 표시하는 개체에 대한 또 다른 배열을 만들어야 합니다. 이건 당신의 숙제가 될 겁니다.

개체 번호를 수신하는 GetPropertyValue() 함수는 값을 반환하고 이 값은 CreateLabel() 함수에 네 번째 매개변수(표시된 텍스트)로 전달됩니다. 이것은 포지션 속성 값을 표시할 모든 개체와 관련이 있습니다. 함수에 의해 반환된 값은 조정된 문자열 값이며 궁극적으로 패널에 표시됩니다. 아래는 이 코드입니다.

//+------------------------------------------------------------------+
//| RETURNING THE STRING WITH POSITION PROPERTY VALUE                |
//+------------------------------------------------------------------+
string GetPropertyValue(int number)
  {
//--- Sign indicating the lack of an open position or a certain property
//    E.g. the lack of a comment, Stop Loss or Take Profit
   string empty="-";
//--- If an open position exists, return the value of the requested property
   if(pos_open)
     {
      switch(number)
        {
         case 0  : return(pos_symbol);                                           break;
         case 1  : return(IntegerToString((int)pos_magic));                      break;
         //--- return the value of the comment, if any, otherwise return the sign indicating the lack of comment
         case 2  : return(pos_comment!="" ? pos_comment : empty);                break;
         case 3  : return(DoubleToString(pos_swap,2));                           break;
         case 4  : return(DoubleToString(pos_commission,2));                     break;
         case 5  : return(DoubleToString(pos_price,_Digits));                    break;
         case 6  : return(DoubleToString(pos_cprice,_Digits));                   break;
         case 7  : return(DoubleToString(pos_profit,2));                         break;
         case 8  : return(DoubleToString(pos_volume,2));                         break;
         case 9  : return(pos_sl!=0.0 ? DoubleToString(pos_sl,_Digits) : empty); break;
         case 10 : return(pos_tp!=0.0 ? DoubleToString(pos_tp,_Digits) : empty); break;
         case 11 : return(TimeToString(pos_time,TIME_DATE|TIME_MINUTES));        break;
         case 12 : return(IntegerToString((int)pos_id));                         break;
         case 13 : return(PositionTypeToString(pos_type));                       break;
         
         default : return(empty);
        }
     }
//---
// If there is no open position, return the sign indicating the lack of the open position "-"
   return(empty);
  }

위의 코드는 열린 포지션이 있는 경우 함수에 전달된 모든 숫자에 대해 특정 값이 준비됨을 나타냅니다. 현재 열려 있는 포지션이 없는 경우 이 함수는 포지션 속성 값과 관련된 모든 개체에 대해 표시된 대시(-)를 반환합니다.

SetInfoPanel() 함수의 끝에서 강제 차트 다시 그리기를 위해 설계된 ChartRedraw() 함수를 호출합니다. 호출하지 않으면 변경된 내용을 볼 수 없습니다.

이제 Expert Advisor에서 생성한 모든 그래픽 개체를 삭제하는 함수를 작성해야 합니다. DeleteInfoPanel()이라고 합시다.

//+------------------------------------------------------------------+
//| DELETING THE INFO PANEL                                          |
//+------------------------------------------------------------------+
void DeleteInfoPanel()
  {
   DeleteObjectByName("InfoPanelBackground");   // Delete the panel background
   DeleteObjectByName("InfoPanelHeader");       // Delete the panel header
//--- Delete position properties and their values
   for(int i=0; i<INFOPANEL_SIZE; i++)
     {
      DeleteObjectByName(pos_prop_names[i]);    // Delete the property
      DeleteObjectByName(pos_prop_values[i]);   // Delete the value
     }
//---
   ChartRedraw(); // Redraw the chart
  }

이제 MQL5 Wizard에서 템플릿을 생성한 후 템플릿에 원래 존재했던 Expert Advisor의 주요 기능 중에서 생성한 메소드를 배포하기만 하면 됩니다. 이것은 가장 쉬운 부분입니다.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Get the properties and set the panel
   GetPositionProperties();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| 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();

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Get the properties and update the values on the panel
   GetPositionProperties();

  }
//+------------------------------------------------------------------+

당신이 우연히 발견할 수 있는 유일한 것은 초기화 해제 이유 코드의 텍스트 설명을 반환하는 GetDeinitReasonText() 함수입니다.

//+---------------------------------------------------------------------+
//| RETURNING A TEXTUAL DESCRIPTION OF THE DEINITIALIZATION REASON CODE |
//+---------------------------------------------------------------------+
string GetDeinitReasonText(int reason_code)
  {
   string text="";
//---
   switch(reason_code)
     {
      case REASON_PROGRAM :     // 0
         text="The Expert Advisor has stopped working calling the ExpertRemove() function.";   break;
      case REASON_REMOVE :      // 1
         text="The '"+EXPERT_NAME+"' program has been removed from the chart.";                break;
      case REASON_RECOMPILE :   // 2
         text="The '"+EXPERT_NAME+"' program has been recompiled.";                            break;
      case REASON_CHARTCHANGE : // 3
         text="Chart symbol or period has been changed.";                                      break;
      case REASON_CHARTCLOSE :  // 4
         text="The chart is closed.";                                                          break;
      case REASON_PARAMETERS :  // 5
         text="Input parameters have been changed by the user.";                               break;
      case REASON_ACCOUNT :     // 6
         text="A different account has been activated.";                                       break;
      case REASON_TEMPLATE :    // 7
         text="A different chart template has been applied.";                                  break;
      case REASON_INITFAILED :  // 8
         text="A flag specifying that the OnInit() handler returned zero value.";              break;
      case REASON_CLOSE :       // 9
         text="The terminal has been closed.";                                                 break;
      default : text="The reason is undefined.";
     }
//---
   return text;
  }

현재 열린 포지션이 없는 차트 기호에서 Expert Advisor를 사용하려고 하면 패널에 포지션 속성 값 대신 대시가 표시됩니다. 특정 포지션을 닫은 후에도 패널은 동일하게 보입니다.

그림 2. 열린 포지션이 없는 경우 정보 패널.

그림. 2. 열린 자리가 없는 경우의 정보 패널

오픈 포지션이 있는 심볼의 차트에 Expert Advisor가 추가되거나 Expert Advisor를 차트에 추가한 후 포지션이 열리면 모든 대시가 적절한 포지션 속성 값으로 대체됩니다.

그림 3. 열린 포지션의 속성을 표시하는 정보 패널.

그림 3. 열린 포지션의 속성을 표시하는 정보 패널.

작은 특이점이 하나 있습니다. 포지션을 닫은 후 패널의 값이 업데이트되는 것은 새 틱이 될 때까지가 아닙니다. 값을 즉시 업데이트하는 방법이 있지만 이를 구현하기 위해 수행해야 하는 작업은 다음 시리즈 글에서 설명합니다.

 

결론

이 글에서 소개된 일부 기능은 MQL5 Cookbook 시리즈의 다음 글에서도 사용될 것이며, 나머지는 당면한 작업에 따라 수정 및 개선될 것입니다. 각각의 새로운 글은 이전 글의 논리적 연속이므로 글을 순서대로 차례로 읽는 것이 좋습니다. 그것은 확실히 당신의 능력과 기술의 수준에 달려 있으므로 더 최근 출판물부터 시작하는 것이 더 합리적이고 흥미로울 수 있습니다. 

소스 코드 파일은 글에 첨부되어 있습니다.

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

MQL5 Cookbook: MetaTrader 5 전략 테스터의 포지션 속성 분석 MQL5 Cookbook: MetaTrader 5 전략 테스터의 포지션 속성 분석
이전 글 "MQL5 Cookbook: 사용자 지정 정보 패널의 포지션 속성"에서 수정된 버전의 Expert Advisor를 소개합니다. 우리가 다룰 문제 중 일부는 바에서 데이터 가져오기, 파일에 대한 표준 라이브러리의 거래 클래스를 포함하여 현재 기호에 대한 새로운 바 이벤트 확인, 거래 신호를 검색하는 기능 및 거래 작업을 실행하는 기능 만들기를 포함합니다. OnTrade() 함수에서 거래 이벤트를 결정하는 것 외에도 말이죠.
MQL5 Cookbook: 포지션 속성 가져오기 MQL5 Cookbook: 포지션 속성 가져오기
이 글에서는 모든 포지션 속성을 가져와 대화 상자에서 사용자에게 표시하는 스크립트를 만들 것입니다. 스크립트를 실행하면 외부 매개변수의 드롭다운 목록에서 사용 가능한 두 가지 모드 중에서 선택할 수 있습니다. 현재 심볼에서만 포지션 속성을 보거나 모든 심볼에서 포지션 속성을 보는 것입니다.
MQL5 Cookbook: 거래 수준을 설정/수정할 때 오류를 피하는 방법 MQL5 Cookbook: 거래 수준을 설정/수정할 때 오류를 피하는 방법
"MQL5 Cookbook: MetaTrader 5 Strategy Tester의 포지션 속성 분석" 시리즈의 이전 글에서 Expert Advisor에 대한 작업을 계속하면서 많은 유용한 기능으로 기존의 기능들과 더불어 이를 개선하고 최적화할 것입니다. Expert Advisor는 이번에 MetaTrader 5 전략 테스터에서 최적화할 수 있는 외부 매개변수를 가지며 어떤 면에서는 단순한 거래 시스템과 유사합니다.
MQL5 Cookbook: 다양한 프린트 모드 사용 MQL5 Cookbook: 다양한 프린트 모드 사용
MQL5 Cookbook 시리즈의 첫 번째 글입니다. 프로그래밍을 처음 시작하는 사람들이 새로운 언어에 점차 익숙해질 수 있도록 간단한 예제부터 시작하겠습니다. 나는 그것이 내 인생의 첫 번째 프로그래밍 언어라는 사실을 감안할 때 꽤 어려웠다고 말할 수 있는 거래 시스템을 설계하고 프로그래밍하는 첫 번째 노력을 기억합니다. 그러나 생각보다 쉬웠고 상당히 복잡한 프로그램을 개발하는 데 몇 달 밖에 걸리지 않았습니다.