![HedgeTerminal API를 사용한 MetaTrader 5의 양방향 거래 및 포지션 헤지, 파트 2](https://c.mql5.com/i/articles/overlay.png)
HedgeTerminal API를 사용한 MetaTrader 5의 양방향 거래 및 포지션 헤지, 파트 2
목차
- 소개
- 1장. HEDGE TERMINAL API 및 해당 패널과 EXPERT ADVISOR의 상호 작용
- 제 2 장. HEDGE TERMINAL API 매뉴얼
- 3 장. 비동기식 거래의 기본
- 4장. METATRADER 5 IDE에서 멀티 스레드 프로그래밍의 기초
- 첨부 설명
- 결론
소개
이 글은 첫 번째 파트 "MetaTrader 5에서 HedgeTerminal 패널을 사용한 포지션의 양방향 거래 및 헤징, 파트 1"의 연속입니다. 두 번째 부분에서는 Expert Advisors 및 기타 MQL5 프로그램과 HedgeTerminalAPI 라이브러리의 통합에 대해 설명합니다. 이 문서를 읽고 라이브러리를 사용하는 방법을 알아보세요. 편안하고 간단한 거래 환경에서 작업하면서 양방향 거래 Expert Advisors를 만드는 데 도움이 됩니다.
라이브러리 설명 외에도 이 글에서는 비동기 거래 및 다중 스레드 프로그래밍의 기본 사항을 다룹니다. 이러한 설명은 이 문서의 세 번째 및 네 번째 섹션에 나와 있습니다. 따라서 이 자료는 양방향 거래에 관심이 없지만 비동기 및 다중 스레드 프로그래밍에 대해 새로운 것을 찾고자 하는 거래자에게 유용할 것입니다.
아래에 제시된 자료는 MQL5 프로그래밍 언어를 알고 있는 숙련된 알고리즘 거래자를 위한 것입니다. MQL5를 모른다면 라이브러리와 HedgeTerminal 패널의 일반 원리를 설명하는 간단한 다이어그램과 그림이 포함된 글의 첫 번째 부분을 읽어보세요.
1장. Expert Advisor과 HedgeTerminal 및 해당 패널의 상호 작용
1.1. HedgeTermianlAPI 설치. 라이브러리의 첫 시작
HedgeTerminalAPI 설치 프로세스는 MetaTrader 5에서 라이브러리를 단독으로 실행할 수 없기 때문에 HT 비주얼 패널 설치와 다릅니다. 대신 라이브러리에서 HedgeTerminalInstall() 함수를 호출하기 위해 특별한 Expert Advisor를 개발해야 합니다. 이 함수는 HedgeTerminalAPI에서 사용 가능한 함수를 설명하는 특수 헤더 파일 Prototypes.mqh를 설정합니다.
라이브러리는 다음 세 단계로 컴퓨터에 설치됩니다.
1단계. 컴퓨터에 HedgeTerminalAPI 라이브러리를 다운로드합니다. 터미널을 기준으로 한 라이브러리 위치: \MQL5\Experts\Market\HedgeTerminalApi.ex5.
2단계. 표준 템플릿을 사용하여 MQL5 마법사에서 새로운 Expert Advisor를 만듭니다. MQL5 마법사는 다음 소스 코드를 생성합니다.
//+------------------------------------------------------------------+ //| InstallHedgeTerminalExpert.mq5 | //| Copyright 2015, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- }-->
3단계. 결과로 생성된 Expert Advisor의 기능 - OnInit() 및 특수 설치 프로그램 기능을 설명하는 내보내기 지시문 HedgeTerminalInstall만 있으면 됩니다. () HedgeTerminalApi 라이브러리에서 내보냅니다. OnInit() 함수에서 바로 이 함수를 실행하십시오. 노란색으로 표시된 소스 코드는 다음 작업을 수행합니다.
//+------------------------------------------------------------------+ //| InstallHedgeTerminalExpert.mq5 | //| Copyright 2015, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #import "HedgeTerminalAPI.ex5" void HedgeTerminalInstall(void); #import //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- HedgeTerminalInstall(); ExpertRemove(); //--- return(INIT_SUCCEEDED); }-->
4단계. 추가 조치는 라이브러리 구매 여부에 따라 다릅니다. 구매하셨다면 차트에서 바로 Expert Advisor를 실시간으로 실행할 수 있습니다. 이것은 HedgeTerminal의 전체 제품 라인의 표준 설치 프로그램을 시작합니다. "MetaTrader 5에서 HedgeTerminal 패널을 사용한 포지션의 양방향 거래 및 헤징" 글의 섹션 2.1 및 2.2에 설명된 지침을 따르면 이 단계를 쉽게 완료할 수 있습니다. 설치 마법사는 헤더 파일과 테스트 Expert Advisor를 포함한 모든 필수 파일을 컴퓨터에 설치합니다.
라이브러리를 구매하지 않고 테스트만 하고 싶다면 실시간 EA 운용은 불가능하지만 전략 테스터에서 EA를 실행하여 API를 테스트할 수 있습니다. 이 경우 설치 프로그램이 실행되지 않습니다. 테스트 모드에서 HedgeTermianalAPI는 단일 사용자 모드에서 작동하므로 일반 모드에서 파일을 설치할 필요가 없습니다. 즉, 다른 것을 구성할 필요가 없습니다.
EA 테스트가 완료되는 즉시 터미널의 공통 폴더에 \HedgeTerminal 폴더가 생성됩니다. MetaTrader 터미널의 공통 디렉토리에 대한 일반 경로는 c:\Users\<사용자 이름>\AppData\Roaming\MetaQuotes\Terminal\Common\Files\HedgeTerminal\입니다. 여기서 <Username>은 현재 컴퓨터 계정의 이름입니다. \HedgeTerminal 폴더에는 이미 \MQL5\Include\Prototypes.mqh 및 \MQL5\Experts\Chaos2.mq5 파일이 있습니다. 다음 파일을 터미널의 동일한 디렉토리에 복사하십시오: Prototypes.mqh 파일을 \MetaTrader5\MQL5\Include로, Chaos2.mq5 파일을 \MetaTrader5\MQL5\Experts로.
Prototypes.mqh 파일은 HedgeTerminalAPI 라이브러리에서 내보낸 함수에 대한 설명이 포함된 헤더 파일입니다. 그들의 목적과 설명은 그들에 대한 주석에 포함되어 있습니다.
파일 Chaos2.mq5에는 "Chaos II EA의 예를 통한 SendTradeRequest 기능 및 HedgeTradeRequest 구조" 섹션에 설명된 샘플 EA가 포함되어 있습니다. 이를 통해 HedgeTerminalAPI의 작동 방식과 HedgeTerminal의 가상화 기술을 활용하여 Expert Advisor를 개발하는 방법을 시각적으로 이해할 수 있습니다.
복사된 파일은 EA에서 사용할 수 있습니다. 따라서 라이브러리 사용을 시작하려면 Expert Advisor 소스 코드에 헤더 파일만 포함하면 됩니다. 다음은 예입니다.
#include <Prototypes.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- int transTotal = TransactionsTotal(); printf((string)transTotal); }-->
예를 들어, 위의 코드는 활성 포지션의 총 수를 가져오고 MetaTrader 5 터미널의 "전문가" 탭에 숫자를 표시합니다.
HedgeTerminal은 실제로 함수 중 하나를 처음 호출할 때 초기화된다는 점을 이해하는 것이 중요합니다. 이 초기화를 지연이라고 합니다. 따라서 함수 중 하나를 처음 호출하는 데 시간이 오래 걸릴 수 있습니다. 첫 번째 실행 중에 빠른 응답을 원하면 미리 HT를 초기화해야 합니다. 예를 들어 OnInit() 블록에서 TransactionTotal() 함수를 호출할 수 있습니다. .
지연 초기화를 사용하면 Expert Advisor의 명시적 초기화를 생략할 수 있습니다. 이것은 HedgeTerminal 작업을 크게 단순화하고 사전 구성이 필요하지 않게 합니다.
1.2. HedgeTerminal 패널과 Expert Advisor의 통합
HedgeTerminal 시각 패널과 실시간으로 실행할 수 있는 완전한 기능의 라이브러리 버전이 있는 경우 Expert Advisors를 패널과 통합하여 그들이 수행하는 모든 거래 작업도 패널에 표시되도록 할 수 있습니다. 일반적으로 통합은 보이지 않습니다. HedgeTermianalAPI 기능을 사용하면 로봇이 수행하는 작업이 패널에 자동으로 표시됩니다. 그러나 커밋된 각 거래에 EA 이름을 표시하여 가시성을 확장할 수 있습니다. Settings.xml 파일에서 아래 줄의 주석 처리를 제거하면 됩니다.
<Column ID="Magic" Name="Magic" Width="100"/>
--> 이 태그는 섹션 <Active-Position> 및 <History-Position>에 있습니다.
이제 주석이 제거되고 태그가 처리에 포함됩니다. 패널이 다시 시작되면 활성 및 포지션 내역 테이블에 "Magic"의 새 열이 나타납니다. 열에는 해당 직책이 속한 Expert Advisor의 매직 넘버가 포함됩니다.
매직 넘버 대신 EA 이름을 표시하려면 별칭 파일 ExpertAliases.xml에 이름을 추가하세요. 예를 들어 EA의 매직 넘버는 123847이고 "ExPro 1.1"과 같은 이름을 표시하려면 파일에 다음 태그를 추가합니다.
<Expert Magic="123847" Name="ExPro 1.1"></Expert>-->
올바르게 완료되면 해당 열에 마법 대신 EA 이름이 표시됩니다.
그림 1. Magic 대신 EA 이름 표시
패널과 Expert Advisors는 실시간으로 소통합니다. 즉, 패널에서 직접 EA의 포지션을 닫으면 EA가 다음 TransactionsTotal() 함수 호출로 이에 대해 알게 됩니다. 그 반대의 경우도 마찬가지입니다. Expert Advisor가 포지션을 종료한 후 활성 포지션 탭에서 즉시 사라집니다.
1.3. HedgeTerminalAPI의 일반 원칙 동작
양방향 포지션 외에도 HedgeTerminal은 보류 중인 주문, 거래 및 중개인의 작업과 같은 다른 거래 유형과도 작동합니다. HedgeTerminal은 이러한 모든 유형을 거래의 단일 그룹으로 취급합니다. 거래, 보류 중인 주문, 방향성 포지션 - 모두 거래입니다. 그러나 거래는 단독으로 존재할 수 없습니다. 객체 지향 프로그래밍의 관점에서 거래는 거래 및 양방향 포지션과 같은 가능한 모든 거래 엔터티가 상속되는 추상 기본 클래스로 도입될 수 있습니다. 이와 관련하여 HedgeTerminalAPI의 모든 기능은 여러 그룹으로 나눌 수 있습니다.
- 거래 검색 및 선택 기능. 함수의 공통 서명과 작동 방식은 MetaTrader 4의 OrderSend() 및 OrderSelect() 함수와 거의 완전히 일치합니다.
- 선택한 거래의 속성을 가져오는 함수입니다. 모든 거래에는 특정 속성 집합과 속성 선택을 위한 특정 기능이 있습니다. 함수의 공통 서명과 작동 방식은 포지션, 거래 및 주문(예: OrderGetDouble() 또는 HistoryDealGetInteger();에 액세스하는 방식에서 MetaTrader 5 시스템 함수와 유사합니다.
- HedgeTerminalAPI는 하나의 거래 기능인 SendTradeRequest()만 사용합니다. 이 기능을 사용하면 양방향 포지션 또는 그 일부를 닫을 수 있습니다. 손절매, 이익실현 또는 나가는 주석 수정에도 동일한 기능이 사용됩니다. 함수로 작업하는 것은 MetaTrader 5의 OrderSend()와 유사합니다.
- 일반적인 오류를 가져오는 함수 GetHedgeError(), HedgeTerminal 거래 작업의 세부 분석을 위한 함수: TotalActionsTask() 및 GetActionResult(). 오류 감지에도 사용됩니다. MetaTrader 4 또는 MetaTrader 5에는 유사 제품이 없습니다.
거의 모든 기능으로 작업하는 것은 MetaTrader 4 및 MetaTrader 5 시스템 기능을 사용하는 것과 유사합니다. 일반적으로 함수 입력은 일부 식별자(열거값)이며 함수는 이에 해당하는 값을 반환합니다.
각 기능에 대해 특정 열거를 사용할 수 있습니다. 일반적인 호출 서명은 다음과 같습니다.
<value> = Function(<identifier>);-->
고유한 포지션 식별자를 가져오는 예를 살펴보겠습니다. MetaTrader 5의 라인은 다음과 같습니다.
ulong id = PositionGetInteger(POSITION_IDENTIFIER);-->
HedgeTerminal에서 양방향 포지션의 고유 식별자를 수신하는 방법은 다음과 같습니다.
ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID)
--> 기능의 일반적인 원리는 동일합니다. 열거 유형만 다릅니다.
1.4. 거래 선택
거래를 선택하는 것은 MetaTrader 4에서 주문을 검색하는 것과 유사한 거래 목록을 통해 진행됩니다. 그러나 MetaTrader 4에서는 주문만 검색되는 반면 HedgeTerminal에서는 보류 중인 주문이나 헤징 포지션과 같은 모든 것이 거래로 발견될 수 있습니다. 따라서 먼저 TransactionSelect() 함수를 사용하여 각 거래를 선택한 다음 TransactionType()을 통해 유형을 식별해야 합니다.
현재까지 활성 및 기록 거래의 두 가지 거래 목록을 사용할 수 있습니다. 적용할 목록은 ENUM_MODE_TRADES 수정자 (modifier)를 기반으로 정의됩니다. MetaTrader 4의 MODE_TRADES 수정자 (modifier)와 유사합니다.
거래 검색 및 선택 알고리즘은 다음과 같습니다.
1: for(int i=TransactionsTotal(MODE_TRADES)-1; i>=0; i--) 2: { 3: if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES))continue; 4: if(TransactionType()!=TRANS_HEDGE_POSITION)continue; 5: if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue; 6: if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue; 7: if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN)continue; 8: ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID) 9: }-->
코드는 for 주기(1행)의 활성 거래 목록을 반복합니다. 거래를 진행하기 전에 TransactionSelect()(3행)를 사용하여 거래를 선택하십시오. 이러한 거래(라인 4)에서 양방향 포지션만 선택됩니다. 포지션의 매직 넘버와 그 심볼이 현재 EA와 그것이 실행되고 있는 심볼의 매직 넘버와 일치하지 않으면 HT는 다음 포지션(5, 6행)으로 이동합니다. 그런 다음 고유한 포지션 식별자를 정의합니다(8행).
7행에 특별한 주의를 기울여야 합니다. 선택한 직위는 수정 가능성을 확인해야 합니다. 포지션이 이미 수정 중인 경우 속성 중 하나를 얻을 수 있지만 현재 스레드에서 변경할 수 없습니다. 포지션이 잠겨 있으면 해제될 때까지 기다려 속성에 액세스하거나 수정을 다시 시도하는 것이 좋습니다. HEDGE_POSITION_STATE 속성은 포지션 수정이 가능한지 여부를 확인하는 데 사용됩니다.
POSITION_STATE_FROZEN 수정자 (modifier)는 포지션이 "고정"되어 변경할 수 없음을 나타냅니다. POSITION_STATE_ACTIVE 수정자 (modifier)는 포지션이 활성 상태이며 변경할 수 있음을 나타냅니다. 이러한 수정자 (modifier)는 ENUM_HEDGE_POSITION_STATE 열거에 나열되며 해당 섹션에 설명되어 있습니다.
과거 거래를 통한 검색이 필요한 경우 TransactionTotal() 및 TransactionSelect() 함수의 MODE_TRADES 수정자 (modifier)를 MODE_HISTORY로 대체해야 합니다.
HedgeTerminal의 한 거래는 다른 거래에 중첩될 수 있습니다. 이것은 중첩이 없는 MetaTrader 5의 개념과 매우 다릅니다. 예를 들어, HedgeTerminal의 역사적 양방향 포지션은 각각 임의의 거래 세트를 포함하는 두 개의 주문으로 구성됩니다. 중첩은 다음과 같이 나타낼 수 있습니다.
그림 2. 중첩 거래
거래의 중첩은 HedgeTerminal 시각적 패널에서 명확하게 볼 수 있습니다.
아래 스크린샷은 MagicEx 1.3의 포지션에 대한 세부 정보를 보여줍니다.
그림 3. HedgeTerminal 패널의 중첩 거래
양방향 포지션에서 특정 주문 또는 거래의 속성에 액세스할 수 있습니다.
이것을 하기 위해:
- 과거 거래를 선택하고 양방향 포지션인지 확인하십시오.
- HedgeOrderSelect()를 사용하여 이 포지션의 주문 중 하나를 선택하십시오.
- 선택한 주문의 속성 중 하나를 가져옵니다: 포함된 거래 수;
- 모든 거래를 검색하여 주문에 속하는 거래 중 하나를 선택합니다.
- 필요한 거래 속성을 가져옵니다.
거래를 선택한 후에는 거래의 특정 속성을 사용할 수 있습니다. 예를 들어 거래가 주문인 경우 HedgeOrderSelect()를 통해 선택하면 해당 거래의 수를 찾을 수 있습니다(HedgeOrderGetInter(HEDGE_ORDER_DEALS_TOTAL)) 또는 가중 평균 진입 가격(HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED)).
스크린샷에서 빨간색으로 표시된 거래 #1197610의 가격을 알아보겠습니다. 이 거래는 MagicEx 1.3 EA의 양방향 입장의 일부입니다.
아래 코드를 통해 EA는 자신의 포지션과 이 거래에 액세스할 수 있습니다.
#include <Prototypes.mqh> ulong Magic=5760655; // MagicEx 1.3. //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--) { if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue; // Select transaction #i; if(TransactionType()!=TRANS_HEDGE_POSITION)continue; // If transaction is not position - continue; if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue; // If position is not main - continue; ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID); // Get id for closed order; if(id!=5917888)continue; // If id of position != 5917888 - continue; printf("1: -> Select position #"+(string)id); // Print position id; if(!HedgeOrderSelect(ORDER_SELECTED_CLOSED))continue; // Select closed order or continue; ulong order_id = HedgeOrderGetInteger(HEDGE_ORDER_ID); // Get id closed order; printf("2: ----> Select order #" + (string)order_id); // Print id closed order; int deals_total = (int)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL);// Get deals total in selected order; for(int deal_index = deals_total-1; deal_index >= 0; deal_index--) // Search deal #1197610... { if(!HedgeDealSelect(deal_index))continue; // Select deal by index or continue; ulong deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID); // Get id for current deal; if(deal_id != 1197610)continue; // Select deal #1197610; double price = HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED); // Get price executed; printf("3: --------> Select deal #"+(string)deal_id+ // Print price excecuted; ". Executed price = "+DoubleToString(price,0)); } } }-->
코드 실행 후 MetaTrader 5 터미널의 Experts 탭에 다음 항목이 생성됩니다.
2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1) 3: --------> Select deal #1197610. Executed price = 4735 2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1) 2: ----> Select order #6389111 2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1) 1: -> Select position #5917888-->
EA는 먼저 포지션 #5917888을 선택한 다음 해당 포지션 내에서 주문 #6389111을 선택합니다. 주문이 선택되면 EA는 거래 번호 1197610을 검색하기 시작합니다. 거래가 발견되면 EA는 실행 가격을 가져와 저널에 추가합니다.
1.5. GetHedgeError()를 사용하여 오류 코드를 얻는 방법
HedgeTerminal 환경에서 작업하는 동안 오류 및 예상치 못한 상황이 발생할 수 있습니다. 이러한 경우 오류 가져오기 및 분석 기능이 사용됩니다.
오류가 발생하는 가장 간단한 경우는 TransactionSelect() 함수를 사용하여 거래를 선택하는 것을 잊었을 때입니다. TransactionType() 함수는 이 경우 수정자 (modifier) TRANS_NOT_DEFINED를 반환합니다.
문제가 어디에 있는지 이해하려면 마지막 오류의 수정자 (modifier)를 가져와야 합니다. 수정자 (modifier)는 이제 거래가 선택되었음을 알려줍니다. 다음 코드는 이 작업을 수행합니다.
for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--) { //if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue; // forgot to select; ENUM_TRANS_TYPE type = TransactionType(); if(type == TRANS_NOT_DEFINED) { ENUM_HEDGE_ERR error = GetHedgeError(); printf("Error, transaction type not defined. Reason: " + EnumToString(error)); } }-->
결과 메시지는 다음과 같습니다.
Error, transaction type not defined. Reason: HEDGE_ERR_TRANS_NOTSELECTED-->
오류 ID는 유형을 가져오기 전에 거래를 선택하는 것을 잊었음을 나타냅니다.
가능한 모든 오류는 ENUM_HEDGE_ERR 구조에 나열됩니다.
1.6. TotalActionsTask() 및 GetActionResult()를 사용한 거래 세부 분석 및 오류 식별
HedgeTerminal 환경에서 작업하는 과정에서 발생하는 오류 외에도 SendTradeRequest() 호출의 결과로 거래 오류가 발생할 수 있습니다. 이러한 유형의 오류는 처리하기가 더 어렵습니다. SendTradeRequest()가 수행하는 하나의 작업에는 여러 거래 활동(하위 작업)이 포함될 수 있습니다. 예를 들어, 손절매 수준으로 보호되는 활성 포지션에서 나가는 댓글을 변경하려면 두 가지 거래 조치를 취해야 합니다.
- 중지 수준을 나타내는 보류 중지 주문을 취소합니다.
- 이전 주문 대신 새 설명을 사용하여 보류 중인 중지 주문을 새로 넣으십시오.
새로운 중지 주문이 트리거되면 해당 설명이 포지션을 닫는 설명으로 표시되며 이는 올바른 방법입니다.
그러나 작업은 부분적으로 실행할 수 있습니다. 보류 중인 주문이 성공적으로 취소되었지만 어떤 이유로든 새 주문이 실패했다고 가정합니다. 이 경우 포지션은 손절매 수준 없이 남게 됩니다. 이 오류를 처리할 수 있으려면 EA는 특수 작업 로그를 호출하고 실패한 하위 작업을 찾기 위해 검색해야 합니다.
이것은 두 가지 함수를 사용하여 수행됩니다. TotalActionsTask()는 이 작업 내에서 총 거래 작업(하위 작업) 수를 반환합니다. GetActionResult()는 하위 작업 색인을 수락하고 해당 유형과 실행 결과를 반환합니다. 모든 거래 작업은 표준 MetaTrader 5 도구를 사용하여 수행되기 때문에 성능 결과는 거래 서버의 반환 코드에 해당합니다.
일반적으로 실패 원인 검색 알고리즘은 다음과 같습니다.
- TotalActionsTask()를 사용하여 작업의 총 하위 작업 수 얻기;
- for 루프에서 모든 하위 작업을 검색합니다. 각 하위 작업의 유형과 결과를 결정합니다.
주문 실행 가격이 현재 가격 수준에 너무 가깝기 때문에 새 설명이 있는 중지 주문을 할 수 없다고 가정합니다.
아래 예제 코드는 EA가 이 실패의 원인을 찾는 방법을 보여줍니다.
#include <Prototypes.mqh> ulong Magic=5760655; // MagicEx 1.3. //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //detect active position for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--) { if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue; ENUM_TRANS_TYPE type=TransactionType(); if(type==TRANS_NOT_DEFINED) { ENUM_HEDGE_ERR error=GetHedgeError(); printf("Error, transaction not defined. Reason: "+EnumToString(error)); } if(TransactionType()!=TRANS_HEDGE_POSITION)continue; if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue; if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue; HedgeTradeRequest request; request.action=REQUEST_MODIFY_COMMENT; request.exit_comment="My new comment"; if(!SendTradeRequest(request)) // Is error? { for(uint action=0; action < TotalActionsTask(); action++) { ENUM_TARGET_TYPE typeAction; int retcode=0; GetActionResult(action, typeAction, retcode); printf("Action#" + (string)action + ": " + EnumToString(type) +(string)retcode); } } } }-->
코드 실행 후 다음 메시지가 나타납니다.
Action #0 TARGET_DELETE_PENDING_ORDER 10009 (TRADE_RETCODE_PLACED) Action #1 TARGET_SET_PENDING_ORDER 10015 (TRADE_RETCODE_INVALID_PRICE)-->
거래 서버 반환 코드의 표준 수정자 (modifier)와 숫자를 비교하여 보류 중인 주문이 성공적으로 제거되었지만 새 주문을 하는 데 실패했음을 알 수 있습니다. 거래 서버가 오류 10015(잘못된 가격)를 반환했는데, 이는 현재 가격이 정지 수준에 너무 가깝다는 의미일 수 있습니다.
이를 알면 EA는 정지 수준을 제어할 수 있습니다. 그렇게 하기 위해 EA는 동일한 SendTradeRequest() 함수를 사용하여 이 포지션을 청산하기만 하면 됩니다.
1.7. Trade Task 실행 현황 추적
모든 거래 작업은 순차적으로 수행되어야 하는 여러 하위 작업으로 구성될 수 있습니다.
비동기 모드에서는 코드의 여러 패스에서 하나의 작업을 수행할 수 있습니다. 작업이 '중단'될 수도 있습니다. 따라서 작업 실행에 대한 EA의 제어가 필요합니다. HEDGE_POSITION_TASK_STATUS 수정자 (modifier)를 사용하여 HedgePositionGetInteger() 함수를 호출하면 현재 포지션 작업의 상태를 포함하는 ENUM_TASK_STATUS 유형 열거를 반환합니다.
예를 들어, 포지션을 청산하라는 명령을 보낸 후 문제가 발생하여 포지션이 마감되지 않은 경우 작업 상태를 가져와야 합니다.
다음 예는 비동기 Expert Advisor가 해당 직책에 대한 작업 상태를 분석하기 위해 실행할 수 있는 코드를 보여줍니다.
ENUM_TASK_STATUS status=HedgePositionGetInteger(HEDGE_POSITION_TASK_STATUS); switch(status) { case TASK_STATUS_COMPLETE: printf("Task complete!"); break; case TASK_STATUS_EXECUTING: printf("Task executing. Waiting..."); Sleep(200); break; case TASK_STATUS_FAILED: printf("Filed executing task. Print logs..."); for(int i=0; i<TotalActionsTask(); i++) { ENUM_TARGET_TYPE type; uint retcode; GetActionResult(i,type,retcode); printf("#"+i+" "+EnumToString(type)+" "+retcode); } break; case TASK_STATUS_WAITING: printf("task will soon start."); break; }-->
일부 복잡한 작업을 실행하려면 여러 번 반복해야 합니다.
비동기 모드에서 거래 환경의 변화를 알리는 다가오는 이벤트는 새로운 반복을 시작합니다. 따라서 모든 반복은 거래 서버에서 수신된 응답에 따라 지연 없이 차례로 수행됩니다. 작업 실행은 동기 모드에서 다릅니다.
동기 방식은 동기 작업 에뮬레이터를 사용하므로 사용자는 단일 패스에서 복합 작업도 수행할 수 있습니다. 에뮬레이터는 시간 지연을 사용합니다. 예를 들어, 하위 작업 실행이 시작된 후 에뮬레이터는 실행 스레드를 EA로 반환하지 않습니다. 대신, 거래 환경이 바뀔 것으로 예상하는 동안 잠시 기다립니다. 그 후 거래 환경을 다시 읽습니다. 하위 작업이 성공적으로 완료되었음을 이해하면 다음 하위 작업을 시작합니다.
이 프로세스는 대기하는 데 시간이 걸리므로 전체 성능을 다소 저하시킵니다. 그러나 복잡한 작업의 실행을 단일 함수 호출로 수행되는 매우 간단한 순차적 작업으로 바꿉니다. 따라서 동기 방식으로 작업 실행 로그를 분석할 필요가 거의 없습니다.
1.8. 양방향 포지션을 수정하고 닫는 방법
양방향 포지션은 SendTradeRequest() 함수를 사용하여 수정되고 닫힙니다. 활성 포지션에는 세 가지 옵션만 적용할 수 있습니다.
- 포지션은 완전히 또는 부분적으로 닫힐 수 있습니다.
- 포지션 손절매 및 이익실현 가능;
- 포지션의 나가는 코멘트는 변경될 수 있습니다.
내역 상의 포지션은 변경할 수 없습니다. MetaTrader 5의 OrderSend() 함수와 유사하게, SendTradeRequest()는 거래 구조 HedgeTraderRequest의 형태로 미리 컴파일된 쿼리를 사용합니다. SendTradeRequest() 함수 및 HedgeTraderRequest 구조에 대한 자세한 내용은 문서를 참조하십시오. 포지션 수정 및 폐쇄를 보여주는 예는 Chaos II EA 섹션에서 사용할 수 있습니다.
1.9. Expert Advisor에서 HedgeTerminal 속성을 설정하는 방법
HedgeTerminal은 새로 고침 빈도, 서버의 응답을 기다리는 시간(초) 등의 속성 집합을 가지고 있습니다.
이러한 모든 속성은 Settings.xml에 정의되어 있습니다. EA가 실시간으로 실행될 때 라이브러리는 파일에서 속성을 읽고 적절한 내부 매개변수를 설정합니다. EA가 차트에서 테스트될 때 Settings.xml 파일은 사용되지 않습니다. 그러나 일부 상황에서는 EA 속성이 차트에서 실행 중이든 전략 테스터에서 실행 중이든 상관없이 EA 속성을 개별적으로 수정해야 할 수도 있습니다.
이것은 HedgePropertySet…특수 기능 세트를 통해 수행됩니다. 현재 버전에는 이 세트의 프로토타입이 하나만 있습니다.
enum ENUM_HEDGE_PROP_INTEGER { HEDGE_PROP_TIMEOUT, }; bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, int value)-->
예를 들어, 라이브러리가 서버 응답을 기다리는 시간 초과를 설정하려면 다음을 작성하십시오.
bool res = HedgePropertySetInteger(HEDGE_PROP_TIMEOUT, 30);-->
비동기 요청을 보낸 후 30초 이내에 서버 응답이 수신되지 않으면 잠긴 포지션이 해제됩니다.
1.10. 동기 및 비동기 작동 모드
HedgeTerminal과 그 API는 완전히 비동기적으로 거래 활동을 수행합니다.
그러나 이 모드는 EA의 더 복잡한 로직을 필요로 합니다. 이러한 복잡성을 숨기기 위해 HedgeTerminalAPI에는 동기 작업의 특수 에뮬레이터가 포함되어 있어 기존 동기 방식으로 개발된 EA가 HedgeTerminalAPI의 비동기 알고리즘과 통신할 수 있습니다. 이 상호작용은 SendTradeRequest()를 통해 양방향 포지션 수정 및 폐쇄 시에 드러납니다. 이 기능을 사용하면 동기 또는 비동기 모드에서 거래 작업을 실행할 수 있습니다. 기본적으로 모든 거래 작업은 동기 작업 에뮬레이터를 통해 동기적으로 실행됩니다. 그러나 거래 요청(HedgeTradeRequest 구조)에 명시적으로 지정된 플래그 async_mode = true가 포함된 경우 거래 작업은 비동기 모드에서 수행됩니다.
비동기 모드에서 작업은 메인 스레드와 독립적으로 수행됩니다. HedgeTerminal의 비동기 EA와 비동기 알고리즘 간의 상호 작용 구현은 아직 완료되지 않았습니다.
동기식 에뮬레이터는 매우 간단합니다. 하위 작업을 순차적으로 시작한 다음 MetaTrader 5의 거래 환경이 변경될 때까지 일정 시간 기다립니다. 에뮬레이터는 이러한 변경 사항을 분석하고 현재 작업의 상태를 결정합니다. 작업 실행이 성공하면 에뮬레이터가 다음 에뮬레이터로 이동합니다.
동기식 에뮬레이터는 거래 주문 실행에 약간의 지연을 일으킵니다. 이는 MetaTrader 5의 거래 환경이 실행된 거래 활동을 반영하는 데 시간이 걸리기 때문입니다. 환경에 대한 액세스의 필요성은 주로 HedgeTermianlAPI가 동기 스레드 에뮬레이션 모드에서 OnTradeTransaction() 핸들러로 오는 이벤트에 액세스할 수 없다는 사실과 연결됩니다.
에뮬레이션을 통한 비동기 스레드와 동기 스레드 간의 상호 작용은 물론 비동기 스레드 간의 상호 작용 문제는 너무 복잡하고 명확한 솔루션이 없습니다.
1.11. 스크립트 예제를 통한 양방향 포지션 속성 관리
아래 스크립트에서 TransactionSelect() 함수는 활성 거래 목록에서 사용 가능한 모든 거래를 검색합니다.
각 거래는 목록에서 선택됩니다. 거래가 포지션인 경우 해당 속성 중 일부에 액세스한 다음 인쇄합니다. 포지션의 속성에 더하여, 포지션 내의 주문과 거래의 속성도 인쇄됩니다. 주문과 거래는 각각 HedgeOrderSelect() 및 HedgeDealSelect()를 사용하여 먼저 선택됩니다.
포지션, 주문 및 거래의 모든 속성은 시스템 기능 printf.를 사용하여 결합되고 한 줄로 인쇄됩니다.
//+------------------------------------------------------------------+ //| sample_using_htapi.mq5 | //| Copyright 2014, Vasiliy Sokolov, Russia, St.-Petersburg. | //| https://login.mql5.com/ru/users/c-4 | //+------------------------------------------------------------------+ #property copyright "Copyright 2014, Vasiliy Sokolov." #property link "https://login.mql5.com/ru/users/c-4" #property version "1.00" // Include prototypes function of HedgeTerminalAPI library. #include <Prototypes.mqh> //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnStart() { // Search all transaction in list transaction... for(int i=TransactionsTotal(); i>=0; i--) { if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES)) // Selecting from active transactions { ENUM_HEDGE_ERR error=GetHedgeError(); // Get reason if selecting has failed printf("Error selecting transaction # "+(string)i+". Reason: "+ // Print reason EnumToString(error)); ResetHedgeError(); // Reset error continue; // Go to next transaction } // Only for hedge positions if(TransactionType()==TRANS_HEDGE_POSITION) { // --- Position captions --- // ENUM_TRANS_DIRECTION direction=(ENUM_TRANS_DIRECTION) // Get direction caption HedgePositionGetInteger(HEDGE_POSITION_DIRECTION); double price_entry = HedgeOrderGetDouble(HEDGE_ORDER_PRICE_EXECUTED); // Get volume of positions string symbol = HedgePositionGetString(HEDGE_POSITION_SYMBOL); // Get symbol of position // --- Order captions --- // if(!HedgeOrderSelect(ORDER_SELECTED_INIT))continue; // Selecting init order in position double slippage = HedgeOrderGetDouble(HEDGE_ORDER_SLIPPAGE); // Get some slippage was uint deals_total = (uint)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL); // Get deals total // --- Deals captions --- // double commissions=0.0; ulong deal_id=0; //Search all deals in list deals... for(uint d_index=0; d_index<deals_total; d_index++) { if(!HedgeDealSelect(d_index))continue; // Selecting deal by its index deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID); // Get deal id commissions += HedgeDealGetDouble(HEDGE_DEAL_COMMISSION); // Count commissions } int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); printf("Position #" + (string)i + ": DIR " + EnumToString(direction) + // Print result line "; PRICE ENTRY " + DoubleToString(price_entry, digits) + "; INIT SLIPPAGE " + DoubleToString(slippage, 2) + "; LAST DEAL ID " + (string)deal_id + "; COMMISSIONS SUM " + DoubleToString(commissions, 2)); } } }-->
1.12. Chaos II EA의 예제를 통한 SendTradeRequest() 함수와 HedgeTradeRequest 구조
예를 들어 Bill Williams가 그의 책 Trading Chaos에서 제안한 거래 전술을 기반으로 거래 로봇을 개발해 보겠습니다. 제2판.
그의 권장 사항을 모두 따르지는 않지만 악어 표시 및 기타 조건을 생략하여 계획을 단순화합니다. 이 전략의 선택은 몇 가지 고려 사항에서 비롯됩니다. 가장 중요한 것은 이 전략에 복합 포지션 유지 전술이 포함된다는 것입니다. 때로는 포지션의 일부를 청산하고 손절매를 손익분기점으로 이동해야 합니다.
손익분기점으로 이동할 때 스탑 레벨은 가격 뒤에 따라야 합니다. 두 번째 고려 사항은 이 전술이 충분히 알려져 있고 이를 위해 개발된 지표가 표준 MetaTrader 5 배달 팩에 포함되어 있다는 것입니다. Expert Advisor의 복잡한 논리가 HedgeTerminalAPI 라이브러리와 EA 상호 작용의 예를 보여주는 주요 목표를 방해하지 않도록 규칙을 약간 수정하고 단순화하겠습니다. EA의 로직은 HedgeTerminalAPI의 대부분의 거래 기능을 사용합니다. 이것은 라이브러리에 대한 좋은 테스트입니다.
반전 바부터 시작하겠습니다. 강세 반전 바는 상단 3분의 1에 종가가 있는 바로, 저점이 마지막 N 바에서 가장 낮은 것입니다. 약세 반전 바는 하단 1/3에 종가가 있는 바로, 고가는 마지막 N 바에서 가장 높은 것입니다. N은 임의로 선택한 매개변수이며 Expert Advisor 시작 시 설정할 수 있습니다. 이것은 고전적인 전략 "Chaos 2"와 다릅니다.
취소 바가 정의된 후 두 개의 보류 주문이 배치됩니다. 강세 바의 경우 주문은 고점 위에, 약세 바는 저점 바로 아래에 배치됩니다. 이 두 주문이 OldPending 바 중에 트리거되지 않으면 신호가 더 이상 사용되지 않는 것으로 간주되고 주문이 취소됩니다. OldPending 및 N의 값은 차트에서 EA를 실행하기 전에 사용자가 설정합니다.
주문이 트리거되고 두 개의 양방향 포지션으로 바뀝니다. EA는 주석의 숫자로 각각 "# 1"과 "# 2"를 구분합니다. 이것은 매우 우아한 솔루션은 아니지만 데모 목적으로는 괜찮습니다. 주문이 발동되면 반전 바의 고점(약세 바의 경우) 또는 저점(봉 강세의 경우)에 손절매가 설정됩니다.
첫 번째 포지션은 타이트한 목적을 갖고 있습니다. 테이크 이익은 트리거의 경우 포지션 이익이 트리거된 스톱로스의 절대 손실과 같도록 설정됩니다. 예를 들어, 매수 포지션이 1.0000의 가격에 개설되고 그 손절매가 0.9000 수준에 있다면, 이익실현 수준은 1.0000 + (1.0000 - 0.9000) = 1.1000이 됩니다. EA는 손절매 또는 이익실현 상태에서 포지션을 종료합니다.
두 번째 포지션은 장기적 포지션입니다. 손절매는 가격을 따라갑니다. 정지는 새로 형성된 Bill Williams의 프랙탈 이후로 이동합니다. 롱 포지션의 경우 스톱은 하위 프랙탈에 따라 이동하고 상위 프랙탈은 숏 포지션에 사용됩니다. EA는 스톱로스시에만 포지션을 종료합니다.
다음 차트는 이 전략을 보여줍니다.
그림 4. ㄴ가격 차트에서 Chaos 2 EA의 양방향 포지션 표시
반전 바는 빨간색 프레임으로 표시됩니다. 이 차트의 N 기간은 2와 같습니다. 이 전략에는 가장 적절한 순간이 선택됩니다. 숏 포지션은 파란색 점선으로 표시되고 롱 포지션은 녹색으로 표시됩니다. 보시다시피 비교적 간단한 전략에서도 롱 포지션과 숏 포지션이 동시에 존재할 수 있습니다. 2014년 1월 5일부터 8일까지의 기간에 주목하십시오.
이것은 AUDCAD 하락 추세의 전환점입니다. 1월 4일 강세 반전 바에서 신호를 받았고 1월 5일 두 개의 롱 포지션이 열렸습니다. 동시에, 추세(빨간색 점선)를 따라 스톱을 추적한 3개의 숏 포지션이 있었습니다. 그런 다음 1월 7일에 숏 포지션에 대해 스톱이 발동되어 롱 포지션만 시장에 남게 되었습니다.
순 거래량은 EA가 실제로 유지 관리하는 포지션의 수를 고려하지 않기 때문에 순 포지션에서 변경 사항을 모니터링하기 어려울 것입니다. HedgeTerminal을 사용하면 EA가 현재 순 포지션에 관계없이 개별 포지션을 모니터링할 수 있으므로 이러한 차트를 얻고 유사한 전략을 개발할 수 있습니다.
아래는 이 전략을 구현하는 코드입니다.
저는 의도적으로 객체 지향 프로그래밍을 사용하지 않고 초보자를 위해 코드를 조정했습니다.
//+------------------------------------------------------------------+ //| Chaos2.mq5 | //| Copyright 2014, Vasiliy Sokolov specially for HedgeTerminal. | //| St.-Petersburg, Russia. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2014, Vasiliy Sokolov." #property link "https://login.mql5.com/ru/users/c-4" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Prototypes.mqh> // Include prototypes function of HedgeTerminalAPI library //+------------------------------------------------------------------+ //| Input parameters. | //+------------------------------------------------------------------+ input uint N=2; // Period of extermum/minimum input uint OldPending=3; // Old pending //+------------------------------------------------------------------+ //| Private variables of expert advisor. | //+------------------------------------------------------------------+ ulong Magic = 2314; // Magic number of expert datetime lastTime = 0; // Remembered last time for function DetectNewBar int hFractals = INVALID_HANDLE; // Handle of indicator 'Fractals'. See: 'https://www.mql5.com/ko/docs/indicators/ifractals' //+------------------------------------------------------------------+ //| Type of bar by Bill Wiallams strategy. | //+------------------------------------------------------------------+ enum ENUM_BAR_TYPE { BAR_TYPE_ORDINARY, // Ordinary bar. BAR_TYPE_BEARISH, // This bar close in the upper third and it's minimum is lowest at N period BAR_TYPE_BULLISH, // This bar close in the lower third and it's maximum is highest at N period }; //+------------------------------------------------------------------+ //| Type of Extremum. | //+------------------------------------------------------------------+ enum ENUM_TYPE_EXTREMUM { TYPE_EXTREMUM_HIGHEST, // Extremum from highest prices TYPE_EXTREMUM_LOWEST // Extremum from lowest prices }; //+------------------------------------------------------------------+ //| Type of position. | //+------------------------------------------------------------------+ enum ENUM_ENTRY_TYPE { ENTRY_BUY1, // Buy position with short target ENTRY_BUY2, // Buy position with long target ENTRY_SELL1, // Sell position with short target ENTRY_SELL2, // Sell position with long target ENTRY_BAD_COMMENT // My position, but wrong comment }; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create indicator 'Fractals' ---// hFractals=iFractals(Symbol(),NULL); if(hFractals==INVALID_HANDLE) printf("Warning! Indicator 'Fractals' not does not create. Reason: "+ (string)GetLastError()); //--- Corection magic by timeframe ---// int minPeriod=PeriodSeconds()/60; string strMagic=(string)Magic+(string)minPeriod; Magic=StringToInteger(strMagic); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Delete indicator 'Fractals' ---// if(hFractals!=INVALID_HANDLE) IndicatorRelease(hFractals); //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Run logic only open new bar. ---// int totals=SupportPositions(); if(NewBarDetect()==true) { MqlRates rates[]; CopyRates(Symbol(),NULL,1,1,rates); MqlRates prevBar=rates[0]; //--- Set new pendings order ---// double closeRate=GetCloseRate(prevBar); if(closeRate<=30 && BarIsExtremum(1,N,TYPE_EXTREMUM_HIGHEST)) { DeleteOldPendingOrders(0); SetNewPendingOrder(1,BAR_TYPE_BEARISH); } else if(closeRate>=70 && BarIsExtremum(1,N,TYPE_EXTREMUM_LOWEST)) { DeleteOldPendingOrders(0); SetNewPendingOrder(1,BAR_TYPE_BULLISH); } DeleteOldPendingOrders(OldPending); } //--- } //+------------------------------------------------------------------+ //| Analyze open positions and modify it if needed. | //+------------------------------------------------------------------+ int SupportPositions() { //--- int count=0; //--- Analize active positions... ---// for(int i=0; i<TransactionsTotal(); i++) // Get total positions. { //--- Select main active positions ---// if(!TransactionSelect(i, SELECT_BY_POS, MODE_TRADES))continue; // Select active transactions if(TransactionType() != TRANS_HEDGE_POSITION)continue; // Select hedge positions only if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic) // Select main positions by magic if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN) // If position is frozen - continue continue; // Let's try to get access to positions later count++; //--- What position do we choose?... ---// ENUM_ENTRY_TYPE type=IdentifySelectPosition(); bool modify=false; double sl = 0.0; double tp = 0.0; switch(type) { case ENTRY_BUY1: case ENTRY_SELL1: { //--- Check sl, tp levels and modify it if need. ---// double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL); sl=GetStopLossLevel(); if(!DoubleEquals(sl,currentStop)) modify=true; tp=GetTakeProfitLevel(); double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); //--- Close by take-profit if price more tp level bool isBuyTp=tp<bid && !DoubleEquals(tp,0.0) && type==ENTRY_BUY1; bool isSellTp=tp>ask && type==ENTRY_SELL1; if(isBuyTp || isSellTp) { HedgeTradeRequest request; request.action=REQUEST_CLOSE_POSITION; request.exit_comment="Close by TP from expert"; request.close_type=CLOSE_AS_TAKE_PROFIT; if(!SendTradeRequest(request)) { ENUM_HEDGE_ERR error=GetHedgeError(); string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : ""; printf("Close position by tp failed. Reason: "+EnumToString(error)+" "+logs); if(error==HEDGE_ERR_TASK_FAILED) PrintTaskLog(); ResetHedgeError(); } else break; } double currentTakeProfit=HedgePositionGetDouble(HEDGE_POSITION_TP); if(!DoubleEquals(tp,currentTakeProfit)) modify=true; break; } case ENTRY_BUY2: { //--- Check sl level and set modify flag. ---// sl=GetStopLossLevel(); double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL); if(sl>currentStop) modify=true; break; } case ENTRY_SELL2: { //--- Check sl level and set modify flag. ---// sl=GetStopLossLevel(); double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL); bool usingSL=HedgePositionGetInteger(HEDGE_POSITION_USING_SL); if(sl<currentStop || !usingSL) modify=true; break; } } //--- if need modify sl, tp levels - modify it. ---// if(modify) { HedgeTradeRequest request; request.action=REQUEST_MODIFY_SLTP; request.sl = sl; request.tp = tp; if(type==ENTRY_BUY1 || type==ENTRY_SELL1) request.exit_comment="Exit by T/P level"; else request.exit_comment="Exit by trailing S/L"; if(!SendTradeRequest(request)) { ENUM_HEDGE_ERR error=GetHedgeError(); string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : ""; printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs); if(error==HEDGE_ERR_TASK_FAILED) PrintTaskLog(); ResetHedgeError(); } else break; } } return count; //--- } //+------------------------------------------------------------------+ //| Return stop-loss level for selected position. | //| RESULT | //| Stop-loss level | //+------------------------------------------------------------------+ double GetStopLossLevel() { //--- double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3; double fractals[]; double sl=0.0; MqlRates ReversalBar; if(!LoadReversalBar(ReversalBar)) { printf("Reversal bar load failed."); return sl; } //--- What position do we choose?... ---// switch(IdentifySelectPosition()) { case ENTRY_SELL2: { if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL)) { sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits()); CopyBuffer(hFractals,UPPER_LINE,ReversalBar.time,TimeCurrent(),fractals); for(int i=ArraySize(fractals)-4; i>=0; i--) { if(DoubleEquals(fractals[i],DBL_MAX))continue; if(DoubleEquals(fractals[i],sl))continue; if(fractals[i]<sl) { double price= SymbolInfoDouble(Symbol(),SYMBOL_ASK); int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL); double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze; if(fractals[i]>price+freeze) sl=NormalizeDouble(fractals[i]+point,Digits()); } } break; } } case ENTRY_SELL1: sl=ReversalBar.high+point; break; case ENTRY_BUY2: if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL)) { sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits()); CopyBuffer(hFractals,LOWER_LINE,ReversalBar.time,TimeCurrent(),fractals); for(int i=ArraySize(fractals)-4; i>=0; i--) { if(DoubleEquals(fractals[i],DBL_MAX))continue; if(DoubleEquals(fractals[i],sl))continue; if(fractals[i]>sl) { double price= SymbolInfoDouble(Symbol(),SYMBOL_BID); int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL); double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze; if(fractals[i]<price-freeze) sl=NormalizeDouble(fractals[i]-point,Digits()); } } break; } case ENTRY_BUY1: sl=ReversalBar.low-point; } sl=NormalizeDouble(sl,Digits()); return sl; //--- } //+------------------------------------------------------------------+ //| Return Take-Profit level for selected position. | //| RESULT | //| Take-profit level | //+------------------------------------------------------------------+ double GetTakeProfitLevel() { //--- double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3; ENUM_ENTRY_TYPE type=IdentifySelectPosition(); double tp=0.0; if(type==ENTRY_BUY1 || type==ENTRY_SELL1) { if(!HedgePositionGetInteger(HEDGE_POSITION_USING_SL)) return tp; double sl=HedgePositionGetDouble(HEDGE_POSITION_SL); double openPrice=HedgePositionGetDouble(HEDGE_POSITION_PRICE_OPEN); double deltaStopLoss=MathAbs(NormalizeDouble(openPrice-sl,Digits())); if(type==ENTRY_BUY1) tp=openPrice+deltaStopLoss; if(type==ENTRY_SELL1) tp=openPrice-deltaStopLoss; return tp; } else return 0.0; //--- } //+------------------------------------------------------------------+ //| Identify what position type is select. | //| RESULT | //| Return type position. See ENUM_ENTRY_TYPE | //+------------------------------------------------------------------+ ENUM_ENTRY_TYPE IdentifySelectPosition() { //--- string comment=HedgePositionGetString(HEDGE_POSITION_ENTRY_COMMENT); int pos=StringLen(comment)-2; string subStr=StringSubstr(comment,pos); ENUM_TRANS_DIRECTION posDir=(ENUM_TRANS_DIRECTION)HedgePositionGetInteger(HEDGE_POSITION_DIRECTION); if(subStr=="#0") { if(posDir==TRANS_LONG) return ENTRY_BUY1; if(posDir==TRANS_SHORT) return ENTRY_SELL1; } else if(subStr=="#1") { if(posDir==TRANS_LONG) return ENTRY_BUY2; if(posDir==TRANS_SHORT) return ENTRY_SELL2; } return ENTRY_BAD_COMMENT; //--- } //+------------------------------------------------------------------+ //| Set pending orders under or over bar by index_bar. | //| INPUT PARAMETERS | //| index_bar - index of bar. | //| barType - type of bar. See enum ENUM_BAR_TYPE. | //| RESULT | //| True if new order successfully set, othewise false. | //+------------------------------------------------------------------+ bool SetNewPendingOrder(int index_bar,ENUM_BAR_TYPE barType) { //--- MqlRates rates[1]; CopyRates(Symbol(),NULL,index_bar,1,rates); MqlTradeRequest request={0}; request.volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); double vol=request.volume; request.symbol = Symbol(); request.action = TRADE_ACTION_PENDING; request.type_filling=ORDER_FILLING_FOK; request.type_time=ORDER_TIME_GTC; request.magic=Magic; double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3; string comment=""; if(barType==BAR_TYPE_BEARISH) { request.price=rates[0].low-point; comment="Entry sell by bearish bar"; request.type=ORDER_TYPE_SELL_STOP; } else if(barType==BAR_TYPE_BULLISH) { request.price=rates[0].high+point; comment="Entry buy by bullish bar"; request.type=ORDER_TYPE_BUY_STOP; } MqlTradeResult result={0}; //--- Send pending order twice... for(int i=0; i<2; i++) { request.comment=comment+" #"+(string)i; // Detect order by comment; if(!OrderSend(request,result)) { printf("Trade error #"+(string)result.retcode+" "+ result.comment); return false; } } return true; //--- } //+------------------------------------------------------------------+ //| Delete old pending orders. If pending order set older that | //| n_bars ago pending orders will be removed. | //| INPUT PARAMETERS | //| period - count bar. | //+------------------------------------------------------------------+ void DeleteOldPendingOrders(int n_bars) { //--- for(int i=0; i<OrdersTotal(); i++) { ulong ticket = OrderGetTicket(i); // Get ticket of order by index. if(!OrderSelect(ticket)) // Continue if not selected. continue; if(Magic!=OrderGetInteger(ORDER_MAGIC)) // Continue if magic is not main. continue; if(OrderGetString(ORDER_SYMBOL)!=Symbol()) // Continue if symbol is not main. continue; //--- Count time elipsed ---// datetime timeSetup=(datetime)OrderGetInteger(ORDER_TIME_SETUP); int secElapsed=(int)(TimeCurrent()-timeSetup); //--- delete old pending order ---// if(secElapsed>=PeriodSeconds() *n_bars) { MqlTradeRequest request={0}; MqlTradeResult result={0}; request.action= TRADE_ACTION_REMOVE; request.order = ticket; if(!OrderSend(request,result)) printf("Delete pending order failed. Reason #"+(string)result.retcode+" "+result.comment); } } //--- } //+------------------------------------------------------------------+ //| Detect new bar. | //+------------------------------------------------------------------+ bool NewBarDetect(void) { //--- datetime timeArray[1]; CopyTime(Symbol(),NULL,0,1,timeArray); if(lastTime!=timeArray[0]) { lastTime=timeArray[0]; return true; } return false; //--- } //+------------------------------------------------------------------+ //| Get close rate. Type bar defined in trade chaos strategy | //| and equal enum 'ENUM_TYPE_BAR'. | //| INPUT PARAMETERS | //| index - index of bars series. for example: | //| '0' - is current bar. 1 - previous bar. | //| RESULT | //| Type of ENUM_TYPE_BAR. | //+------------------------------------------------------------------+ double GetCloseRate(const MqlRates &bar) { //--- double highLowDelta = bar.high-bar.low; // Calculate diaposon bar. double lowCloseDelta = bar.close - bar.low; // Calculate Close - Low delta. double percentClose=0.0; if(!DoubleEquals(lowCloseDelta, 0.0)) // Division by zero protected. percentClose = lowCloseDelta/highLowDelta*100.0; // Calculate percent 'lowCloseDelta' of 'highLowDelta'. return percentClose; //--- } //+------------------------------------------------------------------+ //| If bar by index is extremum - return true, otherwise | //| return false. | //| INPUT PARAMETERS | //| index - index of bar. | //| period - Number of bars prior to the extremum. | //| type - Type of extremum. See ENUM_TYPE_EXTREMUM TYPE enum. | //| RESULT | //| True - if bar is extremum, otherwise false. | //+------------------------------------------------------------------+ bool BarIsExtremum(const int index,const int period,ENUM_TYPE_EXTREMUM type) { //--- Copy rates --- // MqlRates rates[]; ArraySetAsSeries(rates,true); CopyRates(Symbol(),NULL,index,N+1,rates); //--- Search extremum --- // for(int i=1; i<ArraySize(rates); i++) { //--- Reset comment if you want include volume analize. ---// //if(rates[0].tick_volume<rates[i].tick_volume) // return false; if(type==TYPE_EXTREMUM_HIGHEST && rates[0].high<rates[i].high) return false; if(type==TYPE_EXTREMUM_LOWEST && rates[0].low>rates[i].low) return false; } return true; //--- } //+------------------------------------------------------------------+ //| Print current error and reset it. | //+------------------------------------------------------------------+ void PrintTaskLog() { //--- uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL); for(uint i = 0; i<totals; i++) { uint retcode=0; ENUM_TARGET_TYPE type; GetActionResult(i,type,retcode); printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode); } //--- } //+------------------------------------------------------------------+ //| Load reversal bar. The current position must be selected. | //| OUTPUT PARAMETERS | //| bar - MqlRates bar. //+------------------------------------------------------------------+ bool LoadReversalBar(MqlRates &bar) { //--- datetime time=(datetime)(HedgePositionGetInteger(HEDGE_POSITION_ENTRY_TIME_SETUP_MSC)/1000+1); MqlRates rates[]; ArraySetAsSeries(rates,true); CopyRates(Symbol(),NULL,time,2,rates); int size=ArraySize(rates); if(size==0)return false; bar=rates[size-1]; return true; //--- } //+------------------------------------------------------------------+ //| Compares two double numbers. | //| RESULT | //| True if two double numbers equal, otherwise false. | //+------------------------------------------------------------------+ bool DoubleEquals(const double a,const double b) { //--- return(fabs(a-b)<=16*DBL_EPSILON*fmax(fabs(a),fabs(b))); //--- }-->
다음은 이 코드의 작동 방식에 대한 간략한 설명입니다. EA는 매 틱마다 호출됩니다. BarIsExtremum() 함수를 사용하여 이전 바를 분석합니다. 약세 또는 강세이면 두 개의 보류 주문을 넣습니다(함수 SetNewPendingOrder()). 활성화되면 보류 중인 주문이 포지션으로 전환됩니다. EA는 손절매를 설정하고 해당 포지션에 대해 이익을 얻습니다.
불행히도, 이러한 레벨은 아직 실제 포지션이 없기 때문에 보류 중인 주문과 함께 배치할 수 없습니다. 레벨은 SupportPositions() 함수를 통해 설정됩니다. 제대로 작동하려면 이익을 취해야 하는 포지션과 프랙탈 다음에 따라야 하는 포지션을 알아야 합니다. 이 포지션에 대한 정의는 IdentifySelectPosition() 함수에 의해 수행됩니다. 시작 포지션 주석을 분석하고 문자열 "#1"이 포함되어 있으면 엄격한 대상이 설정됩니다. "# 2"가 포함되어 있으면 후행 정지가 적용됩니다.
열린 양방향 포지션을 수정하거나 청산하기 위해 특별한 거래 요청이 생성된 다음 실행을 위해 SendTradeRequest() 함수로 전송됩니다.
... if(modify) { HedgeTradeRequest request; request.action=REQUEST_MODIFY_SLTP; request.sl = sl; request.tp = tp; if(type==ENTRY_BUY1 || type==ENTRY_SELL1) request.exit_comment="Exit by T/P level"; else request.exit_comment="Exit by trailing S/L"; if(!SendTradeRequest(request)) { ENUM_HEDGE_ERR error=GetHedgeError(); string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : ""; printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs); if(error==HEDGE_ERR_TASK_FAILED) PrintTaskLog(); ResetHedgeError(); } else break; } ...-->
오류 처리에 주의하십시오.
전송이 실패하고 함수가 false를 반환하면 GetHedgeError() 함수를 사용하여 마지막 오류 코드를 가져와야 합니다. 어떤 경우에는 거래 주문 실행이 시작되지 않습니다. 포지션이 미리 선택되지 않은 경우 쿼리가 잘못 수행되어 실행이 불가능합니다.
주문이 실행되지 않으면 구현 로그를 분석하는 것은 의미가 없으며 오류 코드를 얻는 것으로 충분합니다.
그러나 쿼리가 정확하지만 어떤 이유로 주문이 실행되지 않은 경우 오류 HEDGE_ERR_TASK_FAILED가 반환됩니다. 이 경우 로그를 통해 검색하여 주문 실행 로그를 분석할 필요가 있습니다. 이것은 특수 함수 PrintTaskLog()를 통해 수행됩니다.
//+------------------------------------------------------------------+ //| Print current error and reset it. | //+------------------------------------------------------------------+ void PrintTaskLog() { //--- uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL); for(uint i = 0; i<totals; i++) { uint retcode=0; ENUM_TARGET_TYPE type; GetActionResult(i,type,retcode); printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode); } //--- }-->
이러한 메시지를 통해 실패 원인을 식별하고 수정할 수 있습니다.
이제 실시간으로 Chaos2 EA의 표시와 HedgeTerminal에서의 포지션을 설명하겠습니다. EA는 M1 차트에서 실행 중입니다.
그림 5. HedgeTerminal 패널에서 Chaos 2 EA의 양방향 포지션 표현
보시다시피, 하나의 EA의 양방향 포지션도 완벽하게 공존할 수 있습니다.
1.13. "중복 기호" 및 브로커에 의한 가상화에 대해
MetaTrader 5가 출시되었을 때 일부 브로커는 소위 중복 기호를 제공하기 시작했습니다. 그들의 따옴표는 원래 악기와 동일하지만 일반적으로 "_m" 또는 "_1"과 같은 접미사가 있습니다. 거래자가 거의 동일한 기호에 대해 양방향 포지션을 가질 수 있도록 도입되었습니다.
그러나 이러한 기호는 로봇을 사용하는 알고리즘 트레이더에게는 거의 쓸모가 없습니다. 그리고 여기에 이유가 있습니다. HedgeTerminalAPI 라이브러리 없이 "Chaos II" EA를 작성해야 한다고 가정합니다. 대신 일부 중복 기호가 있습니다. 어떻게 할까요? 모든 매도 작업은 EURUSD와 같은 단일 상품에서 시작되었고 모든 구매 작업은 EURUSD_m1과 같은 다른 상품에서 시작되었다고 가정합니다.
그러나 포지션을 여는 순간에 심볼 중 하나가 이미 다른 로봇이나 트레이더에 의해 거래된다면 어떻게 될까요? 이러한 기호가 항상 비어 있더라도 같은 방향으로 동시에 여러 포지션을 가질 수 있는 이 로봇의 경우 문제가 해결되지 않습니다.
위의 스크린샷은 3개의 매도 포지션을 보여주고 있으며 더 많은 포지션이 있을 수 있습니다. 포지션은 서로 다른 보호 정지 수준을 가지므로 단일 순 포지션으로 결합할 수 없습니다. 해결책은 새 중복 기호에 대한 새 포지션을 여는 것입니다. 그러나 로봇 한 대가 6개의 중복 도구(각 거래 방향에 3개)가 필요하기 때문에 그러한 기호가 충분하지 않을 수 있습니다. 두 로봇이 다른 시간 프레임에서 실행되는 경우 12개의 기호가 필요합니다.
브로커 중 어느 것도 그렇게 많은 중복 기호를 제공하지 않습니다. 그러나 그러한 기호가 무제한으로 존재하고 항상 무료라고 해도 알고리즘의 복잡한 분해가 필요합니다. 로봇은 복제품과 자신의 포지션을 찾기 위해 사용 가능한 모든 기호를 살펴봐야 합니다 이것은 해결할 수 있는 것보다 더 많은 문제를 일으킬 것입니다.
중복 기호에는 더 많은 어려움이 있습니다. 다음은 사용으로 인해 발생하는 추가 문제에 대한 간략한 목록입니다.
- 잠금 또는 부분 잠금을 위한 스왑은 항상 음수이기 때문에 음수 스왑 형태로 각 중복 기호에 대해 비용을 지불합니다. 이는 두 개의 서로 다른 도구에서 두 개의 양방향 포지션를 유지하는 경우입니다.
- 모든 브로커가 중복 기호를 제공하는 것은 아닙니다. 중복 기호를 제공하는 브로커를 위해 개발된 전략은 하나의 도구만 제공하는 브로커와 작동하지 않습니다. 기호 이름의 차이는 문제의 또 다른 잠재적 원인입니다.
- 복제 기호를 만드는 것이 항상 가능한 것은 아닙니다. 엄격한 규제가 적용되는 투명한 시장에서 모든 거래는 재무 문서입니다. 순 포지션은 이러한 시장에서 사실상의 표준이므로 개별 기호를 만드는 것은 불가능합니다. 예를 들어 중복 기호를 제공하는 브로커는 모스크바 거래소 MOEX에 나타날 수 없습니다. 덜 엄격하게 규제되는 시장에서 중개인은 고객을 위한 기호를 만들 수 있습니다.
- 로봇을 사용하여 거래할 때 중복 상품은 비효율적입니다. 그들의 비효율에 대한 이유는 Chaos 2 EA의 위의 예에서 공개되었습니다.
중복 기호는 본질적으로 브로커 측의 가상화입니다. HedgeTerminal은 클라이언트 측에서 가상화를 사용합니다.
두 경우 모두 가상화를 그대로 사용합니다. 그것은 트레이더의 의무의 실제 표현을 변경합니다. 가상화를 사용하면 한 포지션이 두 포지션으로 바뀔 수 있습니다. 클라이언트 측에서 발생하면 클라이언트가 원하는 대로 표현할 수 있으므로 문제가 없습니다. 그러나 브로커가 가상화를 수행하는 경우 규제 기관 및 라이선스 기관은 제공된 정보가 실제 정보와 어떻게 관련되는지에 대해 의문을 가질 수 있습니다. 두 번째 어려움은 하나에 두 개의 API가 있어야 한다는 것입니다. 하나는 네트 모드에서 사용하기 위한 함수 및 수정자 (modifier) 세트이고 다른 하나는 양방향 모드에서 사용하기 위한 것입니다.
많은 알고리즘 거래자는 거래를 단일 포지션으로 묶는 고유한 방법을 찾았습니다. 이러한 방법 중 많은 부분이 잘 작동하며 이러한 방법을 설명하는 문서가 있습니다. 하지만 포지션 가상화는 생각보다 복잡한 절차다. HedgeTerminal에서 포지션 가상화와 관련된 알고리즘은 약 20,000줄의 소스 코드를 사용합니다. 또한, HedgeTerminal은 기본적인 기능만을 구현합니다. 양방향 포지션를 동반하기 위해 EA에 비슷한 양의 코드를 생성하는 것은 너무 많은 리소스를 소모합니다.
제 2 장. HedgeTerminal API 매뉴얼
2.1. 거래 선택 기능
함수 TransactionTotal()
이 제품은 총 목록을 반환합니다. 이것은 사용 가능한 거래를 검색하는 기본 기능입니다(이 글의 1.4 및 1.11 섹션의 예 참조).
int TransactionsTotal(ENUM_MODE_TRADES pool = MODE_TRADES);
--> 매개변수
- [in] pool=MODE_TRADES – 선택할 데이터 소스의 식별자를 지정합니다. ENUM_MODE_TRADES 열거형의 값 중 하나일 수 있습니다.
반환 값
이 제품은 총 목록을 반환합니다.
함수 TransactionType()
이 제품은 총 목록을 반환합니다.
ENUM_TRANS_TYPE TransactionType(void);
--> 반환 값
반환 유형. 값은 ENUM_TRANS_TYPE 값 중 하나일 수 있습니다.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 TransactionSelect()
이 기능은 추가 조작을 위해 거래를 선택합니다. 함수는 거래 목록에서 인덱스 또는 고유 식별자로 거래를 선택합니다.
bool TransactionSelect(int index, ENUM_MODE_SELECT select = SELECT_BY_POS, ENUM_MODE_TRADES pool=MODE_TRADES );-->
매개변수
- [in] index – 주문 목록의 주문 색인 또는 'select' 매개변수에 따른 거래의 고유 식별자입니다.
- [in] select=SELECT_BY_POS – 매개변수의 '색인' 유형의 식별자입니다. 값은 ENUM_MODE_SELECT 값 중 하나일 수 있습니다.
- [in] pool=MODE_TRADES – 선택할 데이터 소스의 식별자를 지정합니다. ENUM_MODE_TRADES 열거형의 값 중 하나일 수 있습니다.
반환 값
Returns true if a transaction has been successfully selected or false otherwise. 오류 세부 정보를 얻으려면 GetHedgeError()를 호출하세요.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
메모
인덱스를 기준으로 거래를 선택하면 연산의 복잡성은 O(1)에 해당합니다. 거래가 고유 식별자를 기반으로 선택되면 작업의 복잡성은 점근적으로 O(log2(n)) 경향이 있습니다.
함수 HedgeOrderSelect()
기능은 양방향 포지션에 포함된 주문 중 하나를 선택합니다. 필수 주문을 포함하는 양방향 포지션는 TransactionSelect()를 사용하여 미리 선택해야 합니다.
bool HedgeOrderSelect(ENUM_HEDGE_ORDER_SELECTED_TYPE type);
--> 매개변수
- [in] 유형 – 선택할 주문의 식별자입니다. 값은 ENUM_HEDGE_ORDER_SELECTED_TYPE 열거형 값 중 하나일 수 있습니다.
반환 값
주문이 성공적으로 선택되었으면 true를 반환하고 그렇지 않으면 false를 반환합니다. 오류 세부 정보를 얻으려면 GetHedgeError()를 호출하세요.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 HedgeDealSelect()
함수는 주문을 실행한 거래 중 하나를 선택합니다. 선택된 거래가 있는 주문은 HedgeOrderSelect() 함수를 사용하여 미리 선택되어야 합니다.
bool HedgeDealSelect(int index);-->
매개변수
- [in] index – 주문을 실행한 거래 목록에서 선택할 거래의 인덱스입니다. 한 주문의 총 거래 수를 확인하려면 HedgeOrderGetInteger() 함수를 사용하여 적절한 주문 속성을 호출하십시오. 매개변수의 경우 HEDGE_ORDER_DEALS_TOTAL 값과 동일한 ENUM_HEDGE_ORDER_PROP_INTEGER 수정자 (modifier)를 사용합니다.
반환 값
Returns true if a deal has been successfully selected or false otherwise. 오류 세부 정보를 얻으려면 GetHedgeError()를 호출하세요.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
2.2. 선택한 거래의 속성을 가져오는 함수
함수 HedgePositionGetInteger()
이 함수는 선택한 양방향 포지션의 속성을 반환합니다. 속성은 int, long, datetime< 유형일 수 있습니다. 또는 bool은 요청된 속성의 유형에 따라 다릅니다. 양방향 포지션는 TransactionSelect() 함수를 사용하여 미리 선택해야 합니다.
ulong HedgePositionGetInteger(ENUM_HEDGE_POSITION_PROP_INTEGER property);
--> 매개변수
- [in] 속성 – 양방향 포지션 속성의 식별자입니다. 값은 ENUM_HEDGE_DEAL_PROP_INTEGER 열거형 값 중 하나일 수 있습니다.
반환 값
ulong 유형의 값입니다. 값을 추가로 사용하려면 해당 유형을 요청된 속성의 유형으로 명시적으로 캐스팅해야 합니다.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 HedgePositionGetDouble()
이 함수는 선택한 양방향 포지션의 속성을 반환합니다. 반환 속성의 유형은 double입니다. 속성 유형은 ENUM_HEDGE_POSITION_PROP_DOUBLE 열거를 통해 지정됩니다. 양방향 포지션는 TransactionSelect()를 사용하여 미리 선택해야 합니다.
ulong HedgePositionGetDouble(ENUM_HEDGE_POSITION_PROP_DOUBLE property);
--> 매개변수
- [in] 속성 – 양방향 포지션 속성의 식별자입니다. 값은 ENUM_HEDGE_DEAL_PROP_DOUBLE 열거형 값 중 하나일 수 있습니다.
반환 값
double 유형의 값입니다.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 HedgePositionGetString()
이 함수는 선택한 양방향 포지션의 속성을 반환합니다. 속성은 문자열 유형입니다. 속성 유형은 ENUM_HEDGE_POSITION_PROP_STRING 열거를 통해 지정됩니다. 양방향 포지션는 TransactionSelect()를 사용하여 미리 선택해야 합니다.
ulong HedgePositionGetString(ENUM_HEDGE_POSITION_PROP_STRING property);
--> 매개변수
- [in] 속성 – 양방향 포지션 속성의 식별자입니다. 값은 ENUM_HEDGE_POSITION_PROP_STRING 열거형 값 중 하나일 수 있습니다.
반환 값
문자열 유형의 값입니다.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 HedgeOrderGetInteger()
이 함수는 양방향 포지션의 일부인 선택한 주문의 속성을 반환합니다. 속성은 int, long, datetime유형일 수 있습니다. 또는 bool. 속성 유형은 ENUM_HEDGE_ORDER_PROP_INTEGER 열거를 통해 지정됩니다. HedgeOrderSelect() 함수를 사용하여 주문을 미리 선택해야 합니다.
ulong HedgeOrderGetInteger(ENUM_HEDGE_ORDER_PROP_INTEGER property);
--> 매개변수
- [in] 속성 – 양방향 포지션의 일부인 주문 속성의 식별자입니다. 값은 ENUM_HEDGE_ORDER_PROP_INTEGER 열거형 값 중 하나일 수 있습니다.
반환 값
ulong 유형의 값입니다. For further use of the value, its type must be explicitly cast to the type of the requested property.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 HedgeOrderGetDouble()
이 함수는 양방향 포지션의 일부인 선택한 주문의 속성을 반환합니다. 요청한 속성은 이중 유형입니다. 속성 유형은 ENUM_HEDGE_ORDER_PROP_DOUBLE 열거를 통해 지정됩니다. HedgeOrderSelect() 함수를 사용하여 주문을 미리 선택해야 합니다.
double HedgeOrderGetDouble(ENUM_HEDGE_ORDER_PROP_DOUBLE property);
--> 매개변수
- [in] 속성 – 양방향 포지션의 일부인 주문 속성의 식별자입니다. 값은 ENUM_HEDGE_ORDER_PROP_DOUBLE 열거형 값 중 하나일 수 있습니다.
반환 값
double 유형의 값입니다.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 HedgeDealGetInteger()
이 함수는 실행된 주문의 일부인 선택한 거래의 속성을 반환합니다. 속성은 int, long, datetime유형일 수 있습니다. 또는 bool. 속성 유형은 ENUM_HEDGE_DEAL_PROP_INTEGER 열거를 통해 지정됩니다. HedgeDealSelect() 함수를 사용하여 거래를 미리 선택해야 합니다.
ulong HedgeOrderGetInteger(ENUM_HEDGE_DEAL_PROP_INTEGER property);
--> 매개변수
- [in] 속성 – 실행된 주문에 포함된 선택한 거래의 속성 식별자입니다. 값은 ENUM_HEDGE_DEAL_PROP_INTEGER 열거형 값 중 하나일 수 있습니다.
반환 값
ulong 유형의 값입니다. 값을 추가로 사용하려면 해당 유형을 요청된 속성의 유형으로 명시적으로 캐스팅해야 합니다.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 HedgeDealGetDouble()
이 함수는 실행된 주문의 일부인 선택한 거래의 속성을 반환합니다. 속성은 double 유형일 수 있습니다. 속성 유형은 ENUM_HEDGE_DEAL_PROP_DOUBLE 열거를 통해 지정됩니다. HedgeDealSelect() 함수를 사용하여 거래를 미리 선택해야 합니다.
ulong HedgeOrderGetDouble(ENUM_HEDGE_DEAL_PROP_DOUBLE property);
--> 매개변수
- [in] 속성 – 실행된 주문에 포함된 선택한 거래의 속성 식별자입니다. 값은 ENUM_HEDGE_DEAL_PROP_DOUBLE 열거형 값 중 하나일 수 있습니다.
반환 값
double 유형의 값입니다.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
2.3. Expert Advisors로부터 HedgeTerminal 속성을 설정하고 가져오기 위한 기능
함수 HedgePropertySetInteger()
함수는 HedgeTerminal 속성 중 하나를 설정합니다. 속성은 int, long, datetime유형일 수 있습니다. 또는 bool. 속성 유형은 ENUM_HEDGE_PROP_INTEGER 열거를 통해 지정됩니다.
bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, long value);-->
매개변수
- [in] 속성 – HedgeTerminal에 대해 설정해야 하는 속성의 식별자입니다. 값은 ENUM_HEDGE_PROP_INTEGER 열거형 값 중 하나일 수 있습니다.
반환 값
bool 유형의 값입니다. 속성이 성공적으로 설정되면 함수는 true를 반환하고, 그렇지 않으면 false를 반환합니다.
사용 예
예제에서 함수는 비동기 요청을 보내는 동안 포지션 잠금 시간을 설정하는 데 사용됩니다. 비동기 요청을 보낸 후 30초 이내에 서버 응답이 수신되지 않으면 차단된 포지션이 차단 해제됩니다.
void SetTimeOut() { bool res=HedgePropertySetInteger(HEDGE_PROP_TIMEOUT,30); if(res) printf("The property is set successfully"); else printf("Property is not set"); }-->
함수 HedgePropertyGetInteger()
함수는 HedgeTerminal 속성 중 하나를 가져옵니다. 속성은 int, long, datetime유형일 수 있습니다. 또는 bool. 속성 유형은 ENUM_HEDGE_PROP_INTEGER 열거를 통해 지정됩니다.
long HedgePropertyGetInteger(ENUM_HEDGE_PROP_INTEGER property);
--> 매개변수
- [in] 속성 – HedgeTerminal에서 수신해야 하는 속성의 식별자입니다. 값은 ENUM_HEDGE_PROP_INTEGER 열거형 값 중 하나일 수 있습니다.
반환 값
long 유형의 값입니다.
사용 예
이 함수는 비동기 요청을 보내는 동안 포지션 차단 시간을 수신하여 터미널에 표시합니다.
void GetTimeOut() { int seconds=HedgePropertyGetInteger(HEDGE_PROP_TIMEOUT); printf("Timeout is "+(string) seconds); }-->
2.4. 함수 for Getting and Handling Error Codes
함수 GetHedgeError()
이 함수는 마지막 작업에서 얻은 오류의 식별자를 반환합니다. 오류 식별자는 ENUM_HEDGE_ERR 열거에 해당합니다.
ENUM_HEDGE_ERR GetHedgeError(void);
--> 반환 값
포지션 ID. 값은 ENUM_HEDGE_ERR 열거 유형 중 하나일 수 있습니다.
메모
호출 후 GetHedgeError() 함수는 오류 ID를 재설정하지 않습니다. 오류 ID를 재설정하려면 ResetHedgeError() 함수를 사용하십시오.
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 ResetHedgeError()
이 함수는 마지막으로 수신된 오류의 식별자를 재설정합니다. 호출 후 GetHedgeError()에 의해 반환된 ENUM_HEDGE_ERR 식별자는 HEDGE_ERR_NOT_ERROR와 같습니다.
void ResetHedgeError(void);-->
사용 예
이 글의 섹션 1.11에서 함수 사용의 예를 참조하십시오: "스크립트의 예를 통한 양방향 포지션 속성 관리".
함수 TotalActionsTask()
HedgePositionSelect() 함수를 사용하여 포지션이 선택되면 SendTradeRequest() 함수를 사용하여 수정할 수 있습니다. 예를 들어 닫거나 나가는 주석을 변경할 수 있습니다. 이 수정은 특별한 거래 작업에 의해 수행됩니다. 각 작업은 여러 거래 활동(하위 작업)으로 구성될 수 있습니다. 작업이 실패할 수 있습니다. 이 경우 작업에 포함된 모든 하위 작업의 결과를 분석하여 어떤 종류의 하위 작업이 실패했는지 확인해야 할 수 있습니다.
TotalActionTask() 함수는 선택한 포지션 대해 실행 중인 마지막 거래 작업에 포함된 하위 작업의 수를 반환합니다. 전체 하위 작업의 수를 알면 모든 하위 작업을 인덱스별로 검색하고 GetActionResult() 함수를 사용하여 실행 결과를 분석하여 실패 상황을 파악할 수 있습니다.
uint TotalActionsTask(void);-->
반환 값
작업 내의 총 하위 작업 수를 반환합니다.
사용 예
이 문서의 섹션 1.6에서 사용 예를 참조하십시오. "TotalActionsTask() 및 GetActionResult()를 사용하여 거래에 대한 세부 분석 및 오류 식별".
함수 GetActionResult()
이 함수는 작업 내 하위 작업의 인덱스를 사용합니다(TotalActionTask() 참조). 참조 매개변수를 통해 하위 작업의 유형과 실행 결과를 반환합니다. 하위 작업의 유형은 ENUM_TARGET_TYPE 열거형으로 정의됩니다. 하위 작업 실행 결과는 MetaTrader 5 거래 서버 반환 코드에 해당합니다.
void GetActionResult(uint index, ENUM_TARGET_TYPE& target_type, uint& retcode);-->
매개변수
- [in] index – 하위 작업 목록에서 하위 작업의 인덱스입니다.
- [out] target_type – 하위 작업의 유형입니다. 값은 ENUM_TARGET_TYPE 열거형 값 중 하나일 수 있습니다.
- [out] retcode – 하위 작업 실행 시 수신된 거래 서버 반환 코드입니다.
사용 예
이 문서의 섹션 1.6에서 사용 예를 참조하십시오. "TotalActionsTask() 및 GetActionResult()를 사용하여 거래에 대한 세부 분석 및 오류 식별".
2.5. 거래
함수 SendTradeRequest()
이 함수는 HedgeTerminalAPI에서 선택한 양방향 포지션를 변경하라는 요청을 보냅니다. 함수 실행 결과는 다음 세 가지 작업 중 하나입니다.
- 포지션 또는 거래량의 일부를 청산하는 행위;
- 손절매 및 이익실현 수준 수정;
- 나가는 댓글 수정.
작업 유형 및 해당 매개변수는 참조로 매개변수로 전달되는 HedgeTradeRequest 구조에 지정됩니다. 함수를 호출하기 전에 TransactionSelect() 함수를 사용하여 양방향 포지션를 미리 선택해야 합니다.
bool SendTradeRequest(HedgeTradeRequest& request);
--> 매개변수
[in] 요청 – 양방향 포지션 수정 요청의 구조입니다. HedgeTradeRequest 구조 설명에서 구조 설명과 해당 필드에 대한 설명을 참조하세요.
반환 값
포지션 수정 요청이 성공적으로 실행되면 true를 반환합니다. 그렇지 않으면 false를 반환합니다. 요청 실행 실패의 경우 TotalActionsTask() 및 GetActionResult() 함수를 사용하여 실패와 그 이유를 찾습니다.
메모
요청 전송의 비동기 모드에서 작업이 성공적으로 배치되고 시작된 경우 반환 플래그에 true가 포함됩니다. 그러나 태스크가 성공적으로 시작되어도 실행이 보장되지 않는다는 점을 기억해야 합니다. 따라서 이 플래그는 비동기 모드에서 작업 완료를 제어하는 데 사용할 수 없습니다. 동기 모드에서는 단일 스레드에서 작업을 시작하고 실행하므로 동기 모드에서는 이 플래그를 사용하여 거래 요청 실행 결과를 제어할 수 있습니다.
거래 요청 구조 HedgeTradeRequest()
HedgeTerminal의 양방향 포지션은 거래 요청이 인수로 사용되는 SendTradeRequest() 함수의 호출을 통해 닫히고 수정됩니다. 요청은 선택한 포지션을 닫거나 수정하는 데 필요한 모든 필드를 포함하는 사전 정의된 특수 구조 HedgeTradeRequest로 표시됩니다.
struct HedgeTradeRequest { ENUM_REQUEST_TYPE action; // type of action double volume; // volume of position ENUM_CLOSE_TYPE close_type; // Marker of closing order double sl; // stop-loss level double tp; // take-profit level string exit_comment; // outgoing comment uint retcode; // last retcode in executed operation bool asynch_mode; // true if the closure is performed asynchronously, otherwise false ulong deviation; // deviation in step price HedgeTradeRequest() // default params { action=REQUEST_CLOSE_POSITION; asynch_mode=false; volume=0.0; sl = 0.0; tp = 0.0; retcode=0; deviation=3; } };-->
필드 설명
필드 | 설명 |
---|---|
동작 | 포지션에 필요한 작업 유형입니다. 값은 ENUM_REQUEST_TYPE 열거형 값 중 하나일 수 있습니다. |
용량 | 닫을 볼륨입니다. 현재 활성 포지션의 볼륨보다 작을 수 있습니다. 볼륨이 0이면 활성 포지션이 완전히 닫힙니다. |
sl | 활성 포지션에 배치할 손절매 수준입니다. |
tp | 활성 포지션에 배치할 이익실현 수준입니다. |
exit_comment | 활성 포지션에 대한 나가는 주석입니다. |
재코드 | Result code of the last executed operation. |
asynch_mode | True if the asynchronous mode for sending requests is used, false otherwise. |
deviation | Maximum deviation from the used price. |
2.6. 거래 선택 함수 작업을 위한 열거
ENUM_TRANS_TYPE
보류 중인 주문 및 양방향 포지션을 포함하여 분석에 사용할 수 있는 모든 거래는 활성 및 과거 거래 목록에 있습니다.
ENUM_TRANS_TYPE 열거형에는 선택한 각 거래의 유형이 포함됩니다. 이 열거는 TransactionType() 함수에 의해 반환됩니다. 다음은 열거 필드와 해당 설명입니다.
필드 | 설명 |
---|---|
TRANS_NOT_DEFINED | 거래가 TransactionSelect() 함수에 의해 선택되지 않았거나 해당 유형이 정의되지 않았습니다. |
TRANS_HEDGE_POSITION | 거래는 양방향 포지션입니다. |
TRANS_BROKERAGE_DEAL | 거래는 브로커의 거래(계정 작업)입니다. 예를 들어, 계정에 돈을 추가하거나 수정합니다. |
TRANS_PENDING_ORDER | 거래가 보류 중인 주문입니다. |
TRANS_SWAP_POS | 거래는 순 포지션에 대해 부과되는 스왑입니다. |
ENUM_MODE_SELECT
열거형은 TransactionSelect() 함수에 설정된 index 매개변수의 유형을 정의합니다.
필드 | 설명 |
---|---|
SELECT_BY_POS | index 매개변수는 목록에 있는 거래의 인덱스를 전달하는 데 사용됩니다. |
SELECT_BY_TICKET | 티켓 번호는 색인 매개변수로 전달됩니다. |
ENUM_MODE_TRADES
열거형은 TransactionSelect()를 사용하여 거래가 선택되는 데이터 소스를 정의합니다.
필드 | 설명 |
---|---|
MODE_TRADES | 거래는 활성 거래에서 선택됩니다. |
MODE_HISTORY | 거래는 과거 거래에서 선택됩니다. |
2.7. 거래 속성을 가져오는 함수로 작업하기 위한 열거
열거 ENUM_TRANS_DIRECTION
거래든 양방향 포지션이든 모든 거래에는 시장 방향이 있습니다.
이 시장 방향은 ENUM_TRANS_DIRECTION 열거에 의해 정의됩니다. 다음은 해당 필드와 설명입니다.
필드 | 설명 |
---|---|
TRANS_NDEF | 거래의 방향은 정의되지 않습니다. 예를 들어, 계정에 대한 브로커의 거래에는 시장 방향이 없으며 이 수정자 (modifier)와 함께 제공됩니다. |
TRANS_LONG | 거래(주문 또는 양방향 포지션)가 매수 거래임을 나타냅니다. |
TRANS_SHORT | 거래(주문 또는 양방향 포지션)가 매도 거래임을 나타냅니다. |
열거 ENUM_HEDGE_POSITION_STATUS
열거형에는 양방향 포지션의 상태가 포함됩니다.
필드 | 설명 |
---|---|
HEDGE_POSITION_ACTIVE | 활성 포지션. 활성 포지션는 HedgeTerminal 패널의 활성 탭에 나타납니다. |
HEDGE_POSITION_HISTORY | 역사적 입장. 내역상 포지션 HedgeTerminal 패널의 History 탭에 나타납니다. |
열거 ENUM_HEDGE_POSITION_STATE
열거형에는 양방향 포지션의 상태가 포함됩니다.
필드 | 설명 |
---|---|
POSITION_STATE_ACTIVE | 선택한 포지션은 활성 상태이며 HedgeTradeRequest를 사용하여 수정할 수 있습니다. |
POSITION_STATE_FROZEN | 선택한 포지션이 잠겨 있어 수정할 수 없습니다. 이 수정자 (modifier)가 수신되면 포지션이 잠금 해제될 때까지 기다려야 합니다. |
열거 ENUM_HEDGE_POSITION_PROP_INTEGER
열거형은 HedgePositionGetInteger()에 의해 반환된 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_POSITION_ENTRY_TIME_SETUP_MSC | 양방향 포지션를 시작하는 주문이 이루어진 1970년 1월 1일 이후의 시간(밀리초)입니다. |
HEDGE_POSITION_ENTRY_TIME_EXECUTED_MSC | 양방향 포지션를 시작하는 주문이 실행된 1970년 1월 1일 이후의 시간(밀리초)입니다(포지션 열림 시간). |
HEDGE_POSITION_EXIT_TIME_SETUP_MSC | 양방향 포지션을 청산하라는 명령이 내려진 1970년 1월 1일 이후의 시간(밀리초)입니다. |
HEDGE_POSITION_EXIT_TIME_EXECUTED_MSC | 양방향 포지션을 청산하라는 명령이 실행된 1970년 1월 1일 이후의 시간(밀리초)입니다(포지션 청산 시간). |
HEDGE_POSITION_TYPE | 양방향 포지션의 유형입니다. 시작 순서의 유형과 동일합니다. 시스템 열거 ENUM_ORDER_TYPE의 값 중 하나를 포함합니다. |
HEDGE_POSITION_DIRECTION | 포지션 방향. 열거형 ENUM_TRANS_DIRECTION에 의해 정의됩니다. |
HEDGE_POSITION_MAGIC | 선택한 직책이 속한 Expert Advisor의 매직 넘버입니다. 값이 0이면 포지션이 수동으로 열렸음을 나타냅니다. |
HEDGE_POSITION_CLOSE_TYPE | 포지션을 마감하는 주문의 마커. ENUM_CLOSE_TYPE에 의해 정의됩니다. |
HEDGE_POSITION_ID | 포지션 ID. 시작 주문의 식별자와 동일합니다. |
HEDGE_POSITION_ENTRY_ORDER_ID | 시작 주문의 식별자입니다. |
HEDGE_POSITION_EXIT_ORDER_ID | 내역상 포지션에 대한 마감 주문의 식별자입니다. |
HEDGE_POSITION_STATUS | 포지션 상태. ENUM_HEDGE_POSITION_STATUS에 의해 정의됩니다. |
HEDGE_POSITION_STATE | 포지션 상태. ENUM_HEDGE_POSITION_STATE에 의해 정의됩니다. |
HEDGE_POSITION_USING_SL | 손절매 사용 플래그입니다. 손절매가 사용되면 HedgePositionGetInteger() 함수는 true를 반환하고 그렇지 않으면 false를 반환합니다. |
HEDGE_POSITION_USING_TP. | 사용된 테이크 프로핏 레벨의 플래그입니다. 이익실현을 사용하면 HedgePositionGetInteger()가 true를 반환하고, 그렇지 않으면 false를 반환합니다. |
HEDGE_POSITION_TASK_STATUS | 선택한 포지션에 대해 수행 중인 작업의 상태입니다. 포지션은 수정 중일 수 있습니다. 이 수정자 (modifier)는 이 포지션의 변경 사항을 추적하는 데 사용됩니다. 포지션 상태는 ENUM_TASK_STATUS에 의해 정의됩니다. |
HEDGE_POSITION_ACTIONS_TOTAL | 이 포지션을 변경하기 위해 시작된 하위 작업의 총 수를 반환합니다. |
열거 ENUM_HEDGE_POSITION_PROP_DOUBLE
열거형은 HedgePositionGetDouble() 함수에 의해 반환된 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_POSITION_VOLUME | 양방향 포지션의 볼륨입니다. |
HEDGE_POSITION_PRICE_OPEN | 포지션의 가중 평균 열린 가격. |
HEDGE_POSITION_PRICE_CLOSED | 포지션의 가중 평균 종가입니다. |
HEDGE_POSITION_PRICE_CURRENT | 활성 포지션의 현재 가격. 과거 포지션의 경우 이 수정자 (modifier)는 포지션 마감 가격을 반환합니다. |
HEDGE_POSITION_SL | 손절매 수준. 정지 손실이 사용되지 않으면 0입니다. |
HEDGE_POSITION_TP | 이익 수준. 이익실현을 사용하지 않으면 0입니다. |
HEDGE_POSITION_COMMISSION | 직책에 대해 지불한 수수료 금액입니다. |
HEDGE_POSITION_SLIPPAGE | 점 슬리피지. |
HEDGE_POSITION_PROFIT_CURRENCY | 지위의 이익 또는 손실. The value is specified in the deposit currency. |
HEDGE_POSITION_PROFIT_POINTS | 지위의 이익 또는 손실. 값은 포지션의 재무 기호의 포인트에 지정됩니다. |
메모
슬리피지 HEDGE_POSITION_SLIPPAGE는 최고의 포지션 진입 거래와 평균 가중 진입 가격 간의 포인트 차이로 계산됩니다.
열거 ENUM_HEDGE_POSITION_PROP_STRING
열거형은 HedgePositionGetString() 함수에 의해 반환된 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_POSITION_SYMBOL | 현재 포지션의 기호입니다. |
HEDGE_POSITION_ENTRY_COMMENT | 포지션의 들어오는 코멘트. |
HEDGE_POSITION_EXIT_COMMENT | 포지션의 나가는 코멘트. |
Enumeration ENUM_HEDGE_ORDER_STATUS
열거형에 주문 유형이 포함되어 있습니다.
필드 | 설명 |
---|---|
HEDGE_ORDER_PENDING | 주문이 보류 중이며 MetaTrader 5의 거래 탭에서 사용할 수 있습니다. |
HEDGE_ORDER_HISTORY | 주문은 기록이며 MetaTrader 5의 주문 기록에서 사용할 수 있습니다. |
열거 ENUM_HEDGE_ORDER_SELECTED_TYPE
열거형은 HedgeOrderSelect() 함수에 의해 선택된 주문 유형을 정의합니다.
필드 | 값 |
---|---|
ORDER_SELECTED_INIT | 주문은 양방향 포지션를 시작합니다. |
ORDER_SELECTED_CLOSED | 주문은 양방향 포지션를 시작합니다. |
ORDER_SELECTED_SL | 주문은 손절매 수준으로 작동합니다. |
열거 ENUM_HEDGE_ORDER_PROP_INTEGER
열거형은 HedgePositionGetInteger() 함수에 의해 반환된 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_ORDER_ID | 고유한 주문 식별자입니다. |
HEDGE_ORDER_STATUS | 주문 상태. 값은 ENUM_HEDGE_ORDER_STATUS 열거형 값 중 하나일 수 있습니다. |
HEDGE_ORDER_DEALS_TOTAL | 주문을 완료한 총 거래 수입니다. 보류 중인 주문의 경우 값은 0입니다. |
HEDGE_ORDER_TIME_SETUP_MSC | 1970년 1월 1일부터 대기 중인 주문 시간(밀리초)입니다. |
HEDGE_ORDER_TIME_EXECUTED_MSC | 1970년 1월 1일 이후 실행된 주문의 실행 시간(밀리초)입니다. |
HEDGE_ORDER_TIME_CANCELED_MSC | 1970년 1월 1일 이후 실행된 주문을 취소한 시간(밀리초)입니다. |
메모
주문 실행 시간 HEDGE_ORDER_TIME_EXECUTED_MSC은(는) 최근 거래 시간과 같습니다.
열거 ENUM_HEDGE_ORDER_PROP_DOUBLE
열거형은 HedgeOrderGetDouble() 함수에 의해 반환된 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_ORDER_VOLUME_SETUP | 주문에 지정된 주문의 양입니다. |
HEDGE_ORDER_VOLUME_EXECUTED | 주문의 실행된 볼륨입니다. 주문이 보류 중인 경우 실행된 볼륨은 0입니다. |
HEDGE_ORDER_VOLUME_REJECTED | 실행할 수 없는 주문의 양입니다. 초기 볼륨과 실행 볼륨의 차이와 같습니다. |
HEDGE_ORDER_PRICE_SETUP | 주문 가격. |
HEDGE_ORDER_PRICE_EXECUTED | 주문의 평균 가중 실행 가격입니다. |
HEDGE_ORDER_COMMISSION | 주문 실행을 위해 브로커에게 지불한 수수료 금액입니다. 예금 통화로 지정됩니다. |
HEDGE_ORDER_SLIPPAGE | 주문 슬리피지. |
메모
슬리피지 HEDGE_ORDER_SLIPPAGE는 가장 잘 실행된 거래와 주문의 가중 평균 진입 가격 간의 포인트 차이로 계산됩니다.
Enumeration ENUM_HEDGE_DEAL_PROP_INTEGER
열거형은 HedgeDealGetInteger()에 의해 반환된 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_DEAL_ID | 고유한 거래 식별자입니다. |
HEDGE_DEAL_TIME_EXECUTED_MSC | 1970년 1월 1일 이후의 거래 실행 시간(밀리초) |
Enumeration ENUM_HEDGE_DEAL_PROP_DOUBLE
열거형은 HedgeDealGetDouble()에 의해 반환된 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_DEAL_VOLUME_EXECUTED | 거래량. |
HEDGE_DEAL_PRICE_EXECUTED | 거래 실행 가격. |
HEDGE_DEAL_COMMISSION | 거래 실행을 위해 브로커에게 지불한 수수료 금액입니다. 예금 통화로 지정됩니다. |
2.8. HedgeTerminal 속성 설정 및 가져오기를 위한 열거형
열거 ENUM_HEDGE_PROP_INTEGER
열거형은 HedgeTerminal에서 가져오거나 설정하려는 속성의 유형을 설정합니다.
필드 | 설명 |
---|---|
HEDGE_PROP_TIMEOUT | HedgeTerminal이 수정 중인 포지션을 잠금 해제하기 전에 서버의 응답을 기다리는 시간(초)입니다. |
2.9. 오류 코드 처리 함수 작업을 위한 열거
열거 ENUM_TASK_STATUS
모든 양방향 포지션는 수정될 수 있습니다. 포지션은 트레이드 태스크를 통해 수정됩니다.
실행 중인 모든 거래 작업에는 ENUM_TASK_STATUS에 정의된 실행 상태가 있습니다. 다음은 해당 필드와 설명입니다.
필드 | 설명 |
---|---|
TASK_STATUS_WAITING | 현재 작업이 없거나 작업이 대기 중입니다. |
TASK_STATUS_EXECUTING | 거래 작업이 현재 실행 중입니다. |
TASK_STATUS_COMPLETE | 포지션에 대한 거래 작업이 성공적으로 완료되었습니다. |
TASK_STATUS_FAILED | 포지션에 대한 거래 작업이 실패했습니다. |
Enumeration ENUM_HEDGE_ERR
열거형에는 GetHedgeError()에서 반환할 수 있는 오류의 ID가 포함되어 있습니다.
필드 | 설명 |
---|---|
HEDGE_ERR_NOT_ERROR | 오류가 없습니다. |
HEDGE_ERR_TASK_FAILED | 선택한 직책에 대한 작업이 실패했습니다. |
HEDGE_ERR_TRANS_NOTFIND | 거래를 찾을 수 없습니다. |
HEDGE_ERR_WRONG_INDEX | 잘못된 색인입니다. |
HEDGE_ERR_WRONG_VOLUME | 잘못된 볼륨입니다. |
HEDGE_ERR_TRANS_NOTSELECTED | TransactionSelect()를 사용하여 거래가 미리 선택되지 않았습니다. |
HEDGE_ERR_WRONG_PARAMETER | 전달된 매개변수 중 하나가 올바르지 않습니다. |
HEDGE_ERR_POS_FROZEN | 양방향 포지션는 현재 수정 중이며 새로운 변경 사항에 사용할 수 없습니다. 포지션이 해제될 때까지 기다리십시오. |
HEDGE_ERR_POS_NO_CHANGES | 거래 요청에 변경 사항이 없습니다. |
열거 ENUM_TARGET_TYPE
열거형은 GetActionResult() 함수에 의해 선택된 작업의 유형을 정의합니다.
필드 | 설명 |
---|---|
TARGET_NDEF | 하위 작업이 정의되지 않았습니다. |
TARGET_CREATE_TASK | 현재 하위 작업을 만드는 중입니다. 이 유형은 HedgeTerminalAPI의 내부 로직에서 사용됩니다. |
TARGET_DELETE_PENDING_ORDER | 보류 중인 주문을 삭제합니다. |
TARGET_SET_PENDING_ORDER | 보류 중인 주문을 제출합니다. |
TARGET_MODIFY_PENDING_ORDER | 보류 주문 가격 수정. |
TARGET_TRADE_BY_MARKET | 거래 작업을 수행합니다. |
2.10. 오류 코드 처리 함수 작업을 위한 열거
열거 ENUM_REQUEST_TYPE
열거형은 양방향 포지션에 적용된 HedgeTerminal의 동작을 설명합니다.
필드 | 설명 |
---|---|
REQUEST_CLOSE_POSITION | 포지션을 닫습니다. HedgeTradeRequest 구조의 볼륨 필드에 현재 볼륨보다 낮은 볼륨이 포함된 경우 해당 포지션의 일부만 청산됩니다. 이 경우 닫힌 포지션의 일부는 볼륨 필드의 값에 해당합니다. |
REQUEST_MODIFY_SLTP | 손절매 및 이익실현의 기존 수준을 설정하거나 수정합니다. |
REQUEST_MODIFY_COMMENT | 활성 포지션의 나가는 주석을 수정합니다. |
열거 ENUM_CLOSE_TYPE
열거형은 양방향 포지션를 닫는 주문에 대한 특수 마커를 정의합니다. 마커는 포지션 마감 사유를 나타냅니다. 다음 이유 중 하나일 수 있습니다.
- 포지션이 최대 손실 수준 또는 손절매에 도달했습니다.
- 포지션이 특정 이익 수준에 도달했거나 이익을 얻었습니다.
- 시장에 의해 폐쇄된 포지션. 손절매 및 이익실현 수준이 설정되거나 도달되지 않았습니다.
필드 | 설명 |
---|---|
CLOSE_AS_MARKET | 포지션이 시장에 의해 마감되었음을 나타냅니다. 손절매 및 이익실현 수준이 설정되거나 도달되지 않았습니다. |
CLOSE_AS_STOP_LOSS | 손절매 수준에 도달하여 포지션이 마감되었음을 나타냅니다. |
CLOSE_AS_TAKE_PROFIT | 이익 실현 수준에 도달하여 포지션이 마감되었음을 나타냅니다. |
3 장. 비동기 거래의 기초
비동기 작업의 주제는 복잡하며 별도의 자세한 문서가 필요합니다. 그러나 HedgeTerminal은 비동기식 작업을 적극적으로 사용하기 때문에 이러한 유형의 요청 제출을 사용하여 Expert Advisors의 구성 원칙을 간략하게 설명하는 것이 적절합니다. 또한 주제에 대한 자료가 거의 없습니다.
3.1. 동기 거래 주문을 보내는 조직 및 체계
MetaTrader 5는 거래 요청을 서버로 보내기 위한 두 가지 기능을 제공합니다.
OrderSend() 함수는 채워진 MqlTradeRequest 구조로 요청을 수락하고 구조 정확성의 기본 검증을 수행합니다. 기본 검증이 성공하면 서버에 요청을 보내고 그 결과를 기다린 후 MqlTradeResult 구조와 반환 플래그를 통해 결과를 커스텀 쓰레드에 반환합니다. 기본 검증이 실패하면 함수는 음수 값을 반환합니다.
요청을 확인할 수 없는 이유도 MqlTradeResult에 포함되어 있습니다.
아래 체계는 OrderSend() 함수를 사용하여 사용자 정의 MQL5 프로그램의 스레드를 실행하는 것을 특징으로 합니다.
그림 6. 동기 거래 요청의 조직 및 전송 계획.
스키마에서 볼 수 있듯이 MQL5 프로그램의 스레드는 서버에 요청을 보내고 거래소에서 거래 작업을 수행하는 공통 시스템 스레드와 분리될 수 없습니다.
그렇기 때문에 OrderSend()가 완료된 후 실제 거래 요청 결과를 분석할 수 있습니다. 사용자 정의 스레드는 빨간색 화살표로 표시됩니다. 거의 즉시 실행됩니다. 대부분의 시간은 거래소에서 거래 작업을 수행하는 데 소요됩니다. 두 개의 쓰레드가 연결되어 있기 때문에 OrderSend() 함수의 시작과 끝 사이에 상당한 시간이 흐른다. 거래 작업이 단일 스레드에서 실행된다는 사실 때문에 MQL5 프로그램의 논리는 순차적일 수 있습니다.
3.2. 비동기 거래 주문을 보내는 조직 및 체계
OrderSendAsync() 함수가 다릅니다. OrderSend()와 마찬가지로 거래 요청 MqlTradeRequest을 수락하고 그 결과를 나타내는 플래그를 반환합니다.
그러나 첫 번째 예와 달리 서버에서 거래 요청이 실행될 때까지 기다리지 않고 거래 요청 값의 기본 검증(단말기 내부 기본 검증) 모듈에서 얻은 값만 반환합니다. 아래 체계는 OrderSendAsync() 함수를 사용할 때 사용자 정의 스레드 실행 절차를 보여줍니다.
그림 7. 비동기 거래 요청의 조직 및 전송 계획.
거래 요청이 성공적으로 확인되면 메인 스레드와 병렬로 거래 서버로 전송됩니다. 네트워크를 통한 거래 요청의 전달과 교환에서의 실행은 첫 번째 경우와 같이 시간이 걸립니다. 그러나 사용자 정의 스레드는 OrderSendAsync() 함수에서 거의 즉각적인 결과를 얻습니다.
위의 방식은 OrderSendAsync()가 실제로 거래 서버에 의해 실행되는 새로운 병렬 스레드를 형성하고 그 실행 결과가 OnTradeTransaction() 또는 OnTrade() 함수. 이 함수는 새로운 사용자 정의 스레드를 시작합니다. 거래 요청을 보낸 결과는 이 새 스레드에서 처리되어야 합니다. 이것은 Expert Advisor의 논리를 크게 복잡하게 합니다. 비동기 주문 전송을 사용하면 단일 스레드에서 요청 전송 및 확인을 구성하는 것이 불가능하기 때문입니다. 예를 들어 OnTick()에 요청을 보내고 확인하는 코드를 순차적으로 배치할 수 없습니다.
위의 내용을 설명하기 위해 간단한 테스트 EA를 작성해 보겠습니다.
//+------------------------------------------------------------------+ //| AsynchExp.mq5 | //| Copyright 2014, Vasiliy Sokolov (C-4). | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2014, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" input bool UsingAsynchMode=true; bool sendFlag=false; //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- if(sendFlag)return; printf("Formation of order and send to the server..."); MqlTradeRequest request={0}; request.magic=12345; request.symbol = Symbol(); request.volume = 0.1; request.type=ORDER_TYPE_BUY; request.comment= "asynch test"; request.action = TRADE_ACTION_DEAL; request.type_filling=ORDER_FILLING_FOK; MqlTradeResult result; uint tiks= GetTickCount(); bool res = false; if(UsingAsynchMode) res=OrderSendAsync(request,result); else res=OrderSend(request,result); uint delta=GetTickCount()-tiks; if(OrderSendAsync(request,result)) { printf("The order has been successfully"+ "sent to the server."); } else { printf("The order is not shipped."+ " Reason: "+(string)result.retcode); } printf("Time to send a trade request: "+(string)delta); sendFlag=true; //--- }-->
UsingAsynchMode = false로 시작하여 EA가 작동하는지 확인합시다.
EA는 0.1랏 매수 포지션을 엽니다. 거래 요청은 OrderSend() 함수를 사용하여 동기적으로 수행됩니다. 다음은 샘플 로그입니다.
2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1) Time to send a trade request: 94 2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1) The order has been successfullysent to the server. 2014.11.06 17:49:28.345 AsynchExp (AUDCAD,H1) Formation of order and send to the server...-->
거래 요청은 94밀리초 이내에 완료되었습니다. 이번에는 요청이 기본 확인을 통과하고 서버로 전송된 다음 채워졌음을 알려줍니다.
이제 거래 볼륨을 가능한 최대값 DBL_MAX으로 변경하여 EA 코드를 수정합니다.
request.volume = DBL_MAX;
--> 분명히 이 값은 실제 범위를 벗어났습니다. 동기 모드에서 이 요청을 실행해 보겠습니다.
2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1) Time to send a trade request: 0 2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1) The order is not shipped. Reason: 10014 2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1) Formation of order and send to the server...-->
요청을 보내지 못했습니다. 실패 이유는 오류 10014(잘못된 요청 볼륨)입니다. 기본 검증 과정에서 요청이 실패했고, 요청 실행 시간이 0밀리초인 만큼 서버에도 제대로 전달되지 않았다.
다시 요청을 변경해 보겠습니다. 이번에는 충분히 큰 볼륨을 지정했지만 극단적인 값이 아닌 15랏을 지정했습니다. EA가 테스트되는 1,000달러의 계정에는 너무 많습니다. 이 계정에서는 이러한 포지션을 열 수 없습니다.
OrderSend()가 반환하는 내용을 살펴보겠습니다.
2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1) Time to send a trade request: 78 2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1) The order is not shipped. Reason: 10019 2014.11.06 17:59:22.550 AsynchExp (AUDCAD,H1) Formation of order and send to the server...-->
이번에는 오류가 다릅니다: 10019(요청 실행을 위한 자금 부족, 사실임). 요청 실행 시간은 이제 79밀리초입니다. 요청이 서버로 전송되었고 서버가 오류를 반환했음을 나타냅니다.
이제 OrderSendAsync() 함수를 사용하여 15-lot 볼륨으로 동일한 요청을 보내겠습니다. OrderSend()와 마찬가지로 포지션이 열리지 않습니다. 하지만 로그를 분석해보겠습니다.
2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1) Time to send a trade request: 0 2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1) The order has been successfully sent to the server. 2014.11.06 18:03:58.104 AsynchExp (AUDCAD,H1) Formation of order and send to the server...-->
로그는 오류가 없다고 알려줍니다! 오류 10019는 거래 서버에서 감지했기 때문에 비동기 주문 전송 모드에서 현재 스레드에 사용할 수 없습니다. 반환 값은 요청이 기본 확인을 통과했음을 나타냅니다. 실제 오류 10019를 얻으려면 OnTradeTransaction() 시스템 함수의 새 사용자 정의 스레드에서 결과를 분석해야 하며, 이는 EA에 추가되어야 합니다.
void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { uint delta = GetTickCount() - tiks; printf("Server answer: " + (string)result.retcode + "; Time: " + (string)delta); }-->
EA를 다시 실행하고 로그를 살펴보겠습니다.
2014.11.06 18:17:00.943 AsynchExp (AUDCAD,H1) Server answer: 10019; Time: 94 2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1) Time to send a trade request: 0 2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1) The order has been successfully sent to the server. 2014.11.06 18:17:00.851 AsynchExp (AUDCAD,H1) Formation of order and send to the server...-->
오류 10019가 수신되었지만 전송 직후가 아닙니다. OnTradeTransaction()에서 실행 중인 새 맞춤 스레드에서 수신되었습니다.
3.3. 비동기식 주문 실행 속도
거래자는 비동기 요청의 실행 속도가 0에 가깝다고 잘못 생각합니다.
이는 OrderSendAsync()의 관찰에서 비롯되며 일반적으로 1밀리초 이내에 완료됩니다. 실제로는 위에서 보았듯이 OnTradeTransaction() 또는 OnTrade()< 함수 내에서 서브로부터 응답을 받았을 때 실제 거래 실행 시간을 측정해야 합니다. . 이 측정은 단일 주문에 대한 동기 실행 속도와 동일한 실제 속도를 보여줍니다. 실행 시간의 실질적인 이점은 거래 그룹을 보낼 때 감지할 수 있습니다. 여러 요청을 보내야 하는 세 가지 상황이 있습니다.
- 두 개의 연속 요청 사이에 필요한 시간이 너무 짧아 다음 요청을 보내기 전에 요청 결과를 확인할 가능성이 없습니다. 다음 요청이 전송되면 이전 요청이 실행되기를 바랍니다. 유사한 전술이 고주파 거래에 사용됩니다.
- 한 번에 여러 기호에 대해 여러 포지션을 열어야 합니다. 예를 들어, 차익 거래 전략과 합성 합성 포지션은 현재 가격으로 다양한 상품에 대한 포지션을 동시에 개설해야 합니다. 이러한 전술에서 점진적으로 포지션을 형성하는 것은 바람직하지 않습니다.
- 가능한 한 빨리 스레드를 완료하고 추가 이벤트 및 사용자 명령을 기다려야 합니다. 이 요구 사항은 다중 스레드 및 인프라 솔루션에 중요합니다. 이것이 HedgeTerminal이 비동기식 요청을 사용하는 주된 이유입니다. HT가 동기식 요청 전송을 사용하는 경우 사용자가 포지션을 닫거나 수정할 때마다 1-2초 동안 계속 정지되어 허용되지 않습니다.
여러 주문을 할 때 요청 전송 한도를 고려하십시오.
MetaTrader 5 빌드 1010 이상에서 한도는 64개 거래이며, 그 중 4개는 사용자를 위해 예약되어 있고 나머지는 Expert Advisors가 사용할 수 있습니다. 이 제한은 초보 거래자를 프로그램의 심각한 오류로부터 보호하고 거래 서버의 스팸 부하를 줄이는 것을 목표로 합니다.
이는 예를 들어 for 루프에서 동시에 적절한 거래 요청과 함께 SendOrderAsync()를 호출하여 최대 60개의 거래 주문을 보낼 수 있음을 의미합니다. 60개의 거래가 모두 전송된 후에는 거래 버퍼가 가득 찰 것입니다. 거래 중 하나가 서버에서 처리되었다는 서버의 확인을 기다려야 합니다.
처리된 후 거래 버퍼의 거래 포지션이 해제되고 새로운 거래 요청이 이를 차지할 수 있습니다. 버퍼가 가득 차면 거래 서버가 각 거래를 처리하는 데 시간이 필요하고 처리 시작을 알리는 TradeTransaction() 이벤트가 네트워크를 통해 전달되기 때문에 새로운 거래를 위한 공간이 천천히 해제됩니다. , 추가 지연이 발생합니다.
따라서 요청을 보내는 데 필요한 시간은 요청 수의 증가에 비해 비선형적으로 증가합니다. 아래 표는 비동기 모드에서 예상되는 주문 전송률을 나타냅니다. 테스트는 여러 번 수행되었으며 표시된 비율은 평균값입니다.
요청 수 | 시간, 밀리초 |
---|---|
50 | 50 |
100 | 180 |
200 | 2100 |
500 | 9000 |
1000 | 23000 |
요청 수가 60개 미만인 경우 스크립트는 서버 응답을 기다리지 않기 때문에 시간이 매우 짧습니다. 단일 요청을 보내는 데 걸리는 시간과 거의 같습니다. 실제로 대략적인 실제 실행 시간을 얻으려면 테이블에 지정된 요청 배치 시간에 평균 요청 실행 시간을 더하십시오.
4장. MetaTrader 5 IDE에서 멀티 스레드 프로그래밍의 기초
MQL5 프로그래머는 스레드가 MQL 프로그램에서 직접 제어될 수 없다는 것을 알고 있습니다. 스레드를 사용하면 프로그램 알고리즘이 크게 복잡해지기 때문에 이 제한은 초보 프로그래머를 위한 것입니다. 그러나 어떤 상황에서는 둘 이상의 EA가 서로 통신해야 합니다. 예를 들어 글로벌 데이터를 생성하고 읽어야 합니다.
HedgeTerminal은 그러한 EA 중 하나입니다. HedgeTerminalAPI 라이브러리를 사용하는 모든 EA에 다른 Expert Advisors의 작업을 알리기 위해 HT는 ActivePositions.xml 파일의 다중 스레드 읽기 및 쓰기를 통해 데이터 교환을 구성합니다. 이 솔루션은 중요하지 않으며 MQL 프로그래머가 거의 사용하지 않습니다. 따라서 HedgeTerminal과 유사한 알고리즘으로 다중 스레드 EA를 생성합니다. 이것은 다중 스레드 프로그래밍을 더 잘 이해하고 HedgeTerminal이 어떻게 작동하는지 더 잘 이해하는 데 도움이 됩니다.
4.1. Quote Collector 예제를 통한 다중 스레드 프로그래밍 UnitedExchangeQuotes
특정 예를 통해 다중 스레드 프로그래밍의 기본 사항을 배웁니다. 다양한 제공자(브로커)의 견적 수집기를 작성할 것입니다.
아이디어는 다음과 같습니다. 동일한 상품에 대해 견적을 제공하는 중개인이 6-7명 있다고 가정합니다. 당연히 다른 브로커의 견적은 약간 다를 수 있습니다. 이러한 차이점에 대한 분석은 차익거래 전략의 길을 열어줍니다. 또한 시세 역학을 비교하면 최상의 공급자와 최악의 공급자를 식별하는 데 도움이 됩니다. 예를 들어 브로커가 더 나은 가격을 제공하는 경우 이 브로커를 선택하여 거래합니다. 우리는 결과의 실용적인 가치를 추구하는 것이 아니라 이러한 결과를 얻을 수 있는 메커니즘만 설명합니다.
다음은 이 장의 끝에서 작성해야 할 EA의 스크린샷입니다.
그림 8. 견적 수집가 UnitedExhangesQuotes의 모습입니다.
Expert Advisor는 4개의 열과 무제한 행으로 구성된 간단한 테이블에 결과를 표시합니다.
각 행은 기호 따옴표(이 경우 EURUSD)를 제공하는 브로커를 나타냅니다. Ask and Bid는 브로커의 최고의 제안이자 요구입니다. 스크린샷은 가격이 약간 다르다는 것을 보여줍니다. 현재 중개인의 제안과 다른 중개인의 제안 간의 차이는 D-ASK(Delta Ask) 열에 나타납니다. 마찬가지로 수요 값의 차이는 D-BID(Delta Bid)로 표시됩니다. 예를 들어 스크린샷을 찍을 당시 가장 좋은 Ask는 "Alpari Limited"가 제공했고 가장 비싼 것은 "Bank VTB 24"가 제공했습니다.
MQL 프로그램은 다른 MetaTrader 터미널의 환경에 액세스할 수 없습니다. 즉, 프로그램이 한 터미널에서 실행 중이면 다른 터미널에서 데이터를 받을 수 없습니다. 그러나 모든 MQL 프로그램은 MetaTrader 터미널의 공유 디렉토리에 있는 파일을 통해 통신할 수 있습니다. 프로그램이 정보를 쓰는 경우, 예를 들어 현재 따옴표를 파일에 대한 다른 터미널의 MQL 프로그램이 읽을 수 있습니다. MQL에는 외부 DLL이 없는 다른 수단이 없습니다. 따라서 우리는 이 방법을 사용할 것입니다.
가장 큰 어려움은 이러한 액세스를 구성하는 것입니다. 한편으로 EA는 다른 공급자의 견적을 읽어야 하고 다른 한편으로는 공급자의 견적을 동일한 파일에 작성해야 합니다. 또 다른 문제는 인용문을 읽을 때 다른 EA가 이 파일에 새 인용문을 쓸 수 있다는 사실입니다. 이러한 병렬 작업의 결과는 예측할 수 없습니다. 기껏해야 충돌 및 프로그램 중단이 뒤따를 것이며, 최악의 경우 따옴표 표시와 관련된 이상한 미묘한 오류가 가끔 발생하게 됩니다.
이러한 오류를 제거하거나 최소한 발생 가능성을 최소화하기 위해 명확한 계획을 개발할 것입니다.
첫째, 모든 정보는 XML 형식으로 저장됩니다. 이 형식은 서투른 ini 파일을 대체했습니다. XML을 사용하면 노드를 클래스와 같은 복잡한 데이터 구조에 유연하게 배포할 수 있습니다. 다음으로 읽기 쓰기의 일반적인 알고리즘을 알아봅시다. 데이터 읽기와 데이터 쓰기의 두 가지 기본 작업이 있습니다. MQL 프로그램이 읽거나 쓸 때 다른 프로그램은 이 파일에 액세스할 수 없습니다. 따라서 우리는 한 프로그램이 데이터를 읽고 두 번째 프로그램이 데이터를 변경하는 상황을 제거합니다. 이로 인해 데이터에 대한 액세스가 항상 가능한 것은 아닙니다.
XML 액세스 알고리즘과 이 파일의 모든 인용 데이터를 포함하는 특수 클래스 CQuoteList를 만들어 보겠습니다.
이 클래스의 함수 중 하나는 TryGetHandle()이며 파일 액세스를 시도하고 성공하면 핸들을 반환합니다. 다음은 함수의 구현입니다.
int CQuoteList::TryGetHandle(void) { int attempts = 10; int handle = INVALID_HANDLE; // We try to open 'attemps' times for(att = 0; att < attempts; att++) { handle = FileOpen("Quotes.xml", FILE_WRITE|FILE_READ|FILE_BIN|FILE_COMMON); if(handle == INVALID_HANDLE) { Sleep(15); continue; } break; } return handle; }-->
결합된 읽기/쓰기 모드에서 파일을 열려고 여러 번 시도합니다. 기본 시도 횟수는 10입니다.
시도가 성공하지 못하면 함수는 15밀리초 동안 정지하고 파일 열기를 다시 시도하므로 최대 10번의 시도를 합니다.
파일이 열리면 해당 핸들이 LoadQuotes() 함수로 전달됩니다. 이 함수의 전체 목록과 CQuoteList 클래스는 글의 첨부 파일로 사용할 수 있습니다. 따라서 여기서는 함수의 작업 순서만 설명합니다.
- TryGetHandle()은 읽고 쓸 파일을 엽니다.
- XML 문서는 XML Parser 라이브러리를 사용하여 EA 메모리에 업로드됩니다.
- 업로드된 XML 문서를 기반으로 필요한 정보를 저장하는 새로운 따옴표 배열이 형성됩니다.
- 생성된 배열에는 현재 EA에 속하는 견적이 포함되어 있습니다. 값이 업데이트됩니다.
- 따옴표 배열은 다시 XML 문서로 변환됩니다. 열린 XML 파일의 내용은 이 XML 문서로 대체됩니다.
- 따옴표의 XML 파일이 닫힙니다.
LoadQuotes() 함수는 훌륭한 작업을 수행하지만 대부분의 경우 1밀리초 미만이 소요됩니다.
추가 저장과 함께 데이터 읽기 및 데이터 업데이트가 하나의 블록으로 결합됩니다. 이것은 읽기와 쓰기 작업 사이에 파일에 대한 액세스 제어를 잃지 않기 위해 의도적으로 수행됩니다.
따옴표가 로드되고 클래스 안에 있으면 MetaTrader 5의 다른 데이터와 마찬가지로 액세스할 수 있습니다. 이것은 CQuotesList 클래스에 구현된 특별한 MetaTrader 5 스타일 프로그램 인터페이스를 통해 수행됩니다.
함수 호출 및 데이터 렌더링은 OnTick() 블록 내에서 수행됩니다. 내용은 다음과 같습니다..
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT)) return; if(!QuotesList.LoadQuotes()) return; PrintQuote quote = {0}; Panel.DrawAccess(QuotesList.CountAccess()); double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); string brokerName = AccountInfoString(ACCOUNT_COMPANY); for(int i = 0; i < QuotesList.BrokersTotal(); i++) { if(!QuotesList.BrokerSelect(i)) continue; if(!QuotesList.SymbolSelect(Symbol())) continue; quote.ask = QuotesList.QuoteInfoDouble(QUOTE_ASK); quote.bid = QuotesList.QuoteInfoDouble(QUOTE_BID); quote.delta_ask = ask - quote.ask; quote.delta_bid = quote.bid - bid; quote.broker_name = QuotesList.BrokerName(); quote.index = i; Panel.DrawBroker(quote); } }-->
샘플 코드는 추가 수정 없이 MetaTrader 4와 MetaTrader 5 모두에서 작동합니다!
다른 버전의 터미널에서 패널이 표시되는 방식에는 약간의 외관상의 차이점만 있습니다. 의심할 여지 없이 이것은 플랫폼 간의 코드 이식을 용이하게 하는 놀라운 사실입니다.
EA의 작동은 역학에서 가장 잘 관찰됩니다. 아래 비디오는 다른 계정에 대한 EA 작업을 보여줍니다.
파일을 읽고 쓰는 것은 상당한 이점이 있지만 몇 가지 단점도 있습니다.
주요 이점은 다음과 같습니다.
- 유연성. 전체 클래스를 포함한 모든 데이터를 저장하고 로드할 수 있습니다.
- 비교적 빠른 속도. 읽기 및 다시 쓰기의 전체 주기는 거의 항상 1밀리초 이상이 소요되며, 이는 80 - 150밀리초 또는 때로는 그 이상이 소요되는 비교적 느린 거래 작업과 비교할 때 좋은 시간입니다.
- DLL을 호출하지 않고 MQL5 언어의 표준 도구를 기반으로 합니다.
이러한 솔루션의 주요 단점은 스토리지 시스템에 대한 심각한 부하입니다. 하나의 따옴표와 두 개의 브로커가 있는 경우 재작성 작업의 수는 비교적 적지만 많은 수의 따옴표와 많은 수의 브로커/심볼로 인해 재작성 작업의 수는 매우 많아집니다. 데모 EA는 1시간 이내에 90,000개 이상의 Quotes.xml 파일 재작성 작업을 생성했습니다. 이러한 통계는 EA 패널 상단에 표시됩니다. "I/O Rewrite"는 총 파일 재작성 횟수를 표시하고, "fps"는 마지막 두 재작성 작업 사이의 비율을 표시하고, "Avrg"는 당 평균 재작성 속도를 표시합니다. 두번째.
SSD 또는 HDD에 파일을 저장하는 경우 이러한 작업은 디스크 수명에 부정적인 영향을 미칩니다. 따라서 이러한 데이터 교환에는 가상 RAM 디스크를 사용하는 것이 좋습니다.
위의 예와 달리 HedgeTerminal은 ActivePositions.xml을 드물게 사용하여 전역 컨텍스트를 통해 액세스할 수 없는 중요한 포지션 변경만 작성합니다. 따라서 위의 예보다 훨씬 적은 읽기/쓰기 작업을 생성하므로 RAM 디스크와 같은 특별한 조건이 필요하지 않습니다.
4.2. Expert Advisor 간의 다중 스레드 상호 작용 사용
독립적인 MQL 프로그램 간의 실시간 상호 작용은 복잡하지만 흥미로운 주제입니다. 글에는 그것에 대한 매우 간단한 설명만 포함되어 있지만 별도의 글로 볼 가치가 있습니다. 대부분의 경우 Expert Advisors 간의 다중 스레드 상호 작용은 필요하지 않습니다. 그러나 다음은 이러한 상호 작용의 조직이 필요한 작업 및 다양한 프로그램 목록입니다.
- 거래 복사기. 거래 복사기는 최소 2개의 EA를 동시에 실행해야 하며, 그 중 하나는 거래를 제공하고 다른 하나는 복사합니다. 이 경우 거래를 제공하고 복사하기 위해 공통 데이터 파일의 다중 스레드 읽기/쓰기를 구성해야 합니다.
- EA 간의 글로벌 데이터 교환, 글로벌 변수. MetaTrader 5의 표준 글로벌 변수는 하나의 터미널 수준에서만 Expert Advisors가 사용할 수 있습니다. 한 터미널에서 선언된 전역 변수는 다른 터미널에서 사용할 수 없습니다. 그러나 공통 데이터를 사용하여 다른 버전의 모든 터미널에 사용할 수 있는 복잡한 전역 변수를 구성할 수 있습니다.
- 차익 거래 전략. 다양한 유동성 제공업체의 견적 분석기. 다른 브로커가 제공하는 가격의 차이가 큰 경우 거래자는 차익 거래 전략을 만들어 이점을 얻을 수 있습니다. 분석기는 또한 최고의 가격에 대한 통계를 수집하고 최고의 유동성 공급자를 객관적으로 식별할 수 있도록 합니다.
첨부 파일에 대한 설명
다음은 글에 첨부된 파일과 편집 절차에 대한 간략한 설명입니다.
Prototypes.mqh는 HedgeTerminalAPI 라이브러리 기능에 대한 설명이 있는 파일입니다. 이 파일에는 HedgeTerminalAPI 라이브러리의 기능에 대한 설명과 프로토타입이 포함되어 있습니다. 이를 통해 EA는 라이브러리에서 사용할 수 있는 함수와 수정자 (modifier), 함수 호출 방법, 반환되는 값을 알 수 있습니다.
이 파일을 C:\Program Files\MetaTrader 5\MQL5\Include에 저장합니다. 여기서 "C:\Program Files\MetaTrader 5\"는 디렉토리 이름입니다. MetaTrader 5 터미널이 설치되었습니다. 파일이 올바른 디렉토리에 복사되면 MQL 프로그램에서 해당 파일을 참조할 수 있습니다. HedgeTerminalAPI 라이브러리를 사용해야 할 때마다 이 작업을 수행해야 합니다. Prototypes.mqh 파일을 참조하려면 코드에 특수 파일 포함 디렉토리를 추가하십시오.
#include <Prototypes.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- //... // Here is the content of your program. //... }-->
위의 예에서 이 지시문은 노란색으로 표시되며 "#include <Ptototypes.mqh>"라고 합니다. 이제 위의 스크립트는 라이브러리 함수를 참조하고 해당 기능을 사용할 수 있습니다.
HedgeTerminalAPI 라이브러리 개발 과정에서 프로토타입 파일이 약간의 변경이 있을 수 있습니다. 종종 라이브러리 버전을 업데이트하면 변경 사항을 설명하는 프로토타입 파일을 업데이트해야 합니다. 이 불편한 점은 너그럽게 이해해 주시기 바랍니다. 어쨌든 최신 버전의 프로토타입 파일은 항상 라이브러리에서 수동으로 설치하거나(설치 절차는 섹션 1.1에 설명되어 있음) 이 문서의 첨부 파일에서 다운로드할 수 있습니다(첨부 파일에 대한 정기적 업데이트 예상).
Chaos2.mqh 는 Chaos2 EA의 소스 코드입니다. 그 작동은 섹션 1.12에 설명되어 있습니다. "Chaos II EA의 예를 통한 SendTradeRequest 기능 및 HedgeTradeRequest 기능의 예. 코드를 성공적으로 컴파일하려면 함수 프로토타입 파일을 해당 디렉토리 \Include에 저장하고 HedgeTerminalAPI 라이브러리를 C:\Program Files\MetaTrader 5\MQL5\Market\hedgeterminalapi에 저장하십시오. 예 5. 여기서 "C:\Program Files\MetaTrader 5\"는 MetaTrader 5 터미널이 있는 디렉토리(터미널 데이터 폴더)의 이름입니다. 설치되었습니다.
UnitedExchangeQuotes 소스 코드는 4장: "다중 스레드 프로그래밍의 기초 MetaTrader 5 IDE". 이 zip에는 다음 파일이 포함되어 있습니다.
- UnitedExchangeQuotes.mq5 - EA의 중앙 파일. 전문가 폴더: \MetaTrader 5\MQL5\Experts에 저장합니다. MetaEditor에서 이 파일을 컴파일하십시오.
- MultiThreadXML.mqh는 XML 파일에 대한 다중 스레드 액세스 알고리즘을 포함하는 기본 파일입니다. 독립적인 스레드 간의 정보 교환을 구성합니다. \MetaTrader 5\MQL5\Include에 저장합니다. 이 파일의 알고리즘은 CodeBase에서 사용할 수 있는 ya-sha가 개발한 특수 라이브러리를 기반으로 합니다. 그러나 다중 스레드 작업을 위해 약간 수정되었습니다. 첨부 파일에는 이 수정된 버전이 포함되어 있습니다. 다음 파일로 구성됩니다.
- XmlBase.mqh;
- XmlDocument.mqh;
- XmlAttribute.mqh;
- XmlElement.mqh.
- Panel.mqh 에는 예제에 설명된 패널 클래스가 포함되어 있습니다. 이 파일을 UnitedEchangesQuotes.mqh를 저장하는 동일한 디렉토리, 즉 \Experts 폴더에 저장하십시오.
아카이브의 모든 파일에는 상대 경로가 포함되어 있습니다. 예를 들어, UnitedExchangeQuotes.mq5 파일은 \MQL5\Experts 폴더에 있습니다. 이는 C:\Program Files\MetaTrader 5\MQL5\Experts\UnitedExchangeQuotes.mq5와 같은 MetaTrader 5 터미널 데이터 폴더의 동일한 하위 디렉토리에 위치해야 함을 의미합니다.
결론
우리는 HedgeTerminal 프로그램 인터페이스 작업에 대한 세부 사항을 고려했습니다.
이 라이브러리의 원칙은 MetaTrader 4 API와 매우 유사합니다. MetaTrader 4 API와 마찬가지로 거래(MetaTrader 4의 "주문" 개념과 유사) 작업을 시작하기 전에 먼저 TransactionSelect()를 사용하여 거래를 선택해야 합니다. 헤지 터미널에서의 거래는 원칙적으로 양방향 포지션입니다. 포지션이 선택되면 해당 속성을 가져오거나 거래 조치를 적용할 수 있습니다(예: 손절매 수준 설정 또는 닫기). 이 일련의 작업은 MetaTrader 4의 주문 작업 알고리즘과 거의 동일합니다.
양방향 포지션의 수와 속성에 대한 기본 정보 외에도 HedgeTerminal은 MetaTrader 5에서 직접 사용할 수 없고 복잡한 분석 계산이 필요한 값에 대한 액세스를 제공합니다. 예를 들어, 속성 중 하나만 요청하면 각 양방향 포지션의 슬리피지 정도를 볼 수 있습니다. 선택한 포지션 내 딜 수를 확인할 수 있습니다. 이러한 모든 계산과 필요한 거래 매칭은 HedgeTerminal이 시작되는 동안 "뒤에서" 수행됩니다. Trading Expert Advisor는 아무것도 계산할 필요가 없기 때문에 편리합니다. 필요한 모든 정보는 이미 계산되었으며 간단하고 직관적인 API를 통해 사용할 수 있습니다.
HedgeTerminal API 및 패널에서 공통 알고리즘을 사용하면 통합 데이터 표시가 가능합니다. 따라서 HedgeTerminal 패널에서 EA를 제어할 수 있으며 EA에서 변경한 사항은 패널에 바로 표시됩니다.
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/1316
![MQL5 Cookbook: 빠른 데이터 액세스를 위한 연관 배열 또는 사전 구현](https://c.mql5.com/2/18/MQL5_Associative_Arrays__1.png)
![HedgeTerminal 패널을 이용하여 MetaTrader 5로 양방향 매매와 포지션 헤징하기, 파트 1](https://c.mql5.com/2/17/HedgeTerminalaArticle200x200_2.png)
![CCanvas 클래스 공부하기. 투명 개체를 그리는 방법](https://c.mql5.com/2/17/CCanvas_class_Standard_library_MetaTrader5.png)
![모스크바 거래소의 파생 상품 시장 사례를 통한 거래소 가격 책정 원칙](https://c.mql5.com/2/12/MOEX.png)