English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MetaTrader 5의 다중 통화 모드 구현

MetaTrader 5의 다중 통화 모드 구현

MetaTrader 5 | 4 8월 2021, 16:56
73 0
Konstantin Gruzdev
Konstantin Gruzdev

소개

현재 개발된 다중 통화 거래 시스템, 지표 및 Expert Advisors가 많이 있습니다. 그럼에도 불구하고 개발자는 다중 통화 시스템 개발의 특정 문제에 계속 직면하고 있습니다.

MetaTrader 5 클라이언트 터미널과 MQL5 프로그래밍 언어의 출시로 우리는 완전한 다중 통화 모드를 구현할 수 있는 새로운 기회를 얻었고 결과적으로 더 효율적인 다중 통화 로봇 및 지표를 구현할 수 있게 되었습니다. 이러한 새로운 기회가 이 글의 주제가 될 것입니다.


기존 접근 방식의 개요

우리의 경우 기존의 접근 방식은 표준 OnTick()OnCalculate() 함수를 기반으로 다중 통화 시스템을 구현하려는 시도로, 이는 start() 함수를 대체했습니다. MQL4. 간단히 말해서, 현재 차트에 새로운 눈금이나 바가 나타나면 모든 통화 쌍(다중 통화 시스템에 참여)이 후속 분석 및 의사 결정을 위해 순차적으로 요청됩니다.

이 접근 방식의 문제점은 다음과 같습니다.

  1. 현재 차트의 거래 기호에 대한 틱 수신에 대한 전체 시스템의 종속성.

    빠른 시장의 경우 현재 차트의 기호에 대한 눈금이 자주 표시되면 실제로 문제가 없습니다. 그러나 예를 들어 시장이 느릴 때 야간에는 틱이 매우 드물게 나타날 수 있습니다. 30분에 한 번 또는 더 드물게 발생할 수 있습니다. 희귀 틱이 들어오는 간격 동안 전체 다중 통화 시스템은 "잠자는" 상태이지만 다른 기호에서 발생하는 변경 사항은 상당히 과감할 수 있습니다.

    이 단점은 시스템이 긴 시간 프레임에서 작동하도록 구성된 경우 그다지 중요하지 않습니다. 그러나 기간이 짧을수록 효과가 더 큽니다. 세상이 날마다 속도를 높이고 컴퓨터는 단위 시간당 점점 더 많은 정보를 처리할 수 있게 되었고 결과적으로 더 많은 사람들이 작은 시간이나 심지어 틱 정도의 시간 간격으로 기꺼이 일을 하게 되었습니다.

  2. 다중 통화 시스템에서 사용되는 모든 기호에 대한 이력 데이터의 복잡성 동기화

    "MetaTrader 4에서는 최소한 한 번의 가격 변경이 발생한 바만 그려집니다. 1분 이내에 가격 변동이 발생하지 않으면 1분 주기로 차트에 1바 간격이 발생합니다."- "구멍 없는" 차트 글의 시작 부분을 나타냅니다.

    차트 작성에 대한 이러한 접근 방식은 MetaTrader 5에서 유지됩니다. 즉, 차트의 각 기호에 대해 동일한 수의 바가 시간에 따라 동기화된다는 의미는 아닙니다. 예를 들어, 100번째 바는 각 기호에 대해 여는 시간이 다를 수 있습니다. 따라서 다중 통화 지표를 구성하고 재계산하는 동안 모든 바가 각 기호에 대해 서로 일치하는지 확인하는 것이 중요합니다.

    기간이 증가함에 따라 바를 놓칠 가능성이 크게 감소하기 때문에 시스템이 큰 시간 프레임에서 작동하도록 구성된 경우에도 이것은 그다지 중요하지 않습니다. 그들이 말했듯이, 당신은 결코 너무 조심할 수 없습니다. 그리고 그 사람이 아무것도 하지 않는 경우에만 알 수 있습니다.

    현재 불완전한 바의 동기화 시간을 별도로 기록해야 합니다. 현재 차트에 새 바가 표시된다고 해서 다른 기호에도 새 바가 형성된 것은 아닙니다. 따라서 CopyXXXX() 함수를 사용하여 다른 기호를 통해 새 바의 가격을 조회하려는 시도는 기호에 대한 이전 바의 가격을 얻거나 단순히 복사 오류로 이어질 수 있습니다. 그건 그렇고, 다른 기호의 새 바는 현재 기호보다 훨씬 일찍 형성될 수 있습니다. 이는 상황 평가의 정확성에도 영향을 미칠 수 있습니다.

    여러 개의 중간 지표 버퍼를 사용하여 다중 통화 지표 만들기 문서에서는 기록 데이터 동기화 문제를 어느 정도 해결하는 옵션에 대해 설명합니다.

  3. 데이터 동기화와 관련된 또 다른 중요한 점: 일부 거래 기호에 대한 기록 업데이트가 있는지 어떻게 알 수 있습니까?

    예를 들어, 단일 통화 지표를 구성할 때 문제가 없습니다. OnCalculate() 함수의 입력 변수 prev_calculated가 0이면 지표를 다시 계산합니다. 그러나 기호에 대한 기록에 업데이트가 있고 현재 차트에는 없으면 어떻게 해야 합니까? 이는 언제든지 발생할 수 있으며 다중 통화 지표를 다시 계산해야 할 수 있습니다. 이 질문에 대한 대답은 상당히 관련이 있습니다.

다른 기능을 살펴보지 않고도 이 세 가지 인스턴스가 너무 많은 문제를 일으키기에 충분하여 다중 통화 EA 또는 지표의 코드가 너무 커지는 것을 알 수 있습니다. 그러나 문제의 전체 범위는 해결되지 않았습니다 ...


OnTimer() 함수로 새로운 희망

Timer 이벤트와 OnTimer() 표준 ​​이벤트 핸들러를 생성하는 MQL 프로그램의 새로운 기능은 새로운 유형의 다중 통화 시스템의 출현에 대한 희망을 주었습니다. 이것은 한편으로는 이제 다중 통화 Expert Advisor/지표가 현재 차트의 기호에 의한 틱 수신과 독립적으로 될 수 있고, 이는 다른 한편으로는 우리가 시간별로 EA의 작업을 모니터링한다는 사실을 통해 이뤄집니다. 하지만 ...

이것은 이전 섹션의 단락 1에서 설명한 문제를 부분적으로 해결하고 물론 몇 가지 이점을 제공합니다. 그러나 NewTick 이벤트를 수신할 때와 마찬가지로 Timer 이벤트를 수신하면 변경 사항을 추적하기 위해 모든 통화 쌍을 순차적으로 조회해야 합니다. 때때로 이것은 매우 자주 수행되어야 하는데, 이는 MQL5 프로그램의 자원 사용을 크게 증가시킬 수 있습니다.

단락 2 및 3에서 식별된 문제는 동일한 솔루션 수준으로 유지됩니다. 이 외에도 OnTimer() 함수에 고유한 문제를 해결해야 합니다. 예를 들어 타이머 초기화/비초기화 문제, 주말 작업 등 말이죠.

OnTimer() 이벤트 핸들러의 명백한 이점을 과소평가하지 않으면서도 완전한 다중 통화 모드를 구현할 수 없다는 점에 유의해야 합니다.


OnChartEvent() 함수의 새로운 가능성

위의 표준 기능의 한계는 좁은 전문화로 인한 것입니다. 즉, 미리 정의된 특정 이벤트를 처리하기 위한 것이며 다중 통화 시스템을 위한 것이 아닙니다. 

다중 통화 시스템 개발자를 위한 구조 요소는 OnChartEvent() 표준 사용자 정의 이벤트 핸들러일 수 있습니다. 프로그래머가 재량에 따라 자신의 이벤트를 생성할 수 있습니다.

예를 들어, 아무도 현재 차트의 모든 기호에 대한 눈금을 얻기 위해 이 함수를 사용하는 것을 막을 수 없습니다. 우리가 해야 할 일은 적절한 기호가 있는 차트에 "스파이"를 보내는 것입니다.

재미있는 단어 "스파이"는 다음 지표를 의미합니다.

//+------------------------------------------------------------------+
//|                                                         iSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                                               lizar-2010@mail.ru |
//+------------------------------------------------------------------+
#define VERSION         "1.00 Build 2 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "iSpy agent-indicator. If you want to get ticks, attach it to the chart"
#property indicator_chart_window

input long            chart_id=0;        // chart id
input ushort          custom_event_id=0; // event id

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,        // size of price[] array
                 const int prev_calculated,  // bars, calculated at the previous call
                 const int begin,            // starting index of data
                 const double& price[]       // array for the calculation
   )
  {
   double price_current=price[rates_total-1];

   //--- Initialization:
   if(prev_calculated==0) 
     { // Generate and send "Initialization" event
      EventChartCustom(chart_id,0,(long)_Period,price_current,_Symbol);
      return(rates_total);
     }
   
   // When the new tick, let's generate the "New tick" custom event
   // that can be processed by Expert Advisor or indicator
   EventChartCustom(chart_id,custom_event_id+1,(long)_Period,price_current,_Symbol);
   
   //--- return value of prev_calculated for next call
   return(rates_total);
  }

이 "스파이"를 원하는 기호의 차트로 시작한 다음 OnChartEvent() 함수를 사용하여 EA 또는 지표에서 해당 메시지를 처리할 수 있습니다. "스파이"의 메시지를 올바르게 디코딩하려면 이 함수의 매개변수를 다음과 같이 해석해야 합니다.

  • id - 이벤트 식별자. id-CHARTEVENT_CUSTOM = 0인 경우 ""스파이"는 변수 prev_calculated가 0이 되었으며 적절한 조치를 취해야 한다고 보고합니다.
  • lparam - 이 경우 "스파이"가 시작된 차트의 기간을 의미합니다.
  • dparam - 틱 가격. 기본적으로 이것은 마지막 종가입니다. "스파이"가 시작되는 동안 ENUM_APPLIED_PRICE 열거형의 값으로 설정할 수 있습니다.
  • sparam - 이벤트가 수신된 거래 기호의 이름.

여러 "스파이"의 동시 작업을 시연하기 위해 간단한 EA exSpy.mq5를 작성합니다(전체 버전은 아카이브에서 사용 가능).

//+------------------------------------------------------------------+
//|                                                        exSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/ru/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "The Expert Advisor shows the work of iSPY indicator"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"iSpy",ChartID(),0)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"iSpy",ChartID(),1)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"iSpy",ChartID(),2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY spy indicator                                               |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event id:
                                     // if id-CHARTEVENT_CUSTOM=0-"initialization" event
                const long&   lparam, // chart period
                const double& dparam, // price
                const string& sparam  // symbol
               )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",
            id-CHARTEVENT_CUSTOM,":  ",sparam," ",
            EnumToString((ENUM_TIMEFRAMES)lparam)," price=",dparam);
     }
  }
//+------------------------------------------------------------------+

모든 차트에서 Expert Advisor를 시작합니다.

결과는 다음과 같습니다.


 

로그에서 볼 수 있듯이 원하는 기호에 대한 모든 틱과 특히 기록의 업데이트 또는 업로드가 있는 경우 수신되는 "초기화" 이벤트를 얻습니다.

중간 결과를 요약하자면 다음과 같습니다.

  • 표준 OnTick()OnCalculate() 함수를 사용할 때와 같이 특정 기호의 눈금에 의존하지 않습니다.
  • OnTimer()를 사용할 때처럼 엄격하게 정의된 시간 간격으로 제한되지 않습니다.
  • 변경 사항을 추적하기 위해 시퀀스 또는 루프의 모든 기호를 요청할 필요가 없습니다. 심볼에 대한 변경이 발생하면 해당 이벤트를 얻습니다. id 이벤트 식별자 또는 sparam 매개변수로 이벤트가 발생한 기호를 식별할 수 있습니다.
  • 각 심볼에 대해 개별적으로 서버와 이력 데이터의 동기화를 추적하는 문제를 해결했습니다.
  • 작업의 작은 부분이 추가 프로그램에 지정됩니다. 이것은 데이터 처리의 병렬화와 관련이 있습니다.
  • OnChartEvent() 함수의 매개변수에서 기간, 가격 및 기호 이름과 같은 추가 정보를 얻습니다. 이 데이터를 추가로 조회할 필요가 없으므로 EA 또는 지표의 코드를 크게 단순화할 수 있습니다.

MetaTrader 5 터미널과 MQL5의 프로그래밍 언어를 통해 다중 통화 모드를 성공적으로 구현했기 때문에 이 시점에서 기본적으로 이 글을 마무리할 수 있습니다. 그러나 이는 "날것의 (raw)" 구현입니다. 그러므로 우리는 더 나아갈 것입니다.


다중 통화 모드 구현

다중 통화 체제를 순수한 형태로 구현한다는 위의 아이디어를 사용하면 어느 시점에서 어려움에 직면하게 될 것입니다. 문제는 이러한 접근 방식을 통해 "스파이"가 실행 중인 각 거래 기호에 대한 모든 틱을 얻을 수 있다는 것입니다.

빠른 시장에서는 1초 동안 각 기호에 여러 개의 눈금이 표시될 수 있습니다. 이것은 이벤트 순서의 "차단"으로 이어질 수 있습니다. 다음은 도움말 섹션의 경고입니다.

클라이언트 터미널은 이벤트 대기열에 나타나는 이벤트를 추가합니다. 따라서 이벤트는 수신된 순서에 따라 차례로 처리됩니다. NewTick 이벤트에 대한 예외가 있습니다. 대기열에 이미 이러한 이벤트가 있거나 이 이벤트가 처리 중인 경우 새 NewTick 이벤트는 대기열에 포함되지 않습니다.

이벤트 대기열의 크기는 제한되어 있습니다. 큐 오버플로에서 새 이벤트를 수신할 수 있도록 기존 이벤트가 처리되지 않고 제거됩니다. 따라서 효율적인 이벤트 핸들러를 작성하는 것을 권장하고 무한 루프를 사용하지 않는 것이 좋습니다(스크립트는 예외로 Start 이벤트만 처리합니다).

대기열의 오버플로는 다중 통화 지표 또는 EA에 대한 중요한 이벤트의 손실로 이어질 수 있습니다. 이것은 한쪽에서입니다. 반면에 모든 기호에 대해 항상 틱이 필요한 것은 아닙니다. 때때로 우리는 모든 시간 프레임에서 "new bar" 이벤트만 얻어야 합니다. 또는 다른 시간대에 대한 여러 "새로운 바" 이벤트. 기본적으로 우리의 "스파이"는 이러한 요구 사항에 적합하지 않으며 사용이 매우 편리하지 않습니다.

다중 통화 EA 또는 지표의 기호를 기반으로 이벤트를 얻는 방법에 대한 질문으로 돌아갈 필요가 없도록 보편적으로 해봅시다. 이를 위해 다중 통화 전문가 자문 및 지표를 위한 "MCM 제어판" 설명의 ENUM_CHART_EVENT_SYMBOL 이벤트 열거를 샘플로 사용하겠습니다.

enum ENUM_CHART_EVENT_SYMBOL
  {
   CHARTEVENT_INIT      =0,         // "Initialization" event
   CHARTEVENT_NO        =0,         // No events

   CHARTEVENT_NEWBAR_M1 =0x00000001, // "New bar" event on M1 chart
   CHARTEVENT_NEWBAR_M2 =0x00000002, // "New bar" event on M2 chart
   CHARTEVENT_NEWBAR_M3 =0x00000004, // "New bar" event on M3 chart
   CHARTEVENT_NEWBAR_M4 =0x00000008, // "New bar" event on M4 chart
   
   CHARTEVENT_NEWBAR_M5 =0x00000010, // "New bar" event on M5 chart
   CHARTEVENT_NEWBAR_M6 =0x00000020, // "New bar" event on M6 chart
   CHARTEVENT_NEWBAR_M10=0x00000040, // "New bar" event on M10 chart
   CHARTEVENT_NEWBAR_M12=0x00000080, // "New bar" event on M12 chart
   
   CHARTEVENT_NEWBAR_M15=0x00000100, // "New bar" event on M15 chart
   CHARTEVENT_NEWBAR_M20=0x00000200, // "New bar" event on M20 chart
   CHARTEVENT_NEWBAR_M30=0x00000400, // "New bar" event on M30 chart
   CHARTEVENT_NEWBAR_H1 =0x00000800, // "New bar" event on H1 chart
   
   CHARTEVENT_NEWBAR_H2 =0x00001000, // "New bar" event on H2 chart
   CHARTEVENT_NEWBAR_H3 =0x00002000, // "New bar" event on H3 chart
   CHARTEVENT_NEWBAR_H4 =0x00004000, // "New bar" event on H4 chart
   CHARTEVENT_NEWBAR_H6 =0x00008000, // "New bar" event on H6 chart
   
   CHARTEVENT_NEWBAR_H8 =0x00010000, // "New bar" event on H8 chart
   CHARTEVENT_NEWBAR_H12=0x00020000, // "New bar" event on H12 chart
   CHARTEVENT_NEWBAR_D1 =0x00040000, // "New bar" event on D1 chart
   CHARTEVENT_NEWBAR_W1 =0x00080000, // "New bar" event on W1 chart
     
   CHARTEVENT_NEWBAR_MN1=0x00100000, // "New bar" event on MN1 chart
   CHARTEVENT_TICK      =0x00200000, // "New tick" event
   
   CHARTEVENT_ALL       =0xFFFFFFFF, // All events
  };

실제로 이 열거형은 사용자 지정 차트 이벤트의 플래그입니다. 다중 통화 모드에 필요할 수 있는 최소 세트입니다. 물론 보완할 수 있습니다. 플래그 조합은 "스파이"에서 보낼 이벤트를 결정합니다.

플래그는 비트 연산 "OR"을 사용하여 결합할 수 있습니다. 예를 들어, CHARTEVENT_NEWBAR_M1 | CHARTEVENT_NEWBAR_H1 조합은 "스파이"의 도움으로 분 및 시간 프레임에서 "새 바" 이벤트를 보낼 것임을 의미합니다. 이 플래그는 스파이 지표의 입력 매개변수가 됩니다. 더 나아가 우리는 그것을 "에이전트-지표"라고 부를 것입니다.

새로운 아이디어에 따라 지표 자체는 다음과 같습니다.

//+------------------------------------------------------------------+
//|                                        Spy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/en/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 3 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/en/users/Lizar"
#property version     VERSION
#property description "This is the MCM Control Panel agent-indicator."
#property description "Is launched on the required symbol on any time-frame"
#property description "and generates the custom NewBar event and/or NewTick"
#property description "for the chart which receives the event"

#property indicator_chart_window
  
input long                    chart_id;                 // identifier of the chart which receives the event
input ushort                  custom_event_id;          // event identifier  
input ENUM_CHART_EVENT_SYMBOL flag_event=CHARTEVENT_NO;// indicator, which determines the event type.

MqlDateTime time, prev_time;

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,       // size of the price[] array
                 const int prev_calculated, // bars processed at the previous call
                 const int begin,           // where the data begins
                 const double& price[]      // calculations array
   )
  {  
   double price_current=price[rates_total-1];

   TimeCurrent(time);
   
   if(prev_calculated==0)
     {
      EventCustom(CHARTEVENT_INIT,price_current);
      prev_time=time; 
      return(rates_total);
     }
   
//--- new tick
   if((flag_event & CHARTEVENT_TICK)!=0) EventCustom(CHARTEVENT_TICK,price_current);       

//--- check change time
   if(time.min==prev_time.min && 
      time.hour==prev_time.hour && 
      time.day==prev_time.day &&
      time.mon==prev_time.mon) return(rates_total);

//--- new minute
   if((flag_event & CHARTEVENT_NEWBAR_M1)!=0) EventCustom(CHARTEVENT_NEWBAR_M1,price_current);     
   if(time.min%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_M2)!=0)  EventCustom(CHARTEVENT_NEWBAR_M2,price_current);
   if(time.min%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_M3)!=0)  EventCustom(CHARTEVENT_NEWBAR_M3,price_current); 
   if(time.min%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_M4)!=0)  EventCustom(CHARTEVENT_NEWBAR_M4,price_current);      
   if(time.min%5 ==0 && (flag_event & CHARTEVENT_NEWBAR_M5)!=0)  EventCustom(CHARTEVENT_NEWBAR_M5,price_current);     
   if(time.min%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_M6)!=0)  EventCustom(CHARTEVENT_NEWBAR_M6,price_current);     
   if(time.min%10==0 && (flag_event & CHARTEVENT_NEWBAR_M10)!=0) EventCustom(CHARTEVENT_NEWBAR_M10,price_current);      
   if(time.min%12==0 && (flag_event & CHARTEVENT_NEWBAR_M12)!=0) EventCustom(CHARTEVENT_NEWBAR_M12,price_current);      
   if(time.min%15==0 && (flag_event & CHARTEVENT_NEWBAR_M15)!=0) EventCustom(CHARTEVENT_NEWBAR_M15,price_current);      
   if(time.min%20==0 && (flag_event & CHARTEVENT_NEWBAR_M20)!=0) EventCustom(CHARTEVENT_NEWBAR_M20,price_current);      
   if(time.min%30==0 && (flag_event & CHARTEVENT_NEWBAR_M30)!=0) EventCustom(CHARTEVENT_NEWBAR_M30,price_current);      
   if(time.min!=0) {prev_time=time; return(rates_total);}
//--- new hour
   if((flag_event & CHARTEVENT_NEWBAR_H1)!=0) EventCustom(CHARTEVENT_NEWBAR_H1,price_current);
   if(time.hour%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_H2)!=0)  EventCustom(CHARTEVENT_NEWBAR_H2,price_current);
   if(time.hour%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_H3)!=0)  EventCustom(CHARTEVENT_NEWBAR_H3,price_current);      
   if(time.hour%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_H4)!=0)  EventCustom(CHARTEVENT_NEWBAR_H4,price_current);      
   if(time.hour%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_H6)!=0)  EventCustom(CHARTEVENT_NEWBAR_H6,price_current);      
   if(time.hour%8 ==0 && (flag_event & CHARTEVENT_NEWBAR_H8)!=0)  EventCustom(CHARTEVENT_NEWBAR_H8,price_current);      
   if(time.hour%12==0 && (flag_event & CHARTEVENT_NEWBAR_H12)!=0) EventCustom(CHARTEVENT_NEWBAR_H12,price_current);      
   if(time.hour!=0) {prev_time=time; return(rates_total);}
//--- new day
   if((flag_event & CHARTEVENT_NEWBAR_D1)!=0) EventCustom(CHARTEVENT_NEWBAR_D1,price_current);      
//--- new week
   if(time.day_of_week==1 && (flag_event & CHARTEVENT_NEWBAR_W1)!=0) EventCustom(CHARTEVENT_NEWBAR_W1,price_current);      
//--- new month
   if(time.day==1 && (flag_event & CHARTEVENT_NEWBAR_MN1)!=0) EventCustom(CHARTEVENT_NEWBAR_MN1,price_current);      
   prev_time=time;
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

void EventCustom(ENUM_CHART_EVENT_SYMBOL event,double price)
  {
   EventChartCustom(chart_id,custom_event_id,(long)event,price,_Symbol);
   return;
  } 

이 지표는 이름을 바꾸지 않은 MCM 제어판의 일부이며 첨부 파일에는 단순히 업데이트된 버전이 포함되어 있습니다("스파이 제어판 MCM.mq5" 참조). 그러나 이것이 패널과 별도로 사용할 수 없다는 것을 의미하지는 않습니다.

이 에이전트 지표는 사용자 지정 사용자 이벤트를 생성하고 OnChartEvent() 함수를 사용하여 EA 또는 지표에서 이러한 이벤트의 추가 처리를 위해 차트 수신자에게 전송합니다. 이제 이 함수의 입력 매개변수는 다음과 같이 해석되어야 합니다.

  • id - 이벤트 식별자;
  • lparam - 패널 에이전트로부터 받은 이벤트 지표. ENUM_CHART_EVENT_SYMBOL 열거에 해당하는 지표.
  • dparam - 특정 기간 동안 새 바의 틱 가격 또는 시작 가격.
  • sparam - 이벤트가 발생한 거래 기호의 이름입니다.

 데모 EA는 이전 버전보다 복잡해 보이지 않습니다(전체 버전은 아카이브에서 사용 가능).

//+------------------------------------------------------------------+
//|                                      exSpy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/en/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (28 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/en/users/Lizar"
#property version     VERSION
#property description "The EA demonstrates the work of the MCM Spy Control panel"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),0,
             CHARTEVENT_NEWBAR_M1|CHARTEVENT_NEWBAR_M5)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),1,
             CHARTEVENT_NEWBAR_M2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"Spy Control panel MCM",ChartID(),2,
             CHARTEVENT_NEWBAR_M6)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY Control panel MCM indicator.                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,           // event identifier
                  const long&   lparam, // the event flag.
                                       // event flag is a bit mask of ENUM_CHART_EVENT_SYMBOL enumeration.
                  const double& dparam, // price
                  const string& sparam  // symbol 
                 )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",id-CHARTEVENT_CUSTOM,
           ":  ",sparam," ",EnumToString((ENUM_CHART_EVENT_SYMBOL)lparam)," price=",dparam);
     }
  }

exSpy 제어판 MCM의 작업 결과:


 

보시다시피 요청된 모든 이벤트를 정기적으로 수신합니다.

중간 결과를 다시 한 번 요약해 보겠습니다.

  • 에이전트 지표의 새 버전 덕분에 이전 성과를 모두 유지했습니다.
  • 이제 다중 통화 MQL 프로그램에서 "new tick", "new bar" 및 "initialization" 이벤트를 포함하여 최소 23개의 이벤트를 지정할 수 있습니다.
  • 에이전트를 언로드하기 위해 EA/지표에서 더 많은 작업이 에이전트에 제공됩니다. 

글쎄요, 결과적으로 MetaTrader 5에서 완전한 다중 통화 모드를 구현하는 것은 그리 어렵지 않습니다.

또한 몇 가지 뉘앙스를 강조하고 싶습니다.

첫 번째. 다중 통화 EA/지표에 대해 "에이전트"가 생성하는 모든 이벤트는 외부 이벤트입니다. 이와 관련하여 "EA/지표에서 직접 에이전트를 실행할 필요가 있습니까?"라는 질문이 발생합니다. 대답은 '아니오." 입니다.

두 번째. OnChartEvent() 함수에서 이벤트 식별자 id는 여분인 것 같습니다. 거래 기호의 이름인 sparam 매개변수에 의해 이벤트가 수신된 기호를 알 수 있기 때문입니다. 따라서 다른 용도로 사용할 수 있습니까? 대답은 "예, 할 수 있습니다."입니다. 

이러한 주장은 다중통화 전문가 자문 및 지표를 위한 "MCM 제어판"의 출현으로 이어졌습니다. 이것은 터미널과 EA/indicator 사이의 일종의 "중간층"입니다. 이를 통해 다중 통화 환경을 구성할 때 더 많은 이점과 유연성을 얻을 수 있었습니다.

  • 패널은 차트에 별도의 지표로 설치한 다음 패널과 호환되는 다중 통화 지표를 할당할 수 있습니다.
  • 패널은 구성 단위로 지표 및 EA를 포함할 수 있습니다. 이는 그것들과 함께 업로드됩니다.  
  • "이벤트" 메뉴를 사용하여 거래 또는 분석을 위해 "시장 감시" 창에서 기호를 활성화/비활성화할 수 있습니다. OnChartEvent() 함수에서 "시장 감시" 창에서 이벤트 식별자 ID로 기호의 일련 번호를 찾을 수 없습니다.
  • 틱 또는 "새 바" 이벤트로 거래 모드를 설정할 수 있으며 "시장 감시"에서 선택한 기호에 대해 설정할 수 있습니다. 이 모든 것은 일반 메뉴를 사용하여 수행됩니다
  • 언로드, 중지 또는 EA 또는 지표의 속성 창으로 이동하지 않고 위의 모든 구성을 변경할 수 있습니다.
  • 이 모든 것이 다중 통화 지표 및 EA 생성의 창의성 잠재력을 제한하지 않습니다. 또한 이제 이 패널의 출력을 코드에 통합할 필요가 없습니다. 에이전트 지표의 관리는 이제 제어판에 구현됩니다.
  • 다중 통화 시스템의 구조는 훨씬 간단합니다.


USDx 달러 인덱스에 대한 다중 통화 지표 RSI

위 방법의 모든 장점을 경험하기 위해 MCM 제어판을 사용하여 USDx 달러 지수에 대한 RSI 지표의 다중 통화 변형을 구현할 것을 제안합니다.

우선 몇 가지 특별한 기능에 주목하겠습니다. 종종 달러 지수를 분석할 때 단순히 지수 판독값의 지표를 계산합니다. 내 관점에서는 통화 쌍 인덱스의 모든 기호가 자체적으로 기여하기 때문에 이것이 완전히 정확하지는 않습니다. 따라서 예를 들어 지수 계산 공식과 유사한 공식으로 달러 지수에 대한 RSI를 계산해 보겠습니다.

즉, 먼저 특정 통화 쌍에 대한 RSI를 계산한 다음 계수의 가중치를 고려하여 지수에 대한 RSI를 읽습니다.

독자는 다중 통화 시스템에서 사용되는 모든 기호의 기록 데이터 동기화에 문제가 있음을 알 수 있습니다. ("기존 접근 방식의 개요" 섹션의 단락 2 참조).

이 문제는 RSI 동기화 버퍼(SynchronizedBufferRSI.mqh 파일)를 구성하기 위한 클래스 함수를 사용하여 지표에서 해결되었습니다. 수업의 전체 코드를 제공하는 것은 의미가 없으므로 아래에는 관련 순간만 제시됩니다. 

먼저 지표 버퍼는 public 액세스 수정자를 사용하여 클래스 내에서 정의됩니다.

public:
   double   buffer[];   // indicator buffer

둘째, 지표 초기화는 클래스 메소드를 사용하여 수행됩니다.

//--- Initialization methods:
bool Init(int n,string symbol,int rsi_count,int rsi_period);

셋째, 각 바에 대해 지표 버퍼의 값은 클래스의 새로 고침 방법을 사용하여 현재 시간 프레임과 동기화됩니다.

//+------------------------------------------------------------------+
//| The method of receiving/updating indicator data for one bar      |
//| of the indicator buffer.                                         |
//| INPUT:  bar   - bar number                                       |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CSynchronizedBufferRSI::Refresh(int bar=0)
  {
   buffer[bar]=EMPTY_VALUE; // Initialization of the bar of the indicator buffer.
     
   //--- Inquire the time of the bar for the current graph:
   datetime time[1];      
   if(CopyTime(_Symbol,_Period,bar,1,time)!=1) return; // In case of an error, we wait for the next tick/bar...

   //--- Request the value of the indicator for the symbol for the time,
   //--- consistent with that of the bar of the current graph:
   double value[1];
   if(CopyBuffer(m_handle,0,time[0],time[0],value)!=1) return; // In case of an error, wait for the next tick/bar...

   buffer[bar]=value[0];
   return;
  }

모든 지표 버퍼의 완전한 동기화를 위해 다음 문서에 설명된 대로 "구멍"이 없는 전체 분 시간 프레임을 사용해야 합니다. 그러나 이 지표 버퍼 동기화 방법의 경우 지표가 표시되기 때문에 현재 그래프의 시간 프레임을 특별히 선택했습니다.

내 경험에 비추어 볼 때, 기호가 현재 그래프의 기호와 다른 경우 시계열 또는 지표 버퍼에 대해 짧은 기간 동안 이러한 동기화 방법을 사용하는 것이 합리적이라고 말할 수 있습니다.

그래프는 이것이 가치가 있는 이유를 명확하게 보여줍니다.


 

더 큰 기간의 경우 이는 일반적으로 관찰되지 않습니다.

그리고 마지막으로 설명 드리지만 중요하지 않은 부분. 다음은 지표에 사용되는 표준 사용자 이벤트 핸들러의 코드입니다.

//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event identifier or position symbol in the "Market Match"+CHARTEVENT_CUSTOM  
                const long& lparam,   // event indicator
                const double& dparam, // price
                const string& sparam  // symbol
                )
  {
   int custom_id=id-CHARTEVENT_CUSTOM-1;
   
   if(custom_id>=0)      
     {
      if(lparam!=CHARTEVENT_NEWBAR_NO)
        { 
         //--- Recalculation of the last uncompleted bar:
         if(EventToPeriod(lparam)==_Period && sparam==_Symbol)
           { // Recalculation of the indicator, if a new bar on the current chart
            iRSIUSDx_Ind[0]=EMPTY_VALUE;
            //--- Updating the value of the RSI for all of the currency pairs for the new bar
            for(int i=0;i<symbol_total;i++) buffer[i].Refresh();
            iRSIUSDx(symbol_total);   // calculation of the current incomplete bar RSI for the index
            return;
           }
         
         buffer[custom_id].Refresh(); // The value of RSI for the custom_id of the currency pair for the current bar
         iRSIUSDx(symbol_total);      // calculation of the RSI for the current(uncompleted) bar RSIx
         return;
        }
      else 
        { 
         //--- Recalculation of the indicator for the "Initialization" event 
         buffer[custom_id].RefreshBuffer();     // Update of the RSI buffer for the custom_id of the currency pair
         Init_iRSIUSDx(symbol_total,calculate); // Update of the RSI buffer for the index
         return;
        }
     }
  }

코드의 특수 기능:

  • 현재 시간 프레임과 동기화된 지표 버퍼 RSI를 계산하기 위한 클래스 인스턴스에 대한 포인터를 포함하는 배열을 참조하기 위해 이벤트 식별자 id를 사용합니다. 이 접근 방식은 코드 구조를 크게 단순화합니다.
  • "초기화" 이벤트는 6개 기호 모두가 아니라 수신된 통화 쌍에 대해서만 지표 버퍼 RSI를 다시 계산하는 데 사용됩니다. 앞서 언급했듯이 이를 통해 예를 들어 하나의 기호에 대한 기록을 업데이트할 때 지표를 동기화할 수 있습니다.
  • "새 바" 이벤트는 현재 그래프의 새 바에 대한 모든 지표 버퍼 RSI를 동기화하는 데 사용됩니다.
  • 이벤트 "new tick"은 모든 통화 쌍에서 사용되어 마지막 불완전 바의 지표를 업데이트합니다. 또한 바의 재계산은 "새 틱"이 수신된 쌍에 대해서만 수행됩니다. 

달러 인덱스 USDx에 대한 RSI 지표의 전체 코드를 살펴보고 나면 이것이 어떻게 작동하는지 더 명확해질 것입니다.

설치 기능:

  • Multicurrency Expert Advisors용 "MCM Control Panel" 및 지표를 다운로드하고 "iControl panel MCM.mq5" 및 "Spy Control panel MCM.mq5" 파일을 컴파일하십시오.
  • "시장 일치" 창에서 다음 기호 순서를 지정하십시오.
    1. EURUSD
    2. USDJPY
    3. GBPUSD
    4. USDCAD
    5. USDSEK
    6. USDCHF
    이것은 내가 지표에 적절한 체크를 하지 않았기 때문에 필요한 것이고, 이 순서는 지표의 정확한 계산을 위해 필요합니다.
  • iRSIUSDx.zip 아카이브를 /MQL5 폴더에 압축을 풉니다. /MQL5/Indicators/iRSIUSDx/ 폴더의 iRSIUSDx.ex5를 M1 기간이 있는 EURUSD 차트에 첨부합니다.
  • 순차적으로 "제어판 MCM" 패널의 "이벤트" 메뉴에 있는 6개 기호 모두에 대해 여기에 설명된 대로 이벤트 "새 눈금"을 설정합니다. 위의 그림과 비슷한 그림을 얻어야 합니다.
  • 또한 EURUSD 기호의 경우 분 차트에서 "새 바" 이벤트를 설정합니다. 지표에서 이 이벤트는 현재 시간 프레임의 새 바가 M1과 같을 때 동기화에 사용됩니다.
  • 더 시각적인 예를 보려면 여기에 설명된 대로 달러 지수를 설정하세요.

결론

논의된 MetaTrader 5의 완전한 다중 통화 모드 구현은 이 문제를 해결하는 데 있어 플랫폼과 MQL5 프로그래밍 언어의 장점을 충분히 보여줍니다. 이전에 대부분의 어려움을 일으켰던 것이 이제 사용할 수 있게 되었습니다.

분명히 이것은 이 방향으로의 움직임의 시작일 뿐입니다. 물론 더 나은 데이터 동기화, 다중 통화 모드 관리 등의 방법에 대한 더 많은 옵션이 있을 것입니다. 하지만 이제 필요한 도구가 모두 갖춰져 있다는 것이 분명해지기를 바랍니다.


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

파일 첨부됨 |
irsiusdx_en.zip (7.54 KB)
NeuroSolutions Neuronet 연결 NeuroSolutions Neuronet 연결
Neuronet 생성 외에도 NeuroSolutions 소프트웨어 제품군을 사용하면 이를 DLL로 내보낼 수 있습니다. 이 글은 뉴로넷 (Neuronet) 생성, DLL 생성 및 MetaTrader 5 거래를 위해 Expert Advisor에 연결하는 과정을 설명합니다.
MQL5 마법사: 미결 포지션의 후행 모듈을 만드는 방법 MQL5 마법사: 미결 포지션의 후행 모듈을 만드는 방법
거래 전략 생성기 MQL5 Wizard는 거래 아이디어 테스트를 크게 단순화합니다. 이 글에서는 거래 시 가격이 포지션 방향으로 이동할 때 손절매 수준을 무손실 영역으로 이동하여 오픈 포지션을 관리하는 MQL5 Wizard 자신의 클래스를 작성하고 거래 전략 생성기에 연결하여 이익 감소 드로다운을 보호할 수 있는 방법에 대해 설명합니다. 또한 MQL5 마법사에 대해 생성된 클래스 설명의 구조 및 형식에 대해서도 알려줍니다.
움직이는 Mini-Max: MQL5의 기술적 분석 및 구현을 위한 새로운 지표 움직이는 Mini-Max: MQL5의 기술적 분석 및 구현을 위한 새로운 지표
다음 글에서는 Z.G.Silagadze의 논문 'Moving Mini-max: 기술 분석을 위한 새로운 지표'를 기반으로 Moving Mini-Max 지표를 구현하는 과정을 설명합니다. 지표의 아이디어는 알파 붕괴 이론에서 G. Gamov가 제안한 양자 터널링 현상의 시뮬레이션을 기반으로 합니다.
MQL5 마법사: 위험 및 자금 관리 모듈을 만드는 방법 MQL5 마법사: 위험 및 자금 관리 모듈을 만드는 방법
MQL5 Wizard의 거래 전략 생성기는 거래 아이디어 테스트를 크게 단순화합니다. 이 문서에서는 맞춤형 위험 및 자금 관리 모듈을 개발하고 MQL5 마법사에서 활성화하는 방법을 설명합니다. 예를 들어 우리는 거래량의 크기가 이전 거래의 결과에 따라 결정되는 자금 관리 알고리즘을 고려했습니다. MQL5 마법사용으로 생성된 클래스의 설명 구조 및 형식도 이 글에서 설명합니다.