English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
MetaTrader 5에서의 거래 이벤트

MetaTrader 5에서의 거래 이벤트

MetaTrader 5트레이딩 | 5 7월 2021, 10:28
57 0
MetaQuotes
MetaQuotes

소개

거래 작업을 수행하기 위한 모든 명령은 전송 요청을 통해 MetaTrader 5 클라이언트 터미널에서 거래 서버로 전달됩니다. 각 요청과 관련된 사항은 요청된 작업에 따라 올바르게 입력되어야 합니다. 그렇지 않으면 기본 유효성 검사를 통과하지 못해 서버에서 추가적으로 처리되지 못하기 때문입니다.

거래 서버가 수락한 요청은 보류 중이거나 시장 가격에 의해 즉시 실행될 수 있는 주문 형태로 저장됩니다. 주문이 작성되거나 취소될 때까지 서버에 저장됩니다. 주문 실행의 결과는 거래입니다.

거래는 주어진 기호에 의해 거래 포지션을 변경하며, 오픈, 클로즈, 증가, 감소 또는 포지션을 뒤집을 수도 있습니다. 따라서 오픈 포지션은 항상 하나 이상의 거래를 수행한 결과라고 볼 수 있습니다. 자세한 내용은 MetaTrader5에서의 주문, 포지션 및 거래라는 글에서 자세히 알아보시기 바랍니다.

이 자료에는 거래 내역상 거래가 처리된 후 요청 발송부터 거래 이동이 이루어지기까지 해당 기간 내에서 살펴볼 수 있는 개념, 용어 및 프로세스에 대해 기술하고 있습니다.


클라이언트 터미널에서 거래 서버로 요청 전달

거래 업무를 수행하려면 거래 시스템에 주문을 보내야 합니다. 요청은 항상 클라이언트 터미널에서 주문을 제출하는 방식으로 거래 서버로 전송됩니다. 수동으로하든 MQL5 프로그램을 사용하든지 거래 방법이 뭐든지 간에 요청의 구조가 올바르게 입력되어야 합니다.

거래 작업을 수동으로 수행하려면, 거래 요청을 입력하는 대화창F9 키를 눌러 열어야 합니다. MQL5를 통해 자동으로 거래되는 경우, 해당 요청은 OrderSend() 함수를 사용하여 전송됩니다. 잘못된 요청이 많으면 거래 서버에 불필요한 오버로드가 발생할 수 있으므로, 각 요청은 OrderCheck() 함수를 사용하여 전송되기 전에 확인되어야 합니다. 요청 확인 결과는 MqlTradeCheckresult 구조에서 설명이 가능한 변수에 배치됩니다.

중요: 거래 서버로 전송되기 전에 클라이언트 터미널에서 각각의 요청이 올바른지 확인해야 합니다. 고의적으로 잘못된 요청(100만 랏 구매 또는 마이너스 가격으로 구매)은 클라이언트 터미널 외부로 전달되지 않습니다.. 이는 MQL5 프로그램의 실수로 인한 부정확한 대량의 요청들로부터 거래 서버를 보호하기 위해 행해집니다.

요청이 거래 서버에 도착하면 기본 검사를 통과하게 됩니다:

  • 거래를 수행하기에 충분한 자산이 있는지 여부.
  • 지정된 가격이 올바른지 여부 : 시가, 손절매, 이익 실현 등
  • 즉시 실행을 위해 지정된 가격이 가격 흐름에 있는지 여부
  • 시장가 실행 모드에서 손절매 및 이익 실현 레벨이 없는지 여부.
  • 거래량이 올바른지 여부 : 최소 및 최대 거래량, 단계, 포지션의 최대 거래량 (SYMBOL_VOLUME_MIN, SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_STEP 및 SYMBOL_VOLUME_LIMIT).
  • 기호 상태 : 호가 또는 거래 세션, 기호에 의한 거래 가능성, 특정 거래 모드 (예 : 오직 포지션 마감만) 등.
  • 거래 계정 상태: 특정 유형의 계정에 대한 다른 제한.
  • 요청된 거래 운영에 따라 기타 수표.

서버에서 기본 검사를 통과하지 못한 잘못된 요청은 거부됩니다. 클라이언트 터미널은 항상 응답을 전송하여 요청 확인 결과에 대해 알려줍니다. 거래 서버의 응답은 MqlTradeResult 유형의 변수에서 가져올 수 있으며, 요청을 보낼 때 OrderSend() 함수에서 두 번째 매개 변수로 전달됩니다.

클라이언트 터미널에서 거래 서버로 거래 요청 보내기

요청이 기본 유효성 검사를 통과하면 처리 대기중인 요청에 배치됩니다. 요청 처리 결과, 거래 서버베이스에 주문 (거래 작업 수행 명령)이 생성됩니다. 그러나 주문이 생성되지 못하는 다음의 두 가지 유형의 요청이 있습니다:

  1. 포지션 변경 요청 (손절매 및 / 또는 이익 실현).
  2. 보류중인 주문 (가격 수준 및 만료 시간)을 수정하기 위한 요청.

클라이언트 터미널은 요청이 수락되고 MetaTrader 5 플랫폼의 거래 하위 시스템에 배치된다는 메시지를 수신하게 됩니다. 서버는 승인된 요청을 추가적으로 처리하기 위해 이를 요청 대기열에 배치하며 이에 따라 다음과 같은 결과가 초래될 수 있습니다.

  • 보류중인 주문을 합니다.
  • 시장 가격에 의한 즉석 주문 실행.
  • 주문 또는 포지션 수정.

서버 대기열의 요청 시간은 3 분으로 제한됩니다. 기간이 초과되면 요청이 해당 대기열에서 제거됩니다.


거래 서버에서 클라이언트 터미널로 거래 이벤트 보내기

이벤트 모델 및 이벤트 처리 기능은 MQL5 언어로 구현됩니다. 이는 사전 정의된 이벤트에 대한 응답에서 MQL5 실행 환경이 적절한 함수인 이벤트 핸들러를 호출함을 의미합니다. 거래 이벤트 처리를 위해 미리 정의된 함수 OnTrade()가 있습니다. 주문, 포지션 및 거래 작업을위한 코드가 그 안에 있어야 합니다. 이 함수는 Expert Advisors에 대해서만 호출되며 동일한 이름과 유형의 함수를 추가하더라도 인디케이터 및 스크립트에서 사용되지 않습니다.

거래 이벤트는 다음과 같은 경우 서버에서 생성됩니다.

  • 활성 주문 변경,
  • 포지션 변경,
  • 거래 변경,
  • 거래 내역의 변화.

하나의 작업으로 인해 여러 이벤트가 발생할 수 있습니다. 예를 들어, 보류중인 주문을 트리거하면 다음 두 가지 이벤트가 발생합니다.

  1. 거래 내역에 기록된 거래가 나타납니다.
  2. 보류중인 주문을 활성 주문 목록에서 주문 내역으로 이동 (주문이 내역으로 이동).

여러 이벤트의 또 다른 예는 단일 반대 오퍼에서 필요한 거래량 얻을 수 없는 경우 단일 주문을 기준으로 여러 거래를 수행하는 것입니다. 거래 서버는 각 이벤트에 대한 메시지를 생성하여 클라이언트 터미널로 보냅니다. 그런 고로 이는 겉보기에 하나로 보이는 이벤트에 대해서 OnTrade() 함수를 여러 번 호출 할 수 있는 것입니다. 다음은 MetaTrader 5 플랫폼의 거래 하위 시스템에서 주문 처리 절차의 간단한 예입니다.

예를 들면 다음과 같습니다. EURUSD 10 랏을 매수하기위한 보류 주문이 실행되기를 기다리는 동안 1, 4, 5 랏 매도에 대한 반대 오퍼가 나타납니다. 이 세 가지 요청은 동시에 10개의 랏이라는 필요 거래량을 제공하게 되므로 주문체결정책이 부분적으로 거래 작업을 수행 할 수있는 경우 하나씩 실행됩니다.

4개의 주문을 실행한 결과 서버는 기존의 반대 요청을 기반으로 1, 4, 5 랏의 3건의 거래를 수행하게 됩니다. 이 경우 얼마나 많은 거래 이벤트가 생성됩니까? 첫 번째 반대 요청은 1 랏의 거래로 이어질 것입니다. 이것이 첫 번째 거래 이벤트입니다 (1 랏 거래). 그러나 10 랏 구매를 위한 대기 주문도 변경되었습니다. 즉, 이제 EURUSD 9 랏을 매수하는 주문이 되는 것입니다. 보류중인 주문의 거래량 변경이 두 번째 거래 이벤트 (보류중인 주문의 거래량 변경)입니다.

거래 이벤트 생성

4 랏의 두 번째 거래에 대해 다른 두 개의 거래 이벤트가 생성되고, 각각에 대한 메시지가 10 랏의 EURUSD 구매를 위한 초기 대기 주문을 시작한 클라이언트 터미널로 전송됩니다.

5 랏의 마지막 거래에 대해서는 세 가지 거래 이벤트가 이어집니다.

  1. 5 랏의 거래,
  2. 볼륨의 변화,
  3. 주문을 거래 내역으로 이동.

거래 실행 결과, 클라이언트 터미널은 7개의 거래 이벤트 거래 를 차례로 수신합니다 (클라이언트 터미널과 거래 서버 간의 연결이 안정적이며 메시지가 손실되지 않는다고 가정). 이러한 메시지는 OnTrade() 함수를 사용하여 Expert Advisor에서 처리해야 합니다.

중요: 거래 이벤트인 Trade에 관한 메시지는 하나 또는 여러 요청의 결과로 나타날 수 있습니다. 각 요청은 여러 거래 이벤트 발생으로 이어질 수 있습니다. 이벤트 처리는 여러 단계에서 수행 될 수 있으며 각 작업은 주문 상태, 포지션 및 거래 내역을 변경할 수 있으므로 "하나의 요청-하나의 거래 이벤트"라는 문구를 신뢰해서는 안됩니다.


거래 서버 주문 처리

실행을 기다리는 모든 주문은 마지막에 내역으로 이동됩니다. 여기서 주문은 조건을 충족하여 실행되거나 그렇지 못해 취소되게 됩니다. 주문 취소에는 여러 가지 변형이 있습니다.

  • 주문에 따라 거래를 수행.
  • 딜러에 의한 주문 거부.
  • 트레이더의 요구에 따른 주문 취소 (MQL5 프로그램의 수동 요청 또는 자동 요청).
  • 주문 만료, 요청을 보낼 때 트레이더가 결정하거나 주어진 거래 시스템의 거래 조건에 의해 결정됨.
  • 실행 조건이 충족되는 순간 거래를 수행하기 위한 거래 계정에 자산이 부족함.
  • 주문체결정책으로 인해 주문이 취소됨 (부분적으로 채워진 주문이 취소됨).

활성 주문이 이력으로 이동된 이유가 뭐든지 간에 이와는 상관없이 변경 메시지가 클라이언트 터미널로 전송됩니다. 거래 이벤트에 대한 메시지는 모든 클라이언트 터미널에 전달되지 않고 해당 계정이 연결된 터미널에 전달됩니다.


중요: 거래 서버의 요청 수락 사실이 항상 요청된 작업을 실행하는 것은 아닙니다. 이는 요청이 거래 서버에 도착한 후 유효성 검사를 통과했음을 의미합니다.

이것이 OrderSend() 함수의 문서에 다음과 같이 나와있는 이유입니다.

반환 값

요청에 대한 기본 확인이 성공한 경우 OrderSend() 함수는 참을 반환하게 되는데 이것은 거래 작업이 성공적으로 실행되었다는 신호가 아닙니다. 함수 실행 결과에 대한 자세한 설명은 구조 MqlTradeResult 의 필드를 분석을 통해 더 자세히 알아보십시오.


클라이언트 터미널에서 거래 및 내역 업데이트

거래 이벤트 및 거래 내역 변경에 대한 메시지는 별도의 채널을 통해 전달됩니다. OrderSend() 함수를 사용하여 구매 요청을 보낼 때, 요청이 성공적으로 검증된 결과를 통해 생성되는 주문 티켓을 얻을 수 있습니다. 동시에 주문 자체가 클라이언트 터미널에 나타나지 않을 수 있으며 OrderSelect()를 사용하여 주문을 선택하려는 시도가 실패 할 수 있습니다.

거래 서버의 모든 메시지는 독립적으로 클라이언트 터미널에 도착합니다.

위의 그림에서 거래 서버가 MQL5 프로그램에 주문 티켓을 전달하는 방법을 볼 수 있지만 거래 이벤트인 Trade (새 주문이 나타남)에 대한 메시지가 아직 도착하지 않았습니다. 활성 주문 목록 변경에 대한 메시지도 도착하지 않았습니다.

새로운 주문의 출현에 대한 거래 메시지가 이미 그것의 기초에 의해 거래가 행해진 후에 프로그램에 도착하여 주문이 활성 주문 목록이 아닌 내역에 보이는 경우가 생길 수 있습니다. 네트워크를 통해 메시지를 전달하는 현재 속도에 비해 요청 처리 속도가 훨씬 빠르기 때문에 이는 실제로 일어날 수 있는 상황입니다.


MQL5에서 거래 이벤트 처리

거래 서버의 모든 작업과 거래 이벤트에 대한 메시지 전송은 비대칭적으로 수행됩니다. 거래 계좌에서 정확히 무엇이 변경되었는지 확인하는 확실한 방법은 하나뿐입니다. 이 방법은 거래 상태와 거래 내역을 기억한 후 새로운 상태와 비교하는 것입니다.

Expert Advisors에서 거래 이벤트를 추적하는 알고리즘은 다음과 같습니다.

  1. 글로벌 범위에 대한 주문, 포지션 및 거래 카운터를 선언합니다.
  2. MQL5 프로그램 캐시에 요청 될 거래 내역의 깊이를 결정합니다. 캐시에 더 많은 내역을 로드할수록 터미널과 컴퓨터의 리소스가 더 많이 소비됩니다.
  3. OnInit 함수에서 주문, 포지션 및 거래의 카운터를 초기화합니다.
  4. 캐시에 거래 내역을 요청할 핸들러 기능을 결정합니다.
  5. 여기에서 거래 내역을 로드한 후 기억하고 있는 상태와 현재 상태를 비교하여 거래 계정에 무슨 일이 있었는지 알아볼 것입니다.

이것은 가장 간단한 알고리즘으로, 오픈 포지션 (주문, 거래)의 수가 변경되었는지 여부와 변경 방향을 알아낼 수 있습니다. 변경 사항이 있으면 더 자세한 정보를 얻을 수 있습니다. 주문 수가 변경되지 않았지만 주문 자체가 수정되면 다른 접근 방식이 필요합니다. 따라서 이 변형은 이번 글에선 다루지 않습니다.

카운터 변경은 Expert Advisor의 OnTrade()OnTick() 기능에서 확인할 수 있습니다.

단계별로 프로그램 예제를 작성해 봅시다.

1. 글로벌 범위의 주문, 거래 및 포지션 카운터입니다.

int          orders;            // number of active orders
int          positions;         // number of open positions
int          deals;             // number of deals in the trade history cache
int          history_orders;    // number of orders in the trade history cache
bool         started=false;     // flag of initialization of the counters

2. 캐시에 로드될 거래 내역의 깊이는 입력 변수 (이 변수에 지정된 일수 동안 거래 내역을 로드)로 설정됩니다.

input    int days=7;            // depth of the trade history in days

//--- set the limit of the trade history on the global scope
datetime     start;             // start date of the trade history in cache
datetime     end;               // end date of the trade history in cache

3. 카운터 초기화 및 거래 내역의 한계. 코드의 가독성을 높이기 위해 카운터 초기화를 InitCounters() 함수로 가져갑니다. <부분 0166>

int OnInit()
  {
//---
   end=TimeCurrent();
   start=end-days*PeriodSeconds(PERIOD_D1);
   PrintFormat("Limits of the trade history to be loaded: start - %s, end - %s",
               TimeToString(start),TimeToString(end));
   InitCounters();
//---
   return(0);
  }

InitCounters() 함수는 캐시에 거래 내역을 로드하려고 시도하고 성공시 모든 카운터를 초기화합니다. 또한 히스토리가 성공적으로 로드되면 글로벌 변수 'started'의 값이 'true'로 설정되어 카운터가 성공적으로 초기화되었음을 나타냅니다.

//+------------------------------------------------------------------+
//|  initialization of the counters of positions, orders and deals   |
//+------------------------------------------------------------------+
void InitCounters()
  {
   ResetLastError();
//--- load history
   bool selected=HistorySelect(start,end);
   if(!selected)
     {
      PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d",
                  __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError());
      return;
     }
//--- get current values
   orders=OrdersTotal();
   positions=PositionsTotal();
   deals=HistoryDealsTotal();
   history_orders=HistoryOrdersTotal();
   started=true;
   Print("The counters of orders, positions and deals are successfully initialized");
  }

4. OnTick() 및 OnTrade() 핸들러에서 거래 계좌 상태 변경 확인을 수행합니다. 'started' 변수가 먼저 확인됩니다. 값이 'true'이면 SimpleTradeProcessor() 함수가 호출되고, 그렇지 않으면 카운터 초기화 함수 InitCounters()가 호출됩니다.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(started) SimpleTradeProcessor();
   else InitCounters();
  }
//+------------------------------------------------------------------+
//| called when the Trade event occurs                               |
//+------------------------------------------------------------------+
void OnTrade()
  {
   if(started) SimpleTradeProcessor();
   else InitCounters();
  }

5. SimpleTradeProcessor() 함수는 주문, 거래 및 포지션 수가 변경되었는지 여부를 확인합니다. 모든 검사를 수행한 후 필요한 경우 'start'값을 현재에 더 가깝게 CheckStartDateInTradeHistory() 함수를 호출합니다.

//+------------------------------------------------------------------+
//| simple example of processing changes in trade and history        |
//+------------------------------------------------------------------+
void SimpleTradeProcessor()
  {
   end=TimeCurrent();
   ResetLastError();
//--- load history
   bool selected=HistorySelect(start,end);
   if(!selected)
     {
      PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d",
                  __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError());
      return;
     }

//--- get current values
   int curr_orders=OrdersTotal();
   int curr_positions=PositionsTotal();
   int curr_deals=HistoryDealsTotal();
   int curr_history_orders=HistoryOrdersTotal();

//--- check whether the number of active orders has been changed
   if(curr_orders!=orders)
     {
      //--- number of active orders is changed
      PrintFormat("Number of orders has been changed. Previous number is %d, current number is %d",
                  orders,curr_orders);
     /*
       other actions connected with changes of orders
     */
      //--- update value
      orders=curr_orders;
     }

//--- change in the number of open positions
   if(curr_positions!=positions)
     {
      //--- number of open positions has been changed
      PrintFormat("Number of positions has been changed. Previous number is %d, current number is %d",
                  positions,curr_positions);
      /*
      other actions connected with changes of positions
      */
      //--- update value
      positions=curr_positions;
     }

//--- change in the number of deals in the trade history cache
   if(curr_deals!=deals)
     {
      //--- number of deals in the trade history cache has been changed
      PrintFormat("Number of deals has been changed. Previous number is %d, current number is %d",
                  deals,curr_deals);
      /*
       other actions connected with change of the number of deals
       */
      //--- update value
      deals=curr_deals;
     }

//--- change in the number of history orders in the trade history cache
   if(curr_history_orders!=history_orders)
     {
      //--- the number of history orders in the trade history cache has been changed
      PrintFormat("Number of orders in the history has been changed. Previous number is %d, current number is %d",
                  history_orders,curr_history_orders);
     /*
       other actions connected with change of the number of order in the trade history cache
      */
     //--- update value
     history_orders=curr_history_orders;
     }
//--- check whether it is necessary to change the limits of trade history to be requested in cache
   CheckStartDateInTradeHistory();
  }

CheckStartDateInTradeHistory() 함수는 현재 순간 (curr_start)에 대한 거래 내역 요청 시작일을 계산하여 'start' 변수와 비교합니다. 그 차이가 1일 이상이면 'start'가 수정되고 주문 내역 및 거래 내역의 카운터가 업데이트됩니다.

//+------------------------------------------------------------------+
//|  Changing start date for the request of trade history            |
//+------------------------------------------------------------------+
void CheckStartDateInTradeHistory()
  {
//--- initial interval, as if we started working right now
   datetime curr_start=TimeCurrent()-days*PeriodSeconds(PERIOD_D1);
//--- make sure that the start limit of the trade history period has not gone 
//--- more than 1 day over intended date
   if(curr_start-start>PeriodSeconds(PERIOD_D1))
     {
      //--- we need to correct the date of start of history loaded in the cache
      start=curr_start;
      PrintFormat("New start limit of the trade history to be loaded: start => %s",
                  TimeToString(start));

      //--- now load the trade history for the corrected interval again
      HistorySelect(start,end);

      //--- correct the counters of deals and orders in the history for further comparison
      history_orders=HistoryOrdersTotal();
      deals=HistoryDealsTotal();
     }
  }

Expert Advisordml DemoTradeEventProcessing.mq5의 전체 코드가 이 글에 첨부되어 있습니다.


결론

온라인 거래 플랫폼 MetaTrader 5의 모든 작업은 비동기적으로 수행되며 거래 계정의 모든 변경 사항에 대한 메시지는 서로 독립적으로 전송됩니다. 따라서 "하나의 요청-하나의 거래 이벤트"라는 규칙을 기반으로 단일 이벤트를 추적하는 데는 아무런 의미가 없습니다. 거래가 발생했을 때 무엇이 변경되었는지 정확하게 파악해야 할 경우, 모든 거래, 포지션 및 OnTrade 핸들러의 각 호출에서 현재 상태를 이전 상태와 비교하여 주문합니다.

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

MetaTrader 5 테스트의 기초 MetaTrader 5 테스트의 기초
MetaTrader 5의 세 가지 테스트 모드의 차이점은 무엇이며 특히 무엇을 찾아야 합니까? 여러 상품에서 동시에 거래되는 EA 테스트는 어떻게 이루어 집니까? 테스트 중 지표 ​​값은 언제 어떻게 계산되며 이벤트는 어떻게 처리됩니까? "오픈 프라이스 전용" 모드에서 테스트하는 동안 다른 기기의 바를 동기화하는 방법은 무엇입니까? 이 글에서 저희는 이러한 질문과 다른 많은 질문에 대한 답변을 제공하는 것을 목표로 합니다.
매매봇 프로토타입 매매봇 프로토타입
본 문서는 매매 시스템의 알고리즘과 요소들을 만드는 원리를 요약하고 체계화합니다. 본 문서는 익스퍼트 알고리즘 디자인을 다룹니다. 예시로서 빠르고 손쉬운 매매 시스템에 쓰일 수 있는 CExpertAdvisor 클래스가 사용될 것입니다.
MQL5에서 리소스 사용 MQL5에서 리소스 사용
MQL5 프로그램은 일상적인 계산을 자동화 할 뿐만 아니라 완전한 기능을 갖춘 그래픽 환경을 만들어 주기도 합니다. 진정한 인터랙티브 컨트롤을 만드는 기능은 이제 고전적인 프로그래밍 언어의 기능과 거의 동일합니다. MQL5에서 본격적인 독립 실행형 프로그램을 작성하려면 리소스를 사용하십시오. 리소스가 있는 프로그램은 유지 관리 및 배포가 더 쉽습니다.
20 MQL5에서의 매매 신호들 20 MQL5에서의 매매 신호들
본 문서는 매매시스템이 필요한 매매 신호를 어떻게 받는지 가르쳐줄 것입니다. 이 문서에서 다룰 20개의 매매 신호를 만드는 예시는 Expert Advisor 개발에 쓸 수 있는 별도의 커스텀 함수로 되어있습니다. 편의를 위해서, 본 문서에서 사용된 모든 함수는 미래에 Exper Advisor에 손쉽게 연결할 수 있도록 하나의 mqh include 파일에 들어 있습니다.