English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
Delphi로 MQL5용 DLL 짜는 법

Delphi로 MQL5용 DLL 짜는 법

MetaTrader 5 | 5 7월 2021, 15:19
91 0
Andriy Voitenko
Andriy Voitenko

들어가며

DLL을 쓰는 것은 Delphi 2009 개발 환경을 이용하여 개발한 예시가 될 것입니다.. 이 버전은 MQL5에서 모든 줄이 유니코드 포맷으로 저장되어있다는 점 때문에 선택되었습니다. Delphi의 예전 버전에서는, SysUtils 모듈에 유니코드 포맷을 다룰 함수가 없습니다.

어떤 이유에서건 만약 더 예전 버전 (Delphi 2007 혹은 더 전)을 쓰고 있다면, 각 줄을 ANSI 포맷으로 다뤄야하며 MetaTrader 5와 데이터 교류를 하기 위해서는 유니코드로 변환시킬 준비를 해야합니다. 그런걸 피하기 위해서 저는 DLL 모듈은 Delphi 2009 이후 환경에서 개발하는 것을 추천드립니다. Delphi의 30일 체험판은 공식 홈페이지에서 다운받을 수 있습니다 http://embarcadero.com.

1. 프로젝트 만들기

프로젝트를 만들려면 메뉴 아이템을 골라서 DLL 마법사를 실행해야합니다: 'File -> New -> Other ... -> DLL Wizard ' 1번 그림에서 나오는 것처럼 말이죠.

1번 그림. DLL 마법사를 통해 프로젝트만들기

결과적으로 2번 그림에서처럼 빈 DLL 프로젝트를 만들 수 있었습니다.


2번 그림. 빈 DLL 프로젝트

프로젝트 제목의 긴 설명의 핵심은 동적으로 할당된 메모리로 작업할 때 메모리 관리자의 올바른 연결과 사용을 상기시키는 것입니다. 이에 대해서는 스트링을 다루는 섹션에서 자세히 설명합니다.

새 DLL을 기능으로 채우기 시작하기 전에 프로젝트를 구성하는 것이 중요합니다.

메뉴에서 프로젝트 속성 창: 'Project -> Options ...' 을 열거나 키보드로 'Shift + Ctrl + F11' .

디버깅 과정을 간소화하기위해 DLL을 이하의 폴더 내에 직접 만들겠습니다 '.. \\MQL5\\Libraries' MetaTrader5 매매 터미널. 이를 위해서 DelphiCompiler 탭에서 Output directory 속성을, 3번 그림에 나오듯 설정하십시오. DLL로 생성된 파일을 프로젝트 폴더에서 터미널 폴더로 지속적으로 복사할 필요가 없습니다.

 3번 그림. 결과 DLL 파일을 저장할 폴더를 명시

Windows 시스템 폴더에 없는 BPL 모듈은 향후에 작동하지 않으므로 조립 중에 BPL 모듈이 연결되지 않도록 하려면 패키지 ,탭에서 확인해야 합니다. Build with runtime packages 플래근느 4번 그림에서 보듯 체크 해제 되어 있습니다.

  4번 그림. 어셈블리에서 BPL 모듈 제외

프로젝트 구성을 완료한 후 작업 폴더에 저장하십시오. 프로젝트의 지정된 이름은 컴파일된 DLL 파일의 이후 이름입니다.

2. 프로시져와 함수 추가하기 

패러미터가 없는 절차의 예에서 DLL 모듈에 내보낸 프로시저 및 기능을 쓸 때 일반적인 상황을 고려하겠습니다. 알림과 패러미터 전송은 다음 섹션에서 다뤄질 예정입니다.

여담으로 Object Pascal 언어로 절차와 기능을 작성할 때 프로그래머는 이러한 환경을 위해 개발된 수많은 구성 요소는 말할 것도 없고 내장 라이브러리인 Delphi 기능을 사용할 기회도 있습니다. 예를 들어 텍스트 메시지와 함께 모달 창의 표시를 표시하는 것과 같은 동일한 작업을 위해 VCL 라이브러리 - ShowMessage의 프로시져 뿐 아니라 API 함수 MessageBox 도 사용할 수 있습니다.

두 번째 옵션은 Dialogs 모듈을 포함하며 표준 Windows 대화 상자에서 쉽게 작업할 수 있다는 이점을 제공합니다. 그러나 결과 DLL 파일의 크기는 약 500KB 증가합니다. 따라서 Disk 공간을 많이 차지하지 않는 작은 DLL 파일을 생성하려면 VCL 구성 요소를 사용하지 않는 것이 좋습니다.

설명이 딸린 테스트 프로젝트는 아래에 있습니다:

library dll_mql5;

uses
  Windows, // necessary for the work of the MessageBox function
  Dialogs; // necessary for the work of the ShowMessage procedure from the Dialogs module

var   Buffer: PWideChar;
//------------------------------------------------------+
procedure MsgBox(); stdcall; // 
//to avoid errors, use the stdcall (or cdecl) for the exported functions
//------------------------------------------------------+
begin
    {1} MessageBox(0,'Hello World!','terminal', MB_OK);
    {2} ShowMessage('Hello World!');// alternative to the MessageBox function 
end;

//----------------------------------------------------------+
exports
//----------------------------------------------------------+
  {A} MsgBox,
  {B} MsgBox name 'MessageBox';// renaming of the exported function


//----------------------------------------------------------+
procedure DLLEntryPoint(dwReason: DWord); // event handler
//----------------------------------------------------------+
begin
    case dwReason of

      DLL_PROCESS_ATTACH: // DLL attached to the process;
          // allocate memory
          Buffer:=AllocMem(BUFFER_SIZE);

      DLL_PROCESS_DETACH: // DLL detached from the process;
          // release memory
          FreeMem(Buffer);

    end;
end;

//----------------------------------------------------------+
begin
    DllProc := @DLLEntryPoint; //Assign event handler
    DLLEntryPoint(DLL_PROCESS_ATTACH);
end.
//----------------------------------------------------------+

모든 익스포트 함수는 변경자 stdcall 혹은 cdecl과 함께 선언되어야합니다. 이러한 변경자가 지정되지 않은 경우 Delphi는 기본적으로 패스트콜을 사용합니다. 이 계약은 스택이 아닌 패싱 패러미터에 CPU 레지스터를 주로 사용합니다. 전달 패러미터를 다루다보면 외부 함수 DLL.를 호출하는 단계에서 무조건 에러가 생깁니다.

"begin end" 섹션에는 DLL 이벤트 처리기의 표준 초기화 코드가 포함되어 있습니다. DLL EntryPoint 콜백 절차는 DLL EntryPoint를 호출한 프로세스에서 연결 및 연결을 끊을 때 호출됩니다. 이러한 이벤트는 예와 같이 당사의 필요에 맞게 할당된 올바른 동적 메모리 관리에 사용할 수 있습니다.

MQL5 호출:

#import "dll_mql5.dll"
    void MsgBox(void);
    void MessageBox(void);
#import

// Call of procedure
   MsgBox();
// If the names of the function coincide with the names of MQL5 standard library function
// use the DLL name when calling the function
   dll_mql5::MessageBox();

3. 함수로 패러미터 전달 및 반환값

파라미터 전달을 고려하기 전에 MQL5 및 Object Pascal에 대한 데이터 대응 표를 분석하겠습니다.

MQL5의 자료 타입
Object Pascal의 자료 타입 (Delphi)
기타
char ShortInt
uchar
Byte
short
SmallInt
ushort
Word

int
Integer
 
uint Cardinal
long Int64
ulong
UInt64

float Single
double Double

ushort (символ) WideChar
string PWideChar  
bool Boolean  
datetime TDateTime 변환해야합니다 (이 섹션 하단을 보십시오)
color TColor  

1번 테이블. MQL5 및 Object Pascal에 대한 데이터 대응 표

표에서 볼 수 있듯이, 날짜 시간 이외의 모든 데이터 유형에 대해 Delphi에 완전히 대응하는 타입이 존재합니다.

이제 패러미터 전달의 두가지 방법을 고려해봅시다: 값으로 넘기기, 레퍼런스로 넘기기 두 버전에 대한 패러미터 선언 형식은 표 2에 나와 있습니다.

패러미터 전달 메소드
MQL5 알림
Delphi 알림
기타 
by value
int func (int a); func (a:Integer): Integer;  올바름

int func (int a);
func (var a: Integer): Integer;
 에러: <메모리 주소>쓰기 권한 오류
by link
int func (int &a);
func (var a: Integer): Integer;
 올바름, 하지만 줄이수정자 변수 없이 전달됨!
  int func (int &a);  func (a: Integer): Integer;  에러: 변수의 값 대신 메모리 셀의 주소를 담음

 2번 테이블. 패러미터를 넘기는 방법

이제 전달된 패러미터와 반환된 값으로 작업하는 예를 살펴보겠습니다.

3.1 날짜 시간 변경하기

먼저 변환하려는 날짜 및 시간 유형을 살펴보겠습니다. 날짜 시간 유형은 형식이 아닌 크기에서만 TDateTime에 해당합니다. 변환하기 쉽도록 TDateTime 대신 Int64를 수신된 데이터 유형으로 사용하십시오. 다음은 직접 변환 및 역변환 함수입니다.

uses 
    SysUtils,  // used for the constant UnixDateDelta 
    DateUtils; // used for the function IncSecon, DateTimeToUnix

//----------------------------------------------------------+
Function MQL5_Time_To_TDateTime(dt: Int64): TDateTime;
//----------------------------------------------------------+
begin
      Result:= IncSecond(UnixDateDelta, dt);
end;

//----------------------------------------------------------+
Function TDateTime_To_MQL5_Time(dt: TDateTime):Int64;
//----------------------------------------------------------+
begin
      Result:= DateTimeToUnix(dt);
end;

3.2 단순 자료 타입으로 작업하기

가장 일반적으로 사용되는 데이터 타입(int, double, bool, datetime)의 예에서 간단한 데이터 타입을 전송하는 방법을 살펴보겠습니다.

Object Pascal 호출:

//----------------------------------------------------------+
function SetParam(var i: Integer; d: Double; const b: Boolean; var dt: Int64): PWideChar; stdcall;
//----------------------------------------------------------+
begin
  if (b) then d:=0;                   // the value of the variable d is not changed in the calling program
  i:= 10;                             // assign a new value for i
  dt:= TDateTime_To_MQL5_Time(Now()); // assign the current time for dt
  Result:= 'value of variables i and dt are changed';
end;

MQL5 호출:

#import "dll_mql5.dll"
    string SetParam(int &i, double d, bool b, datetime &dt);
#import

// initialization of variables
   int i = 5;
   double d = 2.8;
   bool b = true;
   datetime dt= D'05.05.2010 08:31:27';
// calling the function
   s=SetParam(i,d,b,dt);
// output of results
   printf("%s i=%s d=%s b=%s dt=%s",s,IntegerToString(i),DoubleToString(d),b?"true":"false",TimeToString(dt));
결과: 
The values of variables i and dt are changed i =  10  d =  2.80000000  b = true dt =  2009.05 .  05  08 :  42 

값으로 전달했기 때문에 d의 값은 변하지 않습니다. 변수 내 값의 변화를 막기 위하여, DLL 함수 내에 수정자 상수를 두어 b 변수에 사용되었습니다.

3.3 구조와 어레이 다루기

여러 경우에 여러 유형의 패러미터를 구조로 그룹화하고 한 유형의 패러미터를 어레이로 그룹화하는 것이 유용합니다. 이전 예제의 SetParam 함수에서 전송된 모든 패러미터를 구조물에 통합하는 방식으로 작업해 보십시오.

Object Pascal 호출: 

type
StructData = packed record
    i: Integer;
    d: Double;
    b: Boolean;
    dt: Int64;
  end;

//----------------------------------------------------------+
function SetStruct(var data: StructData): PWideChar; stdcall;
//----------------------------------------------------------+
begin
  if (data.b) then data.d:=0;
  data.i:= 10;                                 // assign a new value for i
  data.dt:= TDateTime_To_MQL5_Time(Now()); // assign the current time for dt
  Result:= 'The values of variables i, d and dt are changed';
end

MQL5 호출:  

struct STRUCT_DATA
  {
   int i;
   double d;
   bool b;
   datetime dt;
  };

#import "dll_mql5.dll"
    string SetStruct(STRUCT_DATA &data);
#import

   STRUCT_DATA data;
   
   data.i = 5;
   data.d = 2.8;
   data.b = true;
   data.dt = D'05.05.2010 08:31:27';
   s = SetStruct(data);
   printf("%s i=%s d=%s b=%s dt=%s", s, IntegerToString(data.i),DoubleToString(data.d), 
             data.b?"true":"false",TimeToString(data.dt));
결과:
The values of variables i,  d  and dt are changed i =  10  d =  0.00000000  b = true dt =  2009.05 .  05  12 :  19 

이전 예제의 결과와 한 가지 중요한 차이를 주목할 필요가 있습니다. 구조가 레퍼런스를 통해 전송되므로 호출된 함수에서 선택한 필드가 편집되지 않도록 보호할 수 없습니다. 이 경우 데이터의 무결성을 모니터링하는 작업은 전적으로 프로그래머에게 달려 있습니다.

배열에 Fibonaci 번호 시퀀스를 채우는 예를 들어 어레이 작업을 고려해 보십시오.

Object Pascal 호출:

//----------------------------------------------------------+
function SetArray(var arr: IntegerArray; const len: Cardinal): PWideChar; stdcall;
//----------------------------------------------------------+
var i:Integer;
begin
  Result:='Fibonacci numbers:';
  if (len < 3) then exit;
  arr[0]:= 0;
  arr[1]:= 1;
  for i := 2 to len-1 do
    arr[i]:= arr[i-1] + arr[i-2];
end;

MQL5 호출: 

#import "dll_mql5.dll"
    string SetArray(int &arr[],int len);
#import
   
   int arr[12];
   int len = ArraySize(arr);
// passing the array by reference to be filled by data in DLL
   s = SetArray(arr,len);
//output of result
   for(int i=0; i<len; i++) s = s + " " + IntegerToString(arr[i]);
   printf(s);
결과:  
Fibonacci numbers 0 1 1 2 3 5 8 13 21 34 55 89

3.4 스트링 다루기

메모리 관리로 돌아가겠습니다. DLL 에서는 자신만의 메모리 관리자를 운영할 수 있습니다. 그러나 DLL 과 그 DLL을 호출한 프로그램은 각기 다른 언어로 쓰여있기 마련이고, 범용적인 시스템 메모리 보다는 자신만의 메모리 관리자를 지니고 있기 마련입니다. DLL 및 응용 프로그램에서 메모리 작동의 정확성에 대한 모든 책임은 프로그래머에게 있습니다.

메모리를 관리하려면 가장 중요한 법칙 "메모리에 할당한 자, 반드시 그걸 해방시키는 자가 되어야한다"를 잊지 말아야 합니다. mql5- 프로그램 코드에서 DLL의 메모리를 해방하려고하지 않아야고 반대도 마찬가지란 의미입니다

윈도우 API 함수 호출 스타일의 메모리 관리 예를 살펴보겠습니다. mql프로그램이 버퍼에 메모리를 할당하는 경우, PWideChar 및 DLL로 DLL에 전달되는 버퍼에 대한 포인터가 다음 예와 같이 원하는 값으로만 이 버퍼를 채웁니다. 예시:

Object Pascal 호출:

//----------------------------------------------------------+
procedure SetString(const str:PWideChar) stdcall;
//----------------------------------------------------------+
begin
  StrCat(str,'Current time:');
  strCat(str, PWideChar(TimeToStr(Now)));
end;

MQL5 호출: 

#import "dll_mql5.dll"
    void SetString(string &a);
#import

// the string must be initialized before the use
// the size of the buffer must be initially larger or equal to the string length
   StringInit(s,255,0); 
//passing the buffer reference to DLL 
   SetString(s);
// output of result 
   printf(s);

결과:

Current Time: 11: 48:51 

라인 버퍼의 메모리는 다음 예에서 볼 수 있는 여러 가지 방법으로 DLL에서 선택할 수 있습니다:

Object Pascal 호출: 

//----------------------------------------------------------+
function GetStringBuffer():PWideChar; stdcall;
//----------------------------------------------------------+
var StrLocal: WideString;
begin
     // working through the dynamically allocated memory buffer
     StrPCopy(Buffer, WideFormat('Current date and time: %s', [DateTimeToStr(Now)]));
     // working through the global varialble of WideString type
     StrGlobal:=WideFormat('Current time: %s', [TimeToStr(Time)]);
     // working through the local varialble of WideString type
     StrLocal:= WideFormat('Current data: %s', [DateToStr(Date)]);

{A}  Result := Buffer;

{B}  Result := PWideChar(StrGlobal);
     // it's equal to the following
     Result := @StrGlobal[1];

{С}  Result := 'Return of the line stored in the code section';

     // pointer to the memory, that can be released when exit from the function
{D}  Result := @StrLocal[1];
end;
MQL5 호출: 
#import "dll_mql5.dll"
    string GetStringBuffer(void);
#import

   printf(GetStringBuffer());

 결과: 

Current Date: 19.05.2010

중요한 것은 이 4개의 옵션이 모두 먹힌다는 것입니다. 처음 두 옵션에서는 전역으로 할당된 메모리를 통해 라인 작업이 수행됩니다.

옵션 A에서는 메모리가 독립적으로 할당되고 옵션 B에서는 메모리 관리 작업이 메모리 관리자에 의해 수행된다고 가정합니다.

옵션 C에서는 라인 상수가 메모리가 아니라 코드 세그먼트에 저장되므로 메모리 관리자가 스토리지에 동적 메모리를 할당하지 않습니다. 로컬 변수에 할당된 메모리는 기능을 종료한 후 즉시 해제될 수 있기 때문에 옵션 D는 프로그래밍계 기준으로는 꽤나 대담한 오류입니다.

메모리 관리자가 이 메모리를 즉시 릴리스하지 않고 휴지통에 가득 찰 시간이 없더라도 후자 옵션은 사용하지 않는 것이 좋습니다.

3.5 기본 패러미터 사용하기

옵션 패러미터 사용에 대해서 논해봅시다. 프로시져 및 기능을 불러올 때 값을 지정할 필요가 없기 때문에 흥미롭습니다. 한편, 다음 예에서 볼 수 있듯이 프로시져 및 함수 선언의 모든 필수 패러미터 후에 무조건 설명되어야 합니다.

Object Pascal 호출: 

//----------------------------------------------------------+
function SetOptional(var a:Integer; b:Integer=0):PWideChar; stdcall;
//----------------------------------------------------------+
begin
    if (b=0) then Result:='Call with default parameters'
    else          Result:='Call without default parameters';
end;
MQL5 호출:
#import "dll_mql5.dll"
    string SetOptional(int &a, int b=0);
#import

  i = 1;
  s = SetOptional(i); // second parameter is optional
  printf(s);

결과: 

Call with default parameters

디버깅을 보다 쉽게 하기 위하여 윗 예시들의 코드는 스크립트로 정리되었으며 Testing_DLL.mq5파일에서 찾아보실 수 있습니다.

4. 디자인 과정에서의 잠재적인 에러

에러: DLL Loading 이 허락되지 않음.

해결책: 5번 그림과 같이 ' Tools-Options'' 메뉴를 통해 MetaTrader 5 설정으로 이동하여 DLL 기능 가져오기를 허용합니다.

  5번 그림. DLL 함수를 임포트할 수 있는 허가

 5번 그림. DLL 함수를 임포트할 수 있는 허가

에러: 'DLL 이름'에서 '함수 이름' 을 찾을 수 없습니다.
해결책: DLL 프로젝트의 내보내기 섹션에 콜백 기능이 지정되어 있는지 확인하십시오. 만약 그렇다면 DLL 내 함수명과 mql5 프로그램 내의 함수명이 일치하는지 확인해야합니다 - 대소문자를 가린다는 점을 잊지 마시고요!

에러: [메모리 주소]에 쓰기 권한 없음
해결책: 전송된 패러미터에 대한 설명의 정확성을 확인해야 합니다(표 2 참조). 일반적으로 이 오류는 라인 처리와 관련되기 때문에 이 글의 단락 3.4에 명시된 라인 작업에 대한 권장 사항을 따르는 것이 중요합니다.

5. DLL 코드 예시

DLL을 사용한 시각적 예로 회귀채널의 패러미터를 세줄로 계산해 보십시오. 운하 구조의 정확성을 확인하기 위해, 우리는 내장된 객체 "운하 회귀"를 사용할 것입니다. LS(최소 제곱법)에 대한 근사 라인의 계산은 데이터 처리를 위한 알고리즘 컬렉션이 있는 사이트 http://alglib.sources.ru/에서 구합니다. 알고리즘 코드는 Delphi를 포함한 여러 프로그래밍 언어로 표시됩니다.

a와 b의 계수를 계산하려면 근사 선 y = a + b * x를 사용하여 LRLine linreg.pas 파일에 설명된 절차를 사용하십시오.

 procedure  LRLine (  const  XY: TReal2DArray;  / / Two-dimensional array of real numbers for X and Y coordinates 
                           N : AlglibInteger;  // number of points
                     var Info : AlglibInteger; // conversion status
                       var  A: Double;  / / Coefficients of the approximating line 
                        var  B: Double);
  

채널의 패러미터들을 계산하려면 CalcLRChannel 함수를 사용하세요.

Object Pascal 호출:  

//----------------------------------------------------------+
function CalcLRChannel(var rates: DoubleArray; const len: Integer;
                          var A, B, max: Double):Integer; stdcall;
//----------------------------------------------------------+
var arr: TReal2DArray;
    info: Integer;
    value: Double;
begin

    SetLength(arr,len,2);
    // copy the data to a two-dimensional array
    for info:= 0 to len - 1 do
    begin
      arr[info,0]:= rates[info,0];
      arr[info,1]:= rates[info,1];
    end;

    // calculation of linear regression coefficients
    LRLine(arr, len, info, A,  B);

    // find the maximal deviation from the approximation line found
    // and determine the width of the channel 
    max:= rates[0,1] - A;
    for info := 1 to len - 1 do
    begin
      value:= Abs(rates[info,1]- (A + B*info));
      if (value > max) then max := value;
    end;

    Result:=0;
end;

MQL5 호출:

#import "dll_mql5.dll"
    int CalcLRChannel(double &rates[][2],int len,double &A,double &B,double &max);
#import

   double arr[][2], //data array for processing in the ALGLIB format
              a, b,  // Coefficients of the approximating line  
              max; // maximum deviation from the approximating line is equal to half the width of the channel
   
   int len = period; //number of points for calculation
   ArrayResize(arr,len);

// copying the history to a two-dimensional array
   int j=0;
   for(int i=rates_total-1; i>=rates_total-len; i--)
     {
      arr[j][0] = j;
      arr[j][1] = close[i];
      j++;
     }

// calculation of channel parameters
   CalcLRChannel(arr,len,a,b,max);

계산에 CalcLRChannel 함수를 사용하는 인디케이터 코드는 LR_Channel.mq5 파일과 그 밑에 있습니다: 

//+------------------------------------------------------------------+
//|                                                   LR_Channel.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#include <Charts\Chart.mqh>
#include <ChartObjects\ChartObjectsChannels.mqh>

#import "dll_mql5.dll"
int CalcLRChannel(double &rates[][2],int len,double &A,double &B,double &max);
#import

input int period=75;

CChart               *chart;
CChartObjectChannel  *line_up,*line_dn,*line_md;
double                arr[][2];
//+------------------------------------------------------------------+
int OnInit()
//+------------------------------------------------------------------+
  {

   if((chart=new CChart)==NULL)
     {printf("Chart not created"); return(false);}

   chart.Attach();
   if(chart.ChartId()==0)
     {printf("Chart not opened");return(false);}

   if((line_up=new CChartObjectChannel)==NULL)
     {printf("Channel not created"); return(false);}

   if((line_dn=new CChartObjectChannel)==NULL)
     {printf("Channel not created"); return(false);}

   if((line_md=new CChartObjectChannel)==NULL)
     {printf("Channel not created"); return(false);}

   return(0);
  }
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
//+------------------------------------------------------------------+
  {

   double a,b,max;
   static double save_max;
   int len=period;

   ArrayResize(arr,len);

// copying of history to a two-dimensional array
   int j=0;
   for(int i=rates_total-1; i>=rates_total-len; i--)
     {
      arr[j][0] = j;
      arr[j][1] = close[i];
      j++;
     }

// procedure of calculating the channel parameters
   CalcLRChannel(arr,len,a,b,max);

// if the width of the channel has changed
   if(max!=save_max)
     {
      save_max=max;

      // Delete the channel
      line_md.Delete();
      line_up.Delete();
      line_dn.Delete();

      // Creating a channel with new coordinates
      line_md.Create(chart.ChartId(),"LR_Md_Line",0, time[rates_total-1],     a, time[rates_total-len], a+b*(len-1)    );
      line_up.Create(chart.ChartId(),"LR_Up_Line",0, time[rates_total-1], a+max, time[rates_total-len], a+b*(len-1)+max);
      line_dn.Create(chart.ChartId(),"LR_Dn_Line",0, time[rates_total-1], a-max, time[rates_total-len], a+b*(len-1)-max);

      // assigning the color of channel lines     
      line_up.Color(RoyalBlue);
      line_dn.Color(RoyalBlue);
      line_md.Color(RoyalBlue);

      // assigning the line width
      line_up.Width(2);
      line_dn.Width(2);
      line_md.Width(2);
     }

   return(len);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
//+------------------------------------------------------------------+
  {
// Deleting the created objects
   chart.Detach();

   delete line_dn;
   delete line_up;
   delete line_md;
   delete chart;
  }

6번 그림과 같이 파란색 회귀 채널을 생성하는 것이 인디케이터 작업의 결과입니다. 채널 구성의 정확성을 확인하기 위해 차트에는 MetaTrader 5 기술 분석 도구 중 "회귀 캐널"이 빨갛게 표시됩니다.

위 그림에서 볼 수 있듯이, 채널의 중앙 라인이 합쳐집니다. 그와중에 채널의 두께엔 서로 다른 계산 접근으로 인한 미세한 차이가 생깁니다 (몇포인트). 

 6번 그림. 회귀채널 비교

6번 그림. 회귀채널 비교

마치며

이 문서는 Delphi 개발 플랫폼에서 DLL을 쓰는 기능에 대해서 다루었습니다.

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

파일 첨부됨 |
mql5_sources.zip (276.43 KB)
dll_mql5_sources.zip (453.47 KB)
캔들스틱 패턴 분석 캔들스틱 패턴 분석
일본 캔들스틱 차트의 구성과 캔들스틱 패턴 분석은 놀라운 기술적 분석 영역을 구성합니다. 캔들스틱의 장점은 데이터 내부의 역학을 추적 할 수 있는 방식으로 데이터를 표현한다는 것입니다. 이 글에서는 캔들스틱 유형, 캔들스틱 패턴 분류를 분석하고 캔들스틱 패턴을 결정할 수 있는 인디케이터를 제시합니다.
Haiken-Ashi(평균족) 인디케이터에 기반한 매매 시스템 예시 Haiken-Ashi(평균족) 인디케이터에 기반한 매매 시스템 예시
이 문서에서 우리는 매매에서 Haiken-Ashi 인디케이터를 쓰는 법에 대해 알아보겠습니다 . 이 인디케이터를 기반으로 간단한 매매 시스템을 고안해보고 MQL5 Expert Advisor를 하나 짜보겠습니다. 매매 작업은 표준 클래스 라이브러리의 클래스들을 기반으로 구현되었습니다. 검토된 매매 전략의 과거 기력을 기반으로한 시험 결과는 내장 MetaTrader 5 전략 테스터를 이용하여 이루어졌으며, 이 문서 내에서 확인하실 수 있습니다.
여러 상품을 거래하는 Expert Advisor 생성 여러 상품을 거래하는 Expert Advisor 생성
금융 시장에서 자산 다각화의 개념은 꽤 오래된 것인데 항상 초보 트레이더를 끌어 들였습니다. 이 글에서 저자는 이러한 거래 전략 방향에 대한 초기 소개를 위해 다중 통화 Expert Advisor의 구성에 대한 최대한 간단한 접근 방식을 제안합니다.
포지션 중심적 MetaTrader5 환경에서 주문 추적을 위해 가상 주문 매니저 활용하기 포지션 중심적 MetaTrader5 환경에서 주문 추적을 위해 가상 주문 매니저 활용하기
이 클래스 라이브러리를 MetaTrader 5 Expert Advisor에 추가하면 MetaTrader 5의 포지션 기반 접근 방식이 아닌, MetaTrader 4와 거의 유사한 주문 중심의 접근 방식으로 작성할 수 있습니다. 이를 위해 MetaTrader 5 클라이언트 터미널의 가상 주문을 추적하는 동시에 재앙 보호를 위한 각 위치에 대한 보호 브로커 스톱을 유지합니다.