인터넷을 통한 터미널 간 데이터 교환에 WinInet.dll 사용하기
MetaTrader 5는 무기고에서 여러 새로운 사용자 인터페이스 요소를 사용하여 사용자에게 고유한 기회를 제공합니다. 이로 인해 이전에는 사용할 수 없었던 기능을 이제 최대값까지 사용할 수 있습니다.
이 과정에서는 다음 사항에 대해 알아보겠습니다:
- 기본 인터넷 기술을 사용합니다;
- 서버를 통해 터미널 간에 데이터를 교환합니다;
- MQL5 환경에서 인터넷과 함께 사용할 일반 라이브러리 클래스를 생성합니다.
The MQL5의 코드 베이스(CodeBase)에는 wininet.dll 라이브러리와 함께 작동하며 서버 페이지 요청의 예를 보여주는스크립트의 예가 포함되어 있습니다. 하지만 오늘은 훨씬 더 나아가서, 서버를 만들고, 우리에게 페이지를 줄 뿐만 아니라, 다른 요청 단말기로의 후속 전송을 위해 이 데이터를 전송하고 저장할 것입니다.
참고: PHP로 구성된 서버에 액세스할 수 없는 사용자를 위해 Denwer 키트를 다운로드하여 작업 플랫폼으로 사용하는 것이 좋습니다. 또한 테스트를 위해 로컬 호스트의 Apache 서버와 PHP를 사용하는 것이 좋습니다.
서버에 요청을 보내기 위해서는 라이브러리의 7 주요 기능이 필요합니다.
InternetAttemptConnect | 인터넷 연결을 찾아 설정합니다. |
InternetOpen | WinInet 라이브러리 기능 작업에 대한 구조를 초기화합니다. 라이브러리의 다른 기능을 활성화하기 전에 이 기능을 활성화해야 합니다. |
InternetConnect | HTTP URL 또는 FTP 주소로 지정된 리소스를 엽니다. 설명자를 오픈 커넥션으로 반환합니다. |
HttpOpenRequest | 연결을 설정하기 위한 HTTP 요청에 대한 설명자를 작성합니다. |
HttpSendRequest | 작성된 설명자를 사용하여 쿼리를 보냅니다. |
InternetReadFile | 쿼리를 처리한 후 서버에서 수신한 데이터를 읽습니다. |
InternetCloseHandle | 전송된 설명자를 릴리즈합니다. |
모든 기능과 해당 매개 변수에 대한 자세한 설명은 MSDN 도움말 시스템에서 확인할 수 있습니다.
함수의 선언은 링크에 의한 유니코드 호출 및 회선 전송을 사용하는 것을 제외하고 MQL4와 동일하게 유지되었습니다.
#import "wininet.dll" int InternetAttemptConnect(int x); int InternetOpenW(string &sAgent,int lAccessType,string &sProxyName,string &sProxyBypass,int lFlags); int InternetConnectW(int hInternet,string &szServerName,int nServerPort,string &lpszUsername,string &lpszPassword,int dwService,int dwFlags,int dwContext); int HttpOpenRequestW(int hConnect,string &Verb,string &ObjectName,string &Version,string &Referer,string &AcceptTypes,uint dwFlags,int dwContext); int HttpSendRequestW(int hRequest,string &lpszHeaders,int dwHeadersLength,uchar &lpOptional[],int dwOptionalLength); int HttpQueryInfoW(int hRequest,int dwInfoLevel,int &lpvBuffer[],int &lpdwBufferLength,int &lpdwIndex); int InternetReadFile(int hFile,uchar &sBuffer[],int lNumBytesToRead,int &lNumberOfBytesRead); int InternetCloseHandle(int hInet); #import //To make it clear, we will use the constant names from wininet.h. #define OPEN_TYPE_PRECONFIG 0 // use the configuration by default #define FLAG_KEEP_CONNECTION 0x00400000 // do not terminate the connection #define FLAG_PRAGMA_NOCACHE 0x00000100 // no cashing of the page #define FLAG_RELOAD 0x80000000 // receive the page from the server when accessing it #define SERVICE_HTTP 3 // the required protocol
플래그에 대한 자세한 설명은 각 기능에 대한 동일한 섹션 MSDN에 있습니다. 다른 상수와 함수의 선언을 보려면 문서의 첨부 파일에 있는 원본 wininet.h 파일을 다운로드하면 됩니다.
1. 인터넷 세션 생성 및 삭제 안내서
먼저 세션을 생성하고 호스트에 대한 연결을 여는 것이 좋습니다. 세션은 프로그램 초기화 중에 한 번만 생성하는 것이 좋습니다 (예시: OnInit 함수에서). 또는 Expert Advisor를 시작할 때 시작할 수 있지만 세션을 닫기 전에 한 번만 성공적으로 작성했는지 확인하는 것이 중요합니다. 그리고 매번 새로운 구현 OnStart 또는 OnTimer를 반복할 때마다 불필요하게 반복 호출되어서는 안 됩니다. 잦은 호출와 각 호출에 필요한 구조의 생성을 피하는 것이 중요합니다.
따라서 세션 및 연결 설명자를 설명하는 데 하나의 글로벌 클래스 인스턴스만 사용합니다.
string Host; // host name int Port; // port int Session; // session descriptor int Connect; // connection descriptor bool MqlNet::Open(string aHost,int aPort) { if(aHost=="") { Print("-Host is not specified"); return(false); } // checking the DLL resolution in the terminal if(!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)) { Print("-DLL is not allowed"); return(false); } // if the session was identifies, then we close if(Session>0 || Connect>0) Close(); // record of attempting to open into the journal Print("+Open Inet..."); // if we were not able to check for the presence of an Internet connection, then we exit if(InternetAttemptConnect(0)!=0) { Print("-Err AttemptConnect"); return(false); } string UserAgent="Mozilla"; string nill=""; // open a session Session=InternetOpenW(UserAgent,OPEN_TYPE_PRECONFIG,nill,nill,0); // if we were not able to open a session, then exit if(Session<=0) { Print("-Err create Session"); Close(); return(false); } Connect=InternetConnectW(Session,aHost,aPort,nill,nill,SERVICE_HTTP,0,0); if(Connect<=0) { Print("-Err create Connect"); Close(); return(false); } Host=aHost; Port=aPort; // otherwise all attempts were successful return(true); }
초기화 후 설명자 세션 및 연결은 다음 모든 기능에 사용할 수 있습니다. 모든 작업이 완료되고 MQL-프로그램이 제거되면 해당 프로그램을 제거해야 합니다. 이 작업은 InternetCloseHandle 함수를 사용하여 수행됩니다.
void MqlNet::CloseInet() { Print("-Close Inet..."); if(Session>0) InternetCloseHandle(Session); Session=-1; if(Connect>0) InternetCloseHandle(Connect); Connect=-1; }
주의! 인터넷 기능을 사용할 경우, 해당 기능에서 파생된 모든 설명자는, InternetCloseHandle을 사용하여 해제해야 합니다.
2. 서버에 요청을 보내고 페이지를 수신하기
이 요청에 대한 요청을 보내고 페이지를 받으려면 나머지 세 기능이 필요합니다: HttpOpenRequest, HttpSendRequest и InternetReadFile. 요청에 대한 응답으로 페이지를 수신하는 본질은 기본적으로 로컬 파일에 내용을 저장하는 간단한 프로세스입니다.
요청과 콘텐츠 작업의 편의를 위해 두 가지 보편적인 기능을 만들 것입니다.
요청 보내기:
bool MqlNet::Request(string Verb,string Object,string &Out,bool toFile=false,string addData="",bool fromFile=false) { if(toFile && Out=="") { Print("-File is not specified "); return(false); } uchar data[]; int hRequest,hSend,h; string Vers="HTTP/1.1"; string nill=""; if(fromFile) { if(FileToArray(addData,data)<0) { Print("-Err reading file "+addData); return(false); } } // read file in the array else StringToCharArray(addData,data); if(Session<=0 || Connect<=0) { Close(); if(!Open(Host,Port)) { Print("-Err Connect"); Close(); return(false); } } // create a request descriptor hRequest=HttpOpenRequestW(Connect,Verb,Object,Vers,nill,nill,FLAG_KEEP_CONNECTION|FLAG_RELOAD|FLAG_PRAGMA_NOCACHE,0); if(hRequest<=0) { Print("-Err OpenRequest"); InternetCloseHandle(Connect); return(false); } // send request // headline for request string head="Content-Type: application/x-www-form-urlencoded"; // sent file hSend=HttpSendRequestW(hRequest,head,StringLen(head),data,ArraySize(data)-1); if(hSend<=0) { Print("-Err SendRequest"); InternetCloseHandle(hRequest); Close(); } // read the page ReadPage(hRequest,Out,toFile); // close all handles InternetCloseHandle(hRequest); InternetCloseHandle(hSend); return(true); }
MqlNet:: Request: 함수의 매개변수
- string Verb – 요청 유형 “GET” 또는 “POST”;
- string Object – 전달된 매개 변수와 함께 페이지의 이름;
- string &Out – 답을 받을 수 있는 라인;
- bool toFile – toFile=true 인 경우, Out은 응답을 수신해야 하는 파일 이름을 나타냅니다;
- string addData - 추가 데이터;
- bool fromFile - fromFile = true 인 경우, addData는 전송해야 하는 파일의 이름입니다.
수신된 설명자의 내용을 읽기
void MqlNet::ReadPage(int hRequest,string &Out,bool toFile) { // read the page uchar ch[100]; string toStr=""; int dwBytes,h; while(InternetReadFile(hRequest,ch,100,dwBytes)) { if(dwBytes<=0) break; toStr=toStr+CharArrayToString(ch,0,dwBytes); } if(toFile) { h=FileOpen(Out,FILE_BIN|FILE_WRITE); FileWriteString(h,toStr); FileClose(h); } else Out=toStr; }
MqlNet:: ReadPage: 함수의 매개변수
- Int hRequest - 데이터를 읽을 수 있는 설명자를 요청합니다;
- string &Out – 답을 받을 수 있는 라인;
- bool toFile - toFile = true 인 경우, Out 은 답변을 받을 파일의 이름입니다.
그리고 이 모든 것을 하나로 모아 인터넷 작업을 위한 MqlNet 라이브러리 클래스를 확보할 것입니다.
class MqlNet { string Host; // host name int Port; // port int Session; // session descriptor int Connect; // connection descriptor public: MqlNet(); // class constructor ~MqlNet(); // destructor bool Open(string aHost,int aPort); // create a session and open a connection void Close(); // close session and connection bool Request(string Verb,string Request,string &Out,bool toFile=false,string addData="",bool fromFile=false); // send request bool OpenURL(string URL,string &Out,bool toFile); // somply read the page into the file or the variable void ReadPage(int hRequest,string &Out,bool toFile); // read the page int FileToArray(string FileName,uchar &data[]); // copy the file into the array for sending };
이는 기본적으로 인터넷 작업에 필요한 다양한 요구를 충족할 수 있는 모든 기능입니다. 사용 예를 고려해 보십시오.
예시 1. 터미널 폴더에 MQL 프로그램의 자동 다운로드 MetaGrabber script
클래스 작업에 대한 테스트를 시작하려면 페이지를 읽고 지정된 폴더에 내용을 저장하는 가장 쉬운 작업부터 시작하겠습니다. 그러나 단순히 페이지를 읽는 것은 그다지 흥미로울 것 같지는 않으므로, 스크립트 작업을 통해 얻을 수 있는 것을 얻기 위해 사이트에서 mql 프로그램을 가져오는 기능을 할당해 보겠습니다. MetaGrabber 스크립트의 작업은 다음과 같습니다:
- URL 분석 및 호스트, 요청 및 파일 이름으로 구분합니다;
- 호스트에 요청을 보내고 파일을 수신하여 터미널 폴더 \\ Files 에 저장합니다;
- 파일을 필수 데이터 폴더 중 하나로 이동합니다:
\Experts, \Indicators, \Scripts, \Include, \Libraries, \Tester(set), \Templates.
두 번째 문제를 해결하기 위해 MqlNet 클래스를 사용합니다. 세 번째 작업에서는 Kernel32.dll 에서 함수 MoveFileEx를 사용합니다.
#import "Kernel32.dll" bool MoveFileExW(string &lpExistingFileName, string &lpNewFileName, int dwFlags); #import "Kernel32.dll"
첫 번째 문제에서, URL 행을 구문 분석하는 작은 서비스 기능을 만들어 보겠습니다.
주소에서 호스트, 사이트 경로, 파일 이름의 세 줄을 할당해야 합니다.
예를 들어, 라인 http://www.mysite.com/folder/page.html 에서,
- Host = www.mysite.com
- Request = / folder / page.html
- File name = page.html
MQL5 사이트의 CodeBase의 경우 경로의 구조가 동일합니다. 예를 들어, 페이지 https://www.mql5.com/ru/code/79 의 라이브러리 ErrorDescription.mq5에 대한 경로는 http://p.mql5.com/data/18/79/ErrorDescription.mqh 과 같습니다. 이 경로는 링크에서 마우스 오른쪽 단추를 클릭하고 "링크 복사"를 선택하면 쉽게 얻을 수 있습니다. 따라서 URL은 파일 저장 편의를 위해 요청용과 파일 이름의 두 부분으로 나뉩니다.
- Host = p.mql5.com
- Request = / data/18/79/5/ErrorDescription.mqh
- File name = ErrorDescription.mqh
이것은 다음 ParseURL 함수가 처리할 줄 구문 분석입니다.
void ParseURL(string path,string &host,string &request,string &filename) { host=StringSubstr(URL,7); // removed int i=StringFind(host,"/"); request=StringSubstr(host,i); host=StringSubstr(host,0,i); string file=""; for(i=StringLen(URL)-1; i>=0; i--) if(StringSubstr(URL,i,1)=="/") { file=StringSubstr(URL,i+1); break; } if(file!="") filename=file; }
스크립트의 외부 매개 변수에는 URL(mql5 파일의 경로)과 이후 배치의 폴더 유형, 즉 배치할 터미널 폴더에 두 개의 매개 변수만 만듭니다.
그 결과, 우리는 짧지만 매우 유용한 스크립트를 얻게 됩니다.
//+------------------------------------------------------------------+ //| MetaGrabber.mq5 | //| Copyright © 2010 www.fxmaster.de | //| Coding by Sergeev Alexey | //+------------------------------------------------------------------+ #property copyright "www.fxmaster.de © 2010" #property link "www.fxmaster.de" #property version "1.00" #property description "Download files from internet" #property script_show_inputs #include <InternetLib.mqh> #import "Kernel32.dll" bool MoveFileExW(string &lpExistingFileName,string &lpNewFileName,int dwFlags); #import #define MOVEFILE_REPLACE_EXISTING 0x1 enum _FolderType { Experts=0, Indicators=1, Scripts=2, Include=3, Libraries=4, Files=5, Templates=6, TesterSet=7 }; input string URL=""; input _FolderType FolderType=0; //------------------------------------------------------------------ OnStart int OnStart() { MqlNet INet; // variable for working in the Internet string Host,Request,FileName="Recieve_"+TimeToString(TimeCurrent())+".mq5"; // parse url ParseURL(URL,Host,Request,FileName); // open session if(!INet.Open(Host,80)) return(0); Print("+Copy "+FileName+" from http://"+Host+" to "+GetFolder(FolderType)); // obtained file if(!INet.Request("GET",Request,FileName,true)) { Print("-Err download "+URL); return(0); } Print("+Ok download "+FileName); // move to the target folder string to,from,dir; // if there is no need to move it elsewhere if(FolderType==Files) return(0); // from from=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\"+FileName; // to to=TerminalInfoString(TERMINAL_DATA_PATH)+"\\"; if(FolderType!=Templates && FolderType!=TesterSet) to+="MQL5\\"; to+=GetFolder(FolderType)+"\\"+FileName; // move file if(!MoveFileExW(from,to,MOVEFILE_REPLACE_EXISTING)) { Print("-Err move to "+to); return(0); } Print("+Ok move "+FileName+" to "+GetFolder(FolderType)); return(0); } //------------------------------------------------------------------ GetFolder string GetFolder(_FolderType foldertype) { if(foldertype==Experts) return("Experts"); if(foldertype==Indicators) return("Indicators"); if(foldertype==Scripts) return("Scripts"); if(foldertype==Include) return("Include"); if(foldertype==Libraries) return("Libraries"); if(foldertype==Files) return("Files"); if(foldertype==Templates) return("Profiles\\Templates"); if(foldertype==TesterSet) return("Tester"); return(""); } //------------------------------------------------------------------ ParseURL void ParseURL(string path,string &host,string &request,string &filename) { host=StringSubstr(URL,7); // removed int i=StringFind(host,"/"); request=StringSubstr(host,i); host=StringSubstr(host,0,i); string file=""; for(i=StringLen(URL)-1; i>=0; i--) if(StringSubstr(URL,i,1)=="/") { file=StringSubstr(URL,i+1); break; } if(file!="") filename=file; } //+------------------------------------------------------------------+
우리가 좋아하는 섹션, https://www.mql5.com/ko/code에서 실험을 진행하겠습니다. 다운로드한 파일은 편집기의 네비게이터에 즉시 나타나며 터미널이나 편집기를 재시작하지 않고도 컴파일할 수 있습니다. 또한 파일을 이동할 폴더를 검색하기 위해 파일 시스템의 긴 경로를 돌아다니지 않습니다.
주의! 많은 사이트에서 대량 컨텐츠 다운로드에 대한 보안을 설정하며, 이러한 대량 다운로드의 경우 사용자의 IP 주소가 이 리소스에 의해 차단될 수 있습니다. 따라서 자주 액세스하고 사용을 금지하지 않으려는 리소스에서 파일을 다운로드하는 "기계" 사용에 주의하십시오.
제안된 서비스를 개선하여 한 단계 더 나아가고자 하는 사용자는 클립보드의 내용을 낚아채고 더 나아가 자동 다운로드를 수행하는 클립보드 스크립트를 사용할 수 있습니다.
예시 2. 단일 차트에서 여러 브로커의 견적 모니터링
그래서 우리는 인터넷에서 파일을 얻는 법을 배웠습니다. N이제 이 데이터를 서버에 보내고 저장하는 방법이라는 좀 더 흥미로운 질문을 고려해 보겠습니다. 이를 위해 서버에 위치할 작은 추가 PHP 스크립트가 필요합니다. 작성된 MqlNet 클래스를 사용하여 모니터링용 Expert Advisor를 만듭니다 - MetaArbitrage . PHP 스크립트와 함께 전문가(EA)의 작업은 다음과 같습니다:
- 서버에 Expert Advisor 요청을 보내기;
- 서버의 응답 페이지(PHP)를 구성합니다;
- Expert Advisor가 이 페이지를 수신합니다;
- 분석 및 결과를 화면에 전달합니다.
MQL-모듈과 PHP-스크립트 간의 상호 작용에 대한 도식 다이어그램은 다음과 같습니다:
우리는 이러한 작업을 해결하기 위해 MqlNet 클래스를 사용할 것입니다.
데이터의 중복을 방지하고 오래된 견적을 제거하기 위해 브로커의 서버 이름(현재 가격의 출처), 통화, UTC에서의 견적의 가격 및 시간 등 4가지 주요 매개 변수를 전송합니다. 예를 들어, 우리 회사의 리소스에서 스크립트에 액세스하기 위한 요청은 다음과 같습니다:
www.fxmaster.de/metaarbitr.php?server=Metaquotes&pair=EURUSD&bid=1.4512&time=13286794
이러한 매개 변수와 실제 견적은 서버에 저장되며 이 통화의 다른 모든 저장된 견적과 함께 응답 페이지에서 발행됩니다.
이 교환의 "부차적" 편의상 견적은 MT5뿐만 아니라 MT4에서도 보낼 수 있습니다!
서버에 의해 형성된 페이지는 일반 CSV 파일입니다. 이 스크립트는 다음과 같습니다:
ServerName1; Bid1; Time1
ServerName 2; Bid2; Time2
ServerName 3; Bid3; Time3
…
ServerName N; BidN; TimeN
그러나 추가 매개변수(예: 서버 유형 - 데모 또는 실제)를 추가할 수 있습니다. 우리는 이 CSV 파일을 저장하고 라인별로 분석하며, 화면에 표시된 가치 표와 라인 가격표를 출력합니다.
이 파일은 여러 가지 방법으로 처리할 수 있으며, 각 경우에 필요한 파일 하나를 선택할 수 있습니다. 예를 들어, MetaTrader 4 데모 서버에서 받은 견적 등을 필터링합니다.
인터넷 서버의 장점은 명백합니다. 즉, 견적을 보내므로 다른 거래자가 보고 받을 수 있습니다. 마찬가지로, 다른 거래자에게 발송되는 견적을 받게 됩니다. 즉, 터미널 간의 상호 작용이 쌍방이며, 다음과 같은 방식으로 데이터 교환이 이루어집니다:
이 형식은 터미널 간 정보 교환의 원칙에 대한 기초가 됩니다. 전체 MetaArbitrage Expert Advisor 및 설명이 포함된 PHP 스크립트는 첨부 파일의 링크에서 다운로드할 수 있습니다. PHP 사용 함수에 대한 자세한 내용은 다음 사이트 php.su에서 확인할 수 있습니다.
예시 3. 터미널 내에서 메시지를 교환합니다(미니 채팅). MetaChat Expert Advisor
거래와 번호에서 벗어나 터미널을 종료하지 않고 여러 사람과 한 번에 채팅할 수 있는 애플리케이션을 생성해 보겠습니다. 이를 위해서는 일반적으로 이전과 매우 유사한 PHP 스크립트가 하나 더 필요합니다. 이 스크립트에서는 시간 견적을 분석하는 대신 파일의 줄 수를 분석할 것입니다. Expert Advisor의 과제는 다음과 같습니다:
- 서버로 텍스트 줄을 보내기;
- 공유 파일에 이 줄을 추가하여 파일 크기를 제어하고 응답 파일(php)을 발행합니다;
- 현재 채팅을 수신하여 화면에 표시합니다.
MetaChat의 작업은 이전 Expert Advisor의 작업과 다르지 않습니다. 동일한 원리와 출력에 대한 동일한 단순 CSV 파일입니다.
MetaChat 및 MetaArbitrage는 개발자의 사이트에 유지됩니다. 작업의 PHP 스크립트도 여기에 있습니다.
따라서 작업을 테스트하거나 이 서비스를 사용하려면 다음 링크를 통해 액세스할 수 있습니다:
MetaСhat - www.fxmaster.de/metachat.php
MetaArbitrage - www.fxmaster.de/metaarbitr.php
결론
그래서 우리는 HTTP 요청을 익혔습니다. 우리는 인터넷을 통해 데이터를 주고받을 수 있고, 작업 과정을 보다 편안하게 정리할 수 있게 되었습니다. 하지만 어떤 능력도 항상 향상될 수 있습니다. 다음은 개선의 새로운 잠재적 방향을 고려할 수 있습니다:
- Expert Advisors의 분석을 위해 뉴스를 읽거나 터미널에 기타 정보를 직접 수신할 수 있습니다;
- Expert Advisors의 원격 관리;
- Expert Advisor/지표의 자동 업데이트;
- 거래의 복사기/번역기, 신호 전송;
- Expert Advisor용 라이트 및 세트 파일과 함께 템플릿 다운로드:
- 그리고 훨씬 더 많이 있습니다 ...
이 기사에서는 GET 유형의 요청을 사용했습니다. 서버 분석을 위해 파일을 얻거나 적은 수의 매개변수로 요청을 보내야 할 때 작업을 충분히 수행합니다.
다음 과정에서는 POST 요청, 즉 서버로 파일 전송 또는 터미널 간 파일 공유에 대해 자세히 살펴보고, 사용 사례를 살펴보겠습니다.
유용한 리소스
- Apache 서버 + PHP 설치를 위한 데번 세트(Denver Set) http://www.denwer.ru/
- 보낸 헤드라인 보기를 위한 프록시 http://www.charlesproxy.com/
- 헤드라인 요청 유형 http://www.codenet.ru/webmast/php/HTTP-POST.php#part_3_2
- Типы запросов http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type
- 요청 유형 ftp://ftp.isi.edu/in-notes/iana/assignments/media-types/media-types
- WinHTTP 상세설명 http://msdn.microsoft.com/en-us/library/aa385331%28VS.85%29.aspx
- HTTP 세션 상세설명 http://msdn.microsoft.com/en-us/library/aa384322%28VS.85%29.aspx
- HINTERNET 할당량 구조 http://msdn.microsoft.com/en-us/library/aa383766%28VS.85%29.aspx
- 파일 작업 http://msdn.microsoft.com/en-us/library/aa364232%28VS.85%29.aspx
- MQL으로 전송을 위한 데이터 유형 http://msdn.microsoft.com/en-us/library/aa383751%28VS.85%29.aspx
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/73