English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5 Cookbook: 가격 다이버전스를 분석하기 위한 다중 기호 지표 개발

MQL5 Cookbook: 가격 다이버전스를 분석하기 위한 다중 기호 지표 개발

MetaTrader 5 | 12 10월 2021, 12:42
92 0
Anatoli Kazharski
Anatoli Kazharski

소개

이 글에서는 지정된 기간의 가격 다이버전스를 분석하기 위해 다중 기호 지표의 개발을 고려할 것입니다. 핵심 주제는 다중 통화 표시기 프로그래밍에 대한 이전 글 MQL5 Cookbook: MQL5의 다중기호 변동성 지표 개발에서 이미 논의되었습니다. 따라서 이번에는 극적으로 변경된 새로운 기능에 대해서만 설명하겠습니다. 다중 통화 표시기 프로그래밍이 처음이라면 먼저 이전 글을 읽는 것이 좋습니다.

이 글에서는 다음 질문을 고려할 것입니다.

  • 차트 속성 변경.
  • CHARTEVENT_OBJECT_DRAG(차트 개체 끌기) 및 CHARTEVENT_CHART_CHANGE(차트 크기 조정 또는 속성 대화 상자 창을 사용하여 차트 속성 수정) 이벤트 처리.
  • 둘 이상의 색상을 사용하는 렌더링 표시기 버퍼.
  • 차트 고/저를 설정하기 위해 가시성 영역 내의 표시기 버퍼에서 고점과 저점을 정의합니다.
  • 시리즈의 반전.

결과적으로 표시기에 대한 코드 양은 약 1500줄로 상당히 큽니다. 따라서 모든 기능을 별도의 파일로 배포하고 메인 프로젝트 파일에 연결합니다. 외부 파일에는 세 가지 범주의 기능이 있습니다.

  • Checks.mqh - 다양한 검사를 수행하고 사용 가능한 데이터를 다운로드하는 기능입니다.
  • Objects.mqh - 그래픽 객체를 관리하기 위한 함수입니다.
  • Chart.mqh - 차트 속성을 관리하기 위한 함수입니다.

위의 카테고리에 속하지 않는 모든 기능은 메인 파일에 남게 됩니다.


지표 개발

다음으로 표시기 프로그래밍을 진행합니다. 먼저 새 프로젝트를 만들어야 합니다. 이렇게 하려면 Metatrader 5\MQL5\Indicators 디렉토리에 지표로 이름이 지정된 폴더를 만들고 그 안에 포함 파일을 저장할 Include 폴더를 만드십시오. 다음으로, 표시기 폴더에 메인 파일을 생성합니다. 이것은 *.mq5 확장자를 가진 텍스트 파일을 생성하거나 템플릿으로 MQL5 마법사를 사용하여 수동으로 수행할 수 있습니다. 프로그램 OnInit(), OnDeinit()OnCalculate()의 핵심 기능 외에도 OnChartEvent ()OnTimer().

이전 글과 마찬가지로 현재 기호에 추가하여 외부 매개변수에 지정된 5개 기호에 대한 데이터를 표시합니다. 그러나 이번에는 어떤 공식으로 계산된 값 대신 차트에 원시 가격 데이터를 출력합니다. 사용자는 드롭다운 목록에서 외부 매개변수의 데이터 표현 유형을 자유롭게 선택할 수 있습니다: , 또는 촛대.

데이터를 단색 선으로만 표시해야 하는 경우 표시기 속성(#property)의 기호 수와 동일한 버퍼 수를 지정하는 것으로 충분합니다. 그러나 바와 양초로 시리즈를 그리기 위한 두 가지 모드가 있기 때문에 2색 모드에 대해 더 많은 버퍼가 필요합니다. 각 시리즈를 렌더링하는 4개의 버퍼와 그래픽 시리즈의 각 요소에 대한 색상(조건에 따라 다름)을 설정하는 1개의 버퍼 .

각 시리즈에 대해 프로그램 속성 섹션에서 색상을 지정해야 합니다. 이렇게 하려면 쉼표로 구분하여 나열하면 됩니다. 먼저 단색 모드에서 사용되는 색상이 나옵니다. 2색 모드에서는 위쪽 바/촛대에 사용됩니다. 두 번째 색상은 아래 바/촛대의 2색 모드에서만 사용됩니다.

이러한 모든 매개변수의 코드는 다음과 같습니다.

#property indicator_chart_window // Indicator is in the main window
#property indicator_buffers 25   // Number of buffers for indicator calculation
#property indicator_plots   5    // Number of plotting series
//--- Indicator buffers colors
#property indicator_color1  clrDodgerBlue,C'0,50,100'
#property indicator_color2  clrLimeGreen,C'20,80,20'
#property indicator_color3  clrGold,C'160,140,0'
#property indicator_color4  clrAqua,C'0,140,140'
#property indicator_color5  clrMagenta,C'130,0,130'
-->

#define 지시문을 사용하여 상수를 선언하고 #include 명령줄을 사용하여 위에서 이미 설명한 기능이 있는 파일과 캔버스에서 작업하기 위해 표준 라이브러리의 클래스를 포함해봅시다.

//--- Constants 
#define RESET           0 // Returning the indicator recalculation command to the terminal
#define SYMBOLS_COUNT   5 // Number of symbols
//--- Include the class for working with the canvas
#include <Canvas\Canvas.mqh>
//--- Include the class for working with the canvas
#include "Include/Checks.mqh"
#include "Include/Chart.mqh"
#include "Include/Objects.mqh"

-->

ENUM_DRAWTYPEENUM_START_POINT 열거형을 추가하여 가격 데이터의 그리기 유형과 외부 매개변수에서 가격 차이 시작점의 모드를 선택할 수 있는 드롭다운 목록을 만듭니다.

//--- Drawing type of the price data
enum ENUM_DRAWTYPE
  {
   LINE   =0,  // Line
   BARS   =1,  // Bars
   CANDLES=2   // Candlesticks
  };
//--- Mode of the price divergence starting point
enum ENUM_START_POINT
  {
   VERTICAL_LINE=0,  // Vertical line
   MONTH        =1,  // Month
   WEEK         =2,  // Week
   DAY          =3,  // Day
   HOUR         =4   // Hour
  };
-->

데이터 렌더링의 유형은 이미 위에서 설명했습니다. 이제 가격 다이버전스 시작점이 무엇을 의미하는지 조금 더 이야기해 보겠습니다.

총 5가지 모드가 있습니다: 수직선, , , . 수직선 모드의 경우 차트에 표시기를 로드할 때 수직선이 추가됩니다. 이 선을 끌어 모든 기호의 가격이 단일 지점에서 만나는 바를 지정합니다. 현재 심볼에 대해 지정된 바의 시가는 이 회의의 기준점으로 간주됩니다. 다른 모드는 지정된 기간이 시작될 때마다 가격이 충족되어야 한다고 프로그램에 알립니다. 즉, 매월 초와 매주 초와 매일의 시작과 매시 초와 같은 때.

아래에서 표시기의 입력 매개변수 목록을 찾을 수 있습니다.

//--- External parameters
input  ENUM_DRAWTYPE    DrawType             =CANDLES;       // Drawing type
input  ENUM_START_POINT StartPriceDivergence =VERTICAL_LINE; // Start of price divergence
input  bool             TwoColor             =false;         // Two-color bars/candlesticks
sinput string dlm01=""; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
input  string           Symbol02             ="GBPUSD";      // Symbol 2
input  bool             Inverse02            =false;         // Inverse symbol 2
input  string           Symbol03             ="AUDUSD";      // Symbol 3
input  bool             Inverse03            =false;         // Inverse symbol 3
input  string           Symbol04             ="NZDUSD";      // Symbol 4
input  bool             Inverse04            =false;         // Inverse symbol 4
input  string           Symbol05             ="USDCAD";      // Symbol 5
input  bool             Inverse05            =false;         // Inverse symbol 5
input  string           Symbol06             ="USDCHF";      // Symbol 6
input  bool             Inverse06            =false;         // Inverse symbol 6
-->

1이 차트의 현재 기호이기 때문에 기호는 2부터 번호가 매겨집니다.

포함된 각 기호에 대해 반전을 적용할 수 있습니다. 반전은 기호 데이터가 거꾸로 렌더링됨을 의미합니다. 이는 분석된 기호 목록에 동일한 통화(예: 미국 달러)가 기준 통화일 수도 있고 카운터 통화일 수도 있는 통화 쌍이 포함된 경우에 유용할 수 있습니다. 예를 들어, EURUSD 통화 쌍에서 미국 달러는 상대 통화이고 USDCHF 통화 쌍에서는 기준 통화입니다. 차트의 현재 기호가 EURUSD인 경우 USDCHF에 대한 반전을 켤 수 있습니다. 그러면 분석에 가격 표시가 더 편리해집니다.

다음은 전역 변수 및 배열 목록입니다.

//--- Structure of the indicator buffers arrays
struct buffers
  {
   double            open[];   // Open prices buffer
   double            high[];   // High prices buffer
   double            low[];    // Low prices buffer
   double            close[];  // Close prices buffer
   double            icolor[]; // Buffer to determine the color of element
  };
buffers           buffer_data[SYMBOLS_COUNT];
//--- Load the class
CCanvas           canvas;
//--- Variables/arrays for copying data from OnCalculate()
int               OC_rates_total     =0; // Size of input time series
int               OC_prev_calculated =0; // Bars processed at the previous call
datetime          OC_time[];             // Opening time
double            OC_open[];             // Open prices
double            OC_high[];             // High prices
double            OC_low[];              // Low prices
double            OC_close[];            // Close prices
long              OC_tick_volume[];      // Tick volumes
long              OC_volume[];           // Real volumes
int               OC_spread[];           // Spread

//--- For the purpose of storing and checking the time of the first bar in the terminal
datetime          series_first_date[SYMBOLS_COUNT];
datetime          series_first_date_last[SYMBOLS_COUNT];
//--- Time array of the bar from which we will start drawing
datetime          limit_time[SYMBOLS_COUNT];
//--- Symbol names array
string            symbol_names[SYMBOLS_COUNT];
//--- Array of symbol inverse flags
bool              inverse[SYMBOLS_COUNT];
//--- Colors of indicator lines
color             line_colors[SYMBOLS_COUNT]={clrDodgerBlue,clrLimeGreen,clrGold,clrAqua,clrMagenta};
//--- String representing the lack of the symbol
string            empty_symbol="EMPTY";
//--- Chart properties
int               window_number              =WRONG_VALUE;               // Indicator window number
int               chart_width                =0;                         // Chart width
int               chart_height               =0;                         // Chart height
int               last_chart_width           =0;                         // Last saved chart width
int               last_chart_height          =0;                         // Last saved chart height
int               chart_center_x             =0;                         // Horizontal center of chart
int               chart_center_y             =0;                         // Vertical center of chart
color             color_bar_up               =clrRed;                    // Up bar color
color             color_bar_down             =C'100,0,0';                // Down bar color
string            indicator_shortname        ="MS_PriceDivergence";      // Short name of the indicator
string            prefix                     =indicator_shortname+"_";   // Prefix for objects
//--- Name of vertical line of the price divergence starting point
string            start_price_divergence=prefix+"start_price_divergence";
//--- Canvas properties
string            canvas_name             =prefix+"canvas";          // Canvas name
color             canvas_background       =clrBlack;                 // Canvas background color
uchar             canvas_opacity          =190;                      // Opacity
int               font_size               =16;                       // Font size
string            font_name               ="Calibri";                // Font
ENUM_COLOR_FORMAT clr_format              =COLOR_FORMAT_ARGB_RAW;    // Color components should be correctly set by the user
//--- Canvas messages
string            msg_prepare_data        ="Preparing data! Please wait...";
string            msg_not_synchronized    ="Unsynchronized data! Please wait...";
string            msg_load_data           ="";
string            msg_sync_update         ="";
string            msg_last                ="";
//---
ENUM_TIMEFRAMES   timeframe_start_point  =Period();    // Timeframe for the price divergence starting point
datetime          first_period_time      =NULL;        // Time of the first specified period on chart
double            divergence_price       =0.0;         // Price of the price divergence starting point
datetime          divergence_time        =NULL;        // Time of the price divergence starting point
double            symbol_difference[SYMBOLS_COUNT];    // Difference in price relative to the current symbol
double            inverse_difference[SYMBOLS_COUNT];   // Difference that is formed when calculating inversion
-->

다음으로 표시기 초기화 중에 사용되는 기능에 대해 살펴보겠습니다. 일반적으로 이전 글의 OnInit() 함수와 비교하면 큰 변화는 없습니다.

표시기가 사용되는 위치에 대한 확인을 추가해 보겠습니다. 요점은 현재 터미널 개발자들이 Strategy Tester에서 차트 속성을 제어하는 ​​모든 기능을 구현하지 않았기 때문에 우리 지표를 Strategy Tester 외부에서만 사용하도록 제한한다는 것입니다. 이를 구현하기 위해 간단한 함수인 CheckTesterMode()를 작성합니다. Checks.mqh 파일에 있습니다.

//+------------------------------------------------------------------+
//| Checks if indicator is used in Strategy Tester                   |
//+------------------------------------------------------------------+
bool CheckTesterMode()
  {
//--- Report that indicator is not intended to be used in Strategy Tester
   if(MQLInfoInteger(MQL_TESTER) || 
      MQLInfoInteger(MQL_VISUAL_MODE) || 
      MQLInfoInteger(MQL_OPTIMIZATION))
     {
      Comment("Currently, the <- "+MQLInfoString(MQL_PROGRAM_NAME)+" -> indicator is not intended to be used in Strategy Tester!");
      return(false);
     }
//---
   return(true);
  }
-->

또 다른 새로운 함수 SetBarsColors()는 현재 기호의 바/촛대의 색상을 설정하기 위한 것입니다. Chart.mqh 파일에 있습니다.

//+------------------------------------------------------------------+
//| Sets colors for the current symbol bars                          |
//+------------------------------------------------------------------+
void SetBarsColors()
  {
//--- Color for the up bar, shadows and body borders of bull candlesticks
   ChartSetInteger(0,CHART_COLOR_CHART_UP,color_bar_up);
//--- Body color of a bull candlestick
   ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,color_bar_up);
//--- Line chart color and color of "Doji" Japanese candlesticks
   ChartSetInteger(0,CHART_COLOR_CHART_LINE,color_bar_up);
//--- For two-color mode
   if(TwoColor)
     {
      //--- Color for the down bar, shadows and body borders of bear candlesticks
      ChartSetInteger(0,CHART_COLOR_CHART_DOWN,color_bar_down);
      //--- Body color of a bear candlestick
      ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,color_bar_down);
     }
//--- If two-color mode is turned off
   else
     {
      //--- Color for the down bar, shadows and body borders of bear candlesticks
      ChartSetInteger(0,CHART_COLOR_CHART_DOWN,color_bar_up);
      //--- Body color of a bear candlestick
      ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,color_bar_up);
     }
  }
-->

초기화하는 동안 StartPriceDivergence 외부 매개변수에서 어떤 모드가 선택되었는지 확인해야 합니다. 수직선이 선택되면 timeframe_start_point 전역 변수가 기본값, 즉 현재 시간 프레임으로 할당됩니다. 그렇지 않으면 선택한 기간이 적용됩니다. 이를 위해 InitStartPointTF() 함수를 작성해 보겠습니다.

//+------------------------------------------------------------------+
//| Identifies timeframe for the price starting point mode           |
//+------------------------------------------------------------------+
void InitStartPointTF()
  {
//--- Exit if vertical line mode is selected
   if(StartPriceDivergence==VERTICAL_LINE)
      return;
//--- Otherwise define the timeframe
   switch(StartPriceDivergence)
     {
      case MONTH : timeframe_start_point=PERIOD_MN1; break;
      case WEEK  : timeframe_start_point=PERIOD_W1;  break;
      case DAY   : timeframe_start_point=PERIOD_D1;  break;
      case HOUR  : timeframe_start_point=PERIOD_H1;  break;
     }
  }
-->

이전 글과 달리 CheckInputParameters() 함수는 이제 다음과 같이 보입니다.

//+------------------------------------------------------------------+
//| Checks input parameters for correctness                          |
//+------------------------------------------------------------------+
bool CheckInputParameters()
  {
//--- For all other modes except the 'Vertical Line'
   if(StartPriceDivergence!=VERTICAL_LINE)
     {
      //--- If the current period is greater than or equal to the specified period of the price divergence starting point, report of it and exit
      if(PeriodSeconds()>=PeriodSeconds(timeframe_start_point))
        {
         Print("Current timeframe should be less than one specified in the Start Price Divergence parameter!");
         Comment("Current timeframe should be less than one specified in the Start Price Divergence parameter!");
         return(false);
        }
     }
//---
   return(true);
  }
-->

배열은 이전 글과 마찬가지로 초기화됩니다. 배열의 이름과 개수만 변경되었습니다.

//+------------------------------------------------------------------+
//| First initialization of arrays                                   |
//+------------------------------------------------------------------+
void InitArrays()
  {
   ArrayInitialize(limit_time,NULL);
   ArrayInitialize(symbol_difference,0.0);
   ArrayInitialize(inverse_difference,0.0);
   ArrayInitialize(series_first_date,NULL);
   ArrayInitialize(series_first_date_last,NULL);
//---
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      ArrayInitialize(buffer_data[s].open,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].high,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].low,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].close,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].icolor,EMPTY_VALUE);
     }
  }
//+------------------------------------------------------------------+
//| Initializes array of symbols                                     |
//+------------------------------------------------------------------+
void InitSymbolNames()
  {
   symbol_names[0]=AddSymbolToMarketWatch(Symbol02);
   symbol_names[1]=AddSymbolToMarketWatch(Symbol03);
   symbol_names[2]=AddSymbolToMarketWatch(Symbol04);
   symbol_names[3]=AddSymbolToMarketWatch(Symbol05);
   symbol_names[4]=AddSymbolToMarketWatch(Symbol06);
  }
//+------------------------------------------------------------------+
//| Initializes array of inversions                                  |
//+------------------------------------------------------------------+
void InitInverse()
  {
   inverse[0]=Inverse02;
   inverse[1]=Inverse03;
   inverse[2]=Inverse04;
   inverse[3]=Inverse05;
   inverse[4]=Inverse06;
  }
-->

SetIndicatorProperties() 함수가 크게 변경되었습니다. 사실 이것은 완전히 새로운 기능입니다. 이제 선택한 데이터 렌더링 모드에 따라 초기화 중에 해당 속성이 설정됩니다.

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetIndicatorProperties()
  {
//--- Set the short name
   IndicatorSetString(INDICATOR_SHORTNAME,indicator_shortname);
//--- Set the number of decimal digits
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//---  In the 'Line' mode we need only one buffers that displays the open price
   if(DrawType==LINE)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
         SetIndexBuffer(s,buffer_data[s].close,INDICATOR_DATA);
     }
//--- In other modes we use all prices for drawing 
//    bars/candlesticks and additional buffer for the two-color mode
   else if(DrawType==BARS || DrawType==CANDLES)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
        {
         static int buffer_number=0;
         SetIndexBuffer(buffer_number,buffer_data[s].open,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].high,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].low,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].close,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].icolor,INDICATOR_COLOR_INDEX);
         buffer_number++;
        }
     }
//--- Set labels for the current timeframe
//    In the 'Line' mode only opening price is used
   if(DrawType==LINE)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetString(s,PLOT_LABEL,symbol_names[s]+",Close");
     }
//--- In other modes all prices of bars/candlesticks
//    ";" is used as a separator
   else if(DrawType==BARS || DrawType==CANDLES)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
        {
         PlotIndexSetString(s,PLOT_LABEL,
                            symbol_names[s]+",Open;"+
                            symbol_names[s]+",High;"+
                            symbol_names[s]+",Low;"+
                            symbol_names[s]+",Close");
        }
     }
//--- Set the type of lines for indicator buffers
//--- Line
   if(DrawType==LINE)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_LINE);
//--- Bars
   if(DrawType==BARS)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_COLOR_BARS);
//--- Candlesticks
   if(DrawType==CANDLES)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_COLOR_CANDLES);

//--- Set the type of lines for data of current symbol
//--- Line
   if(DrawType==LINE)
      ChartSetInteger(0,CHART_MODE,CHART_LINE);
//--- Bars
   if(DrawType==BARS)
      ChartSetInteger(0,CHART_MODE,CHART_BARS);
//--- Candlesticks
   if(DrawType==CANDLES)
      ChartSetInteger(0,CHART_MODE,CHART_CANDLES);

//--- Set the line width
   for(int s=0; s<SYMBOLS_COUNT; s++)
      PlotIndexSetInteger(s,PLOT_LINE_WIDTH,1);
//--- Set the line color for the 'Line' mode
   if(DrawType==LINE)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_LINE_COLOR,line_colors[s]);
//--- Display data in Data Window only for existing symbols
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      if(symbol_names[s]!=empty_symbol)
         PlotIndexSetInteger(s,PLOT_SHOW_DATA,true);
      else
         PlotIndexSetInteger(s,PLOT_SHOW_DATA,false);
     }
//--- Empty value for plotting where nothing will be drawn
   for(int s=0; s<SYMBOLS_COUNT; s++)
      PlotIndexSetDouble(s,PLOT_EMPTY_VALUE,EMPTY_VALUE);
  }
-->

마지막으로 OnInit()에서 사용할 또 다른 새로운 함수 SetDivergenceLine()입니다. 수직선 모드에서 가격 차이 시작점을 조작하기 위해 수직 녹색 선을 설정합니다.

//+------------------------------------------------------------------+
//| Sets vertical line for price divergence starting point           |
//+------------------------------------------------------------------+
void SetDivergenceLine()
  {
//--- If there is no vertical line yet, set it
   if(StartPriceDivergence==VERTICAL_LINE && ObjectFind(0,start_price_divergence)<0)
      //--- Place a vertical line on the true bar
      CreateVerticalLine(0,0,TimeCurrent()+PeriodSeconds(),start_price_divergence,
                         2,STYLE_SOLID,clrGreenYellow,true,true,false,"","\n");
//--- For all other modes except the 'Vertical Line'
   if(StartPriceDivergence!=VERTICAL_LINE)
      DeleteObjectByName(start_price_divergence);
  }
-->

아래는 OnInit() 함수 내에서 위에서 설명한 모든 것을 표현한 것입니다. 모든 것이 별도의 기능과 파일로 분할되면 프로그램 코드를 읽는 것이 매우 편리해집니다.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Check if indicator is currently being used in Strategy Tester
   if(!CheckTesterMode())
      return(INIT_FAILED);
//--- Set the color for bars/candlesticks
   SetBarsColors();
//--- Define the timeframe for the price divergence starting point
   InitStartPointTF();
//--- Check input parameters for correctness
   if(!CheckInputParameters())
      return(INIT_PARAMETERS_INCORRECT);
//--- Set the timer at 1-second intervals
   EventSetMillisecondTimer(1000);
//--- Set the font to be displayed on the canvas
   canvas.FontSet(font_name,font_size,FW_NORMAL);
//--- Initialization of arrays
   InitArrays();
//--- Initialize the array of symbols 
   InitSymbolNames();
//--- Initialize the array of inversions
   InitInverse();
//--- Set indicator properties
   SetIndicatorProperties();
//--- Set vertical line of the price divergence start
   SetDivergenceLine();
//--- Clear the comment
   Comment("");
//--- Refresh the chart
   ChartRedraw();
//--- Initialization completed successfully
   return(INIT_SUCCEEDED);
  }
-->

OnCalculate() 함수에서 프로그램 코드는 거의 변경되지 않았습니다. 이전 글에서 데이터 가용성에 대한 모든 확인이 완료된 후 프로그램은 먼저 보조 배열을 채운 다음 준비된 데이터로 표시기 버퍼를 채웠습니다. 이번에는 모든 것을 단일 루프로 정렬하려고 합니다.

데이터 유효성 검사 및 로드 기능을 보다 엄격하게 만들었습니다. 이제 얻고자 하는 각 값이 지정된 시도 횟수를 통과합니다. 값을 얻으면 루프가 중지됩니다. 그리고 이제 기간의 시작(월, 주, 일, 시간)을 결정해야 하는 모드가 있으므로 더 높은 기간을 통해 기간 시작 시간을 얻습니다. 따라서 LoadAndFormDataHighTF()라는 유사한 이름을 가진 LoadAndFormData()와 유사한 추가 함수를 만들었습니다. 코드는 원본 코드와 매우 유사하므로 여기에 게시하지 않겠습니다.

현재 및 더 높은 기간에 대한 데이터 가용성 확인이 하나의 함수 CheckAvailableData()에서 구현되었습니다.

//+------------------------------------------------------------------+
//| Checks the amount of available data for all symbols              |
//+------------------------------------------------------------------+
bool CheckAvailableData()
  {
   int attempts=100;
   
//---
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      //--- If this symbol is available
      if(symbol_names[s]!=empty_symbol)
        {
datetime time[];                    // Array for checking the number of bars
   int      total_period_bars   =0;    // Number of bars of the current period
   datetime terminal_first_date =NULL; // First date of the current time frame data available in the terminal
         //--- Get the first date of the current time frame data in the terminal
         terminal_first_date=(datetime)SeriesInfoInteger(symbol_names[s],Period(),SERIES_TERMINAL_FIRSTDATE);
         //--- Get the number of available bars from the date specified
         total_period_bars=Bars(symbol_names[s],Period(),terminal_first_date,TimeCurrent());
         //--- Check the readiness of bar data
         for(int i=0; i<attempts; i++)
           {
            //--- Copy the specified amount of data
            if(CopyTime(symbol_names[s],Period(),0,total_period_bars,time))
              {
               //--- If the required amount has been copied, terminate the loop
               if(ArraySize(time)>=total_period_bars)
                  break;
              }
           }
         //--- If the amount of data copied is not sufficient, one more attempt is required
         if(ArraySize(time)==0 || ArraySize(time)<total_period_bars)
           {
            msg_last=msg_prepare_data;
            ShowCanvasMessage(msg_prepare_data);
            OC_prev_calculated=0;
            return(false);
           }
        }
     }
//--- Exit if current mode is vertical line of the price divergence starting point
   if(StartPriceDivergence==VERTICAL_LINE)
      return(true);
   else
     {
      datetime time[];                    // Array for checking the number of bars
      int      total_period_bars   =0;    // Number of bars of the current period
      datetime terminal_first_date =NULL; // First date of the current time frame data available in the terminal
      //--- Get the first date of the current time frame data in the terminal
      for(int i=0; i<attempts; i++)
         if((terminal_first_date=(datetime)SeriesInfoInteger(Symbol(),Period(),SERIES_FIRSTDATE))>0)
            break;
      //--- Get the number of available bars from the date specified
      for(int i=0; i<attempts; i++)
         if((total_period_bars=(int)SeriesInfoInteger(Symbol(),timeframe_start_point,SERIES_BARS_COUNT))>0)
            break;
      //--- Check the readiness of bar data
      for(int i=0; i<attempts; i++)
         //--- Copy the specified amount of data
         if(CopyTime(Symbol(),timeframe_start_point,
            terminal_first_date+PeriodSeconds(timeframe_start_point),TimeCurrent(),time)>0)
            break;
      //--- If the amount of data copied is not sufficient, one more attempt is required
      if(ArraySize(time)<=0 || total_period_bars<=0)
        {
         msg_last=msg_prepare_data;
         ShowCanvasMessage(msg_prepare_data);
         OC_prev_calculated=0;
         return(false);
        }
     }
//---
   return(true);
  }
-->

FillIndicatorBuffers() 함수는 현재 작업에 대해 상당히 복잡했습니다. 이것은 이제 몇 가지 모드가 있고 각 모드에는 자체 작업이 필요하기 때문입니다. 사실, 모든 것은 네 단계로 나눌 수 있습니다.

  • 지정된 기호에 대한 데이터를 가져옵니다.
  • 더 높은 시간대에 대한 데이터를 얻고 모든 기호의 가격이 만나는 시간과 가격 수준을 결정합니다.
  • 값 계산 및 표시기 버퍼 채우기.
  • 계산된 값의 확인.

기능 코드는 고려 사항에 대한 자세한 설명과 함께 제공됩니다.

//+------------------------------------------------------------------+
//| Fills indicator buffers                                          |
//+------------------------------------------------------------------+
void FillIndicatorBuffers(int i,int s,datetime const &time[])
  {
   MqlRates    rates[];             // Data structure
   double      period_open[];       // Opening price for bar at the price divergence starting point
   datetime    period_time[];       // Time of the price divergence starting point
   int         attempts=100;        // Number of copying attempts
   datetime    high_tf_time=NULL;   // Time of higher timeframe's bar

//--- Exit if we are out of "true" bars zone
   if(time[i]<limit_time[s])
      return;
//--- Reset the last error
   ResetLastError();
//--- Get data of current bar for the specified symbol
   for(int j=0; j<attempts; j++)
      if(CopyRates(symbol_names[s],Period(),time[i],1,rates)==1)
        { ResetLastError(); break; }
//--- Exit if failed to get data
   if(ArraySize(rates)<1 || GetLastError()!=0)
      return;
//--- If the current time is before the first timeframe's time or
//    bar time is not equal to the bar time of the current symbol or
//    empty values are fetched
   if(rates[0].time==NULL || 
      time[i]!=rates[0].time || 
      time[i]<first_period_time || 
      rates[0].low==EMPTY_VALUE || 
      rates[0].open==EMPTY_VALUE ||
      rates[0].high==EMPTY_VALUE ||
      rates[0].close==EMPTY_VALUE)
     {
      //--- Write empty value
      if(DrawType!=LINE)
        {
         buffer_data[s].low[i]   =EMPTY_VALUE;
         buffer_data[s].open[i]  =EMPTY_VALUE;
         buffer_data[s].high[i]  =EMPTY_VALUE;
        }
      buffer_data[s].close[i]=EMPTY_VALUE;
      return;
     }
//--- If current mode is vertical line of the price divergence starting point
   if(StartPriceDivergence==VERTICAL_LINE)
     {
      //--- Get the time of the line
      divergence_time=(datetime)ObjectGetInteger(0,start_price_divergence,OBJPROP_TIME);
      //--- Get the time of the first bar
      first_period_time=time[0];
     }
//--- For all other modes, we will keep track the beginning of period
   else
     {
      //--- If we are here for the first time, store data of the first bar of higher timeframe
      if(divergence_time==NULL)
        {
         ResetLastError();
         //--- Get opening time of the first bar of higher timeframe
         for(int j=0; j<attempts; j++)
            if(CopyTime(Symbol(),timeframe_start_point,time[0]+PeriodSeconds(timeframe_start_point),1,period_time)==1)
              { ResetLastError(); break; }
         //--- Exit if failed to get price/time
         if(ArraySize(period_time)<1 || GetLastError()!=0)
            return;
         //--- Otherwise store time of the first bar of higher timeframe
         else
            first_period_time=period_time[0];
        }
      //--- If current bar's time on the current timeframe is before the first bar's time on higher timeframe
      if(time[i]<first_period_time)
         high_tf_time=first_period_time;
      //--- Otherwise we will receive data of the last bar of the higher timeframe with respect to the current bar on the current timeframe
      else
         high_tf_time=time[i];
      //--- Reset the last error
      ResetLastError();
      //--- Get the opening price of the first bar of the higher timeframe
      for(int j=0; j<attempts; j++)
         if(CopyOpen(Symbol(),timeframe_start_point,high_tf_time,1,period_open)==1)
           { ResetLastError(); break; }
      //--- Get opening time of the first bar of higher timeframe
      for(int j=0; j<attempts; j++)
         if(CopyTime(Symbol(),timeframe_start_point,high_tf_time,1,period_time)==1)
           { ResetLastError(); break; }
      //--- Exit if failed to get price/time
      if(ArraySize(period_open)<1 || ArraySize(period_time)<1 || GetLastError()!=0)
         return;
      //--- If the current timeframe's time is before the first period's time or
      //    time of specified period is not equal to the one in memory
      if(time[i]<first_period_time || divergence_time!=period_time[0])
        {
         symbol_difference[s]  =0.0; // Zero out difference in symbol prices
         inverse_difference[s] =0.0; // Zero our difference of inversion
         //--- Store time of the price divergence starting point
         divergence_time=period_time[0];
         //--- Store price of the price divergence starting point
         divergence_price=period_open[0];
         //--- Set vertical line in the beginning of the price divergence start
         CreateVerticalLine(0,0,period_time[0],start_price_divergence+"_"+TimeToString(divergence_time),
                            2,STYLE_SOLID,clrWhite,false,false,true,TimeToString(divergence_time),"\n");
        }
     }
//--- If current mode is 'Vertical Line' and bar's time is less than line's time
   if(StartPriceDivergence==VERTICAL_LINE && time[i]<divergence_time)
     {
      //--- Keep zero values of difference
      symbol_difference[s]  =0.0;
      inverse_difference[s] =0.0;
      //--- For the 'Line' drawing mode only opening price is used
      if(DrawType==LINE)
         buffer_data[s].close[i]=rates[0].close-symbol_difference[s];
      //--- For all other modes all prices are used
      else
        {
         buffer_data[s].low[i]   =rates[0].low-symbol_difference[s];
         buffer_data[s].open[i]  =rates[0].open-symbol_difference[s];
         buffer_data[s].high[i]  =rates[0].high-symbol_difference[s];
         buffer_data[s].close[i] =rates[0].close-symbol_difference[s];
         //--- Set color for the current element of indicator buffer
         SetBufferColorIndex(i,s,rates[0].close,rates[0].open);
        }
     }
//--- For all other modes
   else
     {
      //--- If inversion of symbol data is required
      if(inverse[s])
        {
         //--- If new period has started, recalculate variables
         if(symbol_difference[s]==0.0)
           {
            //--- For the 'Vertical Line' mode
            if(StartPriceDivergence==VERTICAL_LINE)
              {
               //--- Calculate the difference
               symbol_difference[s]  =rates[0].open-OC_open[i];
               inverse_difference[s] =OC_open[i]-(-OC_open[i]);
              }
            //--- For all other modes
            else
              {
               //--- Calculate the difference
               symbol_difference[s]  =rates[0].open-divergence_price;
               inverse_difference[s] =divergence_price-(-divergence_price);
              }
           }
         //--- In the 'Line' mode only opening price is used
         if(DrawType==LINE)
            buffer_data[s].close[i]=-(rates[0].close-symbol_difference[s])+inverse_difference[s];
         //--- For all other modes all prices are used
         else
           {
            buffer_data[s].low[i]   =-(rates[0].low-symbol_difference[s])+inverse_difference[s];
            buffer_data[s].open[i]  =-(rates[0].open-symbol_difference[s])+inverse_difference[s];
            buffer_data[s].high[i]  =-(rates[0].high-symbol_difference[s])+inverse_difference[s];
            buffer_data[s].close[i] =-(rates[0].close-symbol_difference[s])+inverse_difference[s];
            //--- Set color for the current element of indicator buffer
            SetBufferColorIndex(i,s,rates[0].close,rates[0].open);
           }
        }
      //--- If inversion is not used, then we need to calculate only the difference between symbol prices at the beginning of period
      else
        {
         //--- If new period has started
         if(symbol_difference[s]==0.0)
           {
            //--- For the 'Vertical Line' mode
            if(StartPriceDivergence==VERTICAL_LINE)
               symbol_difference[s]=rates[0].open-OC_open[i];
            //--- For all other modes
            else
               symbol_difference[s]=rates[0].open-divergence_price;
           }
         //--- For the 'Line' drawing mode only opening price is used
         if(DrawType==LINE)
            buffer_data[s].close[i]=rates[0].close-symbol_difference[s];
         //--- For all other modes all prices are used
         else
           {
            buffer_data[s].low[i]   =rates[0].low-symbol_difference[s];
            buffer_data[s].open[i]  =rates[0].open-symbol_difference[s];
            buffer_data[s].high[i]  =rates[0].high-symbol_difference[s];
            buffer_data[s].close[i] =rates[0].close-symbol_difference[s];
            //--- Set color for the current element of indicator buffer
            SetBufferColorIndex(i,s,rates[0].close,rates[0].open);
           }
        }
     }
//--- Verification of the calculated values
//    In the 'Line' mode only opening price is used
   if(DrawType==LINE)
     {
      //--- If the current time is before the first timeframe's time or
      //    bar time is not equal to the bar time, write empty value
      if(time[i]!=rates[0].time || time[i]<first_period_time)
         buffer_data[s].close[i]=EMPTY_VALUE;
     }
//--- For all other modes all prices are used
   else
     {
      //--- If the current time is before the first timeframe's time or
      //    bar time is not equal to the bar time of the current symbol or
      //    empty values are fetched
      if(rates[0].time==NULL || 
         time[i]!=rates[0].time || 
         time[i]<first_period_time || 
         rates[0].low==EMPTY_VALUE || 
         rates[0].open==EMPTY_VALUE ||
         rates[0].high==EMPTY_VALUE ||
         rates[0].close==EMPTY_VALUE)
        {
         //--- Write empty value
         buffer_data[s].low[i]   =EMPTY_VALUE;
         buffer_data[s].open[i]  =EMPTY_VALUE;
         buffer_data[s].high[i]  =EMPTY_VALUE;
         buffer_data[s].close[i] =EMPTY_VALUE;
        }
     }
  }
-->

위의 함수를 공부할 때 또 다른 사용자 정의 함수 SetBufferColorIndex()를 확인해야 합니다. 이 함수는 표시기 색상 버퍼의 색상을 설정합니다.

//+------------------------------------------------------------------+
//| Sets the color for buffer element by condition                   |
//+------------------------------------------------------------------+
void SetBufferColorIndex(int i,int symbol_number,double close,double open)
  {
//--- For two-color mode, check condition
   if(TwoColor)
     {
      //--- If the closing price is more than the opening price, this is up bar, so we use the first color
      if(close>open)
         buffer_data[symbol_number].icolor[i]=0;
      //--- otherwise it is down bar, so we use the second color
      else
         buffer_data[symbol_number].icolor[i]=1;
     }
//--- For one-color mode we use the first color for all bars/candlesticks
   else
      buffer_data[symbol_number].icolor[i]=0;
  }
-->

표시기 버퍼가 채워지면 현재 차트 창에 표시되는 모든 값에서 최대값과 최소값을 결정해야 합니다. MQL5를 사용하면 차트 창에서 첫 번째로 보이는 바와 보이는 바의 수를 얻을 수 있습니다. 우리는 다른 사용자 정의 함수 CorrectChartMaxMin()에서 이러한 기능의 이점을 얻을 것입니다. 함수의 코드 흐름은 여러 단계로 나눌 수 있습니다.

  • 첫 번째와 마지막 보이는 바의 수를 결정합니다.
  • 현재 기호에 대해 표시되는 바의 최대값과 최소값을 결정합니다.
  • 모든 기호 배열 중에서 최대값과 최소값을 결정합니다.
  • 차트 속성에서 최대값과 최소값을 설정합니다.

다음은 Chart.mqh 파일에 있는 CorrectChartMaxMin() 함수의 코드입니다.

//+------------------------------------------------------------------+
//| Corrects chart's high/low with respect to all buffers            |
//+------------------------------------------------------------------+
void CorrectChartMaxMin()
  {
   double low[];                  // Array of lows
   double high[];                 // Array of highs
   int    attempts          =10;  // Number of attempts
   int    array_size        =0;   // Array size for drawing
   int    visible_bars      =0;   // Number of visible bars
   int    first_visible_bar =0;   // Number of the first visible bar
   int    last_visible_bar  =0;   // Number of the last visible bar
   double max_price         =0.0; // Highest price
   double min_price         =0.0; // Lowest price
   double offset_max_min    =0.0; // Offset from chart's high/low
//--- Reset the last error
   ResetLastError();
//--- Number of visible bars
   visible_bars=(int)ChartGetInteger(0,CHART_VISIBLE_BARS);
//--- Number of the first visible bar
   first_visible_bar=(int)ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR);
//--- Number of the last visible bar
   last_visible_bar=first_visible_bar-visible_bars;
//--- Exit in case of error
   if(GetLastError()!=0)
      return;
//--- Fix incorrect value
   if(last_visible_bar<0)
      last_visible_bar=0;
//--- Get the current symbol high/low in visible area of chart
   for(int i=0; i<attempts; i++)
      if(CopyHigh(Symbol(),Period(),last_visible_bar,visible_bars,high)==visible_bars)
         break;
   for(int i=0; i<attempts; i++)
      if(CopyLow(Symbol(),Period(),last_visible_bar,visible_bars,low)==visible_bars)
         break;
//--- Exit if failed to get data
   if(ArraySize(high)<=0 || ArraySize(low)<=0)
      return;
//--- If succeeded to get data, identify high and low in the current symbol arrays
   else
     {
      min_price=low[ArrayMinimum(low)];
      max_price=high[ArrayMaximum(high)];
     }
//--- Get high and low prices in all price arrays
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      //--- If current symbol is not present, go to the next one
      if(symbol_names[s]==empty_symbol)
         continue;
      //---
      datetime time[];         // Time array
      int      bars_count=0; // Number of bars for calculation
      //--- Set zero size for arrays
      ArrayResize(high,0);
      ArrayResize(low,0);
      //--- Get the time of the first bar visible on chart
      for(int i=0; i<attempts; i++)
         if(CopyTime(Symbol(),Period(),last_visible_bar,visible_bars,time)==visible_bars)
            break;
      //--- Exit if the amount of data is less than number of visible bars on chart
      if(ArraySize(time)<visible_bars)
         return;
      //--- If time of the first "true" bar is greater than
      //    time of the first visible bar on the chart, then
      //    get available number of bars of the current symbol in loop
      if(limit_time[s]>time[0])
        {
         //--- Get the array size
         array_size=ArraySize(time);
         //--- Get the number of bars from the first "true" one
         if((bars_count=Bars(Symbol(),Period(),limit_time[s],time[array_size-1]))<=0)
            return;
        }
      //--- Else get number of visible bars on chart
      else
         bars_count=visible_bars;
      //--- Index elements in indicator buffers as timeseries
      ArraySetAsSeries(low,true);
      ArraySetAsSeries(high,true);
      //--- Copy data from the indicator buffer
      //    All modes except 'Line'
      if(DrawType!=LINE)
        {
         ArrayCopy(low,buffer_data[s].low);
         ArrayCopy(high,buffer_data[s].high);
        }
      //--- For the 'Line' mode
      else
        {
         ArrayCopy(low,buffer_data[s].close);
         ArrayCopy(high,buffer_data[s].close);
        }
      //--- Get the array size
      array_size=ArraySize(high);
      //--- Fill empty values,
      //    so they are not considered when calculating high/low
      for(int i=0; i<array_size; i++)
        {
         if(high[i]==EMPTY_VALUE)
            high[i]=max_price;
         if(low[i]==EMPTY_VALUE)
            low[i]=min_price;
        }
      //--- Get high/low with respect to inversion
      if(inverse[s])
        {
         //--- If no errors occur, store values
         if(ArrayMaximum(high,last_visible_bar,bars_count)>=0 && 
            ArrayMinimum(low,last_visible_bar,bars_count)>=0)
           {
            max_price=fmax(max_price,low[ArrayMaximum(low,last_visible_bar,bars_count)]);
            min_price=fmin(min_price,high[ArrayMinimum(high,last_visible_bar,bars_count)]);
           }
        }
      else
        {
         //--- If no errors occur, store values
         if(ArrayMinimum(low,last_visible_bar,bars_count)>=0 && 
            ArrayMaximum(high,last_visible_bar,bars_count)>=0)
           {
            min_price=fmin(min_price,low[ArrayMinimum(low,last_visible_bar,bars_count)]);
            max_price=fmax(max_price,high[ArrayMaximum(high,last_visible_bar,bars_count)]);
           }
        }
     }
//--- Calculate offset (3%) form chart's top and bottom
   offset_max_min=((max_price-min_price)*3)/100;
//--- Turn on the fixed chart scale mode.
   ChartSetInteger(0,CHART_SCALEFIX,true);
//--- Set high/low
   ChartSetDouble(0,CHART_FIXED_MAX,max_price+offset_max_min);
   ChartSetDouble(0,CHART_FIXED_MIN,min_price-offset_max_min);
//--- Refresh the chart
   ChartRedraw();
  }
-->

위에서 설명한 함수는 세로선을 드래그하는 이벤트를 처리할 때 사용됩니다(물론 OnCalculate에서 표시기 값을 계산할 때도).

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Event of dragging a graphical object
   if(id==CHARTEVENT_OBJECT_DRAG)
     {
      //--- If current mode is vertical line for the price divergence starting point, then update indicator buffers
      if(StartPriceDivergence==VERTICAL_LINE)
         OnCalculate(OC_rates_total,
                     0,
                     OC_time,
                     OC_open,
                     OC_high,
                     OC_low,
                     OC_close,
                     OC_tick_volume,
                     OC_volume,
                     OC_spread);
     }
//--- Event of resizing the chart or modifying the chart properties using the properties dialog window.
   if(id==CHARTEVENT_CHART_CHANGE)
      //--- Correct the maximum and minimum of chart with respect to the indicator buffers' values
      CorrectChartMaxMin();
  }
-->

모든 기능이 준비되었습니다. 이 글에 첨부된 전체 주석 코드를 연구할 수 있습니다.

우리가 결국 무엇을 얻었는지 보여줍시다. 기본적으로 GBPUSD, AUDUSD, NZDUSD, USDCAD, USDCHF 기호가 지정됩니다. 외부 매개변수에서. 아래 스크린샷에서 반전이 비활성화된 수직선 모드에서 EURUSD의 주간 차트를 볼 수 있습니다.

"수직선" 모드의 주간 시간대

그림 1 - "수직선" 모드의 주간 시간대

아래 스크린샷에서 모드의 M30 시간대를 볼 수 있지만, 기본 통화로 USD를 사용하는 기호에 대해 시간 반전이 활성화됩니다. 이 경우 USDCAD(하늘색 양초)와 USDCHF(보라색 양초)가 있습니다.

"낮" 모드의 M30 시간 프레임

그림 2 - "주간" 모드의 M30 시간 프레임


결론

저는 우리가 가격 다이버전스의 다중 통화 분석을 위한 매우 흥미롭고 유익한 도구를 만들었다고 생각합니다. 이 지표는 무한히 향상될 수 있습니다.

시간 내어 읽어주셔서 감사합니다!

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

MQL5 Cookbook - MQL5의 다중 통화 Expert Advisor 및 대기 중인 주문 작업 MQL5 Cookbook - MQL5의 다중 통화 Expert Advisor 및 대기 중인 주문 작업
이번에는 보류 중인 주문 Buy Stop 및 Sell Stop 작업을 기반으로 하는 거래 알고리즘을 사용하여 다중 통화 Expert Advisor를 만들 것입니다. 이 글은 다음 사항을 고려합니다: 지정된 시간 범위의 거래, 보류 주문 배치/수정/삭제, 이익 실현 또는 손절매에서 마지막 포지션이 마감되었는지 확인하고 각 기호에 대한 거래 내역 제어.
MQL5 프로그래밍 기본: 목록 MQL5 프로그래밍 기본: 목록
거래 전략 개발을 위한 프로그래밍 언어의 새 버전인 MQL[MQL5]은 이전 버전[MQL4]에 비해 더 강력하고 효과적인 기능을 제공합니다. 이점은 본질적으로 객체 지향 프로그래밍 기능에 있습니다. 이 글에서는 노드 및 목록과 같은 복잡한 사용자 지정 데이터 유형을 사용할 가능성을 조사합니다. 또한 MQL5의 실제 프로그래밍에서 목록을 사용하는 예를 제공합니다.
Renko 차트 표시 Renko 차트 표시
이 글에서는 Renko 차트 작성의 예와 MQL5에서 지표로 구현하는 방법을 설명합니다. 이 지표를 수정하면 기존 차트와 구별됩니다. 지표 창과 메인 차트 모두에서 구성할 수 있습니다. 또한 지그재그 표시기가 있습니다. 차트 구현의 몇 가지 예를 찾을 수 있습니다.
2013년 3분기 MetaTrader AppStore 결과 2013년 3분기 MetaTrader AppStore 결과
또 다른 분기가 지났고, 우리는 MetaTrader 플랫폼용 거래 로봇 및 기술 지표의 최대 매장인 MetaTrader AppStore의 결과를 요약하기로 결정했습니다. 보고된 분기 말까지 500개 이상의 개발자가 1200개 이상의 제품을 시장에 출시했습니다.