English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
Trading Model 기반 Multi-Expert Advisor 양성

Trading Model 기반 Multi-Expert Advisor 양성

MetaTrader 5테스터 | 4 8월 2021, 16:45
104 0
Vasiliy Sokolov
Vasiliy Sokolov


소개

MetaTrader 5 터미널의 기술적 능력과 전략 테스터는 다중 통화 거래 시스템의 작업과 테스트를 결정합니다. MetaTrader 4를 위한 이러한 시스템 개발의 복잡성은 우선 여러 거래 상품의 틱 테스트를 통해 동시 틱이 불가능하다는 점을 감안했습니다. 또한 MQL4 언어의 제한된 언어 자원은 복잡한 데이터 구조의 구성과 데이터의 효율적인 관리를 허용하지 않았습니다.

MQL5 출시로 상황이 달라졌습니다. 이후 MQL5 객체 지향 접근 방식을 지원하고 개발된 보조 기능 메커니즘을 기반으로 하며 표준 라이브러리 기본 세트도 포함합니다. 데이터 구성에서 표준 시스템 기능을 위한 작업 인터페이스에 이르기까지 사용자의 일상적인 작업을 용이하게 하는 클래스라고 볼 수 있죠.

전략 테스터 및 터미널의 기술 사양은 다중 통화 EA를 사용할 수 있지만 여러 상품 또는 타임 프레임에서 동시에 단일 EA 작업을 병렬화하는 내장 방법이 없습니다. 이전과 마찬가지로 가장 간단한 경우 EA 작업의 경우 거래 상품의 이름과 타임 프레임을 결정하는 기호 창에서 실행해야 합니다. 결과적으로 MetaTrader 4 당시부터 받아 들여진 작업 방법론은 전략 테스터와 MetaTrader 5 터미널을 최대한 활용하는 것을 허용하지 않습니다.

상황은 해당 상품에 대한 총 거래 금액과 동일한 각 상품에 대해 하나의 누적 포지션만 허용된다는 사실로 인해 복잡합니다. 확실히 순 포지션으로의 전환은 정확하고 시기 적절합니다. 순 포지션은 특정 시장에 대한 거래자의 관심을 완벽하게 표현하는 데 가장 가깝습니다.

그러나 이러한 거래 구성은 거래 프로세스를 간단하고 쉽게 시각화하지 못합니다. 이전에는 EA가 오픈 오더를 선택하고 충분했습니다 (예: 매직 넘버를 사용하여 오더를 식별 할 수 있음). 이제 상품에 대한 순 포지션이 없다고 해서 현재 EA의 특정 인스턴스가 시장에 없다는 것을 의미하지는 않습니다!

타사 개발자는 가상 주문의 특별 관리자를 작성하는 것에서부터 순 위치 문제를 해결하기위한 다양한 방법을 제공합니다 (위치 중심 MT5 환경 내에서 주문을 추적하는 가상 주문 관리자 글 참조). 매직 넘버를 사용하여 집계 된 포지션의 입력을 통합합니다 (특정 매직 넘버에 의한 총 포지션 볼륨 계산을위한 최적 방법 또는 단일 상품에서의 Expert Advisors와의 거래를 위한 ORDER_MAGIC 사용 참조).

그러나 통합 포지션의 문제 외에도 여러 상품을 거래하기 위해 동일한 EA가 필요한 경우 소위 다중 통화 문제가 있습니다. 이 문제의 해결 방법은 다른 상품에서 거래하는 Expert Advisor 만들기 문서에서 찾을 수 있습니다.

제안된 방법은 모두 작동하며 고유한 장점이 있습니다. 그러나 이들의 치명적인 결점은 이러한 각 방법이 고유 한 관점에서 문제에 접근하려고 시도하는 것입니다. 예를 들어 단일 상품에서 여러 EA가 동시에 거래하는 데 적합하지만 다중 통화 솔루션에는 적합하지 않은 솔루션을 제공합니다.

이 글은 단일 솔루션으로 모든 문제를 해결하는 것을 목표로 합니다. 이 솔루션을 사용하면 단일 상품에서 서로 다른 EA 간의 상호 작용에 대한 다중 통화 및 다중 시스템 테스트 문제를 해결할 수 있습니다. 이것은 달성하기 어렵거나 불가능 해 보이지만 실제로는 훨씬 쉽습니다.

그냥 상상해보세요 당신의 단일 EA가 수십 개의 거래 전략에서 동시에 거래됩니다. 사용 가능한 모든 상품 및 가능한 모든 타임 프레임에서! 또한 EA는 테스터에서 쉽게 테스트되며 구성에 포함된 모든 전략에 대해 하나 또는 여러 개의 자금 관리 시스템이 있습니다.

따라서 해결해야 할 주요 작업은 다음과 같습니다.

  1. EA는 동시에 여러 거래 시스템을 기반으로 거래해야 합니다. 또한 단일 및 여러 거래 시스템에서 똑같이 쉽게 거래해야 합니다.
  2. EA에서 구현된 모든 거래 시스템은 서로 충돌하지 않아야 합니다. 각 거래 시스템은 총 순 포지션에 대한 자체 기여도와 자체 주문만 처리해야 합니다.
  3. 각 시스템은 한 번에 모든 타임 프레임 뿐만 아니라 상품의 단일 타임 프레임에서도 똑같이 쉽게 거래할 수 있어야 합니다.
  4. 각 시스템은 단일 거래 상품 뿐만 아니라 사용 가능한 모든 상품에서 한 번에 똑같이 쉽게 거래 할 수 있어야 합니다.

처리해야 할 작업 목록을 주의 깊게 살펴보면 3 차원 배열에 도달하게 됩니다. 배열의 첫 번째 차원 - 거래 시스템의 수, 두 번째 차원 - 특정 TS가 작동해야 하는 타임 프레임 수, 세 번째 - TS의 거래 상품 수입니다. MACD 샘플과 같은 간단한 EA도 8개의 주요 통화 쌍에서 동시에 작업할 때 152개의 독립 솔루션을 제공한다는 단순한 계산이 나왔습니다. 즉, 1 EA * 8쌍 * 19개의 타임 프레임이 되죠(주간 및 월간 타임 프레임은 포함되지 않음)

거래 시스템이 훨씬 더 크고 EA의 거래 포트폴리오가 훨씬 더 광범위하다면 솔루션의 수는 쉽게 500 개가 넘고 경우에 따라 1000 개가 넘을 수 있습니다! 수동으로 구성한 다음 각 조합을 개별적으로 업로드하는 것은 불가능하다는 것이 분명합니다. 따라서 자동으로 각 조합을 조정하고 EA의 메모리에 로드한 다음 EA가이 조합의 특정 인스턴스 규칙에 따라 거래하는 방식으로 시스템을 구축해야 합니다.


용어 및 개념

여기에서 "거래 전략"이라는 개념은 보다 구체적인 용어 거래 모델 또는 단순히 모델로 대체됩니다. 거래 모델은 거래 전략을 완전히 설명하는 특정 규칙에 따라 구축된 특수 클래스입니다. 거래에 사용되는 지표, 진입 및 퇴장의 거래 조건, 자금 관리 방법 등이 이에 해당하죠. 각 거래 모델은 추상적이며 운영에 대한 특정 매개 변수를 정의하지 않습니다.

간단한 예는 두 이동 평균의 교차를 기반으로 하는 거래 전술입니다. 빠른 이동 평균이 느린 이동 평균을 상향 교차하면 매수 거래를 시작하고, 반대로 하향하면 매도할 거래를 시작합니다. 이 공식은 그 근거로 거래되는 거래 모델을 작성하기에 충분합니다.

그러나 그러한 모델이 일단 설명되면, 평균 기간, 데이터 창의 기간 및 이 모델이 거래될 상품을 사용하여 이동 평균의 방법을 결정해야 합니다. 일반적으로이 추상 모델에는 특정 모델 인스턴스를 생성해야 할 때 채워야 하는 매개 변수가 포함됩니다. 이 접근 방식에서 추상 모델은 매개 변수가 다른 여러 모델 인스턴스의 부모가 될 수 있습니다.


순 포지션 회계의 완전한 거부 

많은 개발자가 집계된 포지션을 추적하려고 합니다. 그러나 위에서 보면 집계된 위치의 크기 나 역학이 모델의 특정 인스턴스와 관련이 없음을 알 수 있습니다. 모델은 짧을 수 있지만 집계 포지션은 전혀 존재하지 않을 수 있습니다 (중립 집계 위치). 반대로 집계된 포지션은 짧을 수 있고 모델은 롱 포지션을 가질 것입니다.

실제로 이러한 경우를 더 자세히 살펴 보겠습니다. 하나의 상품이 세 가지 다른 거래 전략으로 거래되며, 각 거래에는 자체 자금 관리 시스템이 있다고 가정합니다. 또한 3개 시스템 중 첫 번째가 커버없이 3개 계약을 매도하기로 결정했다고 가정합니다. 또는 간단히 말해서 3개 계약 볼륨으로 숏 포지션을 취합니다. 거래가 완료되면 순 포지션은 첫 번째 거래 시스템의 거래로만 구성되며 거래량은 마이너스 3개 계약 또는 커버가 없는 3개 계약의 숏 계약이 됩니다. 얼마 후 두 번째 거래 시스템은 동일한 자산의 4개 계약을 매수하기로 결정합니다.

결과적으로 순 포지션이 변경되고 1개의 매수 계약으로 구성됩니다. 이번에는 두 가지 거래 시스템의 기여도를 포함합니다. 또한 3 차 트레이딩 시스템이 등장해 동일한 자산으로 표준 계약 1건으로 숏 포지션을 하게 된다 -3 + 4 long -1 = 0이므로 순 포지션은 중립이 됩니다.

순 포지션이 없다는 것은 세 가지 거래 시스템이 모두 시장에 없다는 것을 의미합니까? 전혀 그렇지 않습니다. 그들 중 2개는 커버 없이 4개의 계약을 보유하고 있습니다. 반면 세 번째 시스템은 4개의 장기 계약을 보유하고 있으며 아직 매각되지 않았습니다. 4개의 숏 계약의 전액 상환이 완료되고 4개의 롱 계약의 커버 된 판매가 이루어진 경우에만 중립 포지션은 세 시스템 모두에서 포지션이 실질적으로 부족함을 의미합니다.

물론 매번 각 모델에 대한 전체 동작 시퀀스를 재구성하여 현재 위치 크기에서 특정 기여도를 결정할 수 있지만 훨씬 간단한 방법이 존재합니다. 이 방법은 간단합니다. 모든 규모가 될 수 있고 내부 요인(예: 수동 거래)과 내부 요인(한 상품에 대한 EA의 다른 모델의 작업) 모두에 의존할 수 있는 집계 위치의 회계처리를 완전히 포기해야 합니다. 현재 집계된 포지션은 의존할 수 없으므로 특정 모델 인스턴스의 작업을 어떻게 설명할까요?

가장 간단하고 효과적인 방법은 모델의 각 인스턴스에 자체 주문 테이블을 장착하는 것입니다. 이 테이블은 모든 주문 (둘 다, 보류 중, 거래에 의해 시작되거나 삭제 된 주문)을 고려합니다. 주문에 대한 방대한 정보가 트레이딩 서버에 저장됩니다. 주문 티켓을 알면 주문 개시 시간부터 수량에 이르기까지 주문에 대한 거의 모든 정보를 얻을 수 있습니다.

우리가 해야 할 유일한 일은 티켓 주문을 모델의 특정 인스턴스와 연결하는 것입니다. 각 모델 인스턴스는 특수 클래스의 개별 인스턴스 (모델 인스턴스에 의해 설정된 현재 주문 목록을 포함하는 주문 테이블)를 포함해야 합니다.


추상적인 거래 모델 예측

이제 특정 거래 전술의 기반이 될 모델의 공통 추상 클래스를 설명해보겠습니다. EA는 여러 모델 (또는 무제한)을 사용해야 하므로 이 클래스는 외부 전원 전문가가 신호를 제공하는 통일된 인터페이스를 가져야 합니다.

예를 들어, 이 인터페이스는 Processing() 함수 일 수 있습니다. 간단히 말해, 각 CModel 클래스에는 Processing() 함수가 있습니다. 이 함수는 매 틱 또는 매분마다 또는 Trade 유형의 새로운 이벤트가 발생할 때 호출됩니다.

다음은 이 작업을 해결하는 간단한 예입니다.

class CModel
{
protected:
   string            m_name;
public:
   void             CModel(){m_name="Model base";}
   bool virtual      Processing(void){return(true);}
};

class cmodel_macd : public CModel
{
public:
   void              cmodel_macd(){m_name="MACD Model";}
   bool              Processing(){Print("Model name is ", m_name);return(true);}
};

class cmodel_moving : public CModel
{
public:
   void              cmodel_moving(){m_name="Moving Average";}
   bool              Processing(){Print("Model name is ", m_name);return(true);}
};

cmodel_macd     *macd;
cmodel_moving   *moving;

이 코드가 어떻게 작동하는지 알아 봅시다. CModel 기본 클래스에는 m_name라는 문자열 유형의 보호된 변수가 하나 포함되어 있습니다. "protected" 키워드를 사용하면 계급 상속자가 이 변수를 사용할 수 있으므로 해당 자손에 이미 이 변수가 포함됩니다. 또한 기본 클래스는 Processing() 가상 함수를 정의합니다. 이 경우'virtual' 단어는 이것은 Expert Advisor와 모델의 특정 인스턴스 사이의 인터페이스 또는 래퍼 (wrapper)입니다.

CModel에서 상속 된 모든 클래스는 상호 작용을위한 Processing() 인터페이스를 갖도록 보장됩니다. 이 함수의 코드 구현은 그 자손에게 위임됩니다. 모델의 내부 작업이 서로 크게 다를 수 있으므로 이 위임은 분명합니다. 따라서 일반 수준 CModel에 위치 할 수있는 공통 일반화가 없습니다.

또한 두 클래스 cmodel_macdcmodel_moving에 대한 설명입니다. 둘 다 CModel 클래스에서 생성되므로 둘 다 Processing() 함수 및 m_name 변수의 자체 인스턴스를 갖습니다. 두 모델 모두 Processing() 함수의 내부 구현이 다릅니다. 첫 번째 모델에서는 Print ("cmodel_macd. Model name is ", m_name)으로 구성되고,Print("그건 cmodel_moving.의두 번째에서는 Model name is ", m_name)으로 구성됩니다. 다음으로 두 개의 포인터가 생성됩니다. 각각은 모델의 특정 인스턴스를 가리킬 수 있습니다. 하나는 cmodel_macd 유형의 클래스를 가리키고 다른 하나는 cmodel_moving 유형을 가리킵니다.

OnInit 함수에서 이러한 포인터는 동적으로 생성된 클래스 모델을 상속한 후 OnEvent() 함수 내에서 Processing() 함수 가 호출되며, 각 클래스에 포함되어 있습니다. 두 포인터 모두 전역 수준에서 공지되므로 OnInit() 함수를 종료 한 후에도 생성된 클래스는 삭제되지 않고 전역 수준에 계속 존재합니다. 이제 5 초마다 OnTimer() 함수가 두 모델을 차례로 샘플링하여 적절한 Processing() 기능을 호출합니다.

우리가 방금 만든 모델을 샘플링하는 이 원시 시스템은 유연성과 확장성이 부족합니다. 수십 개의 이러한 모델로 작업하려면 어떻게 해야 합니까? 각각 개별적으로 작업하는 것은 불편합니다. 모든 모델을 단일 커뮤니티 (예: 배열)로 수집 한 다음 함수, 각 요소의Processing()을 호출하여 이 배열의 모든 요소를 ​​반복하는 것이 훨씬 쉽습니다.

하지만 문제는 배열을 구성하려면 배열에 저장된 데이터가 동일한 유형이어야 한다는 것입니다. 우리의 경우 모델 cmodel_macdcmodel_moving 는 서로 매우 유사하지만 동일하지는 않습니다. 자동으로 배열에서 사용할 수 없게됩니다.

다행히도 배열은 데이터를 요약하는 유일한 방법이 아니며 더 유연하고 확장 가능한 일반화가 있습니다. 그중 하나는 연결 목록 기술입니다. 작업 계획은 간단합니다. 전체 목록에 포함된 각 항목에는 두 개의 포인터가 있어야 합니다. 하나의 포인터는 이전 목록 항목을 가리키고 두 번째 포인터는 다음 항목을 가리킵니다. 

또한 항목의 인덱스 번호를 알면 언제든지 참조할 수 있습니다. 항목을 추가하거나 삭제하려면 포인터와 인접 항목의 포인터를 다시 작성하면 충분하므로 서로를 지속적으로 참조할 수 있습니다. 이러한 커뮤니티의 내부 조직을 아는 것은 필요하지 않으며 공통 장치를 이해하는 것으로 충분합니다.

MetaTrader 5의 표준 설치에는 연결 목록으로 작업 할 수있는 기회를 제공하는 특수 보조 CList 클래스가 포함됩니다. 그러나 이 목록의 요소는 CObject 유형의 개체만 될 수 있습니다. 연결 목록 작업을 위한 특수 포인터만 가지고 있기 때문입니다. 그 자체로 CObject 클래스는 다소 원시적이며 단순히 CList클래스와 상호 작용하기 위한 인터페이스입니다.

구현을 살펴보면 이를 확인할 수 있습니다.

//+------------------------------------------------------------------+
//|                                                       Object.mqh |
//|                      Copyright © 2010, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//|                                              Revision 2010.02.22 |
//+------------------------------------------------------------------+
#include "StdLibErr.mqh"
//+------------------------------------------------------------------+
//| Class CObject.                                                   |
//| Purpose: Base class element storage.                             |
//+------------------------------------------------------------------+
class CObject
  {
protected:
   CObject          *m_prev;               // previous list item
   CObject          *m_next;               // next list item

public:
                     CObject();
   //--- methods of access to protected data
   CObject          *Prev()                { return(m_prev); }
   void              Prev(CObject *node)   { m_prev=node;    }
   CObject          *Next()                { return(m_next); }
   void              Next(CObject *node)   { m_next=node;    }
   //--- methods for working with files
   virtual bool      Save(int file_handle) { return(true);   }
   virtual bool      Load(int file_handle) { return(true);   }
   //--- method of identifying the object
   virtual int       Type() const          { return(0);      }

protected:
   virtual int       Compare(const CObject *node,int mode=0) const { return(0); }
  };
//+------------------------------------------------------------------+
//| Constructor CObject.                                             |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CObject::CObject()
  {
//--- initialize protected data
   m_prev=NULL;
   m_next=NULL;
  }
//+------------------------------------------------------------------+

보시다시피 이 클래스의 기본은 두 개의 포인터로, 일반적인 기능을 구현합니다.

이제 가장 중요한 부분입니다. 상속 메커니즘으로 인해이 클래스를 거래 모델에 포함 할 수 있습니다. 이는 거래 모델의 클래스가 CList 유형 목록에 포함될 수 있음을 의미합니다! 이렇게 해봅시다.

따라서 추상 CModel 클래스를 CObject 클래스 의 자손으로 만들 것입니다.

class CModel : public CObject

클래스 cmodel_moving cmodel_average 이후는 CModel 클래스에서 상속되며 CObject 클래스의 데이터와 메소드를 포함합니다. 따라서 CList type 목록에 포함될 수 있습니다. 두 가지 조건부 거래 모델을 생성하는 소스 코드는 목록에 배치하고 각 틱을 순차적으로 샘플링합니다.

//+------------------------------------------------------------------+
//|                                            ch01_simple_model.mq5 |
//|                            Copyright 2010, Vasily Sokolov (C-4). |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Vasily Sokolov (C-4)."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Arrays\List.mqh>

// Base model
class CModel:CObject
{
protected:
   string            m_name;
public:
        void              CModel(){m_name="Model base";}
        bool virtual      Processing(void){return(true);}
};

class cmodel_macd : public CModel
{
public:
   void              cmodel_macd(){m_name="MACD Model";}
   bool              Processing(){Print("Processing ", m_name, "...");return(true);}
};

class cmodel_moving : public CModel
{
public:
   void              cmodel_moving(){m_name="Moving Average";}
   bool              Processing(){Print("Processing ", m_name, "...");return(true);}
};

//Create list of models
CList *list_models;

void OnInit()
{
   int rezult;
   // Great two pointer
   cmodel_macd          *m_macd;
   cmodel_moving        *m_moving;
   list_models =        new CList();
   m_macd   =           new cmodel_macd();
   m_moving =           new cmodel_moving();
   //Check valid pointer
   if(CheckPointer(m_macd)==POINTER_DYNAMIC){
      rezult=list_models.Add(m_macd);
      if(rezult!=-1)Print("Model MACD successfully created");
      else          Print("Creation of Model MACD has failed");
   }
   //Check valid pointer
   if(CheckPointer(m_moving)==POINTER_DYNAMIC){
      rezult=list_models.Add(m_moving);
      if(rezult!=-1)Print("Model MOVING AVERAGE successfully created");
      else          Print("Creation of Model MOVING AVERAGE has failed");
   }
}

void OnTick()
{
   CModel               *current_model;
   for(int i=0;i<list_models.Total();i++){
      current_model=list_models.GetNodeAtIndex(i);
      current_model.Processing();
   }
}

void OnDeinit(const int reason)
{
   delete list_models;
}

이 프로그램이 컴파일되고 실행되면 EA의 정상적인 작동을 나타내는 비슷한 줄이 "Experts"저널에 실릴 것입니다.

2010.10.10 14:18:31     ch01_simple_model (EURUSD,D1)   Prosessing Moving Average...
2010.10.10 14:18:31     ch01_simple_model (EURUSD,D1)   Processing MACD Model...
2010.10.10 14:18:21     ch01_simple_model (EURUSD,D1)   Model MOVING AVERAGE was created successfully
2010.10.10 14:18:21     ch01_simple_model (EURUSD,D1)   Model MACD was created successfully  

이 코드가 어떻게 작동하는지 자세히 분석해 보겠습니다. 따라서 위에서 언급했듯이 기본 거래 모델 CModelCObject 클래스에서 파생되며, 기본 모델의 자손을 CList 유형 목록에 포함할 수 있는 권한을 부여합니다.

rezult=list_models.Add(m_macd);
rezult=list_models.Add(m_moving);

데이터 구성에는 포인터 작업이 필요합니다. 특정 모델의 포인터가 OnInit() 함수의 로컬 수준에서 생성되고 글로벌 목록 list_models, 에 입력되면 그 필요성이 사라지고이 함수의 다른 변수와 함께 안전하게 파괴될 수 있습니다.

일반적으로 제안된 모델 의 특징은 유일한 전역 변수 (모델 클래스 자체 외에)가 이러한 모델의 동적 링크 목록이라는 것입니다. 따라서 처음부터 프로젝트의 높은 수준의 캡슐화가 지원됩니다.

어떤 이유로 모델 생성이 실패한 경우 (예: 필수 매개 변수 값이 잘못 나열 됨)이 모델은 목록에 추가되지 않습니다. 목록에 성공적으로 추가 된 모델만 처리하므로 EA의 전체 작업에는 영향을 주지 않습니다.

생성된 모델의 샘플링은 OnTick() 함수에서 이루어집니다. for 루프로 구성됩니다. 이 루프에서 요소의 수가 결정되고, 그 후 주기의 첫 번째 요소 (i = 0)에서 마지막 요소까지의 연속 경로가 있습니다. (i <list_models.Total();i++):

CModel               *current_model;
for(int i=0;i<list_models.Total();i++){
   current_model=list_models.GetNodeAtIndex(i);
   current_model.Processing();
}

CModel 기본 클래스에 대한 포인터는 범용 어댑터로 사용됩니다. 이렇게 하면 이 표시기가 지원하는 모든 함수를 파생 모델에서 사용할 수 있습니다. 이 경우 Processing()기능만 필요합니다. 각 모델에는 Processing()의 자체 버전이 있으며, 내부 구현은 다른 모델의 유사한 기능과 다를 수 있습니다. 이 함수를 오버로드 할 필요는 없습니다. 입력 매개 변수가 없고 bool 유형의 값을 반환하는 한 가지 형식으로만 존재할 수 있습니다.

이 기능의 "어깨"에 해당하는 작업은 광범위합니다.

  1. 이 기능은 자체 거래 모델을 기반으로 현재 시장 상황을 독립적으로 결정해야 합니다.
  2. 시장에 진입하기로 결정한 후 함수는 거래에 포함된 필요한 담보 금액 (마진), 거래량, 가능한 최대 손실 가치 또는 이익 수준을 독립적으로 계산해야 합니다.
  3. 모델의 동작은 이전 동작과 연관되어야 합니다. 예를 들어, 모델에 의해 시작된 숏 포지션이 있는 경우 향후 추가 구축이 불가능할 수 있습니다. 이러한 모든 검증은 Processing() 함수 내에서 수행되어야 합니다.
  4. 이러한 각 함수는 계정 상태와 같은 공통 매개 변수에 액세스 할 수 있어야 합니다. 이 데이터를 기반으로 이 기능은 모델에 포함 된 매개 변수를 사용하여 자체 자금 관리를 수행해야 합니다. 예를 들어, 모델 중 하나의 자금 관리가 최적 공식 f의 상품을 통해 수행되는 경우 그 값은 모델마다 달라야 합니다.

분명히 Processing() 함수,는 그것에 배치된 작업의 크기로 인해 보조 클래스의 개발 장치에 의존하게 될 것입니다. 그리고 해당 클래스들은 MetaTrader5에 포함이 됩니다. 또한 이 솔루션을 위해 특별히 설계된 것들에도 포함이 되죠.

보시다시피 대부분의 작업은 모델의 특정 인스턴스에 위임됩니다. EA의 외부 수준은 각 모델에 차례로 제어권을 부여하고 이에 대한 작업이 완료됩니다. 특정 모델에서 수행할 작업은 내부 논리에 따라 다릅니다.

일반적으로 우리가 구축 한 상호 작용 시스템은 다음 체계로 설명 할 수 있습니다.

위 코드에 표시된대로 모델 정렬은 OnTick() 함수 내부에서 발생합니다. 그럴 필요는 없지만요. 정렬주기는 OnTrade() 또는 OnTimer()과 같이 원하는 다른 함수에 쉽게 배치 할 수 있습니다. 

 

가상 주문 표 - 모델의 기초

모든 거래 모델을 단일 목록으로 통합했으면 거래 프로세스를 설명할 때입니다. CModel 클래스 로 돌아가서 거래 프로세스에서 기반이 될 수 있는 추가 데이터와 기능으로 보완해 보겠습니다.

위에서 언급했듯이 새로운 순 포지션 패러다임은 주문 및 거래 작업을 위한 다양한 규칙을 정의합니다. MetaTrader 4에서 각 거래에는 주문이 수반되며, 해당 주문은 문제 발생 시점부터 다음까지 '거래'탭에 있었습니다. 주문 종료 또는 거래 종료에 의해 시작됩니다.

MetaTrader 5 보류 중인 주문거래가 실제로 완료 될 때까지만 존재합니다. 거래 또는 시장 진입이 이루어지면 거래 서버에 저장되는 주문 내역으로 전달됩니다. 이 상황은 불확실성을 만듭니다. EA가 주문을 내렸다고 가정해 보겠습니다. 집계된 포지션이 변경되었습니다. 얼마 후 EA는 포지션을 청산해야 합니다.

MetaTrader 4에서 할 수있는 것처럼 특정 주문을 닫는 것은 매우 개념이 부족하기 때문에 할 수 없습니다. 주문 마감의 경우 위치 또는 그 일부를 마감 할 수 있습니다. 문제는 포지션의 어떤 부분을 닫아야 하는가입니다. 또는 모든 내역 주문을 살펴보고 EA가 내놓은 주문을 선택한 다음 이러한 주문을 현재 시장 상황과 연관시킬 수 있습니다. 필요한 경우 카운터 주문을 차단하십시오. 이 방법에는 많은 어려움이 있다

예를 들어 주문이 과거에 이미 차단되지 않았는지 어떻게 확인할 수 있습니까? 현재 포지션이 독점적으로 현재 EA에 속한다고 가정하면 다른 방법을 사용할 수 있습니다. 이 옵션은 하나의 EA와 하나의 전략으로 거래하려는 경우에만 사용할 수 있습니다. 이러한 모든 방법으로는 우리가 직면한 문제를 우아하게 해결할 수 없습니다.

가장 분명하고 간단한 해결책은 현재 모델의 주문 (즉, 반대 거래에 의해 차단되지 않는 주문)에 필요한 모든 정보를 모델 자체에 저장하는 것입니다.

예를 들어, 모델이 주문을 제출하면 티켓이 이 모델의 특별한 메모리 영역에 기록됩니다. 예를 들어 이미 익숙한 연결 목록 시스템의 도움을 받아 구성 할 수 있습니다.

티켓 주문을 알면 거의 모든 정보를 찾을 수 있으므로 티켓 주문을 출력한 모델과 연결하기만 하면 됩니다. 티켓 주문을 CTableOrder 스페셜 클래스에 저장합니다. 티켓 외에도 주문량, 설치 시간, 매직 넘버 등과 같은 가장 중요한 정보를 수용 할 수 있습니다.

이 클래스가 어떻게 구성되어 있는지 살펴 보겠습니다.

#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"

#include <Trade\_OrderInfo.mqh>
#include <Trade\_HistoryOrderInfo.mqh>
#include <Arrays\List.mqh>
class CTableOrders : CObject
{
private:
   ulong             m_magic;       // Magic number of the EA that put out the order
   ulong             m_ticket;      // Ticket of the basic order
   ulong             m_ticket_sl;    // Ticket of the simulated-Stop-Loss order, assigned with the basic order
   ulong             m_ticket_tp;    // Ticket of the simulated-Take-Profit, assigned with the basic order
   ENUM_ORDER_TYPE   m_type;         // Order type
   datetime          m_time_setup;  // Order setup time
   double            m_price;       // Order price
   double            m_sl;          // Stop Loss price
   double            m_tp;          // Take Profit price
   double            m_volume_initial;  // Order Volume
public:
                     CTableOrders();
   bool              Add(COrderInfo &order_info, double stop_loss, double take_profit);
   bool              Add(CHistoryOrderInfo &history_order_info, double stop_loss, double take_profit);
   double            StopLoss(void){return(m_sl);}
   double            TakeProfit(void){return(m_tp);}
   ulong             Magic(){return(m_magic);}
   ulong             Ticket(){return(m_ticket);}
   int               Type() const;
   datetime          TimeSetup(){return(m_time_setup);}
   double            Price(){return(m_price);}
   double            VolumeInitial(){return(m_volume_initial);}
};

CTableOrders::CTableOrders(void)
{
   m_magic=0;
   m_ticket=0;
   m_type=0;
   m_time_setup=0;
   m_price=0.0;
   m_volume_initial=0.0;
}

bool CTableOrders::Add(CHistoryOrderInfo &history_order_info, double stop_loss, double take_profit)
{
   if(HistoryOrderSelect(history_order_info.Ticket())){
      m_magic=history_order_info.Magic();
      m_ticket=history_order_info.Ticket();
      m_type=history_order_info.Type();
      m_time_setup=history_order_info.TimeSetup();
      m_volume_initial=history_order_info.VolumeInitial();
      m_price=history_order_info.PriceOpen();
      m_sl=stop_loss;
      m_tp=take_profit;
      return(true);
   }
   else return(false);
}

bool CTableOrders::Add(COrderInfo &order_info, double stop_loss, double take_profit)
{
   if(OrderSelect(order_info.Ticket())){
      m_magic=order_info.Magic();
      m_ticket=order_info.Ticket();
      m_type=order_info.Type();
      m_time_setup=order_info.TimeSetup();
      m_volume_initial=order_info.VolumeInitial();
      m_price=order_info.PriceOpen();
      m_sl=stop_loss;
      m_tp=take_profit;
      return(true);
   }
   else return(false);
}

int   CTableOrders::Type() const
{
   return((ENUM_ORDER_TYPE)m_type);
}

CModel class와 유사하게 CTableOrders 클래스는 CObject에서 상속됩니다. 모델의 클래스와 마찬가지로 CTableOrders의 인스턴스를 CList 유형의ListTableOrders 목록에 배치합니다.

자체 티켓 주문 (m_tiket) 외에도 클래스에는 마법 번호 (ORDER_MAGIC), 해당 유형, 시가 가격, 수량 및 예상 중복 주문 수준: 절매 (m _sl) 및 이익 실현 (m_tp). 마지막 두 값은 따로 말해야 합니다. 모든 거래가 조만간 반대 거래로 성사되어야한다는 것은 분명합니다. 반대 거래는 현재 시장 상황 또는 체결 시점에 미리 결정된 가격으로 포지션의 부분 청산을 기반으로 시작할 수 있습니다.

MetaTrader4에서 이러한 "무조건적인 포지션 이탈"은 특별합니다. 이탈 유형: 절매이익실현. MetaTrader 4의 특징은 이러한 수준이 특정 주문에 적용된다는 사실입니다. 예를 들어, 활성 주문 중 하나에서 중지가 발생하더라도 이 상품의 다른 미결 주문에는 영향을 주지 않습니다.

Meta Trader 5에서는 다소 다릅니다. 각 세트 주문에 대해 무엇보다도 당신은 절매 이익실현에 대한 가격을 명시할 수 있습니다. 그리고 이러한 수준은 가격이 설정된 특정 순서에 반하는 것이 아니라 이 상품의 전체 위치에 대해 작동합니다.

손절매 및 이익 실현 수준이 없는 표준 랏 1 개의 EURUSD에 대해 공개 매수 포지션이 있다고 가정 해보겠습니다. 얼마 후 손절매 및 이익실현의 설정 수준으로 0.1 랏을 구매하기 위해 EURUSD에 대한 또 다른 주문이 발행됩니다. - 각각 현재 가격에서 100 포인트 거리에 있습니다. 얼마 후 가격은 절매수준 또는 이익실현 수준에 도달합니다. 이 경우 EURUSD에 1.1 랏 크기의 전체 포지션이 마감됩니다.

절매 이익실현 은 집계 된 위치와 관련해서만 설정할 수 있으며 특정 주문에 대해서는 설정할 수 없습니다. 따라서 이러한 주문을 다중 시스템 EA에서 사용할 수 없게 됩니다. 하나의 시스템이 자체 절매이익실현을 내놓게 되면, 다른 모든 시스템에 적용되며, 그 이익은 이미 상품의 집계된 위치에 포함됩니다!

따라서 거래 EA의 각 하위 시스템은 자체 내부 절매 이익실현 을 가져옵니다. 또한 이것은 동일한 거래 시스템 내에서도 서로 다른 주문이 손절매와 이익 실현의 수준이 다를 수 있다는 사실에서 파생 될 수 있으며, 다른 주문은 다른 수준의절매 이익실현을 갖게 될 수 있고, 위에서 이미 언급했듯이 MetaTrader 5에서 이러한 결과는 개별 주문에서 지정될 수 없습니다.

가상 주문 내에서 합성 레벨 절매이익실현, EA는 가격이 이 수준에 도달하거나 초과하면 기존 주문을 독립적으로 차단할 수 있습니다. 이러한 주문을 차단한 후 활성 주문 목록에서 안전하게 제거 할 수 있습니다. 이것이 수행되는 방법은 아래에 설명되어 있습니다.

자체 데이터를 제외하고 CTableOrders 클래스는 매우 중요한 Add() 기능을 갖고 있습니다. 이 함수는 테이블에 기록해야 하는 주문 티켓을 받습니다. 주문 티켓 외에도 이 기능은 가상 수주늬 절매이익실현을 받습니다. 첫째, Add() 함수 는 서버에 저장되어 있는 내역 주문 중 주문을 할당합니다. 이렇게 할 수 있으면 티켓의 정보를 history_order_info, 클래스의 인스턴스에 입력 한 다음 이를 통해 새로운 TableOrders 요소에 정보를 입력하기 시작합니다. 또한 이 요소는 주문 목록에 추가됩니다. 주문 선택을 완료 할 수 없는 경우 보류중인 주문을 처리하는 중이므로 OrderSelect() 함수를 통해 현재 주문에서 이 주문을 할당해야 합니다. 이 주문을 성공적으로 선택한 경우 과거 주문과 동일한 작업이 수행됩니다.

현재Trade 이벤트를 설명하는 구조가 도입되기 전에는 다중 시스템 EA에 대한 보류 주문 작업이 어렵습니다. 확실히 이 구조가 도입된 후에는 보류중인 주문을 기반으로 EA를 설계 할 수 있게 될 것입니다. 또한 주문 테이블이 있는 경우 보류중인 주문이있는 거의 모든 거래 전략을 시장 성능으로 이동할 수 있습니다. 이러한 이유로 글에 제시된 모든 거래 모델은 시장 실행입니다 (ORDER_TYPE_BUY or ORDER_TYPE_SELL).


CModel - 거래 모델의 기본 클래스

따라서 주문 테이블이 완전히 디자인되면 기본 모델 CModel의 전체 버전을 설명 할 때가 됩니다.

class CModel : public CObject
{
protected:
   long              m_magic;
   string            m_symbol;
   ENUM_TIMEFRAMES   m_timeframe;
   string            m_model_name;
   double            m_delta;
   CTableOrders      *table;
   CList             *ListTableOrders;
   CAccountInfo      m_account_info;
   CTrade            m_trade;
   CSymbolInfo       m_symbol_info;
   COrderInfo        m_order_info;
   CHistoryOrderInfo m_history_order_info;
   CPositionInfo     m_position_info;
   CDealInfo         m_deal_info;
   t_period          m_timing;
public:
                     CModel()  { Init();   }
                     ~CModel() { Deinit(); }
   string            Name(){return(m_model_name);}
   void              Name(string name){m_model_name=name;}
   ENUM_TIMEFRAMES    Timeframe(void){return(m_timeframe);}
   string            Symbol(void){return(m_symbol);}
   void              Symbol(string set_symbol){m_symbol=set_symbol;}
   bool virtual      Init();
   void virtual      Deinit(){delete ListTableOrders;}
   bool virtual      Processing(){return (true);}
   double            GetMyPosition();
   bool              Delete(ENUM_TYPE_DELETED_ORDER);
   bool              Delete(ulong Ticket);
   void              CloseAllPosition();
   //bool virtual      Trade();
protected:
   bool              Add(COrderInfo &order_info, double stop_loss, double take_profit);
   bool              Add(CHistoryOrderInfo &history_order_info, double stop_loss, double take_profit);

   void              GetNumberOrders(n_orders &orders);
   bool              SendOrder(string symbol, ENUM_ORDER_TYPE op_type, ENUM_ORDER_MODE op_mode, ulong ticket, double lot,
                              double price, double stop_loss, double take_profit, string comment);
};

이 클래스의 데이터에는 모든 거래 모델의 기본 상수가 포함됩니다.

모델이 실행될 기호인 마법 번호 (m_magic)이며, ( m_symbol) 타임 프레임 (m_timeframe) 및 가장 많이 거래된 모델의 이름 (m_이름)입니다.

또한 모델에는 우리에겐 이미 익숙한 orders 테이블 클래스 (CTableOrders * table)와 이 테이블의 인스턴스가 보관되는 목록이 각 주문에 대해 하나씩 포함됩니다 (CList*ListTableOrders). 모든 데이터는 동적으로 생성되므로 필요에 따라 포인터를 통해 작업을 수행합니다.

그 뒤에 m_delta 변수가 나옵니다. 이 변수에는 자금 관리 공식에서 현재 랏을 계산하기 위한 특수 계수가 있어야 합니다. 예를 들어 고정 분수 공식의 경우 이 변수는 계정의 지분을 저장할 수 있습니다. 예를 들어, 계정의 2 % 위험에 대해 이 변수는 위험에 노출 될 수 있습니다. 이는 0.02와 같습니다. 예를 들어보다 공격적인 방법의 경우, 최적 방법의 경우 f이 변수가 더 클 수 있습니다.

이 변수에서 중요한 것은 단일 EA의 일부인 각 모델의 위험을 개별적으로 선택할 수 있다는 것입니다. 대문자 공식을 사용하지 않는 경우 입력 할 필요가 없습니다. 기본적으로 0.0입니다.

그 다음에는 계정 정보부터 포지션 정보에 이르기까지 모든 필수 정보의 수신 및 처리를 용이하게 하도록 설계된 모든 보조 거래 클래스가 포함됩니다. 특정 거래 모델의 파생 상품은 이러한 보조 클래스를 적극적으로 사용해야 하며 OrderSelect 또는 OrderSend유형의 일반 기능을 사용해야 합니다.

m_timing 변수는 별도로 설명해야 합니다. EA 작업 과정에서 특정 시간 간격으로 특정 이벤트를 호출해야 합니다. OnTimer() 함수 는 다른 모델이 다른 시간 간격에 존재할 수 있기 때문에 이에 적합하지 않습니다.

예를 들어, 일부 이벤트는 각 새로운 바에서 호출되어야 합니다. 모델의 경우 시간별 그래프에서 거래하는 경우 이러한 이벤트를 매시간 호출해야 하며, 모델의 경우 일일 그래프에서 거래해야 합니다. 이러한 모델은 서로 다른 시간 설정을 가지고 있으며 각 모델은 자체 모델에 각각 저장되어야 합니다. CModel 클래스에 포함된 t_period 구조를 사용하면 이러한 설정을 모델에 각각 별도로 저장할 수 있습니다.

구조는 다음과 같습니다.

struct t_period
{
   datetime m1;
   datetime m2;
   datetime m3;
   datetime m4;
   datetime m5;
   datetime m6;
   datetime m10;
   datetime m12;
   datetime m15;
   datetime m20;
   datetime m30;
   datetime h1;
   datetime h2;
   datetime h3;
   datetime h4;
   datetime h6;
   datetime h8;
   datetime h12;
   datetime d1;
   datetime w1;
   datetime mn1;  
   datetime current; 
};

보시다시피 여기에는 일반적인 기간 목록이 포함됩니다. 새로운 바가 발생했는지 확인하려면 마지막 바의 시간을 t_기간구조에 기록된 시간과 비교해야 합니다. 시간이 일치하지 않으면 새로운 바가 발생하고 구조의 시간을 현재 바의 시간으로 업데이트하고 긍정적인 결과 (true)를 반환해야 합니다. 마지막 바의 시간과 구조가 동일하면 새로운 바가 아직 발생하지 않았으며 부정적인 결과를 반환해야 합니다 (false).

다음은 설명된 알고리즘을 기반으로 작동하는 함수입니다.

bool timing(string symbol, ENUM_TIMEFRAMES tf, t_period &timeframes)
{
   int rez;
   MqlRates raters[1];
   rez=CopyRates(symbol, tf, 0, 1, raters);
   if(rez==0)
   {
      Print("Error timing");
      return(false);
   }
   switch(tf){
      case PERIOD_M1:
         if(raters[0].time==timeframes.m1)return(false);
         else{timeframes.m1=raters[0].time; return(true);}
      case PERIOD_M2:
         if(raters[0].time==timeframes.m2)return(false);
         else{timeframes.m2=raters[0].time; return(true);}
      case PERIOD_M3:
         if(raters[0].time==timeframes.m3)return(false);
         else{timeframes.m3=raters[0].time; return(true);}
      case PERIOD_M4:
         if(raters[0].time==timeframes.m4)return(false);
         else{timeframes.m4=raters[0].time; return(true);}
     case PERIOD_M5:
         if(raters[0].time==timeframes.m5)return(false);
         else{timeframes.m5=raters[0].time; return(true);}
     case PERIOD_M6:
         if(raters[0].time==timeframes.m6)return(false);
         else{timeframes.m6=raters[0].time; return(true);}
     case PERIOD_M10:
         if(raters[0].time==timeframes.m10)return(false);
         else{timeframes.m10=raters[0].time; return(true);}
     case PERIOD_M12:
         if(raters[0].time==timeframes.m12)return(false);
         else{timeframes.m12=raters[0].time; return(true);}
     case PERIOD_M15:
         if(raters[0].time==timeframes.m15)return(false);
         else{timeframes.m15=raters[0].time; return(true);}
     case PERIOD_M20:
         if(raters[0].time==timeframes.m20)return(false);
         else{timeframes.m20=raters[0].time; return(true);}
     case PERIOD_M30:
         if(raters[0].time==timeframes.m30)return(false);
         else{timeframes.m30=raters[0].time; return(true);}
     case PERIOD_H1:
         if(raters[0].time==timeframes.h1)return(false);
         else{timeframes.h1=raters[0].time; return(true);}
     case PERIOD_H2:
         if(raters[0].time==timeframes.h2)return(false);
         else{timeframes.h2=raters[0].time; return(true);}
     case PERIOD_H3:
         if(raters[0].time==timeframes.h3)return(false);
         else{timeframes.h3=raters[0].time; return(true);}
     case PERIOD_H4:
         if(raters[0].time==timeframes.h4)return(false);
         else{timeframes.h4=raters[0].time; return(true);}
     case PERIOD_H6:
         if(raters[0].time==timeframes.h6)return(false);
         else{timeframes.h6=raters[0].time; return(true);}
     case PERIOD_H8:
         if(raters[0].time==timeframes.h8)return(false);
         else{timeframes.h8=raters[0].time; return(true);}
     case PERIOD_H12:
         if(raters[0].time==timeframes.h12)return(false);
         else{timeframes.h12=raters[0].time; return(true);}
     case PERIOD_D1:
         if(raters[0].time==timeframes.d1)return(false);
         else{timeframes.d1=raters[0].time; return(true);}
     case PERIOD_W1:
         if(raters[0].time==timeframes.w1)return(false);
         else{timeframes.w1=raters[0].time; return(true);}
     case PERIOD_MN1:
         if(raters[0].time==timeframes.mn1)return(false);
         else{timeframes.mn1=raters[0].time; return(true);}
     case PERIOD_CURRENT:
         if(raters[0].time==timeframes.current)return(false);
         else{timeframes.current=raters[0].time; return(true);}
     default:
         return(false);
   }
}

현재는 구조를 순차적으로 정렬할 가능성이 없습니다. 이러한 정렬은 동일한 거래 모델의주기에서 여러 인스턴스를 생성하고 다른 타임 프레임에서 거래해야 할 때 필요할 수 있습니다. 그래서t_period 구조에 관한 특별한 함수 분류기를 작성해야 했습니다.

다음은 이 함수소스 코드입니다.:

int GetPeriodEnumerator(uchar n_period)
{
   switch(n_period)
   {
      case 0: return(PERIOD_CURRENT);
      case 1: return(PERIOD_M1);
      case 2: return(PERIOD_M2);
      case 3: return(PERIOD_M3);
      case 4: return(PERIOD_M4);
      case 5: return(PERIOD_M5);
      case 6: return(PERIOD_M6);
      case 7: return(PERIOD_M10);
      case 8: return(PERIOD_M12);
      case 9: return(PERIOD_M15);
      case 10: return(PERIOD_M20);
      case 11: return(PERIOD_M30);
      case 12: return(PERIOD_H1);
      case 13: return(PERIOD_H2);
      case 14: return(PERIOD_H3);
      case 15: return(PERIOD_H4);
      case 16: return(PERIOD_H6);
      case 17: return(PERIOD_H8);
      case 18: return(PERIOD_H12);
      case 19: return(PERIOD_D1);
      case 20: return(PERIOD_W1);
      case 21: return(PERIOD_MN1);
      default:
         Print("Enumerator period must be smallest 22");
         return(-1);
   }
}

이러한 모든 기능은 \\Include 폴더에 있는 단일 파일로 편리하게 결합됩니다. 이름을 Time.mqh로 지정하겠습니다.

다음은 기본 클래스 CModel에 포함될 것입니다.

#incude <Time.mqh>

간단한 기능 외에도 get/set 의 유형 이름(), Timeframe() 기호(), 클래스 CModel 복합물 포함 유형의  Init(), GetMyPosition(), 삭제(), CloseAllPosition() и 처리 중(). 마지막 함수의 지정은 이미 익숙 할 것입니다. 나중에 내부 구조에 대해 더 자세히 설명하지만 지금은 기본 클래스의 주요 함수에 대한 설명부터 시작하겠습니다. CModel.

CModel::() 함수 클래스의 인스턴스를 동적으로 생성 CTableOrders, 그런 다음 적절한 CTabeOrders::추가() function. 작동 원리는 위에서 설명한 바 있습니다. 채워진 후 이 항목은 현재 모델의 모든 주문 목록에 포함됩니다.(ListTableOrders.Add (t)).

CModel::삭제 () 함수, 의 요소를 삭제했습니다. CTableOrders 유형 활성 주문 목록에서. 이렇게 하려면 삭제해야 하는 주문의 티켓을 지정해야 합니다. 작품의 원리는 간단합니다. 이 기능은 올바른 티켓이 있는 주문을 검색하기 위해 전체 주문 테이블을 순차적으로 정렬합니다. 그러한 주문을 찾으면 삭제합니다.

The CModel::GetNumberOrders() 함수 활성 주문 수를 계산합니다. 그것은 채우기 특별 구조 n_orders:

struct n_orders
{
   int all_orders;
   int long_orders;
   int short_orders;
   int buy_sell_orders;
   int delayed_orders;
   int buy_orders;
   int sell_orders;
   int buy_stop_orders;
   int sell_stop_orders;
   int buy_limit_orders;
   int sell_limit_orders;
   int buy_stop_limit_orders;
   int sell_stop_limit_orders;
};

보시다시피, 호출 후 몇 가지 특정 유형의 주문이 설정되었는지 확인할 수 있습니다. 예를 들어 모든 숏 오더 수를 얻으려면 short_orders의 인스턴스 n_주문.

CModel::SendOrder() 함수 는 거래 서버로 주문을 실제로 전송하기 위한 기본이자 유일한 기능입니다. 주문을 서버로 보내는 고유 한 알고리즘을 갖는 각 특정 모델 대신 SendOrder() 기능 는 이러한 제출에 대한 일반적인 절차를 정의합니다. 모델에 관계없이 주문 처리 프로세스는 중앙 위치에서 효율적으로 수행되는 동일한 checks와 연결됩니다.

우리 친숙해져봅시다 소스 포함 code 기능에 대해 말이죠:

bool CModel::SendOrder(string symbol, ENUM_ORDER_TYPE op_type, ENUM_ORDER_MODE op_mode, ulong ticket, 
                          double lot, double price, double stop_loss, double take_profit, string comment)
{
   ulong code_return=0;
   CSymbolInfo symbol_info;
   CTrade      trade;
   symbol_info.Name(symbol);
   symbol_info.RefreshRates();
   mm send_order_mm;
   
   double lot_current;
   double lot_send=lot;
   double lot_max=m_symbol_info.LotsMax();
   //double lot_max=5.0;
   bool rez=false;
   int floor_lot=(int)MathFloor(lot/lot_max);
   if(MathMod(lot,lot_max)==0)floor_lot=floor_lot-1;
   int itteration=(int)MathCeil(lot/lot_max);
   if(itteration>1)
      Print("The order volume exceeds the maximum allowed volume. It will be divided into ", itteration, " deals");
   for(int i=1;i<=itteration;i++)
   {
      if(i==itteration)lot_send=lot-(floor_lot*lot_max);
      else lot_send=lot_max;
      for(int i=0;i<3;i++)
      {
         //Print("Send Order: TRADE_RETCODE_DONE");
         symbol_info.RefreshRates();
         if(op_type==ORDER_TYPE_BUY)price=symbol_info.Ask();
         if(op_type==ORDER_TYPE_SELL)price=symbol_info.Bid();
         m_trade.SetDeviationInPoints(ulong(0.0003/(double)symbol_info.Point()));
         m_trade.SetExpertMagicNumber(m_magic);
         rez=m_trade.PositionOpen(m_symbol, op_type, lot_send, price, 0.0, 0.0, comment); 
         // Sleeping is not to be deleted or moved! Otherwise the order will not have time to get recorded in m_history_order_info!!!
         Sleep(3000);
         if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||
            m_trade.ResultRetcode()==TRADE_RETCODE_DONE_PARTIAL||
            m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
         {
               //Print(m_trade.ResultComment());
               //rez=m_history_order_info.Ticket(m_trade.ResultOrder());
               if(op_mode==ORDER_ADD){
                  rez=Add(m_trade.ResultOrder(), stop_loss, take_profit);
               }
               if(op_mode==ORDER_DELETE){
                  rez=Delete(ticket);
               }
               code_return=m_trade.ResultRetcode();
               break;
         }
         else
         {
            Print(m_trade.ResultComment());
         }
         if(m_trade.ResultRetcode()==TRADE_RETCODE_TRADE_DISABLED||
            m_trade.ResultRetcode()==TRADE_RETCODE_MARKET_CLOSED||
            m_trade.ResultRetcode()==TRADE_RETCODE_NO_MONEY||
            m_trade.ResultRetcode()==TRADE_RETCODE_TOO_MANY_REQUESTS||
            m_trade.ResultRetcode()==TRADE_RETCODE_SERVER_DISABLES_AT||
            m_trade.ResultRetcode()==TRADE_RETCODE_CLIENT_DISABLES_AT||
            m_trade.ResultRetcode()==TRADE_RETCODE_LIMIT_ORDERS||
            m_trade.ResultRetcode()==TRADE_RETCODE_LIMIT_VOLUME)
         {
            break;
         }
      }
   }
   return(rez);
}

이 함수가 하는 첫 번째는 거래 서버의 명시된 거래량을 실행할 가능성을 확인하는 것입니다. 이는 CheckLot() function기능을 사용하여 수행합니다. 포지션 규모에 거래 제한이 있을 수 있습니다. 이를 고려해야 합니다.

음의 경우를 고려하십시오. 양방향으로 15개의 표준 랏의 거래 포지션 크기에 제한이 있습니다. 현재 포지션은 길고 3개의 랏과 같습니다. 머니 관리 시스템을 기반으로하는 거래 모델은 18.6개의 랏의 매수 포지션을 오픈하려고합니다. CheckLot () 함수 는 수정 된 거래량을 반환합니다. 이 경우 12개의 랏이 됩니다 (15개 중 3 개는 이미 다른 딜에서 점유하고 있기 때문) 현재 열린 위치 가 길지 않고 짧으면 함수는 18.6 15 랏을 반환합니다. 이것은 가능한 최대 위치 볼륨입니다.

15개의 랏을 투입 한 후 구매, 이 경우 순 포지션은 12개의 랏 (3 - 판매, 15 - 구매). 다른 모델이 3 랏의 초기 매도 포지션을 재정의하면, 구매 집계 포지션이 가능한 최대 포지션이 됩니다 - 15개의 랏. 다른 구매 신호는 모델이 15개 랏의 일부 또는 전부를 재정의 할 때까지 처리되지 않습니다. 구매. 요청 된 거래의 가능한 수량이 초과되었습니다. 함수는 상수를 반환합니다. EMPTY_VALUE, 이러한 신호를 전달해야 합니다.

설정된 볼륨의 가능성에 대한 확인이 성공하면 필요한 마진 값에 대한 계산이 이루어집니다. 명시된 볼륨에 대한 계정의 자금이 충분하지 않을 수 있습니다. 이러한 목적을 위해 CheckMargin() 기능. 마진이 충분하지 않으면 현재 여유 마진이 열릴 수 있도록 주문량을 수정하려고 시도합니다. 마진이 최소 금액을 열기에도 충분하지 않으면 마진-.

현재 포지션이없고 마진이 사용되지 않는 경우 기술적인 부분만 의미합니다. 마진- - 거래를 개시 할 수 없는 상태. 계정에 금액을 추가하지 않으면 계속할 수 없습니다. 마진이 아직 사용 중이면 이 마진을 사용하는 거래가 종료 될 때까지 기다릴 수 밖에 없습니다. 어쨌든 여백 부족은 상수 EMPTY_.

이 기능의 특징은 현재 주문을 여러 개의 독립적인 거래로 나누는 기능입니다. 거래 모델이 계정의 자본화 시스템을 사용하는 경우 필요한 금액은 생각할 수 있는 모든 한도를 쉽게 초과할 수 있습니다 (예를 들어 자본화 시스템은 수백, 때로는 수천 개의 표준 랏의 거래를 개시해야 할 수 있습니다). 단일거래에 대해 그러한 금액을 보장하는 것은 분명히 불가능합니다. 일반적으로 거래 조건에 따라 최대 100개 랏의 거래 규모가 결정되지만 일부 거래 서버에는 MetaQuotes Championship 2010 서버 이 제한은 5개 랏이었습니다. 이러한 제한을 고려해야 하며 이를 바탕으로 거래의 실제 거래량을 정확하게 계산해야 합니다.

먼저 설정된 볼륨을 구현하는 데 필요한 주문 수를 계산합니다. 설정된 금액이 거래의 최대 금액을 초과하지 않는 경우 이 주문을 제출하기 위해 한 번의 패스만 필요합니다. 원하는 트랜잭션 볼륨이 가능한 최대 볼륨을 초과하면이 볼륨이 여러 부분으로 나뉩니다. 예를 들어 EURUSD의 11.3개의 랏을 구매하려고 합니다. 이 상품의 최대 거래 규모는 5.0 랏입니다. 그런 다음 OrderSend 함수 는 이 볼륨을 3 개의 주문으로 나눕니다 : 한 주문 - 5.0개의 랏, 두 번째 주문 - 5.0개의 랏, 세 번째 주문 - 1.3개의 랏.

따라서 하나의 주문이 아니라 최대 3 개의 주문이 있을 것입니다. 각각은 주문 테이블에 나열되며 손절매 및 이익 실현의 가상 가치, 마법 넘버 및 기타 매개 변수. 거래 모델은 목록에서 원하는 수의 주문을 처리 할 수 ​​있도록 설계되었으므로 이러한 주문을 처리하는 데 어려움이 없어야 합니다.

실제로 모든 주문은 동일한 값을 갖습니다 이익실현절매. 각각은 LongClose 에 따라 순차적으로 정렬됩니다. 및 ShortClose 함수. 폐쇄를 위한 적절한 조건이 발생하거나 임계값에 도달하면 SLTP, 모두 폐쇄됩니다.

모든 주문은 OrderSend CTrade 클래스의기능을 사용하여 서버로 전송됩니다. 작품의 가장 흥미로운 세부 사항은 아래에 숨겨져 있습니다.

사실은 주문 할당이 두 가지가 될 수 있다는 것입니다. 매수 또는 매도 주문은 시그널 발생시 전송되거나 기존에 존재하는 것을 차단하는 주문일 수 있습니다. The OrderSend 함수가 주문 테이블에 실제로 모든 주문을 배치하는 함수이기 때문에 전송된 주문의 유형을 알아야 합니다. 또는 특정 이벤트 발생시 테이블에서 제거합니다

추가하려는 주문 유형이 ADD_ORDER. 즉 주문 테이블에 배치해야 하는 독립 주문인 경우 함수는 이 주문에 대한 정보를 주문 테이블에 추가합니다. 이전에 주문한 주문을 재정의하기 위해 주문한 경우 -절매), 유형이 있어야 합니다. DELETE_ORDER. 게시된 후 OrderSend 함수는 주문에 대한 정보를 수동으로 제거합니다. 주문 목록에서 연결됩니다. 이를 위해 함수는 주문 유형 외에도 연결된 주문 티켓을 상속합니다. ADD_ORDER, 티켓은 단순한 0으로 채울 수 있습니다.


이동 평균의 교차를 기반으로 한 첫 번째 거래 모델

우리는 다음의 모든 주요 요소에 대해 논의했습니다. CModel 기본 클래스에 대해서 말이죠. 특정 거래 클래스를 고려할 시간입니다.

이러한 목적을 위해 먼저 간단한 지표를 기반으로 간단한 거래 모델을 생성합니다. MACD.

이 모델은 항상 롱 포지션 또는 숏 포지션을 갖습니다. 고속선이 저속선을 아래로 지나가는 즉시 숏 포지션을 오픈하고 롱 포지션이 있으면 청산합니다. 상향 크로스 오버의 경우 롱 포지션을 오픈하고 숏 포지션이 있다면 청산합니다. 이 모델에서는 보호 중지 및 수익 수준을 사용하지 않습니다.

#include <Models\Model.mqh>
#include <mm.mqh>
//+----------------------------------------------------------------------+
//| This model uses MACD indicator.                                      |
//| Buy when it crosses the zero line downward                           |
//| Sell when it crosses the zero line upward                            |
//+----------------------------------------------------------------------+  
struct cmodel_macd_param
{
   string            symbol;
   ENUM_TIMEFRAMES   timeframe;
   int               fast_ema;
   int               slow_ema;
   int               signal_ema;
};
   
class cmodel_macd : public CModel
{
private:
   int               m_slow_ema;
   int               m_fast_ema;
   int               m_signal_ema;
   int               m_handle_macd;
   double            m_macd_buff_main[];
   double            m_macd_current;
   double            m_macd_previous;
public:
                     cmodel_macd();
   bool              Init();
   bool              Init(cmodel_macd_param &m_param);
   bool              Init(string symbol, ENUM_TIMEFRAMES timeframes, int slow_ma, int fast_ma, int smothed_ma);
   bool              Processing();
protected:
   bool              InitIndicators();
   bool              CheckParam(cmodel_macd_param &m_param);
   bool              LongOpened();
   bool              ShortOpened();
   bool              LongClosed();
   bool              ShortClosed();
};

cmodel_macd::cmodel_macd()
{
   m_handle_macd=INVALID_HANDLE;
   ArraySetAsSeries(m_macd_buff_main,true);
   m_macd_current=0.0;
   m_macd_previous=0.0;
}
//this default loader
bool cmodel_macd::Init()
{
   m_magic      = 148394;
   m_model_name =  "MACD MODEL";
   m_symbol     = _Symbol;
   m_timeframe  = _Period;
   m_slow_ema   = 26;
   m_fast_ema   = 12;
   m_signal_ema = 9;
   m_delta      = 50;
   if(!InitIndicators())return(false);
   return(true);
}

bool cmodel_macd::Init(cmodel_macd_param &m_param)
{
   m_magic      = 148394;
   m_model_name = "MACD MODEL";
   m_symbol     = m_param.symbol;
   m_timeframe  = (ENUM_TIMEFRAMES)m_param.timeframe;
   m_fast_ema   = m_param.fast_ema;
   m_slow_ema   = m_param.slow_ema;
   m_signal_ema = m_param.signal_ema;
   if(!CheckParam(m_param))return(false);
   if(!InitIndicators())return(false);
   return(true);
}


bool cmodel_macd::CheckParam(cmodel_macd_param &m_param)
{
   if(!SymbolInfoInteger(m_symbol, SYMBOL_SELECT))
   {
      Print("Symbol ", m_symbol, " selection has failed. Check symbol name");
      return(false);
   }
   if(m_fast_ema == 0)
   {
      Print("Fast EMA must be greater than 0");
      return(false);
   }
   if(m_slow_ema == 0)
   {
      Print("Slow EMA must be greater than 0");
      return(false);
   }
   if(m_signal_ema == 0)
   {
      Print("Signal EMA must be greater than 0");
      return(false);
   }
   return(true);
}

bool cmodel_macd::InitIndicators()
{
   if(m_handle_macd==INVALID_HANDLE)
   {
      Print("Load indicators...");
      if((m_handle_macd=iMACD(m_symbol,m_timeframe,m_fast_ema,m_slow_ema,m_signal_ema,PRICE_CLOSE))==INVALID_HANDLE)
      {
         printf("Error creating MACD indicator");
         return(false);
      }
   }
   return(true);
}

bool cmodel_macd::Processing()
{
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_account_info.TradeAllowed()==false)return(false);
   //if(m_account_info.TradeExpert()==false)return(false);
   
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
   m_macd_current=m_macd_buff_main[0];
   m_macd_previous=m_macd_buff_main[1];
   GetNumberOrders(m_orders);
   if(m_orders.buy_orders>0)   LongClosed();
   else                        LongOpened();
   if(m_orders.sell_orders!=0) ShortClosed();
   else                        ShortOpened();
   return(true);
}

bool cmodel_macd::LongOpened(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   
   bool rezult, ticket_bool;
   double lot=0.1;
   mm open_mm;
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
   
   m_macd_current=m_macd_buff_main[0];
   m_macd_previous=m_macd_buff_main[1];
   GetNumberOrders(m_orders);
   
   //Print("LongOpened");
   if(m_macd_current>0&&m_macd_previous<=0&&m_orders.buy_orders==0)
   {
      //lot=open_mm.optimal_f(m_symbol, ORDER_TYPE_BUY, m_symbol_info.Ask(), 0.0, m_delta);
      lot=open_mm.jons_fp(m_symbol, ORDER_TYPE_BUY, m_symbol_info.Ask(), 0.1, 10000, m_delta);
      rezult=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_ADD, 0, lot, m_symbol_info.Ask(), 0, 0, "MACD Buy");
      return(rezult);
   }
   return(false);
}

bool cmodel_macd::ShortOpened(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   
   bool rezult, ticket_bool;
   double lot=0.1;
   mm open_mm;
   
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
   
   m_macd_current=m_macd_buff_main[0];
   m_macd_previous=m_macd_buff_main[1];
   GetNumberOrders(m_orders);
   
   if(m_macd_current<=0&&m_macd_previous>=0&&m_orders.sell_orders==0)
   {
      //lot=open_mm.optimal_f(m_symbol, ORDER_TYPE_SELL, m_symbol_info.Bid(), 0.0, m_delta);
      lot=open_mm.jons_fp(m_symbol, ORDER_TYPE_SELL, m_symbol_info.Bid(), 0.1, 10000, m_delta);
      rezult=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_ADD, 0, lot, m_symbol_info.Bid(), 0, 0, "MACD Sell");
      return(rezult);
   }
   return(false);
}

bool cmodel_macd::LongClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      if(t.Type()!=ORDER_TYPE_BUY)continue;
      m_symbol_info.Refresh();
      m_symbol_info.RefreshRates();
      CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
      if(m_symbol_info.Bid()<=t.StopLoss()&&t.StopLoss()!=0.0)
      {
         
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(), 
                       m_symbol_info.Bid(), 0.0, 0.0, "MACD: buy close buy stop-loss");
      }
      if(m_macd_current<0&&m_macd_previous>=0)
      {
         //Print("Long position closed by Order Send");
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(), 
                       m_symbol_info.Bid(), 0.0, 0.0, "MACD: buy close by signal");
      }
   }
   return(rez);
}

bool cmodel_macd::ShortClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      if(t.Type()!=ORDER_TYPE_SELL)continue;
      m_symbol_info.Refresh();
      m_symbol_info.RefreshRates();
      CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
      if(m_symbol_info.Ask()>=t.StopLoss()&&t.StopLoss()!=0.0)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                                 m_symbol_info.Ask(), 0.0, 0.0, "MACD: sell close buy stop-loss");
      }
      if(m_macd_current>0&&m_macd_previous<=0)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                                 m_symbol_info.Ask(), 0.0, 0.0, "MACD: sell close by signal");
      }
   }
   return(rez);
}

CModel 기본 클래스 는 자손의 내부 콘텐츠에 제한을 적용하지 않습니다. 이것이 의무화하는 유일한 것은 Processing() 인터페이스 기능. 이 기능의 내부 조직의 모든 문제는 특정 모델 클래스에 위임됩니다. 함수 Processing(), t내부에 배치 할 수있는 범용 알고리즘이 없으므로 특정 모델이 어떻게 배열되어야 하는지에 대한 방법을 자손에게 강요할 이유가 없습니다. 하지만 거의 모든 모델의 내부 구조는 표준화 될 수 있습니다. 이러한 표준화는 외부 및 심지어 코드에 대한 이해를 크게 촉진하고 모델을 보다 "공식적"으로 만들 것입니다.

각 모델에는 자체 이니셜라이저가 있어야 합니다. 모델의 이니셜라이저는 작동에 필요한 올바른 매개 변수를 로드해야 합니다. 예를 들어 모델이 작동하려면 MACD 표시기 값을 선택해야 하고,, 해당 버퍼의 핸들을 가져오고, 게다가, 물론 모델에 대한 모델의 거래 상품 및 타임 프레임을 결정해야 합니다. 이 모든 작업은 이니셜라이저에 의해 수행되어야 합니다.

모델의 이니셜라이저는 클래스의 단순한 오버로드 메소드입니다. 이러한 메소드는 공통 이름 Init를 갖습니다. 사실 MQL5는 오버로드 된 생성자를 지원하지 않으므로 이니셜라이저를 생성할 방법이 없습니다. 입력 매개 변수에 오버로드가 필요하기 때문입니다. 아무도 모델의 생성자에 해당 기본 매개 변수를 표시하는 것을 제한하지 않습니다.

모든 모델에는 세 개의 이니셜라이저가 있어야 합니다. 첫째 - 기본 이니셜라이저입니다. 매개 변수를 요청하지 않고 기본적으로 모델을 구성하고 로드해야 합니다. 이것은 "있는 그대로" 모드에서 테스트 할 때 매우 편리할 수 ​​있습니다. 예를 들어, 모델의 경우 기본 이니셜라이저 상품 도구및 모델의 타임 프레임은 현재 그래프를 선택하고 현재 타임 프레임을 선택하게 됩니다.

설정 MACD indicator 또한 표준이 될 것입니다: 빠른 EMA = 12, 느린 EMA = 26, 신호 MA = 9; 모델을 특정 방식으로 구성해야 하는 경우 이러한 이니셜라이저는 더 이상 적합하지 않습니다. 매개 변수가 있는 이니셜라이저가 필요합니다. 두 가지 유형을 만드는 것이 바람직하지만 반드시 그런 것은 아닙니다. 첫 번째는 매개 변수를 클래식 함수로 수신합니다. Init (type param1, type param2, ..., type paramN). 두 번째는 이러한 매개 변수를 저장하는 특수 구조를 사용하여 모델의 매개 변수를 찾습니다. 이 옵션은 때때로 매개 변수의 수가 클 수 있으므로 구조를 통해 전달하는 것이 편리하기 때문에 더 선호됩니다.

각 모델에는 매개 변수 구조가 있습니다. 이 구조는 모든 이름을 가질 수 있지만 modelname_param패턴으로 호출하는 것이 좋습니다. 모델 구성 - 다중 타임 프레임/다중 시스템/다중 통화 거래의 기회를 사용하는 데 있어 매우 중요한 단계입니다. 이 단계에서 이 모델이 거래될 상품과 방법이 결정됩니다.

우리의 거래 모델에는 4 개의 거래 기능만 있습니다. 롱 포지션을 여는 기능: LongOpen, 숏 포지션을 여는 기능 위치: ShortOpen, 롱 포지션을 여는 기능 위치 : LongClosed, 숏 포지션을 여는 기능 위치: ShortClosed. 기능 작동 LongOpen и ShortOpen 은 사소합니다. 둘 다 MACD 이전 바의 표시기 값을 수신하며 이는 이전 바 두개 값과 비교됩니다. "다시 그리기"를 방지하기 위해 현재 (제로) 바가 사용되지 않습니다.

하향 크로스 오버가 있는 경우 ShortOpen계산 함수 헤더 파일에 포함된 기능 사용 mm.mqh, 그 후 필요한 부지가 명령을 OrderSend 함수. The LongClose 이 시점에서 반대로 모델의 모든 롱 포지션을 닫습니다. 이는 함수가 모델의 주문 테이블에서 현재 진행중인 모든 주문을 순차적으로 정렬하기 때문에 발생합니다. 긴 주문이 발견되면 함수는 카운터 주문으로 이를 마감합니다. 똑같은 것, 반대 방향이지만 ShortClose() 함수. 이러한 기능의 작업은 위에 제공된 목록에서 확인할 수 있습니다.

거래 모델에서 현재 랏가 어떻게 계산되는지 더 자세히 분석해 보겠습니다.

위에서 언급했듯이 이러한 목적을 위해 계정의 대문자 표시를 위한 특수 기능을 사용합니다. 자본화 공식 외에도 이러한 기능에는 사용 된 마진 수준과 거래 포지션 크기 제한에 따른 랏 계산 확인이 포함됩니다.포지션 규모에 일부 거래 제한이 있을 수 있습니다. 그것들을 고려해야 합니다.

음의 경우를 고려하십시오. 양방향으로 15개의 표준 랏의 거래 포지션 크기에 제한이 있습니다. 현재 포지션은 길고 3개의 랏과 같습니다. 자본 관리 시스템을 기반으로 한 거래 모델은 18.6 랏의 거래량으로 롱 포지션을 열기를 원합니다. 기능 CheckLot() 는 수정된 주문량을 반환합니다. 이 경우 12개의 랏이 됩니다 (15개 중 3 개는 이미 다른 딜에서 점유하고 있기 때문) 현재 열린 함수가 짧고 길지 않다면 함수는 18.6 대신 15 랏을 반환했을 것입니다. 이것은 포지션의 가능한 최대 볼륨입니다.

15 랏을 buy, 에 넣은 후, 이 경우에는, 12 랏 (3 - 판매, 15 - 구매). 다른 모델이 3 랏의 초기 매도 포지션을 재정의하면, 구매 집계 포지션이 가능한 최대 포지션이 됩니다 - 15개의 랏. 다른 구매 신호는모델이 15개 랏의 일부 또는 전부를 재정의 할 때까지 처리되지 않습니다. 구매. 요청된 트랜잭션에 사용 가능한 볼륨 EMPTY_VALUE 상수. 이 신호는 전달되어야 합니다.

설정된 볼륨의 가능성에 대한 확인이 성공하면 필요한 마진 값에 대한 계산이 이루어집니다. 설정된 거래량에 비해 계좌 자금이 부족할 수 있습니다. 이러한 목적을 위해 CheckMargin() 함수가 있습니다. 증거금이 충분하지 않은 경우 현재 여유 증거금이 개설을 허용하도록 명시된 거래 금액을 수정하려고 시도합니다. 마진이 최소 볼륨을 열기에도 충분하지 않다면 Margin-Call.

현재 포지션이없고 마진이 사용되지 않는 경우 기술적인 부분만 의미합니다. 마진- - 거래를 개시 할 수 없는 상태. 계정에 금액을 추가하지 않으면 계속할 수 없습니다. 약간의 마진이 여전히 사용된다면, 우리는 기다릴 수밖에 없습니다.그 포지션이 종료 될 때까지 이 여백을 사용합니다. 어쨌든 여백이 없으면 상수 EMPTY_VALUE가 반환됩니다.

랏의 크기와 마진을 제어하는 ​​기능은 일반적으로 직접 사용하지 않습니다. 그들은 자본 관리를 위한 특수 기능에 의해 호출됩니다. 이러한 함수는 계정 대문자화 공식을 구현합니다. mm.mqh 파일에는 자본 관리의 두 가지 기본 기능만 포함됩니다. 하나는 계정의 고정 비율을 기준으로 계산되고 다른 하나는 Ryan Jones가 제안한 방법인 고정 비율 방법을 기반으로 합니다.

첫 번째 방법은 위험이 발생할 수 있는 계정의 고정된 부분을 정의하는 것입니다. 예를 들어, 허용된 위험이 계정의 2 %이고 계정이 10,000 달러인 경우 최대 위험 금액은 $ 200입니다. 200 달러 경유지에 어떤 랏을 사용해야 하는지 계산하려면 오픈 포지션에 대한 가격으로 최대 거리에 도달할 수 있는 최대 거리를 정확히 알아야 합니다. 따라서 이 공식을 통해 랏을 계산하려면 손절매와 거래가 이루어질 가격 수준을 정확하게 결정해야 합니다.

Ryan Jones가 제안한 방법은 이전 방법과 다릅니다. 그 본질은 대문자화가 이차 방정식의 특정 경우에 정의된 함수에 의해 수행된다는 것입니다.

해결책은 다음과 같습니다.

x=((1.0+MathSqrt(1+4.0*d))/2)*단계;

여기서: x - 다음 단계로 전환 d = (이익 / 델타) * 2.0 단계- 0.1 랏과 같은 델타 단계.

델타의 크기가 작을수록 함수는 더 적극적으로 위치 수를 늘리려고 합니다.이 함수가 어떻게 구성되는지에 대한 자세한 내용은 Ryan Jones의 책, The Trading Game: Playing by the Numbers to Make Millions를 참조할 수 있습니다..

자본 관리 기능을 사용할 계획이 없는 경우에는 랏 및 마진을 제어하는 ​​기능을 직접 호출해야 합니다.

그래서 우리는 기본 EA의 모든 요소를 ​​검토했습니다. 우리 일의 결실을 거둘 때입니다.

우선 네 가지 모델을 만들어 보겠습니다. 하나의 모델을 EURUSD 기본 매개 변수로 거래하고 두 번째 모델도 EURUSD에서 거래하게 하되, 15 분의 타임 프레임을 둡니다. 세 번째 모델이 그래프에 표시됩니다. GBPUSD기본 매개 변수 포함. 네 번째 - on USDCHF 2 시간 그래프에서 다음 매개 변수 포함: 느림EMA= 6 빠름EMA = 12 신호EMA = 9. 기간 테스트 - H1, 테스트 모드 - 모든 틱, 2010 년 1 월 1 일부터 2010 년 1 월 9 일까지의 시간입니다.

하지만 이 모델을 네 가지 모드로 실행하기 전에 먼저 각 상품 및 타임 프레임에 대해 개별적으로 테스트 해보겠습니다.

다음은 테스트의 주요 지표가 작성된 표입니다.

시스템
거래 수
이익, $
MACD(9,12,26)H1 EURUSD
123
1092
MACD (9,12,26) EURUSD M15
598
-696
MACD(9,6,12) USDCHF H2
153
-1150
MACD(9,12,26) GBPUSD H1
139
-282
모든 시스템
1013
-1032

표는 모든 모델의 총 거래 수는 1013 개, 총 수익은 $ -1032이어야 함을 보여줍니다.

따라서 이러한 시스템을 동시에 테스트 할 때 얻어야 하는 값과 동일합니다. 약간의 편차가 여전히 발생하더라도 결과는 다르지 않아야 합니다.

그래서 다음은 최종 테스트입니다.


보시다시피 거래가 하나 적고 이익은 $ 10 만 차이가 납니다. 이는 0.1이 많은 10 포인트 차이에 해당합니다. 자금 관리 시스템을 사용하는 경우 통합 테스트의 결과는 각 모델의 개별 테스트 결과의 양과 급격히 다릅니다. 이는 균형의 역학이 각 시스템에 영향을 미치므로 랏의 계산된 값이 달라지기 때문입니다. 

그 자체로는 결과가 흥미롭지 않지만 복잡하지만 매우 유연하고 관리하기 쉬운 EA 구조를 만들었습니다. 그 구조를 다시 간단히 살펴 보겠습니다.

이를 위해 우리는 아래에 표시된 계획으로 돌아갈 것입니다.

클래스 참조

스키마는 모델 인스턴스의 기본 구조를 보여줍니다.

거래 모델의 클래스 인스턴스가 생성되면 오버로드된 Init() 함수를 호출합니다. 필요한 매개 변수를 초기화하고 데이터를 준비하며 사용중인 경우 표시기의 핸들을 로드합니다. 이 모든 작업은 EA 초기화 중에 발생합니다. 즉 OnInit() 함수 내부에서 발생합니다. 데이터에는 거래를 용이하게 하기 위해 설계된 기본 클래스의 인스턴스가 포함됩니다. 거래 모델은 MQL5의 표준 기능 대신 이러한 클래스를 적극적으로 사용해야한다고 가정합니다. 모델 클래스가 성공적으로 생성되고 초기화되면 CList 모델 목록에 포함됩니다. 추가 통신은 범용 어댑터 CObject를 통해 수행됩니다.

OnTrade() 또는 OnTick() 이벤트가 발생한 후 목록에있는 모델의 모든 인스턴스가 순차적으로 정렬됩니다. 그들과의 통신은 Processing() 함수를 호출하여 수행됩니다. 또한 자체 모델 (파란색 기능 그룹)의 거래 기능을 호출합니다. 목록과 이름이 엄격하게 정의되어 있지는 않지만 LongOpened(), ShortClosed() 등과 같은 표준 이름을 사용하는 것이 편리합니다. 이러한 함수는 포함된 논리를 기반으로 거래를 완료 할 시간을 선택한 다음 SendOrder() 함수의 거래 시작 또는 종료를 위해 특별히 구성된 요청을 보냅니다.

후자는 필요한 확인을 거쳐 시장에 주문을 출력합니다. 거래 기능은 모델의 보조 기능 (녹색 그룹)에 의존하며, 차례로 기본 보조 클래스 (보라색 그룹)를 적극적으로 사용합니다. 모든 보조 클래스는 데이터 섹션 (분홍색 그룹)에서 클래스의 인스턴스로 표시됩니다. 그룹 간의 상호 작용은 진한 파란색 화살표로 표시됩니다.


Bollinger Bands 지표를 기반으로 한 거래 모델

이제 데이터 및 방법의 일반적인 구조가 명확해졌으므로 볼린저 밴드의 추세 지표를 기반으로 또 다른 거래 모델을 만들 것입니다. 이 거래 모델의 기초로 Andrei Kornishkin의 간단한 EA Bollinger Bands를 기반으로 한 Expert Advisor를 사용했습니다. 볼린저 밴드 - 단순 이동 평균에서 특정 크기의 표준 편차와 동일한 수준입니다. 이 지표가 어떻게 구성되는지에 대한 자세한 내용은 MetaTrader 5 터미널에 첨부된 기술 분석 도움말 섹션에서 찾을 수 있습니다.

거래 아이디어의 본질은 간단합니다. 가격에는 반환 속성이 ​​있습니다. 즉 가격이 특정 수준에 도달하면 반대 방향으로 바뀔 가능성이 높습니다. 이 논문은 실제 거래 상품의 정규 분포에 대한 테스트로 입증되었습니다. 정규 분포 곡선은 약간 길어질 것입니다. 볼린저 밴드는 가격 수준의 가장 가능성 있는 정점을 결정합니다. 일단 그것들에 도달하면 (상단 또는 하단 볼린저 밴드) 가격은 반대 방향으로 전환될 가능성이 높습니다.

우리는 거래 전술을 약간 단순화하고 보조 표시기인 이중 지수 이동 평균 (이중 지수 이동 평균 또는 DEMA)을 사용하지 않을 것입니다. 그러나 우리는 엄격한 보호 정지, 즉 가상 손절매를 사용할 것입니다. 그들은 거래 프로세스를 더 안정적으로 만들고 동시에 각 거래 모델이 자체 보호 중지 수준을 사용하는 예를 이해하는데 도움이 됩니다.

보호 정지 수준의 경우 현재 가격에 지표값 변동성 ATR을 더하거나 뺀 값을 사용합니다. 예를 들어, ATR의 현재 가치가 68 포인트와 같고 1.25720의 가격에 판매 할 신호가 있는 경우이 거래의 가상 손절매는 1.25720 + 0.0068 = 1.26400이됩니다. 유사하게, 그러나 반대 방향으로 구매를 위해 수행됩니다 : 1.25720 - 0.0068 = 1.25040.

이 모델의 소스 코드는 다음과 같습니다.

#include <Models\Model.mqh>
#include <mm.mqh>
//+----------------------------------------------------------------------+
//| This model use Bollinger bands.
//| Buy when price is lower than lower band
//| Sell when price is higher than upper band
//+----------------------------------------------------------------------+  
struct cmodel_bollinger_param
{
   string            symbol;
   ENUM_TIMEFRAMES   timeframe;
   int               period_bollinger;
   double            deviation;
   int               shift_bands;
   int               period_ATR;
   double            k_ATR;
   double            delta;
};
   
class cmodel_bollinger : public CModel
{
private:
   int               m_bollinger_period;
   double            m_deviation;
   int               m_bands_shift;
   int               m_ATR_period;
   double            m_k_ATR;
   //------------Indicators Data:-------------
   int               m_bollinger_handle;
   int               m_ATR_handle;
   double            m_bollinger_buff_main[];
   double            m_ATR_buff_main[];
   //-----------------------------------------
   MqlRates          m_raters[];
   double            m_current_price;
public:
                     cmodel_bollinger();
   bool              Init();
   bool              Init(cmodel_bollinger_param &m_param);
   bool              Init(ulong magic, string name, string symbol, ENUM_TIMEFRAMES TimeFrame, double delta,
                          uint bollinger_period, double deviation, int bands_shift, uint ATR_period, double k_ATR);
   bool              Processing();
protected:
   bool              InitIndicators();
   bool              CheckParam(cmodel_bollinger_param &m_param);
   bool              LongOpened();
   bool              ShortOpened();
   bool              LongClosed();
   bool              ShortClosed();
   bool              CloseByStopSignal();
};

cmodel_bollinger::cmodel_bollinger()
{
   m_bollinger_handle   = INVALID_HANDLE;
   m_ATR_handle         = INVALID_HANDLE;
   ArraySetAsSeries(m_bollinger_buff_main,true);
   ArraySetAsSeries(m_ATR_buff_main,true);
   ArraySetAsSeries(m_raters, true);
   m_current_price=0.0;
}
//this default loader
bool cmodel_bollinger::Init()
{
   m_magic              = 322311;
   m_model_name         =  "Bollinger Bands Model";
   m_symbol             = _Symbol;
   m_timeframe          = _Period;
   m_bollinger_period   = 20;
   m_deviation          = 2.0;
   m_bands_shift        = 0;
   m_ATR_period         = 20;
   m_k_ATR              = 2.0;
   m_delta              = 0;
   if(!InitIndicators())return(false);
   return(true);
}

bool cmodel_bollinger::Init(cmodel_bollinger_param &m_param)
{
   m_magic              = 322311;
   m_model_name         = "Bollinger Model";
   m_symbol             = m_param.symbol;
   m_timeframe          = (ENUM_TIMEFRAMES)m_param.timeframe;
   m_bollinger_period   = m_param.period_bollinger;
   m_deviation          = m_param.deviation;
   m_bands_shift        = m_param.shift_bands;
   m_ATR_period        = m_param.period_ATR;
   m_k_ATR              = m_param.k_ATR;
   m_delta              = m_param.delta;
   //if(!CheckParam(m_param))return(false);
   if(!InitIndicators())return(false);
   return(true);
}

bool cmodel_bollinger::Init(ulong magic, string name, string symbol, ENUM_TIMEFRAMES timeframe, double delta,
                           uint bollinger_period, double deviation, int bands_shift, uint ATR_period, double k_ATR)
{
   m_magic           = magic;
   m_model_name      = name;
   m_symbol          = symbol;
   m_timeframe       = timeframe;
   m_delta           = delta;
   m_bollinger_period= bollinger_period;
   m_deviation       = deviation;
   m_bands_shift     = bands_shift;
   m_ATR_period      = ATR_period;
   m_k_ATR           = k_ATR;
   if(!InitIndicators())return(false);
   return(true);
}


/*bool cmodel_bollinger::CheckParam(cmodel_bollinger_param &m_param)
{
   if(!SymbolInfoInteger(m_symbol, SYMBOL_SELECT)){
      Print("Symbol ", m_symbol, " select failed. Check valid name symbol");
      return(false);
   }
   if(m_ma == 0){
      Print("Fast EMA must be bigest 0. Set MA = 12 (default)");
      m_ma=12;
   }
   return(true);
}*/

bool cmodel_bollinger::InitIndicators()
{
   m_bollinger_handle=iBands(m_symbol,m_timeframe,m_bollinger_period,m_bands_shift,m_deviation,PRICE_CLOSE);
   if(m_bollinger_handle==INVALID_HANDLE){
      Print("Error in creation of Bollinger indicator. Restart the Expert Advisor.");
      return(false);
   }
   m_ATR_handle=iATR(m_symbol,m_timeframe,m_ATR_period);
   if(m_ATR_handle==INVALID_HANDLE){
      Print("Error in creation of ATR indicator. Restart the Expert Advisor.");
      return(false);
   }
   return(true);
}

bool cmodel_bollinger::Processing()
{
   //if(timing(m_symbol,m_timeframe, m_timing)==false)return(false);
   
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_account_info.TradeAllowed()==false)return(false);
   //if(m_account_info.TradeExpert()==false)return(false);
   
   //m_symbol_info.Name(m_symbol);
   //m_symbol_info.RefreshRates();
   //Copy last data of moving average
 
   GetNumberOrders(m_orders);
   
   if(m_orders.buy_orders>0)   LongClosed();
   else                        LongOpened();
   if(m_orders.sell_orders!=0) ShortClosed();
   else                        ShortOpened();
   if(m_orders.all_orders!=0)CloseByStopSignal();
   return(true);
}

bool cmodel_bollinger::LongOpened(void)
{
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   //Print("Model Bollinger: ", m_orders.buy_orders);
   bool rezult, time_buy=true;
   double lot=0.1;
   double sl=0.0;
   double tp=0.0;
   mm open_mm;
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   //lot=open_mm.optimal_f(m_symbol,OP_BUY,m_symbol_info.Ask(),sl,delta);
   CopyBuffer(m_bollinger_handle,2,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close>m_bollinger_buff_main[1]&&m_raters[1].open<m_bollinger_buff_main[1])
   {
      sl=NormalizeDouble(m_symbol_info.Ask()-m_ATR_buff_main[0]*m_k_ATR,_Digits);
      SendOrder(m_symbol,ORDER_TYPE_BUY,ORDER_ADD,0,lot,m_symbol_info.Ask(),sl,tp,"Add buy");
   }
   return(false);
}

bool cmodel_bollinger::ShortOpened(void)
{
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   
   bool rezult, time_sell=true;
   double lot=0.1;
   double sl=0.0;
   double tp;
   mm open_mm;
   
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(m_bollinger_handle,1,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close<m_bollinger_buff_main[1]&&m_raters[1].open>m_bollinger_buff_main[1])
   {   
      sl=NormalizeDouble(m_symbol_info.Bid()+m_ATR_buff_main[0]*m_k_ATR,_Digits);
      SendOrder(m_symbol,ORDER_TYPE_SELL,ORDER_ADD,0,lot,m_symbol_info.Ask(),sl,tp,"Add buy");
   }
   return(false);
}

bool cmodel_bollinger::LongClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(m_bollinger_handle,1,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close<m_bollinger_buff_main[1]&&m_raters[1].open>m_bollinger_buff_main[1])
   {
      for(int i=total_elements-1;i>=0;i--)
      {
         if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
         t=ListTableOrders.GetNodeAtIndex(i);
         if(CheckPointer(t)==POINTER_INVALID)continue;
         if(t.Type()!=ORDER_TYPE_BUY)continue;
         m_symbol_info.Refresh();
         m_symbol_info.RefreshRates();
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(), 
                       m_symbol_info.Bid(), 0.0, 0.0, "BUY: close by signal");
      }
   }
   return(rez);
}

bool cmodel_bollinger::ShortClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   CopyBuffer(m_bollinger_handle,2,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close>m_bollinger_buff_main[1]&&m_raters[1].open<m_bollinger_buff_main[1])
   {
      for(int i=total_elements-1;i>=0;i--)
      {
         if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
         t=ListTableOrders.GetNodeAtIndex(i);
         if(CheckPointer(t)==POINTER_INVALID)continue;
         if(t.Type()!=ORDER_TYPE_SELL)continue;
         m_symbol_info.Refresh();
         m_symbol_info.RefreshRates();
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                       m_symbol_info.Ask(), 0.0, 0.0, "SELL: close by signal");
      }
   }
   return(rez);
}

bool cmodel_bollinger::CloseByStopSignal(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   bool rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      if(t.Type()!=ORDER_TYPE_SELL&&t.Type()!=ORDER_TYPE_BUY)continue;
      m_symbol_info.Refresh();
      m_symbol_info.RefreshRates();
      CopyRates(m_symbol,m_timeframe,0,3,m_raters);
      if(m_symbol_info.Bid()<=t.StopLoss()&&t.Type()==ORDER_TYPE_BUY)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                       m_symbol_info.Bid(), 0.0, 0.0, "BUY: close by stop");
         continue;
      }
      if(m_symbol_info.Ask()>=t.StopLoss()&&t.Type()==ORDER_TYPE_SELL)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                       m_symbol_info.Ask(), 0.0, 0.0, "SELL: close by stop");
         continue;
      }
   }
   return(rez);
}

보시다시피, 거래 모델의 코드는 이전 거래 전술의 소스 코드와 매우 유사합니다. 여기서 주요 변형은 가상 중지 명령의 출현과 이러한 보호 중지를 제공하는 cmodel_bollinger:: CloseByStopSignal() 함수입니다.

실제로 보호 정지를 사용하는 경우 해당 값은 단순히 SendOrder() 함수로 전달되어야 합니다. 그리고 이 함수는 이러한 레벨을 주문 테이블에 입력합니다. 가격이 이 수준을 넘거나 닿으면 CloseByStopSignal() 함수는 카운터 주문으로 거래를 종료하고 활성 주문 목록에서 제거합니다.


거래 모델, 상품 및 타임 프레임을 단일 엔티티로 결합

이제 두 가지 거래 모델이 있으므로 동시에 테스트 할 차례입니다. 모델의 레이아웃을 만들기 전에 가장 효과적인 매개 변수를 결정하는 것이 유용할 것입니다. 이를 위해서는 각 모델을 개별적으로 최적화해야 합니다.

최적화는 모델이 가장 효과적인 시장과 타임 프레임을 보여줍니다. 각 모델에 대해 우리는 최고의 타임 프레임 중 2 개와 최고의 상품 3 개만 선택합니다. 결과적으로 우리는 12 개의 독립적 인 솔루션 (2개 모델 * 3개 상품 * 2 개의 타임 프레임)을 얻었으며 모두 함께 테스트됩니다. 물론, 선택된 최적화 방법은 소위 "조정" 결과의 문제를 겪지만, 우리의 목적상 이것은 중요하지 않습니다.

아래 그래프는 샘플의 최상의 결과를 보여줍니다.

1.1 MACD EURUSD M30

MACD EURUSD M30

1.2 . MACD EURUSD H3


MACD EURUSD H3

1.3 MACD AUDUSD H4

MACD AUDUSD H4

1.4 . MACD AUDUSD H1


MACD AUDUSD H1

1.5 MACD GBPUSD H12


MACD GBPUSD H12

1.6 MACD GBPUSD H6


MACD GBPUSD H6

2.1 볼린저 GBPUSD M15


볼린저 GBPUSD M15

2.2 볼린저 GBPUSD H1


볼린저 GBPUSD H1

2.3 볼린저 EURUSD M30

볼린저 EURUSD M30

 

2.4  볼린저 EURUSD H4


볼린저 EURUSD H4

 

2.5 볼린저 USDCAD M15


볼린저 USDCAD M15

 

2.6 볼린저 USDCAD H2

볼린저 USDCAD H2

이제 최적의 결과가 알려졌으므로 결과를 단일 항목으로 모으는 작업이 조금 더 남았습니다.

아래는 위에 표시된 12 가지 거래 모델을 생성하는 function-loader의 소스 코드이며, 이후 EA는 지속적으로 거래를 시작합니다.

bool macd_default=true;
bool macd_best=false;
bool bollinger_default=false;
bool bollinger_best=false;

void InitModels()
{
   list_model = new CList;             // Initialized pointer of the model list
   cmodel_macd *model_macd;            // Create the pointer to a model MACD
   cmodel_bollinger *model_bollinger;  // Create the pointer to a model Bollinger
   
//----------------------------------------MACD DEFAULT----------------------------------------
   if(macd_default==true&&macd_best==false)
    {
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      // Loading of the parameters was completed successfully
      if(model_macd.Init(129475, "Model macd M15", _Symbol, _Period, 0.0, Fast_MA,Slow_MA,Signal_MA))
      { 
      
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Загружаем модель в список моделей
      }
      else
      {
                                 // The loading of parameters was completed successfully
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " creation has failed");
      }
   }
//-------------------------------------------------------------------------------------------
//----------------------------------------MACD BEST------------------------------------------
   if(macd_best==true&&macd_default==false)
   {
      // 1.1 EURUSD H30; FMA=20; SMA=24; 
      model_macd = new cmodel_macd; // Initialize the pointer to the model MACD
      if(model_macd.Init(129475, "Model macd H30", "EURUSD", PERIOD_M30, 0.0, 20,24,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
               " on symbol ", model_macd.Symbol(), " created successfully");
         list_model.Add(model_macd);// load the model into the list of models
      }
      else
      {// Loading parameters was completed unsuccessfully
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.2 EURUSD H3; FMA=8; SMA=12; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H3", "EURUSD", PERIOD_H3, 0.0, 8,12,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
       {// Loading of parameters was unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.3 AUDUSD H1; FMA=10; SMA=18; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd M15", "AUDUSD", PERIOD_H1, 0.0, 10,18,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
      {// The loading of parameters was unsuccessful                       
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
               " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.4 AUDUSD H4; FMA=14; SMA=15; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H4", "AUDUSD", PERIOD_H4, 0.0, 14,15,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else{// Loading of parameters was unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.5 GBPUSD H6; FMA=20; SMA=33; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H6", "GBPUSD", PERIOD_H6, 0.0, 20,33,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
      {// Loading of parameters was  unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
               " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.6 GBPUSD H12; FMA=12; SMA=30; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H6", "GBPUSD", PERIOD_H12, 0.0, 12,30,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
      {// Loading of parameters was unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " creation has failed");
      }
   }
//----------------------------------------------------------------------------------------------
//-------------------------------------BOLLINGER DEFAULT----------------------------------------
   if(bollinger_default==true&&bollinger_best==false)
   {
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger",_Symbol,PERIOD_CURRENT,0,
                             period_bollinger,dev_bollinger,0,14,k_ATR))
      {
         Print("Model ", model_bollinger.Name(), " successfully created");
         list_model.Add(model_bollinger);
      }
   }
//----------------------------------------------------------------------------------------------
//--------------------------------------BOLLLINGER BEST-----------------------------------------
   if(bollinger_best==true&&bollinger_default==false)
   {
      //2.1 Symbol: EURUSD M30; period: 15; deviation: 2,75; k_ATR=2,75;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","EURUSD",PERIOD_M30,0,15,2.75,0,14,2.75))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
              ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.2 Symbol: EURUSD H4; period: 30; deviation: 2.0; k_ATR=2.25;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","EURUSD",PERIOD_H4,0,30,2.00,0,14,2.25))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.3 Symbol: GBPUSD M15; period: 18; deviation: 2.25; k_ATR=3.0;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","GBPUSD",PERIOD_M15,0,18,2.25,0,14,3.00))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.4 Symbol: GBPUSD H1; period: 27; deviation: 2.25; k_ATR=3.75;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","GBPUSD",PERIOD_H1,0,27,2.25,0,14,3.75))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.5 Symbol: USDCAD M15; period: 18; deviation: 2.5; k_ATR=2.00;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","USDCAD",PERIOD_M15,0,18,2.50,0,14,2.00))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.6 Symbol: USDCAD M15; period: 21; deviation: 2.5; k_ATR=3.25;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","USDCAD",PERIOD_H2,0,21,2.50,0,14,3.25))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
   }
//----------------------------------------------------------------------------------------------
}

이제 12개 모델을 모두 동시에 테스트 해 보겠습니다.


결과의 대문자

결과 그래프는 인상적입니다. 그러나 중요한 것은 결과가 아니라 모든 모델이 개별 보호 스톱을 사용하고 모두 서로 독립적으로 동시에 거래된다는 사실입니다. 이제 결과 그래프를 대문자로 만들어 보겠습니다. 이를 위해 우리는 고정 비례 방법과 Ryan Jones가 제안한 방법과 같은 대문자의 표준 기능을 사용할 것입니다.

소위 최적 f - 고정 비례 방법의 특별한 경우. 이 방법의 핵심은 각 거래에 계정의 비율과 동일한 손실 한도가 부여된다는 것입니다. 보수적 자본화 전략은 일반적으로 2 % 손실 한도를 적용합니다. 즉 $ 10,000에서는 손절매를 통과한 후 손실이 $ 200을 초과하지 않도록 포지션 크기가 계산됩니다. 그러나 리스크 증가에 따른 최종 잔액의 성장에는 일정한 기능이 있습니다. 이 기능은 종 모양입니다. 즉 처음에는 위험이 증가함에 따라 총 이익이 증가합니다. 그러나 각 거래에는 위험 임계 값이 있으며 그 후 총 이익 잔액이 감소하기 시작합니다. 이 임계 값은 소위 최적 f입니다.

이 글은 자본 관리 문제에 관한 것이 아니며 고정 비례 방법을 사용하기 위해 알아야 할 모든 것은 보호 중지 수준과 위험 할 수있는 계정의 비율입니다. Ryan Jones 공식은 다르게 구성됩니다. 작업을 위해 고정 보호 정지 장치를 사용할 필요가 없습니다. 제안된 첫 번째 모델 (MACD model)은 다소 원시적이며 보호 정지가 없으므로 이 모델의 대문자 사용에 이 방법을 사용합니다. 모델의 경우 볼린저 밴드를 기반으로 고정 비례 비율 방법을 사용합니다.

대문자 공식 사용을 시작하려면 기본 모델에 포함 된 변수 m_delta를 채워야 합니다.

고정 비율 공식을 사용하는 경우 각 거래의 위험 비율과 같아야 합니다. Ryan Jones의 방법을 사용할 때, 이는 소위 델타 증분, 즉 더 높은 수준의 포지션 볼륨에 도달하기 위해 필요한 돈의 양과 같습니다.

아래는 대소문자 그래프입니다.


알 수 있듯이 모든 모델에는 고유한 대문자 공식이 있습니다 (고정 비례 방법 또는 Ryan Jones 방법).

제공된 예에서는 모든 모델에 대해 동일한 최대 위험 및 델타 값을 사용했습니다. 그러나 각각에 대해 대문자 표시를 위해 개별 매개 변수를 선택할 수 있습니다. 각 모델의 미세 조정은 이 글에서 다루는 문제 범위에 포함되지 않습니다.


보류 주문 작업

제시된 거래 모델은 소위 보류 주문을 사용하지 않습니다. 특정 조건이 있는 경우에만 실행되는 주문입니다.

실제로, 보류 주문을 사용하는 모든 거래 전략은 시장에서의 주문 사용에 맞게 조정될 수 있습니다. 오히려 보류중인 주문은보다 안정적인 시스템 운영을 위해 필요합니다. 로봇 또는 로봇이 작동하는 장비의 고장이 발생할 때 보류중인 주문이 여전히 보호 중지를 실행하거나 그 반대의 경우에 이전에 결정된 가격에 기반하여 시장에 진입하기 때문입니다.

제안된 거래 모델을 사용하면 대기중인 거래 주문을 처리 할 수 ​​있지만 이 경우 프레젠테이션 제어 프로세스가 훨씬 더 복잡합니다. 이러한 주문을 처리하기 위해 CTableOrders 클래스의 오버로드 된 메소드 Add (COrderInfo & order_info, double stop_loss, double take_profit)를 사용합니다. 이 경우이 클래스의 변수 m_type에는 적절한 유형의 보류중인 주문이 포함됩니다. ORDER_TYPE_BUY_STOP 또는 ORDER_TYPE_SELL_LIMIT.

나중에 보류중인 주문이 처리 될 때 작동이 시작된 순간을 "잡아야" 합니다. 그렇지 않으면 관련 기간이 만료됩니다. 이것은 단순히 이벤트 거래를 제어하는 ​​것으로는 충분하지 않기 때문에 그렇게 간단하지 않지만, 무엇이 그것을 촉발 시켰는지도 알아야 합니다.

MQL5의 언어는 진화하고 있으며 현재 Trade 이벤트를 설명하는 특수 서비스 구조를 포함하는 문제가 해결되고 있습니다. 그러나 지금은 내역에서 주문 목록을 확인해야 합니다. 주문 테이블의 대기 주문과 동일한 티켓이있는 주문이 내역에서 발견되면 테이블에 반영해야 하는 이벤트가 발생한 것입니다. 

기본 모델의 코드에는 특수 메소드 CModel:: ReplaceDelayedOrders가 포함되어있습니다. 이 방법은 다음 알고리즘에서 작동합니다. 먼저, 주문 테이블의 모든 활성 주문이 확인됩니다. 이러한 주문의 티켓은 기록 (HistoryOrderGetTicket())에 있는 주문의 티켓과 비교됩니다. 내역의 주문 티켓이 주문 테이블의 대기 주문과 일치하지만 주문 상태가 실행되는 경우 (ORDER_STATE_PARTIAL 또는ORDER_STATE_FILLED), 주문 테이블의 보류 주문 상태도 실행을 위해 변경됩니다.

또한 이 주문이 Stop Loss 및 Take Profit의 작업을 모방하여 보류중인 주문과 연결되지 않은 경우 해당 주문이 출력되고 해당 티켓이 적절한 테이블 값 (TicketSL, TicketTP)에 입력됩니다. 그리고 보호 중지 및 이익 수준을 모방 한 보류 주문은 변수 m_sl 및 m_tp의 도움으로 미리 지정된 가격으로 발행됩니다. 즉 이러한 가격은 ReplaceDelayedOrders 메소드를 호출 할 때 알아야 합니다.

이 방법은 주문이 시장에 출시 될 때 필요한 보류 주문이 손절매 및 이익 실현 유형인 상황을 처리하는 데 적용된다는 점에 주목할 필요가 있습니다.

일반적으로 제안된 방법의 작업은 사소하지 않으며 사용을 위해 약간의 이해가 필요합니다.

bool CModel::ReplaceDelayedOrders(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int history_orders=HistoryOrdersTotal();
   ulong ticket;
   bool rez=false;
   long request;
   total_elements=ListTableOrders.Total();
   int try=0;
   if(total_elements==0)return(false);
   // View every order in the table
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      switch(t.Type())
      {
         case ORDER_TYPE_BUY:
         case ORDER_TYPE_SELL:
            for(int b=0;i<history_orders;b++)
            {
               ticket=HistoryOrderGetTicket(b);
               // If the ticket of the historical order is equal to one of the tickets 
               // Stop Loss or Take Profit, then the order was blocked, and it needs to be
               // deleted from the table of orders
               if(ticket==t.TicketSL()||ticket==t.TicketTP())
               {
                  ListTableOrders.DeleteCurrent();
               }
            }
            // If the orders, imitating the Stop Loss and Take Profit are not found in the history,
            // then perhaps they are not yet set. Therefore, they need to be inputted,
            // using the process for pending orders below:
            // the cycle  keeps going, the exit 'break' does not exist!!!
         case ORDER_TYPE_BUY_LIMIT:
         case ORDER_TYPE_BUY_STOP:
         case ORDER_TYPE_BUY_STOP_LIMIT:
         case ORDER_TYPE_SELL_LIMIT:
         case ORDER_TYPE_SELL_STOP:
         case ORDER_TYPE_SELL_STOP_LIMIT:
            for(int b=0;i<history_orders;b++)
            {
               ticket=HistoryOrderGetTicket(b);
               // If the ticket of the historical order is equal to the ticket of the pending order 
               // then the pending order has worked and needs to be put out
               // the pending orders, imitating the work of Stop Loss and Take Profit.
               // It is also necessary to change the pending status of the order in the table
               // of orders for the executed (ORDER_TYPE_BUY или ORDER_TYPE_SELL)
               m_order_info.InfoInteger(ORDER_STATE,request);
               if(t.Ticket()==ticket&&
                  (request==ORDER_STATE_PARTIAL||request==ORDER_STATE_FILLED))
                  {
                  // Change the status order in the table of orders:
                  m_order_info.InfoInteger(ORDER_TYPE,request);
                  if(t.Type()!=request)t.Type(request);
                  //------------------------------------------------------------------
                  // Put out the pending orders, imitating Stop Loss an Take Profit:
                  // The level of pending orders, imitating Stop Loss and Take Profit
                  // should be determined earlier. It is also necessary to make sure that
                  // the current order is not already linked with the pending order, imitating Stop Loss
                  // and Take Profit:
                  if(t.StopLoss()!=0.0&&t.TicketSL()==0)
                    {
                     // Try to put out the pending order:
                     switch(t.Type())
                     {
                        case ORDER_TYPE_BUY:
                           // Make three attempts to put out the pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.SellStop(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                        case ORDER_TYPE_SELL:
                           // Make three attempts to put up a pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.BuyStop(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                     }
                  }
                  if(t.TakeProfit()!=0.0&&t.TicketTP()==0){
                     // Attempt to put out the pending order, imitating Take Profit:
                     switch(t.Type())
                     {
                        case ORDER_TYPE_BUY:
                           // Make three attempts to put out the pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.SellLimit(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                           break;
                        case ORDER_TYPE_SELL:
                           // Make three attempts to put out the pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.BuyLimit(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                     }
                  }
               }
            }
            break;
         
      }
   }
   return(true);
}

이 방법을 사용하면 보류 중인 주문을 기반으로 손쉽게 모델을 만들 수 있습니다.


결론

안타깝게도 단일 글에서 제안된 접근 방식의 모든 뉘앙스, 과제 및 이점을 고려하는 것은 불가능합니다. 데이터의 직렬화 (데이터 파일에서 모델의 현재 상태에 필요한 모든 정보를 저장, 기록 및 검색 할 수있는 방법)는 고려하지 않았습니다. 우리가 고려한 것 외에도 합성 스프레드 거래를 기반으로 한 거래 모델이 남아있었습니다. 이들은 매우 흥미로운 주제이며, 확실히 제안된 개념에 대한 자체 효과적인 솔루션을 가지고 있습니다.

우리가 할 수 있었던 가장 중요한 일은 완전히 동적이고 관리 가능한 데이터 구조를 개발하는 것입니다. 연결 목록의 개념은 이를 효과적으로 관리하고 거래 전술을 독립적이고 개별적으로 사용자 정의 할 수 있도록 합니다. 이 접근 방식의 또 다른 매우 중요한 장점은 완전히 보편적이라는 것입니다.

예를 들어, 기본적으로 두 개의 거래 EA를 생성하고 동일한 상품에 배치하는 것으로 충분합니다. 둘 다 자동으로 자체 주문과 자체 자본 관리 시스템에서만 작동합니다. 따라서 하향 호환성이 지원됩니다. 하나의 EA에서 동시에 수행 할 수있는 모든 작업을 여러 로봇에 배포 할 수 있습니다. 이 속성은 순 위치에서 작업 할 때 매우 중요합니다.

설명된 모델은 단순한 이론이 아닙니다. 여기에는 보조 기능의 고급 장치, 자본 관리 및 한계 요구 사항 확인 기능이 포함됩니다. 주문을 보내는 시스템은 실제 거래에서 흔히 볼 수있는 소위 재호가 및 미끄러짐에 대한 저항력이 있습니다.

거래 엔진은 포지션의 최대 규모와 거래의 최대 거래량을 결정합니다. 특수 알고리즘은 거래 요청을 여러 개의 독립적 인 거래로 분리하며 각 거래는 개별적으로 처리됩니다. 더욱이 제시된 모델2010 년 자동 거래 챔피언십에서 잘 입증되었습니다. 모든 거래는 다음에 제시된 거래 계획, 거래 모델에 따라 정확하게 수행됩니다. 챔피언십은 다양한 수준의 위험과 다양한 자금 관리 시스템으로 관리됩니다.

제시된 접근 방식은 여러 거래 전술에 대한 여러 상품 및 타임 프레임에 대한 병렬 작업 뿐만 아니라 챔피언십 참가를 위한 거의 완벽한 솔루션입니다. 이 접근 방식에 익숙해지는 데 있어 유일한 어려움은 복잡성에 있습니다. 

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

파일 첨부됨 |
files-en.zip (18.6 KB)
마이크로, 미들, 메인 추세의 지표 마이크로, 미들, 메인 추세의 지표
이 글의 목적은 James Hyerczyk의 "Pattern, Price & Time: Using Gann Theory in Trading Systems"라는 책의 몇 가지 아이디어를 기반으로 지표 및 Expert Advisor의 형태로 거래 자동화 및 분석의 가능성을 조사하는 것입니다. 완전하다고 주장하지 않고 여기서 우리는 Gann 이론의 첫 번째 부분인 모델만 조사합니다.
채널 그리기 - 내부 및 외부 보기 채널 그리기 - 내부 및 외부 보기
채널이 시장 분석과 이동 평균 이후 거래 결정을 위한 가장 인기있는 도구라고 말하면 과장이 아닐 것 같습니다. 채널과 그 구성 요소를 사용하는 대량의 거래 전략에 깊이 들어 가지 않고 수학적 기반과 지표의 실제 구현에 대해 논의 할 것입니다.
차트 분석에 대한 계량학적 접근 차트 분석에 대한 계량학적 접근
이 글에서는 계량경제학적 분석 방법, 자기 상관 분석 및 특히 조건부 분산 분석에 대해 설명합니다. 여기에 설명된 접근 방식의 이점은 무엇입니까? 비선형 GARCH 모델을 사용하면 수학적 관점에서 공식적으로 분석된 시리즈를 표현하고 지정된 단계 수에 대한 예측을 생성할 수 있습니다.
MetaTrader 5의 병렬 계산 MetaTrader 5의 병렬 계산
시간은 인류 내역을 통틀어 큰 가치로 여겨져 왔으며, 불필요하게 낭비하지 않도록 노력하고 있습니다. 이 글에서는 컴퓨터에 멀티 코어 프로세서가 있는 경우 Expert Advisor의 작업을 가속화하는 방법에 대해 설명합니다. 또한 제안된 방법의 구현에는 MQL5 외에 다른 언어에 대한 지식이 필요하지 않습니다.