English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5 마법사: 주문, 손실 중지 및 계산된 가격에서 이익 취하기. 표준 라이브러리 확장

MQL5 마법사: 주문, 손실 중지 및 계산된 가격에서 이익 취하기. 표준 라이브러리 확장

MetaTrader 5 | 12 10월 2021, 14:18
118 0
Andrey Shpilev
Andrey Shpilev

소개

The MQL5 표준 라이브러리는 엄격한 아키텍처가 필요한 대규모 프로젝트를 개발하는 데 유용한 보조 도구입니다. The MQL5 마법사를 사용하면 몇 분 내에 대화 상자에서 준비된 부품을 광범위한 구성표로 조립할 수 있으며, 이 경우 과대평가할 수 없습니다. The MQL5 마법사는 Expert의 모든 부분을 함께 수집하는 작업을 자동화하고 해당 핸들에 따라 Expert에서 모듈 파라미터를 자동으로 선언합니다. 다양한 모듈이 많이 관련되면 이러한 자동화는 많은 시간과 일상적인 작업을 절약합니다.

좋은 생각이지만 분명한 단점이 있습니다. 표준 클래스를 기반으로 마법사를 사용하여 만든 거래 시스템의 기능은 제한되어 있습니다. 본 문서에서는 생성된 전문가(EA)의 기능을 크게 확장할 수 있는 보편적인 방법을 고려합니다. 이 메서드가 구현될 때 마법사 및 표준 모듈과의 호환성은 그대로 유지됩니다.

메소드의 아이디어는 객체 지향 프로그래밍의 상속 및 다형성 메커니즘을 사용하거나 생성된 전문가의 코드에서 표준 클래스를 대체하는 클래스를 만드는 것입니다. 이렇게 하면 마법사와 표준 라이브러리의 모든 이점이 활용되므로 필요한 기능을 갖춘 전문가가 개발됩니다. 하지만 거기까지 가려면, 코드를 4개의 문자열로 줄여야만 합니다.

본 조항의 실질적인 목적은 생성된 전문가에게 현재 가격에서 지정된 거리뿐만 아니라 필요한 가격 수준에서 주문하고 손실을 중지하고 이익 취하기의 기능을 추가하는 것입니다.

비슷한 생각이 기사 "MQL5 Wizard: 보류 중인 주문을 어떤 가격으로 열도록 EA를 가르치는 방법"에서 논의되었습니다. 제안된 솔루션의 중요한 단점은 거래 신호 모듈의 매개 변수가 종속 필터에서 "강제" 변경된다는 것입니다. 이 접근 방식은 많은 모듈에서 작업하는 것을 권장하지 않으며 프로세스 최적화를 위해 마법사를 사용하는 것은 의미가 없습니다.

표준에서 상속된 등급의 모든 가격으로 주문 및 손실 중지 및 이익 취하기의 구현은 더 세부적으로 고려됩니다. 즉, 모듈 간의 충돌은 불가능합니다. 본 기사가 모범이 되고 독자들이 표준 프레임워크의 개선 사항을 직접 작성할 수 있도록 영감을 주고 또한 사용자가 개발된 라이브러리 확장을 구현할 수 있기를 바랍니다.


1. 의사 결정의 표준 알고리즘

MQL5 마법사에서 생성된 전문가는 CExpert 클래스 인스턴스를 기반으로 합니다. CExpertSignal 클래스의 개체에 대한 포인터는 이 클래스에 선언됩니다. 이 기사에서는 더 나아가, 이 객체를 간결성을 위한 주요 신호라고 합니다. 그 주요 신호는 종속 필터에 대한 포인터를 포함합니다 (신호 모듈은 CExpertSignal 클래스의 상속자입니다).

열린 포지션과 주문이 없는 경우, 전문가(EA)는 새 틱에서 포지션을 열 기회를 확인하기 위하여, 주요 신호를 참조합니다. 주 신호는 하위 필터를 하나씩 조회하고 얻은 예측을 기반으로 가중 평균 예측(방향)을 계산합니다. 해당 값이 임계값 (주 신호에서 m_threshold_open 파라미터의 값)을 초과하면, bool 유형의 조건에 대한 검사 파라미터 및 결과가 Expert에게 전달됩니다. 이러한 조건이 충족되면 마켓 가격에 대한 포지션이 열리거나 일정 거리에 주문이 보류됩니다(그림 1 참조). 손실 중지는 고정된 거리에만 배치할 수 있습니다. 개시 가격 표시, 손실 중지 및 시장 가격으로부터의 이익 취하기는 전문가(EA) 설정에서 지정되고, 주 신호, m_price_level, m_stop_levelm_take_level 변수에 각각 저장됩니다.

따라서 현재 주문을 하려면 두 가지 조건이 충족되어야 합니다:

  1. 현재 기호의 열린 포지션이 없습니다;
  2. 가중 평균 예측의 절대값이 임계값을 초과합니다(추세가 다소 강하다는 의미).

그림. 1. 시장 진입에 대한 의사 결정 패턴

그림. 1. 시장 진입에 대한 의사 결정 패턴

그림 1의 현재 의사결정 패턴은 MQL5 마법사의 적용 영역을 상당히 제한하고 있습니다. 변동성으로 인해 장기 거래에서 손실 중지 값이 고정된 전략은 거의 효율적이지 않습니다. 보류 중인 주문을 사용하는 시스템은 일반적으로 동적으로 계산된 레벨에 배치해야 합니다.


2. 수정된 의사 결정 알고리즘

레벨을 계산하고 주문을 작성하는 관점에서, 신호 모듈은 예측 값 이외의 다른 값을 생성할 수 없고 주 신호는 레벨 작업을 위해 설계되지 않았기 때문에 막다른 골목 상황입니다. 이와 관련하여 다음을 제안합니다:

  • 주문 매개 변수를 생성할 수 있는 새로운 유형의 신호 모듈(가격 모듈이라고 함)을 도입합니다;
  • 이러한 파라미터를 처리하도록 주 신호를 교육합니다. 즉, 최적의 파라미터를 선택하여 Expert에게 전달합니다.

수정된 알고리즘(그림 2)을 사용하면 보류 중인 주문 외에 다른 요청으로 작업할 수 있습니다. 이 알고리즘의 본질은 진입점(가격)을 추세 정의(가중 평균 예측)와 구분한다는 것입니다. 즉, 결정을 내리기 위해 필터로 거래하는 선호 방향을 정의하고 가격 모듈에서 얻은 적절한 방향의 주문 매개 변수를 선택합니다. 사용 가능한 유사한 집합이 여러 개 있는 경우, 가장 큰 가중치(매개 변수 값 m_weight이 선택됩니다)를 가진 모듈에게서 수신합니다. 방향이 결정되었지만 현재 사용 가능한 진입점이 없는 경우 Expert는 비활성화됩니다.

그림. 2. 시장 진입에 대한 의사 결정의 수정된 패턴

그림. 2. 시장 진입에 대한 의사 결정의 수정된 패턴

새 주문을 하려면 다음 요구 사항을 충족해야 합니다:

  1. 현재 기호의 열린 포지션과 주문이 없습니다;
  2. 가중 평균 예측의 절대값이 임계값을 초과합니다;
  3. 최소 하나 이상의 주문 시작 가격이 발견되었습니다.

그림 2의 알고리즘은 가능한 많은 진입점을 처리하여 방향별로 필터링하고 하나의 Expert에서 최상의 진입점을 선택할 수 있도록 합니다.


3. Expert 및 신호 모듈의 수정된 클래스의 개발

라이브러리의 확장 기능은 두 가지 클래스를 기반으로 합니다: CExpertSignalAdvancedCExpertAdvanced (CExpertSignalCExpert으로 부터 각각 상속됨).

전문가 블록 간의 데이터 교환에 사용되는 인터페이스를 변경하는 것을 목표로 하는 새로운 기능을 추가하는 모든 조치입니다. 예를 들어, 그림 2의 패턴으로 알고리즘을 구현하려면, 주 신호(클래스 CExpertSignalAdvanced)의 상호작용을 가격 모듈(해당 클래스의 후예)과 함께 구성해야 합니다. 데이터가 변경될 때 이미 배치된 주문을 업데이트하는 것은 주 신호와 함께 전문가(클래스 CExpertAdvanced)의 상호작용을 의미합니다.

따라서 이 단계에서 우리는 그림 2의 열린 포지션에 대한 패턴을 구현하고 매개변수가 변경될 때(예: 유리한 진입점이 나타날 때) 이미 배치된 주문의 업데이트를 구성하려고 합니다. CExpertSignalAdvanced 클래스를 생각해 보겠습니다.

3.1. CExpertSignalAdvanced

이 클래스는 주 신호의 역할에서 조상을 대체하고 신호 모듈의 조상이 기본인 것과 같은 방식으로 가격 모듈의 기본 클래스가 됩니다.

class CExpertSignalAdvanced : public CExpertSignal
  {
protected:
   //---data members for storing parameters of the orders being placed
   double            m_order_open_long;         //opening price of the order to buy
   double            m_order_stop_long;         //Stop Loss of the order to buy
   double            m_order_take_long;         //Take Profit of the order to buy
   datetime          m_order_expiration_long;   //expiry time of the order to buy
   double            m_order_open_short;        //opening price of the order to sell
   double            m_order_stop_short;        //Stop Loss of the order to sell
   double            m_order_take_short;        //Take Profit of the order to sell
   datetime          m_order_expiration_short;  //expiry time of the order to sell
   //---             
   int               m_price_module;            //index of the first price module in the m_filters array
public:
                     CExpertSignalAdvanced();
                    ~CExpertSignalAdvanced();
   virtual void      CalcPriceModuleIndex() {m_price_module=m_filters.Total();}
   virtual bool      CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      CheckOpenShort(double &price,double &sl,double &tp,datetime &expiration);
   virtual double    Direction(void);		//calculating weighted average forecast based on the data received from signal modules
   virtual double    Prices(void);		//updating of parameters of the orders being placed according to the data received from price modules
   virtual bool      OpenLongParams(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      OpenShortParams(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      CheckUpdateOrderLong(COrderInfo *order_ptr,double &open,double &sl,double &tp,datetime &ex);
   virtual bool      CheckUpdateOrderShort(COrderInfo *order_ptr,double &open,double &sl,double &tp,datetime &ex);
   double            getOpenLong()              { return m_order_open_long;         }
   double            getOpenShort()             { return m_order_open_short;        }
   double            getStopLong()              { return m_order_stop_long;         }
   double            getStopShort()             { return m_order_stop_short;        }
   double            getTakeLong()              { return m_order_take_long;         }
   double            getTakeShort()             { return m_order_take_short;        }
   datetime          getExpLong()               { return m_order_expiration_long;   }
   datetime          getExpShort()              { return m_order_expiration_short;  }
   double            getWeight()                { return m_weight;                  }
  };
-->

주문 매개 변수를 저장하기 위한 데이터 구성원이 CExpertSignalAdvanced 클래스에 선언되었습니다. 이러한 변수의 값은 Prices() 방법으로 업데이트됩니다. 이러한 변수는 버퍼 역할을 합니다.

그런 다음 매개 변수 m_price_module이 선언됩니다. 그것은 m_filters 배열에 첫 번째 가격 모듈의 인덱스를 저장합니다 (CExpertSignal에서 선언됨). 이 배열에는 포함된 신호 모듈에 대한 포인터가 포함되어 있습니다. 표준 모듈(필터)에 대한 포인터는 배열의 시작 부분에 저장됩니다. 그런 다음, m_price_module 인덱스를 시작으로, 가격 모듈이 제공됩니다. 지시자와 시계열의 초기화 방법을 변경할 필요가 없도록 모든 것을 하나의 배열에 저장하기로 결정했습니다. 또한, 하나의 어레이를 통해 64개의 모듈을 포함할 수 있으며, 보통 이 정도면 충분합니다.

게다가, 도우미 메서드가, 보호된 데이터 구성원의 값을 가져오기 위하여 CExpertSignalAdvanced에 선언되었습니다. 이름은 get으로 시작합니다 (클래스 선언 참조).

3.1.1. 생성자

생성자(constructor) CExpertSignalAdvanced는 클래스 내에 선언된 변수를 초기화합니다:

CExpertSignalAdvanced::CExpertSignalAdvanced()
  {
   m_order_open_long=EMPTY_VALUE;
   m_order_stop_long=EMPTY_VALUE;
   m_order_take_long=EMPTY_VALUE;
   m_order_expiration_long=0;
   m_order_open_short=EMPTY_VALUE;
   m_order_stop_short=EMPTY_VALUE;
   m_order_take_short=EMPTY_VALUE;
   m_order_expiration_short=0;
   m_price_module=-1;
  }
-->

3.1.2. CalcPriceModuleIndex()

메서드 CalcPriceModuleIndex()는 배열 요소의 m_price_module 현재 번호에 할당되며, 이는 다음의 추가된 모듈의 인덱스와 같습니다. 첫 번째 가격 모듈을 추가하기 전에 이 방법을 호출합니다. 함수 본문이 클래스 선언에 있습니다.

virtual void      CalcPriceModuleIndex() {m_price_module=m_filters.Total();}
-->

3.1.3. CheckOpenLong(...) and CheckOpenShort(...)

메서드 CheckOpenLong(...)CExpert 클래스 인스턴스에서 호출되며 아래 설명된 대로 작동합니다:

  1. 포함된 가격 모듈을 위한 확인. 없는 경우 부모 클래스의 동의어 메서드를 호출합니다;
  2. Direction() 방법에서 가중 평균 예측(방향)을 수신합니다;
  3. 방향과 m_threshold_open의 임계값을 비교하여 입력 조건이 충족되는지 확인합니다;
  4. Prices()에 의한 주문 파라미터의 값, 방법을 갱신하고, OpenLongParams(...) 함수를 사용하여 Expert에게 전달합니다. 이 기능의 결과를 저장합니다;
  5. 저장된 결과를 반환합니다.
bool CExpertSignalAdvanced::CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration)
  {
//--- if price modules were not found, call the method of the basic class CExpertSignal
   if(m_price_module<0)
      return(CExpertSignal::CheckOpenLong(price,sl,tp,expiration));

   bool   result   =false;
   double direction=Direction();
//--- prohibitive signal
   if(direction==EMPTY_VALUE)
      return(false);
//--- check for exceeding the threshold
   if(direction>=m_threshold_open)
     {
      Prices();
      result=OpenLongParams(price,sl,tp,expiration);//there's a signal if m_order_open_long!=EMPTY_VALUE
     }
//--- return the result
   return(result);
  }
-->

CheckOpenShort(...)은 동일한 작동 원리를 가지고 있으며 동일한 방식으로 사용되므로 고려하지 않을 것입니다.

3.1.4 Direction()

Direction() 메서드는 가중 평균 예측을 필터링하고 계산합니다. 이 방법은 부모 클래스 CExpertSignal의 동의어 메서드와 매우 유사합니다. 다만, 예외적으로 그 루프에서는, m_filters 배열의 모든 요소를 참조하는 것이 아니라, 0에서부터 m_price_module보다 작은 인덱스를 가진 요소만을 참조합니다. 다른 모든 것은 CExpertSignal::Direction()와 비슷합니다.

double CExpertSignalAdvanced::Direction(void)
  {
   long   mask;
   double direction;
   double result=m_weight*(LongCondition()-ShortCondition());
   int    number=(result==0.0)? 0 : 1;      // number of queried modules
//--- loop by filters
   for(int i=0;i<m_price_module;i++)
     {
      //--- mask for bitmaps (variables, containing flags)
      mask=((long)1)<<i;
      //--- checking for a flag of ignoring a filter signal
      if((m_ignore&mask)!=0)
         continue;
      CExpertSignal *filter=m_filters.At(i);
      //--- checking for a pointer
      if(filter==NULL)
         continue;
      direction=filter.Direction();
      //--- prohibitive signal
      if(direction==EMPTY_VALUE)
         return(EMPTY_VALUE);
      if((m_invert&mask)!=0)
         result-=direction;
      else
         result+=direction;
      number++;
     }
//--- averaging the sum of weighted forecasts
   if(number!=0)
      result/=number;
//--- return the result
   return(result);
  }
-->

3.1.5. Prices()

메서드 Prices()m_filters 배열의 두번째 부분에 걸쳐 반복됩니다 (m_price_module 인덱스에서 시작하여 끝까지). 이 명령은 가격 모듈을 쿼리하고 OpenLongParams(...)OpenShortParams(...) 함수를 사용하여 클래스 변수의 값을 갱신합니다. 사이클 전에 파라미터 값이 삭제됩니다.

사이클 중에, 현재 가격 모듈의 가중치(m_weight)가 값을 제공한 이전에 쿼리된 모듈 중 하나보다 클 경우 매개 변수 값을 덮어씁니다. 결과적으로 빈 매개 변수가 남아 있거나(아무 것도 발견되지 않은 경우) 메서드를 호출할 때 사용할 수 있는 가중치가 가장 높은 매개 변수가 남아 있습니다.

double CExpertSignalAdvanced::Prices(void)
  {
   m_order_open_long=EMPTY_VALUE;
   m_order_stop_long=EMPTY_VALUE;
   m_order_take_long=EMPTY_VALUE;
   m_order_expiration_long=0;
   m_order_open_short=EMPTY_VALUE;
   m_order_stop_short=EMPTY_VALUE;
   m_order_take_short=EMPTY_VALUE;
   m_order_expiration_short=0;
   int    total=m_filters.Total();
   double last_weight_long=0;
   double last_weight_short=0;
//--- cycle for price modules
   for(int i=m_price_module;i<total;i++)
     {   
      CExpertSignalAdvanced *prm=m_filters.At(i);
      if(prm==NULL)
         continue;
//--- ignore the current module if it has returned EMPTY_VALUE
      if(prm.Prices()==EMPTY_VALUE)continue;
      double weight=prm.getWeight();
      if(weight==0.0)continue;
//--- select non-empty values from modules with the greatest weight
      if(weight>last_weight_long && prm.getExpLong()>TimeCurrent())
         if(prm.OpenLongParams(m_order_open_long,m_order_stop_long,m_order_take_long,m_order_expiration_long))
            last_weight_long=weight;
      if(weight>last_weight_short && prm.getExpShort()>TimeCurrent())
         if(prm.OpenShortParams(m_order_open_short,m_order_stop_short,m_order_take_short,m_order_expiration_short))
            last_weight_short=weight;
     }
   return(0);
  }
-->

3.1.6. OpenLongParams(...) 및 OpenShortParams(...)

CExpertSignalAdvanced 클래스 이내에, 메서드 OpenLongParams(...)은 클래스 변수에서 참조로 구입할 주문의 매개 변수 값을 입력 매개 변수로 전달합니다.

부모 클래스에서 이 메서드의 역할이 약간 다릅니다. 이것은 마켓 가격과 주 신호에 명시된 인덴테이션(indentations)을 기반으로 필요한 매개변수를 계산한 것입니다. 이제 준비 매개 변수만 통과합니다. 개시 가격이 올바르면(EMPTY_VALUE와 동일하지 않음) 메서드는 true를 반환하고 그렇지 않으면 false를 반환합니다.

bool CExpertSignalAdvanced::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration)
  {
   if(m_order_open_long!=EMPTY_VALUE)
     {
      price=m_order_open_long;
      sl=m_order_stop_long;
      tp=m_order_take_long;
      expiration=m_order_expiration_long;
      return(true);
     }
   return(false);
  }
-->

OpenShortParams(...)은 작동 원리가 동일하고 비슷하게 사용되므로 고려하지 않을 것입니다.

3.1.7. CheckUpdateOrderLong(...) 및 CheckUpdateOrderShort(...)

CheckUpdateOrderLong(...)CheckUpdateOrderShort(...) 메서드는 CExpertAdvanced 클래스라고 합니다. 그것들은 마지막 가격 수준에 따라 이미 보류 중인 주문을 업데이트하는 데 사용됩니다.

CheckUpdateOrderLong(...) 방법을 좀 더 자세히 살펴보겠습니다. 첫 번째 가격 수준은 Prices(...) 방법을 호출하면 업데이트되며, 데이터 업데이트 확인을 수행하여 가능한 수정 오류를 제외합니다. 마지막으로 업데이트된 데이터를 전달하고 결과를 반환하기 위해 OpenLongParams(...) 메서드가 호출됩니다.

bool CExpertSignalAdvanced::CheckUpdateOrderLong(COrderInfo *order_ptr,double &open,double &sl,double &tp,datetime &ex)
  {
   Prices();   //update prices
//--- check for changes
   double point=m_symbol.Point();
   if(   MathAbs(order_ptr.PriceOpen() - m_order_open_long)>point
      || MathAbs(order_ptr.StopLoss()  - m_order_stop_long)>point
      || MathAbs(order_ptr.TakeProfit()- m_order_take_long)>point
      || order_ptr.TimeExpiration()!=m_order_expiration_long)
      return(OpenLongParams(open,sl,tp,ex));
//--- update is not required   
   return (false);
  }
-->

CheckUpdateOrderShort(...)은 비슷하게 작동하고 동일한 방식으로 적용되므로 고려되지 않습니다.

3.2. CExpertAdvanced

Expert 클래스의 변경은 주 신호의 가격에 대한 업데이트된 데이터에 따라 이미 배치된 주문만 수정합니다. CExpertAdvanced 클래스 선언이 아래에 나와 있습니다.

class CExpertAdvanced : public CExpert
  {
protected:
   virtual bool      CheckTrailingOrderLong();
   virtual bool      CheckTrailingOrderShort();
   virtual bool      UpdateOrder(double price,double sl,double tp,datetime ex);
public:
                     CExpertAdvanced();
                    ~CExpertAdvanced();
  };
-->

보시는 바와 같이, 메서드는 거의 없고 생성자와 소멸자가 비어 있습니다.

3.2.1. CheckTrailingOrderLong() 및 CheckTrailingOrderShort()

메서드 CheckTrailingOrderLong()은 기본 클래스의 익명 메서드를 무시하고 메인 신호의 CheckUpdateOrderLong(...)을 호출하여 주문을 수정할 필요성을 파악합니다. 수정이 필요한 경우, UpdateOrder(...) 메서드가 호출되고 결과가 반환됩니다.

bool CExpertAdvanced::CheckTrailingOrderLong(void)
  {
   CExpertSignalAdvanced *signal_ptr=m_signal;
//--- check for the opportunity to modify the order to buy
   double price,sl,tp;
   datetime ex;
   if(signal_ptr.CheckUpdateOrderLong(GetPointer(m_order),price,sl,tp,ex))
      return(UpdateOrder(price,sl,tp,ex));
//--- return with no actions taken
   return(false);
  }
-->

메서드 CheckTrailingOrderShort()도 비슷하며 동일한 방법으로 사용됩니다.

3.2.2. UpdateOrder()

UpdateOrder() 함수는 관련 가격(EMPTY_VALUE 아님) 확인부터 시작합니다. 없으면 주문이 삭제되고, 그렇지 않으면 수신된 매개 변수에 따라 주문이 수정됩니다.

bool CExpertAdvanced::UpdateOrder(double price,double sl,double tp,datetime ex)
  {
   ulong  ticket=m_order.Ticket();
   if(price==EMPTY_VALUE)
      return(m_trade.OrderDelete(ticket));
//--- modify the order, return the result
   return(m_trade.OrderModify(ticket,price,sl,tp,m_order.TypeTime(),ex));
  }
-->

표준 클래스의 상속자 개발이 완료되었습니다.


4. 가격 모듈 개발

우리는 이미 계산된 수준에서 주문을 하고 손실을 막는 전문가(EA)를 만들 수 있는 기반을 가지고 있습니다. 정확히 말하면, 저희는 가격 레벨의 작업에 사용할 수 있는 클래스를 준비했습니다. 이제 이러한 레벨을 생성하는 모듈만 작성하면 됩니다.

가격 모듈을 개발하는 과정은 거래 신호 모듈을 작성하는 것과 유사합니다. 이들 사이의 유일한 차이점은 모듈 내 가격 업데이트를 담당하는 방법인 Prices()은, 신호 모듈의 LongCondition(), ShortCondition() 또는 Direction()이 아니라 재정의되어야 한다는 것입니다. 이상적으로, 독자는 신호 모듈 개발에 대한 명확한 아이디어를 가지고 있어야 합니다. 기사 "6단계 자체 거래 로봇 만들기!" 및 "사용자 지정 지표기를 기반으로 하는 거래 신호 생성기"는 이것에 유용할 수 있습니다.

몇 가지 가격 모듈의 코드가 예시를 제공할 것입니다.

4.1. "Delta ZigZag" 지표기를 기반으로 한 가격 모듈

지표기 Delta ZigZag은 최신 피크 몇 개로 레벨을 그립니다. 가격이 이러한 수준을 넘으면 추세 반전 가능성이 있음을 의미합니다.

가격 모듈의 목표는 표시기 버퍼에서 진입 레벨을 취하여 손실 중지(Stop Loss)를 배치하기 위한 가장 가까운 로컬 극단을 찾은 다음 손실 중지(Stop Loss)에 설정에 지정된 계수를 곱하여 이익 취하기(Take Profit)를 계산하는 것입니다.

그림. 3. 구매 주문을 사용한 Delta ZigZag 표시기의 가격 모듈 작동 일러스트레이션 예시

그림. 3. 구매 주문을 사용한 Delta ZigZag 표시기의 가격 모듈 작동 일러스트레이션 예시

Fig 3. 가격 모듈에서 생성된 레벨을 나타냅니다. 주문이 트리거되기 전에 최소값 업데이트에 따라 손실 중지(Stop Loss) 및 이익 실현(Take Profit)이 변경됩니다.

4.1.1. 모듈 설명자

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=DeltaZZ Price Module                                       |
//| Type=SignalAdvanced                                              |
//| Name=DeltaZZ Price Module                                        |
//| ShortName=DeltaZZ_PM                                             |
//| Class=CPriceDeltaZZ                                              |
//| Page=not used                                                    |
//| Parameter=setAppPrice,int,1, Applied price: 0 - Close, 1 - H/L   |
//| Parameter=setRevMode,int,0, Reversal mode: 0 - Pips, 1 - Percent |
//| Parameter=setPips,int,300,Reverse in pips                        |
//| Parameter=setPercent,double,0.5,Reverse in percent               |
//| Parameter=setLevels,int,2,Peaks number                           |
//| Parameter=setTpRatio,double,1.6,TP:SL ratio                      |
//| Parameter=setExpBars,int,10,Expiration after bars number         |
//+------------------------------------------------------------------+
// wizard description end
-->

설명자의 처음 5개 파라미터는 사용 중인 표시기를 설정하는 데 필요합니다. 그런 다음 현재 가격 모듈의 주문에 대한 막대의 손실 중지 및 만료 시간을 기준으로 이익 창출을 계산하기 위한 계수가 나옵니다.

4.1.2. 클래스 선언

class CPriceDeltaZZ : public CExpertSignalAdvanced
  {
protected:
   CiCustom          m_deltazz;           //object of the DeltaZZ indicator
   //--- module settings
   int               m_app_price;
   int               m_rev_mode;
   int               m_pips;
   double            m_percent;
   int               m_levels;
   double            m_tp_ratio;          //tp:sl ratio
   int               m_exp_bars;          //lifetime of the orders in bars
   //--- method of indicator initialization
   bool              InitDeltaZZ(CIndicators *indicators);
   //--- helper methods
   datetime          calcExpiration() { return(TimeCurrent()+m_exp_bars*PeriodSeconds(m_period)); }
   double            getBuySL();          //function for searching latest minimum of ZZ for buy SL
   double            getSellSL();         //function for searching latest maximum of ZZ for sell SL
public:
                     CPriceDeltaZZ();
                    ~CPriceDeltaZZ();
   //--- methods of changing module settings
   void              setAppPrice(int ap)           { m_app_price=ap; }
   void              setRevMode(int rm)            { m_rev_mode=rm;  }
   void              setPips(int pips)             { m_pips=pips;    }
   void              setPercent(double perc)       { m_percent=perc; }
   void              setLevels(int rnum)           { m_levels=rnum;  }
   void              setTpRatio(double tpr)        { m_tp_ratio=tpr; }
   void              setExpBars(int bars)          { m_exp_bars=bars;}
   //--- method of checking correctness of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating indicators
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- main method of price module updating the output data
   virtual double    Prices();
  }; 
-->

보호된 데이터 - 설명자에 지정된 유형에 따라 CiCustom 유형의 표시기 개체와 매개 변수가 모듈에 선언됩니다.

4.1.3. 생성자

생성자는 기본값으로 클래스 매개 변수를 초기화합니다. 나중에 Expert의 입력 파라미터에 따라 모듈이 주 신호에 포함되면 이러한 값이 다시 초기화됩니다.

CPriceDeltaZZ::CPriceDeltaZZ() : m_app_price(1),
                                 m_rev_mode(0),
                                 m_pips(300),
                                 m_percent(0.5),
                                 m_levels(2),
                                 m_tp_ratio(1),
                                 m_exp_bars(10)
  {
  }
-->

4.1.4. ValidationSettings()

ValidationSettings()는 입력 파라미터를 확인하는 중요한 방법입니다. 모듈 파라미터 값이 올바르지 않으면 결과 false가 반환되고 저널에 오류 메시지가 인쇄됩니다.

bool CPriceDeltaZZ::ValidationSettings(void)
  {
//--- checking for settings of additional filters
   if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data check
   if(m_app_price<0 || m_app_price>1)
     {
      printf(__FUNCTION__+": Applied price must be 0 or 1");
      return(false);
     }
   if(m_rev_mode<0 || m_rev_mode>1)
     {
      printf(__FUNCTION__+": Reversal mode must be 0 or 1");
      return(false);
     }
   if(m_pips<10)
     {
      printf(__FUNCTION__+": Number of pips in a ray must be at least 10");
      return(false);
     }
   if(m_percent<=0)
     {
      printf(__FUNCTION__+": Percent must be greater than 0");
      return(false);
     }
   if(m_levels<1)
     {
      printf(__FUNCTION__+": Ray Number must be at least 1");
      return(false);
     }
   if(m_tp_ratio<=0)
     {
      printf(__FUNCTION__+": TP Ratio must be greater than zero");
      return(false);
     }
   if(m_exp_bars<0)
     {
      printf(__FUNCTION__+": Expiration must be zero or positive value");
      return(false);
     }
//--- parameter check passed
   return(true);
  } 
-->

4.1.5. InitIndicators(...)

InitIndicators(...) 메서드는 기본 클래스의 익명 메서드를 호출하고 InitDeltaZZ(...)로 현재 모듈의 표시기를 초기화합니다.

bool CPriceDeltaZZ::InitIndicators(CIndicators *indicators)
  {
//--- initialization of indicator filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
//--- creating and initializing of custom indicator
   if(!InitDeltaZZ(indicators))
      return(false);
//--- success
   return(true);
  }
-->

4.1.6. InitDeltaZZ(...)

InitDeltaZZ(...) 메서드는 사용자 지정 표시기 개체를 컬렉션에 추가하고 새 표시기 "Delta ZigZag"를 만듭니다.

bool CPriceDeltaZZ::InitDeltaZZ(CIndicators *indicators)
  {
//--- adds to collection
   if(!indicators.Add(GetPointer(m_deltazz)))
     {
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
//--- specifies indicator parameters
   MqlParam parameters[6];
//---
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="deltazigzag.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_app_price;
   parameters[2].type=TYPE_INT;
   parameters[2].integer_value=m_rev_mode;
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_pips;
   parameters[4].type=TYPE_DOUBLE;
   parameters[4].double_value=m_percent;
   parameters[5].type=TYPE_INT;
   parameters[5].integer_value=m_levels;
//--- object initialization
   if(!m_deltazz.Create(m_symbol.Name(),m_period,IND_CUSTOM,6,parameters))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
//--- number of the indicator buffers
   if(!m_deltazz.NumBuffers(5)) return(false);
//--- ок
   return(true);
  }
-->

ValidationSettings(), InitDeltaZZ(...)InitIndicators(...) 메소드를 성공적으로 완료한 후에, 모듈이 초기화되어 작업할 준비가 됩니다.

4.1.7. Prices()

이 방법은 가격 모듈의 기본입니다. 주문 매개변수가 업데이트되는 바로 그 장소입니다. 이 값은 주 신호에 전달됩니다. 이 메서드는 double 유형의 작업 결과를 반환합니다. 이것은 주로 선물(future) 개발을 위해 구현되었습니다. Prices() 방법의 결과는 일부 특수한 상황 및 이벤트를 코드화할 수 있으므로 주 신호가 이에 따라 이를 처리할 수 있습니다. 현재는 반환된 값 EMPTY_VALUE만 처리합니다. 이 결과를 수신하면 메인 신호는 모듈이 제안하는 파라미터를 무시합니다.

이 모듈에서 Prices() 방법의 연산 알고리즘:

  1. 지표 버퍼 3과 4의 주문 시작 가격을 각각 구매와 판매로 잡습니다;
  2. 주문의 최종 매개 변수를 영점화합니다;
  3. 살 가격을 확인합니다. 예를 들어, getBuySL() 방법으로 손실을 중지시킬 수준을 식별하고 손실 중지 값과 개시 가격을 기준으로 이익 수준을 계산하고 주문 만료 시간을 계산합니다.
  4. 판매할 가격을 확인합니다. getSellSL() 방법을 통해 손실 중지를 표시할 수준을 찾고 손실 중지 값과 개시 가격을 기준으로 수익 수준을 계산하고 주문 만료 시간을 계산합니다.
  5. Exit.
"Delta ZigJag" 지표의 일부 운영적 측면 때문에 구입 및 판매 가격의 존재 조건은 상호 배타적입니다. 버퍼 3과 4는 기본적으로 점으로 그려집니다(그림 3 참조).

double CPriceDeltaZZ::Prices(void)
  {
   double openbuy =m_deltazz.GetData(3,0);//receive the last value from buffer 3
   double opensell=m_deltazz.GetData(4,0);//receive the last value from buffer 4
//--- clear parameter values
   m_order_open_long=EMPTY_VALUE;
   m_order_stop_long=EMPTY_VALUE;
   m_order_take_long=EMPTY_VALUE;
   m_order_expiration_long=0;
   m_order_open_short=EMPTY_VALUE;
   m_order_stop_short=EMPTY_VALUE;
   m_order_take_short=EMPTY_VALUE;
   m_order_expiration_short=0;
   int digits=m_symbol.Digits();
//--- check for the prices to buy
   if(openbuy>0)//if buffer 3 is not empty
     {
      m_order_open_long=NormalizeDouble(openbuy,digits);
      m_order_stop_long=NormalizeDouble(getBuySL(),digits);
      m_order_take_long=NormalizeDouble(m_order_open_long + m_tp_ratio*(m_order_open_long - m_order_stop_long),digits);
      m_order_expiration_long=calcExpiration();
     }
//--- check for the prices to sell
   if(opensell>0)//if buffer 4 is not empty
     {
      m_order_open_short=NormalizeDouble(opensell,digits);
      m_order_stop_short=NormalizeDouble(getSellSL(),digits);
      m_order_take_short=NormalizeDouble(m_order_open_short - m_tp_ratio*(m_order_stop_short - m_order_open_short),digits);
      m_order_expiration_short=calcExpiration();
     }
   return(0);
  }
-->

4.1.8. getBuySL() 및 getSellSL()

getBuySL() getSellSL()은 손실 중지를 배치하기 위한 로컬 최소값과 최대값을 찾고 있습니다. 관련 버퍼에서 마지막 0이 아닌 값(non-zero)  모든 방법에서 마지막 로컬 극단의 가격 수준을 찾습니다.

double CPriceDeltaZZ::getBuySL(void)
  {
   int i=0;
   double sl=0.0;
   while(sl==0.0)
     {
      sl=m_deltazz.GetData(0,i);
      i++;
     }
   return(sl);
  }
double CPriceDeltaZZ::getSellSL(void)
  {
   int i=0;
   double sl=0.0;
   while(sl==0.0)
     {
      sl=m_deltazz.GetData(1,i);
      i++;
     }
   return(sl);
  }
-->

4.2. 내부 막대를 기반으로 한 가격 모듈

인사이드 바(Inside bar)는 가격 조치(Price action)라고 불리는 지표가 없는 거래에서 널리 사용되는 모델 또는 패턴 중 하나입니다. 내부 막대는 이전 막대의 극단 내에 High 및 Low가 있는 막대입니다. 내부 막대는 통합의 시작 또는 가격 이동의 역전 가능성을 나타냅니다.

그림 4에서 막대는 빨간색 타원 안에 있습니다. 내부 바가 감지되면 가격 모듈은 내부 바 앞의 바 극단에서 구매정지 및 판매중지 주문을 개시합니다.

개시 가격은 반대 주문의 경우 손실 중지 수준입니다. 이익 취하기(Take Profit)는 위에서 설명한 모듈과 동일한 방식으로 손실 중지 수준에 설정에 지정된 계수를 곱하여 계산됩니다. 개시 가격 및 손실 방지에는 빨간색 가로줄과 녹색줄로 수익률이 표시됩니다.

그림. 4. 내부 막대와 가격 모듈의 수준 일러스트레이션

그림. 4. 내부 막대와 가격 모듈의 수준 일러스트레이션

그림 4에서는 추세가 다가옴에 따라 판매 주문이 되지 않습니다. 그럼에도 불구하고, 가격 모듈은 양방향으로 엔트리 레벨을 생성합니다. 일부 이익 취하기 수준은 그래프 밖에 있습니다.

이전 모듈과 달리 이 모듈의 알고리즘은 간단하며 표시기를 초기화할 필요가 없습니다. 모듈에 코드가 거의 없습니다. 아래에 자세히 나와 있습니다. 하지만 모든 기능을 개별적으로 분석하지는 않을 것입니다.

#include <Expert\ExpertSignalAdvanced.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Inside Bar Price Module                                    |
//| Type=SignalAdvanced                                              |
//| Name=Inside Bar Price Module                                     |
//| ShortName=IB_PM                                                  |
//| Class=CPriceInsideBar                                            |
//| Page=not used                                                    |
//| Parameter=setTpRatio,double,2,TP:SL ratio                        |
//| Parameter=setExpBars,int,10,Expiration after bars number         |
//| Parameter=setOrderOffset,int,5,Offset for open and stop loss     |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class CPriceInsideBar                                            |
//| Purpose: Class of the generator of price levels for orders based |
//|          on the "inside bar" pattern.                            |
//| Is derived from the CExpertSignalAdvanced class.                 |
//+------------------------------------------------------------------+
class CPriceInsideBar : public CExpertSignalAdvanced
  {
protected:
   double            m_tp_ratio;          //tp:sl ratio
   int               m_exp_bars;          //lifetime of the orders in bars
   double            m_order_offset;      //shift of the opening and Stop Loss levels
   datetime          calcExpiration()  { return(TimeCurrent()+m_exp_bars*PeriodSeconds(m_period)); }
public:
                     CPriceInsideBar();
                    ~CPriceInsideBar();
   void              setTpRatio(double ratio){ m_tp_ratio=ratio; }
   void              setExpBars(int bars)    { m_exp_bars=bars;}
   void              setOrderOffset(int pips){ m_order_offset=m_symbol.Point()*pips;}
   bool              ValidationSettings();
   double            Prices();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPriceInsideBar::CPriceInsideBar()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPriceInsideBar::~CPriceInsideBar()
  {
  }
//+------------------------------------------------------------------+
//| Validation of protected settings                                 |
//+------------------------------------------------------------------+
bool CPriceInsideBar::ValidationSettings(void)
  {
//--- verification of the filter parameters
   if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data check
  if(m_tp_ratio<=0)
     {
      printf(__FUNCTION__+": TP Ratio must be greater than zero");
      return(false);
     }
   if(m_exp_bars<0)
     {
      printf(__FUNCTION__+": Expiration must be zero or positive value");
      return(false);
     }
//--- check passed
   return(true);
  }
//+------------------------------------------------------------------+
//| Price levels refreshing                                          |
//+------------------------------------------------------------------+
double CPriceInsideBar::Prices(void)
  {
   double h[2],l[2];
   if(CopyHigh(m_symbol.Name(),m_period,1,2,h)!=2 || CopyLow(m_symbol.Name(),m_period,1,2,l)!=2)
      return(EMPTY_VALUE);
//--- check for inside bar      
   if(h[0] >= h[1] && l[0] <= l[1])
     {
      m_order_open_long=h[0]+m_order_offset;
      m_order_stop_long=l[0]-m_order_offset;
      m_order_take_long=m_order_open_long+(m_order_open_long-m_order_stop_long)*m_tp_ratio;
      m_order_expiration_long=calcExpiration();
      
      m_order_open_short=m_order_stop_long;
      m_order_stop_short=m_order_open_long;
      m_order_take_short=m_order_open_short-(m_order_stop_short-m_order_open_short)*m_tp_ratio;
      m_order_expiration_short=m_order_expiration_long;
      return(0);
     }
   return(EMPTY_VALUE);
  }  
//+------------------------------------------------------------------+
-->

본 모듈의 Prices() 방법에서 EMPEY_VALUE 결과의 반환은 사용 가능한 가격 수준이 없음을 주 신호에 입증하는 데 사용됩니다.

4.3. 외부 막대를 기반으로 한 가격 모듈

아웃사이드 바는 "흡수(absorption)"라고도 불리는 가격 행동의 또 다른 인기 있는 패턴입니다. 외부(Outside) 막대는 이전 막대의 높음과 낮음이 겹치기 때문에 그렇게 호출됩니다. 외부 막대의 출현은 변동성 증가에 따른 직접적인 가격 변동을 나타냅니다.

그림 5에서 외부 막대는 빨간색 타원형으로 표시되어 있습니다. 외부 바를 발견하면 가격 모듈은 극단적으로 구매중지 및 판매중지 주문의 개시 가격을 생성합니다.

개시 가격은 반대 주문의 경우 손실 중지 수준입니다. 이익 취하기(Take Profit)는 위에서 설명한 모듈과 동일한 방식으로 손실 중지 수준에 설정에 지정된 계수를 곱하여 계산됩니다. 개시 가격 및 손실 방지에는 빨간색 가로줄과 녹색줄로 수익률이 표시됩니다.

그림. 5. 외부 막대와 가격 모듈 레벨 일러스트레이션

그림. 5. 외부 막대와 가격 모듈 레벨 일러스트레이션

그림 5에서 외부 막대는 빨간색 타원형으로 표시되어 있습니다. 가로줄은 가격 모듈에 의해 생성된 보류 중인 주문의 시작 가격을 반영합니다.

이 경우, 하락세가 있기 때문에 판매 주문만 열립니다. 이 모듈의 작동 방식은 기본적으로 이전 모듈과 유사합니다. 코드 간의 유일한 차이점은 Prices() 방법이며, 다른 부분은 정확히 동일하고 이름이 같습니다. 아래는 Prices()의 코드입니다.

double CPriceOutsideBar::Prices(void)
{
   double h[2],l[2];
   if(CopyHigh(m_symbol.Name(),m_period,1,2,h)!=2 || CopyLow(m_symbol.Name(),m_period,1,2,l)!=2)
      return(EMPTY_VALUE);
//--- check of outside bar
   if(h[0] <= h[1] && l[0] >= l[1])
   {
      m_order_open_long=h[1]+m_order_offset;
      m_order_stop_long=l[1]-m_order_offset;
      m_order_take_long=m_order_open_long+(m_order_open_long-m_order_stop_long)*m_tp_ratio;
      m_order_expiration_long=calcExpiration();
      
      m_order_open_short=m_order_stop_long;
      m_order_stop_short=m_order_open_long;
      m_order_take_short=m_order_open_short-(m_order_stop_short-m_order_open_short)*m_tp_ratio;
      m_order_expiration_short=m_order_expiration_long;
      return(0);
   }
   return(EMPTY_VALUE);
} 
-->

4.4. 모듈 성능 테스트

다음 섹션에서는 모듈의 세 가지 예제를 테스트하여 개별 모듈을 검사한 후 한 전문가에게 연결하여 시스템이 설계된 대로 작동하는지 확인합니다. 그 결과, 4명의 Experts가 생성됩니다. H12 시간대에 작동하는 지표 Awesome Oscillator를 기반으로 한 신호 모듈을 생성된 각 전문가에서 필터로 사용합니다. 가격 모듈이 H6에서 작동 중입니다.

자금 관리: 고정 로트 및 후행 중지는 사용되지 않습니다. 모든 테스트는 MetaQuotes-Demo 서버의 데모 계정에서 2013.01.01부터 2014.06.01까지의 EURUSD 견적에 따라 수행됩니다. 터미널 버전: 5.00, 빌드 965 (2014년 6월 27일).

짧게 유지하기 위해, 테스트 결과는 잔액 그림(balance plots)으로만 표시됩니다. 그들은 모든 특정 사례에서 전문가(EA)의 행동을 파악하기에 충분합니다.

4.4.1. Delta ZigZag 표시기를 기반으로 모듈을 테스트하기

그림. 6. 델타 지그재그(Delta ZigZag)를 기반으로 한 가격 모듈 테스트 시 잔액 그림(Balance plot)

그림. 6. "Delta ZigZag" 표시기를 기반으로 가격 모듈을 테스트하는 밸런스 플롯

4.4.2. "바 내부" 패턴을 기반으로 모듈을 테스트하기

그림. 7. 내부 막대를 기준으로 가격 모듈을 테스트하는 잔액 그림

그림. 7. 내부 막대를 기준으로 가격 모듈을 테스트하는 잔액 그림

그림 7을 보면, 이 단계의 테스트의 목적은 우승 전략을 얻기 위한 것이 아니라 코드 성능을 확인하는 것이라는 것을 명심해야 합니다.

4.4.3. "외부 막대" 패턴을 기반으로 모듈을 테스트하기

그림. 8. 외부 막대를 기준으로 가격 모듈을 테스트하는 잔액 그림

그림. 8. 외부 막대를 기준으로 가격 모듈을 테스트하는 잔액 그림

4.4.4. 개발 가격 모듈의 공동 작업을 테스트하기

이전 시험 결과를 고려하여 델타지그재그, 바 내부 및 바 외부 기준 가격 모듈은 각각 1, 0.5, 0.7의 가중치 계수를 받았습니다. 가중치 계수는 가격 모듈의 우선순위를 정의합니다.

그림. 9. 3개의 가격 모듈로 Expert를 테스트하는 잔액 그림

그림. 9. 3개의 가격 모듈로 Expert를 테스트하는 잔액 그림

테스트의 모든 단계에서 가격 그림에 대해 수행된 모든 연산을 주의 깊게 분석했습니다. 전략상의 오류와 편차는 유발되지 않았습니다. 제안된 모든 프로그램이 이 문서에 첨부되어 있으며 사용자가 직접 테스트할 수 있습니다.


5. 사용 지침

3개의 가격모듈을 가진 Expert와 Awesome Oscillator 필터로 사용한 Expert 개발 단계를 검토하겠습니다.

생성을 시작하기 전에, 헤더 파일인 "CExpertAdvanced.mqh" 및 "CExpertSignalAdvanced.mqh"이 MQL5/Include/Expert폴더에 있는 MetaTrader 5 터미널의 카탈로그에 있는지 확인하고, 가격 모듈의 파일이 MQL5/Include/Expert/MySignal 폴더에 있는지 확인합니다. Expert를 시작하기 전에, 컴파일된 지표가 MQL5/Indicators 폴더에 있는지 확인하십시오. 이 경우, "DeltaZigZag.ex5" 파일입니다. 첨부된 보관 파일에서는 모든 파일이 해당 위치에 있습니다. MetaTrader 5 터미널이 있는 폴더에 이 아카이브의 압축을 풀고 카탈로그 병합을 확인하기만 하면 됩니다.

5.1. 전문가(Expert) 생성하기

Expert 생성을 시작하려면, MetaEditor에서 "File"->"New"를 선택합니다.

그림. 10. MQL5 마법사를 사용하여 새 전문가를 생성하기

그림. 10. MQL5 마법사를 사용하여 새 전문가를 생성하기

새 창에서 "Expert Advisor (Generate)"를 선택한 후 "Next"를 누릅니다.


그림. 11. MQL5 WIzard를 사용하여 전문가를 생성하기

그림. 11. MQL5 WIzard를 사용하여 전문가를 생성하기

이름을 지정할 수 있는 창이 나타납니다. 이 예에서 Expert의 이름은 "TEST_EA_AO_DZZ_IB_OB" 이고, 기호 및 시간 프레임 매개 변수에는 기본값이 있습니다. 그런 다음 "다음"을 클릭합니다.

그림. 12. 전문가 조언자(EA)의 공통 매개 변수


그림. 12. 전문가 조언자(Expert Advisor)의 일반적인 속성

나타난 창에서 "추가"를 눌러 include 모듈을 하나씩 추가합니다. 전체 프로세스는 아래에 나와 있습니다.

참고하세요! 모듈을 추가할 때, 처음에는 모든 신호 모듈(상속기 CExpertSignal)과 가격 모듈(상속기 CExpertSignalAdvanced)을 포함합니다. 이 모듈 포함 주문 중단의 결과는 예측할 수 없을 것입니다.

먼저, Awesome Oscillator로부터 신호 모듈을 포함시켜야 합니다. 이 예에서 유일한 필터입니다.

그림. 13. Awesome Oscillator 신호 모듈을 포함하기

그림. 13. Awesome Oscillator 신호 모듈을 포함하기

모든 신호 모듈이 포함된 후 필요한 가격 모듈을 임의 주문에 포함합니다. 이 예에는 세 가지가 있습니다.

그림. 14. DeltaZZ 가격 모듈의 신호 모듈을 포함하기

그림. 14. DeltaZZ 가격 모듈의 신호 모듈을 포함하기


그림. 15. 내부 막대 가격 모듈의 신호 모듈을 포함하기

그림. 15. 내부 막대 가격 모듈의 신호 모듈을 포함하기


그림. 16. 외부 막대 가격 모듈의 신호 모듈을 포함하기

그림. 16. 외부 막대 가격 모듈의 신호 모듈을 포함하기

모든 모듈을 추가하면 창이 다음과 같이 표시됩니다:

그림. 17. 포함된 거래 신호 모듈 목록

그림. 17. 포함된 거래 신호 모듈 목록

가격 모듈에 대한 "가중치" 매개 변수의 값이 우선 순위를 정의합니다.

"다음" 버튼을 누른 후, 마법사는 자금 관리 모듈과 후행 중지(Trailing Stop) 모듈을 선택할 것을 제안합니다. 이 중 하나를 선택하거나 그대로 두고 "다음" 또는 "준비"를 누릅니다.

"Ready"를 누르면 생성된 Advisor 코드가 수신됩니다.

5.2. 생성된 코드를 편집하기

자, 코드가 있습니다.

1. 맨 처음에서 문자열을 찾습니다.

#include <Expert\Expert.mqh>
-->

그 다음 그대로 편집하세요.

#include <Expert\ExpertAdvanced.mqh>
-->

개발된 클래스의 파일을 포함해야 합니다.

2. 그런 다음, 문자열을 찾습니다.

CExpert ExtExpert;
-->

그리고 변경합니다.

CExpertAdvanced ExtExpert;
-->

이렇게 하면 필요한 기능이 있는 하위 항목에 대한 Expert 표준 클래스가 변경됩니다.

3. 이제 문자열을 찾습니다.

CExpertSignal *signal=new CExpertSignal;
-->

그리고 변경합니다.

CExpertSignalAdvanced *signal=new CExpertSignalAdvanced;
-->

이렇게 하면 필요한 기능을 사용하여 하위 신호의 주 신호 표준 클래스를 변경할 수 있습니다.

4. 주 신호의 m_filters 배열에 첫 번째 가격 모듈을 추가하는 문자열을 찾습니다. 이 예에서는 다음과 같습니다:

signal.AddFilter(filter1);
-->

이 전에 우리는 문자열을 삽입합니다.

signal.CalcPriceModuleIndex();
-->

이것은 주 신호가 m_filters 배열의 어느 인덱스까지 신호 모듈 포인터가 있는지, 가격 모듈 포인터가 있는 곳부터 인식하기 위해 필요합니다.

지정된 문자열을 삽입하기 위한 올바른 포지션을 찾으면 문제가 발생할 수 있습니다. "필터" 단어 뒤에 있는 숫자를 참조점으로 사용합니다. 그것은 검색을 단순화하고 올바른 위치를 놓치지 않도록 합니다. MQL5 마법사 이름에는 모듈이 순서대로 자동으로 포함됩니다. 첫 번째 모듈의 이름은 filter0이고, 두 번째 모듈은 – filter1, 세번째는 – filter2 등 입니다. 우리의 경우 신호 모듈은 하나만 있습니다. 따라서, 첫 번째 포함된 가격 모듈은 2번이며 문자열 "signal.AddFilter(filter1);"을 검색해야합니다; 코드의 번호로 필터를 추가하기 위한 것은 0으로 시작합니다. 일러스트레이션은 그림 18에 나와 있습니다:

그림. 18. 포함 순서에 따른 코드의 모듈 이름

그림. 18. 포함 순서에 따른 코드의 모듈 이름

5. 이 부분은 강제 사항이 아닙니다. 도입된 변경을 통해, 개시 가격 표시, 손실 방지, 이익 획득 및 주문 만료 시간을 담당하는 Expert 매개변수는 사용을 상실했습니다. 코드를 더 압축하기 위해 다음 문자열을 삭제할 수 있습니다:

input double Signal_PriceLevel            =0.0;                    // Price level to execute a deal
input double Signal_StopLevel             =50.0;                   // Stop Loss level (in points)
input double Signal_TakeLevel             =50.0;                   // Take Profit level (in points)
input int    Signal_Expiration            =4;                      // Expiration of pending orders (in bars)
-->

위의 문자열을 삭제한 후 컴파일 오류가 발생하면 삭제할 다음 문자열 그룹으로 이동합니다:

   signal.PriceLevel(Signal_PriceLevel);
   signal.StopLevel(Signal_StopLevel);
   signal.TakeLevel(Signal_TakeLevel);
   signal.Expiration(Signal_Expiration);
-->

후자가 삭제되면 컴파일이 성공합니다.

편집에 대한 설명 및 설명은 전문가 "TEST_EA_AO_DZZ_IB_OB" 의 첨부 코드에서 확인할 수 있습니다. 삭제할 수 있는 코드의 문자열에 주석이 첨부되어 있습니다.


결론

이 기사에서는 MQL5 마법사의 응용 프로그램 영역을 크게 확장했습니다. 현재 가격에 관계없이 다양한 가격 수준에서 주문 및 손실 중지 및 이익 취하기를 요구하는 자동 거래 시스템의 개발 최적화에 사용할 수 있습니다.

생성된 전문가에는 발송되는 주문의 매개 변수를 계산하는 일련의 가격 모듈이 포함될 수 있습니다. 사용 가능한 파라미터 중에서 가장 적합한 파라미터 세트를 선택합니다. 환경설정은 설정에 지정되어 있습니다. 다양한 엔트리 포인트를 최대한 효율적으로 사용할 수 있습니다. 이러한 접근 방식은 전문가를 선별적으로 만듭니다. 방향은 알 수 있지만 진입점이 정의되지 않은 경우 전문가는 방향이 나타날 때까지 기다립니다.

가격 수준 검색을 담당하는 표준 호환 모듈을 도입하는 것은 상당한 이점이며 전문가의 개발을 단순화하기 위한 것입니다. 현재 모듈이 세 개에 불과하지만, 그 수는 앞으로 증가할 것이 분명합니다. 이 기사가 유용하다고 생각되면 주석에서 가격 모듈 작동 알고리즘을 제안하십시오. 흥미로운 아이디어가 코드로 구현될 것입니다.

이 문서에는 마법사에서 개발한 Expert 기능을 추가로 확장하는 방법도 나와 있습니다. 상속은 변화를 도입하는 최적의 방법입니다.

지침에 따라 코드를 편집할 수 있지만 프로그래밍 기술이 없는 사용자는 사용 가능한 모델을 기반으로 고급 전문가(EA)를 만들 수 있습니다.

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

가상 호스팅으로 마이그레이션하기 위해 거래 계정을 준비하는 방법 가상 호스팅으로 마이그레이션하기 위해 거래 계정을 준비하는 방법
MetaTrader 클라이언트 터미널은 거래 전략 자동화에 완벽합니다. 이 제품은 로봇 개발자를 거래하는 데 필요한 모든 도구, 강력한 C++ 기반 MQL4/MQL5 프로그래밍 언어, 편리한 MetaEditor 개발 환경 및 MQL5 클라우드 네트워크에서 분산 컴퓨팅을 지원하는 멀티 스레드 전략 테스터를 갖추고 있습니다. 이 문서에서는 모든 사용자 지정 요소가 포함된 가상 환경으로 클라이언트 터미널을 이동하는 방법에 대해 설명합니다.
MQL5(MQL4)에서 MySQL 데이터베이스에 액세스하는 방법 MQL5(MQL4)에서 MySQL 데이터베이스에 액세스하는 방법
이 문서에서는 MQL과 MySQL 데이터베이스 간의 인터페이스 개발에 대해 설명합니다. 기존의 실용적인 솔루션에 대해 설명하고 데이터베이스 작업을 위한 라이브러리를 보다 편리하게 구현할 수 있는 방법을 제공합니다. 이 문서에는 기능, 인터페이스 구조, 예제 및 MySQL 사용 시 특정 기능에 대한 자세한 설명이 포함되어 있습니다. 소프트웨어 솔루션의 경우 문서 첨부 파일에는 동적 라이브러리 파일, MQL4 및 MQL5 언어의 설명서 및 스크립트 예가 포함됩니다.
마켓에 효과적인 제품 프레젠테이션을 위한 팁 마켓에 효과적인 제품 프레젠테이션을 위한 팁
트레이더에게 프로그램을 효과적으로 판매하려면 효율적이고 유용한 제품을 작성한 다음 시장에 게시해야 하는 것만이 아닙니다. 포괄적이고 상세한 설명과 좋은 삽화를 제공하는 것이 중요합니다. 품질 로고와 정확한 스크린샷은 "실제 코딩"만큼 중요합니다. 간단한 공식에 유의하십시오: 다운로드 안 함 = 판매 안 함.
소셜 테크놀로지 스타트업을 구축하기, 1부: MetaTrader 5 신호를 트윗하세요 소셜 테크놀로지 스타트업을 구축하기, 1부: MetaTrader 5 신호를 트윗하세요
오늘은 EA의 거래 신호를 트윗할 수 있도록 MetaTrader 5 단말기를 트위터와 연결하는 방법에 대해 알아보겠습니다. 우리는 RESTful 웹 서비스를 기반으로 PHP의 사회적 의사결정 지원 시스템을 개발하고 있습니다. 이 아이디어는 컴퓨터 지원 거래라고 불리는 자동 거래의 특별한 개념에서 나온 것입니다. 우리는 휴먼 거래자들의 인지 능력이 Expert Advisor가 자동으로 시장에 내놓는 거래 신호를 걸러내기를 원합니다.