English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
지그재그 및 ATR의 예에 의한 지표의 클래스 구현

지그재그 및 ATR의 예에 의한 지표의 클래스 구현

MetaTrader 5지표 | 4 8월 2021, 17:05
69 0
Aleksandr Chugunov
Aleksandr Chugunov

무엇을 위해 필요합니까?

MetaQuotes Software Corp. MetaTrader 클라이언트 터미널의 새로운 5번째 버전에서 맞춤 지표 작업의 개념을 수정했습니다. 이제 훨씬 빠르게 실행됩니다. 고유한 입력 매개변수가 있는 각 지표의 예는 하나뿐이므로 기호의 10개 차트에서도 복사본을 사용하더라도 한 번만 계산됩니다.

그러나 하나의 알고리즘의 작동은 변경 없이 유지됩니다. 서버에 대한 연결이 끊기거나 상당한 기록 동기화가 발생하면 prev_calculated 값(또는 MetaTrader 4의 경우 IndicatorCounted())이 0이 되어 전체 기록에 대한 지표의 전체 재계산으로 이어집니다(개발자는 어떤 상황에서도 지표 값의 정확성을 의도적으로 보장합니다.) 지표 계산 속도에 영향을 줄 수 있는 몇 가지 사항이 있습니다.

  • 큰 기간: Rates_total;
  • 복잡하고 리소스 소모적인 계산;
  • 여러 기호와 마침표 사용;
  • 약한 개인용 컴퓨터;

상황에 적용할 수 있는 항목이 많을수록 전체 기록에 대한 지표 재계산 문제가 더 현실적입니다. 또한, 정보를 전송하기 위한 나쁜 채널로 상황은 더욱 악화됩니다.

물론 추가 입력 매개변수를 사용하여 지표 계산의 깊이를 제한할 수 있지만 iCustom 지표를 사용할 때 뉘앙스가 있습니다. 차트 또는 사용자 지정 지표에서 사용되는 최대 바의 수는 전체 터미널의 전역 범위에서 설정됩니다. 메모리는 사용자 지정 지표의 각 버퍼에 할당되며 TERMINAL_MAXBARS에 의해서만 제한됩니다.

그러나 중요한 추가 사항이 있습니다. 지표 알고리즘에서 바로 계산된 바의 최대 수를 제한하면(예: 입력 매개변수를 사용하거나 코드에서 직접) 메모리는 각각의 새 항목이 올 때 동적으로 할당됩니다. bar(지정된 TERMINAL_MAXBARS 제한까지 점진적으로 증가(또는 조금 더 - 이 알고리즘은 개발자에게 완전히 의존하며 다음 빌드에서 변경할 수 있음)).


전체 기록에 대한 지표의 재계산을 피하는 방법

현재로서는 이 문제를 해결하는 다음과 같은 방법이 있습니다.
  1. MetaQuotes에 플랫폼 수준에서 이 문제를 수정하도록 요청하십시오.
  2. prev_calculated의 유사체 구현을 위한 별도의 클래스 생성

지표에서 prev_calculated의 계산 알고리즘을 바로 구축할 수 있다는 가정으로 또 다른 변형이 있었지만 MetaTrader 4와 달리 MetaTrader 5는 prev_calculated를 0으로 만들 때 모든 지표 버퍼를 "삭제"하는 것으로 나타났습니다(즉, 강제로 수행 모든 지표 배열의 제로화, 이 동작은 플랫폼 수준에서 구현되므로 제어할 수 없습니다.

각 변형을 개별적으로 분석해 보겠습니다.

  • 첫 번째 변형은 개발자에게만 의존합니다. 그들은 글이 출판된 후에 그것을 고려할 것입니다. 그리고 아마도 본격적인 메커니즘의 구현은 사용자 지정 지표 계산 블록의 성능에 큰 영향을 미치고(그러나 이 메커니즘은 선택 사항으로 구현될 수 있음) 모든 것을 지금 그대로 두게 될 것입니다.
  • 두 번째 변종. prev_calculated의 유사체 구현을 담당할 특수 클래스 생성. 우리는 사용자 지정 지표(prev_calculated 값을 얻기 위해서만)와 Expert Advisors(또는 스크립트)에서 사용할 데이터 공급자 모두에서 필요한 사용자 지정 지표 계산을 위해 별도로 개발된 클래스와 함께 사용할 수 있습니다.


문제 해결의 두 번째 변형의 장점과 단점

장점:
  • 배열 요소에 대한 링 액세스 구성과 함께 동적 배열에 대한 단일 메모리 할당을 통해 필요한 메모리의 고정 볼륨;
  • 요청 시 계산을 위해 별도의 클래스를 사용할 때 지표의 동기화 및 계산(세마포어, 플래그, 이벤트 등을 사용하지 않음);
  • 지시자 계산을 위해 별도의 호출을 사용하는 경우 재계산 결과는 확장된 형태로 반환됩니다(예: 변경 사항 없음, 마지막 광선만 변경됨, 새 광선 추가 등).
단점:
  • 지표 값 계산에 사용되는 가격 기록의 자체 사본을 저장할 필요성;
  • 데이터 비교의 논리 연산을 사용하여 단말 기록과 기록의 수동 동기화의 필요성.


prev_calculated의 아날로그 구현을 위한 CCustPrevCalculated 클래스 만들기

클래스 자체의 구현에는 설명할 흥미로운 내용이 포함되어 있지 않습니다. 이 알고리즘은 내역을 양쪽으로 확장하는 것과 왼쪽에서 "차단"할 수 있는 가능성을 모두 고려합니다. 또한 이 알고리즘은 계산된 데이터 내부에 기록 삽입을 처리할 수 있습니다(MetaTrader 4의 경우 실제이며 MetaTrader 5에서는 아직 직면하지 않았습니다). 클래스의 소스 코드는 CustPrevCalculated.mqh 파일에 있습니다.

핵심 사항에 대해 말씀드리겠습니다.


배열 요소에 대한 링 액세스 생성

이 클래스를 생성하기 위해 우리는 배열에 대한 일회성 메모리 할당과 배열 복사의 과도한 절차를 피하기 위해 배열 요소에 대한 링 액세스라는 비 전통적인 방법을 사용할 것입니다. 5가지 요소의 예를 들어 살펴보겠습니다.


배열 요소에 대한 링 액세스


 
처음에는 숫자가 0으로 시작하는 배열로 작업합니다. 그러나 배열 크기를 유지하면서 다음 값을 추가해야 하는 경우(새 바 추가) 어떻게 해야 합니까? 두 가지 방법이 있습니다.
  • 메모리 셀 2-5를 셀 1-4에 각각 복사하고; 따라서 빈 메모리 셀 5가 있습니다.
  • 배열에 저장된 정보를 변경하지 않고 배열 인덱싱을 변경합니다 (랩어라운드 주소 지정).

두 번째 변형을 구현하려면 변수가 필요합니다. 변수를 DataStartInd라고 부르겠습니다. 배열의 0 인덱스 위치를 저장합니다. 추가 계산의 편의를 위해 해당 숫자는 배열의 일반적인 인덱싱에 해당합니다(즉, 0부터 시작). BarsLimit 변수에 배열의 요소 수를 저장할 것입니다. 따라서 가상 인덱스 'I'에 대한 배열 요소의 실제 주소는 다음과 같은 간단한 공식을 사용하여 계산됩니다.

  • (DataStartInd+I) % BarsLimit – 일반적인 계산의 경우
  • (DataStartInd+DataBarsCount-1-I) % BarsLimit – 시계열과 같은 주소 지정용
DataBarsCount 변수는 실제로 사용된 메모리 셀의 수를 저장합니다(예를 들어, 5개의 셀 중 3개만 사용할 수 있음).


히스토리 동기화 알고리즘

나 자신을 위해 클라이언트 터미널의 기록과 기록 사본 (로컬 기록)을 동기화하는 알고리즘의 세 가지 작업 모드를 선택하고 구현했습니다.
  • CPCHSM_NotSynch – 이미 형성된 바에 대해 로컬 기록 동기화가 수행되지 않습니다(위험 및 책임). 실제로 이 모드는 가격 값의 사소한 편차가 계산의 정밀도(MA, ADX 등)에 큰 영향을 미치지 않는 지표에 자유롭게 사용할 수 있습니다. 이 모드는 지그재그에 치명적일 수 있습니다. 예를 들어, 한 피크가 다른 피크보다 과도하게 많은 경우입니다.
  • CPCHSM_Normal – 로컬 기록은 아래 설명된 알고리즘에 의해 모든 새 바에서 동기화됩니다.
  • CPCHSM_Paranoid – 로컬 히스토리는 아래에 설명된 데이터 동기화 기능이 호출될 때마다 동기화됩니다.

동기화 메커니즘 자체는 프로그래머가 설정한 또 다른 매개변수인 HSMinute(HistorySynchSecond로 저장됨)를 기반으로 합니다. 딜러 센터는 기록의 마지막 HSMinute 분만 수정할 수 있다고 가정합니다. 해당 기간의 동기화 중에 차이가 발견되지 않으면 이력이 동일한 것으로 간주되어 비교가 중지됩니다. 차이점이 발견되면 전체 이력을 확인하고 수정합니다.

또한 이 알고리즘은 초기화시 지정된 MqlRates 구조의 가격/스프레드/볼륨만 확인할 수 있습니다. 예를 들어 지그재그를 그리려면 높은 가격과 낮은 가격만 필요합니다.


CCustPrevCalculated 클래스의 실제 사용

CCustPrevCalculated 클래스를 초기화하려면 성공 시 'true'를 반환하는 InitData() 함수를 호출해야 합니다.
CCustPrevCalculated CustPrevCalculated;
CustPrevCalculated.InitData(_Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15);
기록을 동기화하려면 PrepareData() 함수를 호출해야 합니다.
CPCPrepareDataResultCode resData;
resData = CustPrevCalculated.PrepareData();

PrepareData() 함수에서 반환할 수 있는 값의 변형:

enum CPCPrepareDataResultCode
  {
   CPCPDRC_NoData,                     // Returned when there is no data for calculation (not prepared by the server)
   CPCPDRC_FullInitialization,         // Full initialization of the array has been performed
   CPCPDRC_Synch,                      // Synchronization with adding new bars has been performed
   CPCPDRC_SynchOnlyLastBar,           // Synchronization of only the last bar has been performed (possible cutting of the history)
   CPCPDRC_NoRecountNotRequired        // Recalculation has not been performed, since the data was not changed
  };


데이터 액세스를 위해 CCustPrevCalculated 클래스의 기능

참고: 계산을 가속화하기 위해 배열 오버플로 검사는 제외됩니다. 보다 정확하게는 인덱스가 잘못된 경우 잘못된 값이 반환됩니다.

이름
목적
 단위 GetDataBarsCount()
 사용 가능한 바의 수를 반환합니다.
 단위 GetDataBarsCalculated()
 변경되지 않은 바의 수를 반환합니다.
 단위 GetDataStartInd()
 랩어라운드 액세스에 대한 인덱스를 반환합니다(사용자 지정 지표의 경우).
 부울 GetDataBarsCuttingLeft()
 왼쪽에서 바를 자른 결과를 반환합니다.
 더블 GetDataOpen(int shift, bool AsSeries)
 시프트 바에 대해 '열기'를 반환합니다.
 더블 GetDataHigh(int shift, bool AsSeries)
 시프트 바에 대해 '높음'을 반환합니다.
 더블 GetDataLow(int shift, bool AsSeries)
 시프트 바에 대해 '낮음'을 반환합니다.
 더블 GetDataClose(int shift, bool AsSeries)
 shift-bar에 대해 '닫기'를 반환합니다.
 datetime GetDataTime(int shift, bool AsSeries)
 shift-bar에 대한 '시간' 반환
 긴 GetDataTick_volume(int shift, bool AsSeries)
 shift-bar에 대해 'Tick_volume'을 반환합니다.
 긴 GetDataReal_volume(int shift, bool AsSeries)
 shift-bar에 대해 'Real_volume'을 반환합니다.
 int GetDataSpread(int shift, bool AsSeries)
 shift-bar에 대해 'Spread'를 반환합니다.


CCustPrevCalculated 클래스의 추가 최적화 예

  • MqlRates에서 여러(특정 목적에 따라 결정됨) 어레이로 전환하는 것을 거부합니다(메모리 요구사항은 감소하지만 어레이 복사 호출 횟수에 대한 로드는 증가함).
  • 액세스의 각 기능을 특정 유형의 배열 인덱싱(«bool AsSeries» 매개변수에서 거부)과 함께 사용하기 위해 두 개의 독립적인 기능으로 나눕니다. 이점은 논리 조건 «if(AsSeries)»에만 있습니다.


CCustPrevCalculated 클래스의 데이터를 기반으로 사용자 지정 지표 ZigZag 계산을 위한 CCustZigZagPPC 만들기

이 알고리즘은 맞춤 지표 Professional ZigZag를 기반으로 합니다. 클래스의 소스 코드는 ZigZags.mqh 파일에 있습니다. 또한 외부 바 작업에 OutsideBar.mqh 라이브러리가 사용됩니다.

지표의 한 바에 대한 설명을 위한 별도의 구조를 만들어 보겠습니다.

struct ZZBar
  {
   double UP, DN;                      // Buffers of the ZigZag indicator
   OrderFormationBarHighLow OB;       // Buffer for caching of an external bar
  };

또한 클래스의 계산 반환 결과를 결정해 보겠습니다.

enum CPCZZResultCode
  {
   CPCZZRC_NotInitialized,             // Class is no initialized
   CPCZZRC_NoData,                     // Faield to receive data (including the external bar)
   CPCZZRC_NotChanged,                 // No changes of ZZ rays
   CPCZZRC_Changed                     // ZZ rays changed
  };

CCustZigZagPPC 클래스를 초기화하려면 Init() 함수를 한 번 호출해야 합니다. 성공하면 'true'를 반환합니다.

CCustZigZagPPC ZZ1;
ZZ1.Init(CustPrevCalculated, _Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15, 0, true, 12, 10);

지표 계산을 위해 CCustPrevCalculated 클래스의 이전에 계산된 데이터를 기반으로 데이터 업데이트를 시작해야 합니다.

CPCPrepareDataResultCode resZZ1;
resZZ1 = ZZ1.PrepareData(resData);

그런 다음 Calculate() 프로시저를 호출합니다.

if ( (resZZ1 != CPCPDRC_NoData) && (resZZ1 != CPCPDRC_NoRecountNotRequired) )
   ZZ1.Calculate();

하나의 클래스 CCustPrevCalculated를 여러 CCustZigZagPPC 클래스와 함께 사용하는 전체 예는 ScriptSample_CustZigZagPPC.mq5 파일에 나와 있습니다.


CCustZigZagPPC 클래스의 데이터 접근 기능

이름
목적
 단위 GetBarsCount()
 사용 가능한 바의 수를 반환합니다.
 단위 GetBarsCalculated()  계산된 바의 수를 반환합니다.
 더블 GetUP(단위 시프트, bool AsSeries)
 바의 지그재그 피크 값을 반환합니다.
 더블 GetDN(uint shift, bool AsSeries)
 바에 대한 지그재그 낮은 값을 반환합니다.
 OrderFormationBarHighLow GetOB(단위 시프트, 부울 AsSeries)  바의 '외부' 값을 반환합니다.


비주얼 및 프로그램 체크

시각적 확인을 위해 원래 지표를 차트에 첨부하고 그 위에 동일한 입력 매개변수를 사용하여 특별히 작성된 테스트 지표 Indicator_CustZigZag.mq5를 첨부합니다(두 지표를 보려면 다른 색상을 선택해야 함). 작업 결과는 다음과 같습니다.

빨간색 - 원본, 파란색 - 우리 고유, 지난 100개 바에서 계산됨.

같은 방법으로 Expert Advisor에서 비교할 수 있습니다. 차이가 날까요? iCustom("AlexSTAL_ZigZagProf") 및 CCustZigZagPPC 클래스에서 얻은 결과는 Expert Advisor Expert_CustZigZagPPC_test.mq5 테스트의 모든 틱에서 비교됩니다. 계산에 대한 정보가 저널에 표시됩니다(알고리즘에 대한 기록이 없기 때문에 첫 번째 바에 계산이 없을 수 있음).

(EURUSD,M1)                1.35797; 1.35644; 1.35844; 1.35761; 1.35901; 1.35760; 1.35959; 1.35791; 1.36038; 1.35806; 1.36042; 1.35976; 1.36116; 1.35971; // it is normal
(EURUSD,M1) Tick processed: 1.35797; 1.35644; 1.35844; 1.35761; 1.35901; 1.35760; 1.35959; 1.35791; 1.36038; 1.35806; 1.36042; 1.35976; 1.36116; 
(EURUSD,M1) Divergence on the bar: 7 

이 Expert Advisor를 더 자세히 살펴보겠습니다. 작업을 위한 전역 변수를 결정합니다.

#include <ZigZags.mqh>

CCustPrevCalculated CustPrevCalculated;
CCustZigZagPPC ZZ1;
int HandleZZ;

변수 초기화:

int OnInit()
  {
   // Creating new class and initializing it
   CustPrevCalculated.InitData(_Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15);
   
   // Initializing the class ZZ
   ZZ1.Init(GetPointer(CustPrevCalculated), _Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15, 0, true, 12, 10);
   
   // Receiving handle for the custom indicator
   HandleZZ = iCustom(_Symbol, _Period, "AlexSTAL_ZigZagProf", 12, 10, 0 , true);
   Print("ZZ_handle = ", HandleZZ, "  error = ", GetLastError());

   return(0);
  }
Expert Advisor에서 틱 처리:
void OnTick()
  {
   // Calculation of data
   CPCPrepareDataResultCode resData, resZZ1;
   resData = CustPrevCalculated.PrepareData();
   
   // Start recalculation for each indicator! PrepareData obligatory!
   resZZ1 = ZZ1.PrepareData(resData);
   
   // Расчет данных ZZ1
   if ( !((resZZ1 != CPCPDRC_NoData) && (resZZ1 != CPCPDRC_NoRecountNotRequired)) )
      return;

   // Получим результаты расчета
   ZZ1.Calculate();

이제 CCustZigZagPPC에 의해 계산된 ZZ1.GetBarsCalculated() 바가 있습니다. iCustom("AlexSTAL_ZigZagProf")과 CCustZigZagPPC 클래스의 데이터를 비교하는 코드를 추가해 보겠습니다.

   int tmpBars = (int)ZZ1.GetBarsCalculated();
   double zzUP[], zzDN[];
   CopyBuffer(HandleZZ, 0, 0, tmpBars, zzUP);
   CopyBuffer(HandleZZ, 1, 0, tmpBars, zzDN);
   
   // Perform comparison
   string tmpSt1 = "", tmpSt2 = "";
   for (int i = (tmpBars-1); i >= 0; i--)
     {
      double tmpUP = ZZ1.GetUP(i, false);
      double tmpDN = ZZ1.GetDN(i, false);
      if (tmpUP != zzUP[i])
         Print("Divergence on the bar: ", i);
      if (tmpDN != zzDN[i])
         Print("Divergence on the bar: ", i);
      if (tmpUP != EMPTY_VALUE)
         tmpSt1 = tmpSt1 + DoubleToString(tmpUP, _Digits) + "; ";
      if (tmpDN != EMPTY_VALUE)
         tmpSt1 = tmpSt1 + DoubleToString(tmpDN, _Digits) + "; ";

      if (zzUP[i] != EMPTY_VALUE)
         tmpSt2 = tmpSt2 + DoubleToString(zzUP[i], _Digits) + "; ";
      if (zzDN[i] != EMPTY_VALUE)
         tmpSt2 = tmpSt2 + DoubleToString(zzDN[i], _Digits) + "; ";
     }
  Print("Tick processed: ", tmpSt1);
  Print("                              ", tmpSt2);
  }

다음은 Expert Advisor 또는 스크립트에서 CCustZigZagPPC 클래스의 간단한 실제 사용입니다. CopyBuffer() 대신 직접 액세스 GetUP(), GetDN(), GetOB()의 기능.


지표를 별도의 클래스로 이동(iATR의 예)

ZigZags.mqh 파일을 기반으로 위에서 설명한 원칙에 따라 사용자 지정 지표를 빠르게 개발하기 위한 MyIndicator.mqh 템플릿을 만들었습니다.

일반 계획:

1. 준비 단계.

  • MyIndicator.mqh를 다른 이름의 파일로 복사하고(내 예에서는 ATRsample.mqh임) MetaEditor 5에서 후자를 엽니다.
  • "MyInd" 텍스트를 지표의 이름으로 바꿉니다(내 예에서는 "ATR").

2. 초기(원래) 지표에서 클래스로 가져올 외부 매개변수를 선택하고 선언하고 초기화합니다.

내 예에서 ATR 지표에는 하나의 외부 매개변수가 있습니다.
input int InpAtrPeriod=14;  // ATR period
  • 이 매개변수를 우리 클래스와 클래스 초기화 기능에 추가하십시오.
class CCustATR
  {
protected:
   ...
   uchar iAtrPeriod;
   ...
public:
   ...
   bool Init(CCustPrevCalculated *CPC, string Instr, ENUM_TIMEFRAMES TF, int Limit, CPCHistorySynchMode HSM, uchar HS, uint HSMinute, uchar AtrPeriod);
  • Init 함수의 본문 헤더를 변경하고 입력 값으로 변수 매개변수를 초기화합니다.
bool CCustATR::Init(CCustPrevCalculated *CPC, string Instr, ENUM_TIMEFRAMES TF, int Limit, CPCHistorySynchMode HSM, uchar HS, uint HSMinute, uchar AtrPeriod)
{
      ...
      BarsLimit = Limit;
      iAtrPeriod = AtrPeriod;
      ...

3. 초기 지표에서 필요한 버퍼 수를 결정하고 클래스에서 선언합니다. 또한 INDICATOR_DATA 버퍼를 반환하는 기능을 선언합니다.

  • 구조 변경
struct ATRBar
  {
   double Val;                          // Indicator buffers
  };

우리 자신의 구조에:

struct ATRBar
  {
   double ATR;
   double TR;
  };
  • 0 값을 결정합니다.
CPCPrepareDataResultCode CCustATR::PrepareData(CPCPrepareDataResultCode resData)
{
   ...
   for (uint i = (DataBarsCalculated == 0)?0:(DataBarsCalculated+1); i < DataBarsCount; i++)
     {
      Buf[PInd(i, false)].ATR = EMPTY_VALUE;
      Buf[PInd(i, false)].TR = EMPTY_VALUE;
     }
   ...
  • INDICATOR_DATA 버퍼의 값을 반환하는 기능을 변경하고 추가합니다.

변경(버퍼가 하나만 있는 경우 변경을 건너뛸 수 있음)

class CCustATR
  {
   ...
   double GetVal(uint shift, bool AsSeries);                      // returns the Val value of the buffer for a bar
   ...

class CCustATR
  {
   ...
   double GetATR(uint shift, bool AsSeries);                      // Возвращает значение буфера ATR для бара
   ...

해당 함수의 코드를 변경합니다.

double CCustATR::GetATR(uint shift, bool AsSeries)
{
   if ( shift > (DataBarsCount-1) )
      return(EMPTY_VALUE);
   return(Buf[PInd(shift, AsSeries)].ATR);
}
참고: 버퍼 값을 반환하는 여러 함수 대신 버퍼의 번호 또는 이름과 같은 추가 매개변수가 있는 하나만 사용할 수 있습니다.


4. 초기 지표의 OnCalculate() 함수의 논리를 클래스의 해당 함수에 복사합니다.

  • 기본 확인
CPCATRResultCode CCustATR::Calculate()
{
   ...
   // Check if there are enough bars for the calculation
   if (DataBarsCount <= iAtrPeriod)
      return(CPCATRRC_NoData);
   ...
  • 계산: 첫 번째 눈금에서 다음 눈금에서 계산을 위한 바의 수:
   if ( DataBarsCalculated != 0 )
      BarsForRecalculation = DataBarsCount - ATRDataBarsCalculated - 1;
   else
     {
      Buf[PInd(0, false)].TR = 0.0;
      Buf[PInd(0, false)].ATR = 0.0;
      //--- filling out the array of True Range values for each period
      for (uint i = 1; i < DataBarsCount; i++)
         Buf[PInd(i, false)].TR = MathMax(CustPrevCalculated.GetDataHigh(i, false), CustPrevCalculated.GetDataClose(i-1, false)) - 
                                  MathMin(CustPrevCalculated.GetDataLow(i, false), CustPrevCalculated.GetDataClose(i-1, false));
      //--- first AtrPeriod values of the indicator are not calculated
      double firstValue = 0.0;
      for (uint i = 1; i <= iAtrPeriod; i++)
        {
         Buf[PInd(i, false)].ATR = 0;
         firstValue += Buf[PInd(i, false)].TR;
        }
      //--- calculating the first value of the indicator
      firstValue /= iAtrPeriod;
      Buf[PInd(iAtrPeriod, false)].ATR = firstValue;
      
      BarsForRecalculation = DataBarsCount - iAtrPeriod - 2;
     }
  • 모든 틱 자체에서 계산:
   for (uint i = (DataBarsCount - BarsForRecalculation - 1); i < DataBarsCount; i++)
     {
      Buf[PInd(i, false)].TR = MathMax(CustPrevCalculated.GetDataHigh(i, false), CustPrevCalculated.GetDataClose(i-1, false)) - 
                               MathMin(CustPrevCalculated.GetDataLow(i, false), CustPrevCalculated.GetDataClose(i-1, false));
      Buf[PInd(i, false)].ATR = Buf[PInd(i-1, false)].ATR + (Buf[PInd(i, false)].TR-Buf[PInd(i-iAtrPeriod, false)].TR) / iAtrPeriod;
      ...

그게 다예요. 우리 클래스가 생성되었습니다. 시각적 검사를 위해 테스트 지표를 만들 수 있습니다(내 예에서는 Indicator_ATRsample.mq5).



글을 수정하는 동안 CCustPrevCalculated 클래스를 하나의 사용자 지정 지표와 함께 사용하면 이 클래스의 생성, 초기화 및 동기화를 사용자 지정 지표에서 통합할 수 있다는 아이디어가 떠올랐습니다(제 예에서는 CCustZigZagPPC 및 CCustATR입니다. ). 이 목적을 위해 사용자 지정 지표의 초기화 기능을 호출할 때 개체에 대한 0 포인터를 사용해야 합니다.

   ATR.Init(NULL, _Symbol, _Period, iBars, CPCHSM_Normal, 0, 30, InpAtrPeriod);

그 일반적인 구조에서

#include <CustPrevCalculated.mqh>
#include <ATRsample.mqh>
CCustPrevCalculated CustPrevCalculated;
CCustATR ATR;

int OnInit()
  {
   CustPrevCalculated.InitData(_Symbol, _Period, iBars, CPCHSM_Normal, 0, 30);
   ATR.Init(GetPointer(CustPrevCalculated), _Symbol, _Period, iBars, CPCHSM_Normal, 0, 30, InpAtrPeriod);
  }

int OnCalculate(...)
  {
   CPCPrepareDataResultCode resData = CustPrevCalculated.PrepareData();
   CPCPrepareDataResultCode resATR = ATR.PrepareData(resData);
   if ( (resATR != CPCPDRC_NoData) && (resATR != CPCPDRC_NoRecountNotRequired) )
      ATR.Calculate();
  }

다음과 같이 단순화됩니다.

#include <ATRsample.mqh>
CCustATR ATR;

int OnInit()
  {
   ATR.Init(NULL, _Symbol, _Period, iBars, CPCHSM_Normal, 0, 30, InpAtrPeriod);
  }

int OnCalculate(...)
  {
   ATR.Calculate();
  }
실제 예제는 Indicator_ATRsample2.mq5 파일에 나와 있습니다.

전략 테스터의 성능에 대한 설명된 기술의 영향

확인을 위해 세 가지 변형 중 하나에 따라 모든 틱에서 0 바 지표의 값을 수신하는 테스트 Expert Advisor(TestSpeed_IndPrevCalculated.mq5)를 만들었습니다.

enum eTestVariant
  {
   BuiltIn,    // Built-in indicator iATR
   Custom,     // Custom indicator iCustom("ATR")
   IndClass    // Calculation in the class
  };

이 Expert Advisor는 다음 최적화 매개변수를 사용하여 1개의 에이전트에서 10번 실행되었습니다.

  • 기호: EURUSD
  • 기간: 전체 내역 [1993..2001]
  • 거래 모드: 매 틱
  • 외부 매개변수: FalseParameter [0..9]

지표의 세 가지 변형을 각각 사용할 때 최적화 시간을 측정했습니다. 검사 결과는 선형 히스토그램으로 표시됩니다.

ATR 지표의 세 가지 구현 유형에 대한 최적화 시간

    최적화 시간 측정에 사용된 Expert Advisor의 소스 코드:

    //+------------------------------------------------------------------+
    //|                                  TestSpeed_IndPrevCalculated.mq5 |
    //|                                         Copyright 2011, AlexSTAL |
    //|                                           http://www.alexstal.ru |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2011, AlexSTAL"
    #property link      "http://www.alexstal.ru"
    #property version   "1.00"
    //--- connect the include file with the CustATR class
    #include <ATRsample.mqh>
    //--- set the selection of the parameter as an enumeration
    enum eTestVariant
      {
       BuiltIn,    // Built-in indicator iATR
       Custom,     // Custom indicator iCustom("ATR")
       IndClass    // Calculation withing the class
      };
    //--- input variables
    input eTestVariant TestVariant;
    input int          FalseParameter = 0;
    //--- period of the ATR indicator
    const uchar        InpAtrPeriod = 14;
    //--- handle of the built-in or custom indicator
    int                Handle;
    //--- indicator based on the class 
    CCustATR           *ATR;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       //---
       switch(TestVariant)
         {
          case BuiltIn:
             Handle = iATR(_Symbol, _Period, InpAtrPeriod);
             break;
          case Custom:
             Handle = iCustom(_Symbol, _Period, "Examples\ATR", InpAtrPeriod);
             break;
          case IndClass:
             ATR = new CCustATR;
             ATR.Init(NULL, _Symbol, _Period, 100, CPCHSM_Normal, 0, 30, InpAtrPeriod);
             break;
         };
       //---
       return(0);
      }
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
       switch(TestVariant)
         {
          case IndClass:
             delete ATR;
             break;
         };
      }
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
      {
       double tmpValue[1];
       switch(TestVariant)
         {
          case BuiltIn:
             CopyBuffer(Handle, 0, 0, 1, tmpValue);
             break;
          case Custom:
             CopyBuffer(Handle, 0, 0, 1, tmpValue);
             break;
          case IndClass:
             ATR.Calculate();
             tmpValue[0] = ATR.GetATR(0, true);
             break;
         };
      }
    //+------------------------------------------------------------------+

    보시다시피 이 기술은 일반 사용자 지정 지표를 사용하는 것과 비교하여 전략 테스터의 성능을 크게 저하시키지 않습니다.


    이 기술의 실제 사용에 대한 참고 사항

    • 전략 테스터에서 Expert Advisor를 테스트할 때 사용자 지정 지표에서 prev_calculated 값을 0으로 설정할 수 없으므로 이 모드에서 기록 동기화가 비활성화됩니다.
    • 지표의 계산은 클래스의 초기 초기화에서 엄격하게 설정된 마지막 'n' 바에서만 수행됩니다.
    • 계산은 초기화된 클래스의 특정 기호 및 기간에 대한 엄격한 바인딩을 의미합니다. 다른 기호나 마침표에 대한 계산을 수행하려면 클래스의 새 인스턴스를 만들어야 합니다.


    결론

    각 상황에서 프로그래머는 작업 구현의 다양한 변형에 대한 모든 장단점을 고려해야 합니다. 글에서 제안한 구현은 장단점이 있는 방법일 뿐입니다.

    추신 실수하지 않는 사람은 아무것도 이뤄낼 수 없습니다! 오류를 발견하면 저에게 알려주십시오.

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

    랜덤 워크와 추세 표시기 랜덤 워크와 추세 표시기
    랜덤 워크는 실제 시장 데이터와 매우 유사해 보이지만 몇 가지 중요한 기능을 갖고 있습니다. 이 글에서는 동전 던지기 게임을 사용하여 시뮬레이션한 랜덤 워크의 속성을 고려할 것입니다. 데이터의 속성을 연구하기 위해 경향성 지표가 개발되었습니다.
    HTML의 차트 및 다이어그램 HTML의 차트 및 다이어그램
    오늘날에는 웹 브라우저가 설치되어 있지 않은 컴퓨터를 찾기가 어렵습니다. 오랫동안 브라우저는 진화하고 개선되어 왔습니다. 이 문서에서는 브라우저에 표시하기 위해 MetaTrader 5 클라이언트 터미널에서 얻은 정보를 기반으로 차트와 다이어그램을 만드는 간단하고 안전한 방법에 대해 설명합니다.
    관리되지 않는 내보내기를 사용하여 MQL5에 C# 코드 노출 관리되지 않는 내보내기를 사용하여 MQL5에 C# 코드 노출
    이 글에서는 MQL5 코드와 관리되는 C# 코드 간의 다양한 상호 작용 방법을 제시했습니다. 또한 C#에 대해 MQL5 구조를 마샬링하는 방법과 MQL5 스크립트에서 내보낸 DLL 함수를 호출하는 방법에 대한 몇 가지 예를 제공했습니다. 제공된 예제가 관리 코드에서 DLL을 작성하는 향후 연구의 기초가 될 수 있다고 생각합니다. 이 글은 또한 MetaTrader가 C#에서 이미 구현된 많은 라이브러리를 사용할 수 있는 기회를 제공합니다.
    거래 내역을 기반으로 한 거래 플레이어 거래 내역을 기반으로 한 거래 플레이어
    트레이딩 플레이어. 설명이 필요없는 딱 네 단어. 버튼이 있는 작은 상자에 대한 생각이 떠오릅니다. 버튼 하나 누르기 - 재생, 레버 이동 - 재생 속도가 변경됩니다. 실제로는 꽤 비슷합니다. 이 글에서는 거의 실시간으로 트레이딩 히스토리를 재생하는 제 발전을 보여주고 싶습니다. 이 글에서는 지표 작업 및 차트 관리, OOP의 일부 뉘앙스를 다룹니다.