インターネットを介して端末間でデータ交換をするためのWinInet.dll利用
蓄積されている数多くの新しいユーザーインターフェース要素を用いて、MetaTrader 5 はユーザーにまたとないチャンスの扉を開きます。これにより以前には利用できなかった関数がいまや最大限に活用できるのです。
このレッスンで学習することは以下です。
- 基本的なインターネット技術を使用する。
- サーバーを介して端末間でデータ交換を行う。
- MQL5 環境でインターネットと連携するための遺伝的ライブラリ クラスを作成する。
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); }
初期化後、ディスクリプタ Session および Connect は続く関数すべてで使用可能です。作業がすべて完了し、MQL プログラムがアンインストールされたら、それらは消去する必要があります。これは InternetCloseHandle 関数を用いて行います。
void MqlNet::CloseInet() { Print("-Close Inet..."); if(Session>0) InternetCloseHandle(Session); Session=-1; if(Connect>0) InternetCloseHandle(Connect); Connect=-1; }
注意! インターネット関数に連動する際は、InternetCloseHandle を用いてそれらによって駆動されるディスクリプタをすべて解放する必要があります。
2. サーバーに対するリクエスト送信およびページ受け取り
リクエストを送信し、そのリクエストに対する回答を受け取るには関数があと3つ必要です。それらは、HttpOpenRequest、 HttpSendRequest 、InternetReadFileです。リクエストに対する回答ページを受け取る最重要点は基本的にそのコンテンツをローカルファイルに保存するシンプルな手続きです。
リクエストとコンテンツの作業がしやすいように汎用関数を2つ作成します。
リクエスト送信
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 スクリプト
クラス動作検証を始めるにあたりもっとも簡単なタスクから始めます。それはページを読み、そのコンテンツを指定フォルダーに保存することです。ただしページをただ単に読むことはあまり面白いものではなさそうです。よってスクリプトの動作から何かを得るために、そこにサイトからの 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 行をパースする短いサービス関数を作成します。
アドレスから3行を割り当てる必要があります。それはホスト、サイトへのパス、ファイル名です。
たとえば行 http://www.mysite.com/folder/page.html では以下のように行います。
- ホスト = www.mysite.com
- リクエスト = / folder / page.html
- ファイル名 = page.html
MQL5 サイトの CodeBase の場合は、パスウェイ は同じストラクチャをしています。たとえばページ https://www.mql5.com/ru/code/79 looks like http://p.mql5.com/data/18/79/ErrorDescription.mqh のライブラリ ErrorDescription.mq5 へのパスです。このパスはリンクを右クリックし『リンクをコピーする』を選択するだけで簡単に取得されます。よってURL は2つに分割されます。一つはリクエスト用、もう一つはファイル格納をしやすくするファイル名用です。
- ホスト = p.mql5.com
- リクエスト = / data/18/79/5/ErrorDescription.mqh
- ファイル名 = 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; }
スクリプトの外部パラメータではパラメータを2つだけ作成します。 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/en/code について実験をします。ダウンロードされたファイルはすぐにエディタのナビゲータに表示されます。それらは端末またはエディタを再起動しなくてもコンパイル可能です。ファイルの移動先として望ましいフォルダを検索するファイルシステムの長いパスをさまようことはありません。
注意!数多くのサイトは大量のコンテントをダウンロードすることに対してセキュリティを設定しています。そのような大量ダウンロードの場合は、みなさんの IPアドレスがこのリソースによりブロックされる可能性があります。よって頻繁に利用し使用を禁じされたくないリソースからファイルの『マシン』ダウンロードを利用する際は最新の注意を払ってください。
提案のサービスを改良してさらに一歩踏み込みたい方は、クリップボードコンテンツのインターセプションとそれ以上の自動ダウンロードを持つ Clipboard スクリプトの利用が可能です。
例2 単一チャート上で複数仲介会社からのクオート監視
というわけでインターネットからファイルを取得する方法は学習しました。そこでもっと興味深い課題を考察します。サーバー上でのこのデータの送信および格納方法です。これを行うには、短い PHPスクリプトが必要です。それをサーバーにセットします。すでに書かれた MqlNet クラスを利用して監視 MetaArbitrage 用 Expert Advisor を作成します。PHPスクリプトと連動したエキスパートのタスクは次のようなものです。
- サーバーに Expert Advisor のリクエストを送信
- サーバー上に解答ページ(PHP)を作成
- Expert Advisorによるこのページの受け取り
- その分析および画面への結果配信
MQLモジュールと PHPスクリプト間の連携についてのスキーム図を以下に示します。
これら課題を解決するために MqlNet クラスを使います。
データの重複を避けるため、また古いクオートを排除するため、おもなパラメータを4つ送信します。仲介会社のサーバー名(現在価格のソース)、通貨、価格、UTCにおけるクオート時刻です。たとえば、弊社のリソースからのスクリプトへのアクセス リクエストは以下のようなものです。q
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 Advisors またはインディケータの自動更新
- トレードのコピー機能、解釈機能、シグナル送信
- Expert Advisors 用 lights および set-files とともにテンプレートのダウンロード
- などなどです。
本稿ではリクエストの GET タイプを使用しました。サーバー分析のために少数のパラメータを伴ってファイル取得、リクエスト送信が必要なとき、それは十分にタスクを達成します。
次のレッスンでは POST リクエストを細かく見ていきます。それはサーバーへのファイル送信または端末間でのファイル共有を行うものです。そしてその利用例を考察します。
有用な情報リソース
- Denver Set for installing the Apache server + PHP http://www.denwer.ru/
- A proxy for viewing sent headlines http://www.charlesproxy.com/
- Types of headline requests 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
- Request types ftp://ftp.isi.edu/in-notes/iana/assignments/media-types/media-types
- Description of WinHTTP http://msdn.microsoft.com/en-us/library/aa385331%28VS.85%29.aspx
- Description of HTTP Session http://msdn.microsoft.com/en-us/library/aa384322%28VS.85%29.aspx
- Usage structure of HINTERNET http://msdn.microsoft.com/en-us/library/aa383766%28VS.85%29.aspx
- Working with files http://msdn.microsoft.com/en-us/library/aa364232%28VS.85%29.aspx
- Types of data for transfers to MQL http://msdn.microsoft.com/en-us/library/aa383751%28VS.85%29.aspx
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/73
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索