Coffee Break Point

Wine環境のMT5でWinAPIを使ったHTTP通信を実装する方法

はじめに

MetaTrader 5(MT5)は強力なトレーディングプラットフォームですが、Linux環境で使用する場合はWineを介して実行することになります。この環境でのHTTP通信の実装は通常よりも複雑です。本記事では、Wine上のMT5でWinAPIを使って安定したHTTP通信を行う方法を解説します。

課題と解決策

MT5のストラテジーテスターでも動作するHTTP通信を実装する場合、通常のWebRequest()関数は使用できません。代わりにWinAPIを直接呼び出す方法を使いますが、Wine環境では特有の問題が発生します。

ここでは、実際に動作するコードと、遭遇した問題の解決方法を紹介します。

WinAPIのインポート

まず、必要なWinAPI関数をインポートします。Unicode版関数を使用することで、Wine環境での互換性が向上します。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 #import "wininet.dll" int InternetOpenW(string agent, int accessType, string proxy, string proxyBypass, int flags); int InternetConnectW(int hInternet, string server, int port, string user, string password, int service, int flags, int context); int HttpOpenRequestW(int hConnect, string verb, string objectName, string version, string referer, string acceptTypes, int flags, int context); bool HttpSendRequestW(int hRequest, string headers, int headersLength, char &optional[], int optionalLength); bool InternetReadFile(int hFile, uchar &buffer[], int numberOfBytesToRead, int &numberOfBytesRead); bool InternetCloseHandle(int hInternet); int GetLastError(void); #import // 定数定義 #define INTERNET_OPEN_TYPE_DIRECT 1 #define INTERNET_SERVICE_HTTP 3 #define INTERNET_DEFAULT_HTTP_PORT 80

通常のANSI版関数(InternetOpenAなど)ではなく、Unicode版関数(InternetOpenWなど)を使用することがポイントです。

HTTP通信の実装

次に、HTTPリクエストを送信して応答を受け取る関数を実装します。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 string SendHttpRequest(string method, string server, string path, string headers, char &body[]) { string response = ""; // インターネット接続を開く int hInternet = InternetOpenW("MQL5Agent", INTERNET_OPEN_TYPE_DIRECT, "", "", 0); if(hInternet == 0) { int errorCode = GetLastError(); Print("Error: InternetOpenW failed with error code ", errorCode); return ""; } // サーバーに接続 int hConnect = InternetConnectW(hInternet, server, INTERNET_DEFAULT_HTTP_PORT, "", "", INTERNET_SERVICE_HTTP, 0, 0); if(hConnect == 0) { int errorCode = GetLastError(); Print("Error: InternetConnectW failed with error code ", errorCode); InternetCloseHandle(hInternet); return ""; } // HTTPリクエストを作成 int hRequest = HttpOpenRequestW(hConnect, method, path, "HTTP/1.1", "", "", 0, 0); if(hRequest == 0) { int errorCode = GetLastError(); Print("Error: HttpOpenRequestW failed with error code ", errorCode); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); return ""; } // リクエストを送信 int bodyLength = ArraySize(body); bool success = HttpSendRequestW(hRequest, headers, StringLen(headers), body, bodyLength); if(!success) { int errorCode = GetLastError(); Print("Error: HttpSendRequestW failed with error code ", errorCode); // エラーコードの詳細 if(errorCode == 12002) Print("タイムアウトが発生しました"); else if(errorCode == 12007) Print("サーバー名の解決ができませんでした"); else if(errorCode == 12029) Print("接続に失敗しました"); else if(errorCode == 12030) Print("接続が切断されました"); else if(errorCode == 12152) Print("HTTPサーバーエラー"); InternetCloseHandle(hRequest); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); return ""; } // レスポンスを読み込み uchar buffer[4096]; int bytesRead = 0; do { success = InternetReadFile(hRequest, buffer, 4096, bytesRead); if(!success) { int errorCode = GetLastError(); Print("Error: InternetReadFile failed with error code ", errorCode); break; } if(bytesRead > 0) { string chunk = CharArrayToString(buffer, 0, bytesRead, CP_UTF8); response += chunk; } } while(bytesRead > 0); // ハンドルを閉じる InternetCloseHandle(hRequest); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); return response; }

テスト実装

実際に使用するためのシンプルなEAの例です。公開テスト用APIエンドポイント(JSONPlaceholder)にリクエストを送信します。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 int OnInit() { // JSONPlaceholderのテストエンドポイント string apiServer = "jsonplaceholder.typicode.com"; string apiPath = "/todos/1"; // シンプルなヘッダー string headers = "Accept: application/json\r\n"; // 空のボディを明示的に初期化(GETリクエスト用) char emptyBody[]; ArrayResize(emptyBody, 1); emptyBody[0] = 0; Print("OnInit: テストAPIにリクエストを送信中..."); // GETリクエストを送信 string response = SendHttpRequest("GET", apiServer, apiPath, headers, emptyBody); // レスポンスをチェック if(response == "") { Print("OnInit: データ取得に失敗しました。"); return INIT_FAILED; } Print("OnInit: 取得したデータ: ", response); // JSONレスポンスの簡易解析 if(StringLen(response) > 0) { if(StringFind(response, "\"userId\"") >= 0 && StringFind(response, "\"id\"") >= 0 && StringFind(response, "\"title\"") >= 0) { Print("OnInit: APIリクエストが成功し、正しいJSONレスポンスを受信しました。"); return INIT_SUCCEEDED; } } Print("OnInit: 予期しないレスポンス形式です。"); return INIT_FAILED; }

発生した問題と解決法

問題1: HttpSendRequestAのエラー

最初の実装では、HttpSendRequestA関数でエラーコード0が返されていました。

1 2025.04.18 10:35:43.370 2025.01.01 00:00:00 Error: HttpSendRequestA failed with error code 0, Server: jsonplaceholder.typicode.com, Path: /todos/1

解決策:

  1. ANSI版関数からUnicode版関数に変更(A接尾辞からW接尾辞へ)

  2. パラメータ型を修正(特に空のボディの初期化方法)

  3. エラー処理の詳細化

問題2: Wine環境での互換性

Wine環境ではWindowsネイティブAPIとの互換性に問題が生じることがあります。

解決策:

  1. 空の文字列にはNULLではなく""を使用

  2. パラメータを明示的に初期化

  3. デバッグ情報を詳細に出力

まとめ

Wine環境でのMT5からHTTP通信を行うには、いくつかの特別な配慮が必要です。Unicode版のWinAPI関数を使用し、パラメータの型と初期化に注意することで、安定したHTTP通信が実現できます。

この方法を使えば、MT5のストラテジーテスターでも外部APIからデータを取得できるため、よりリアルなバックテストや、外部データを活用したEAの開発が可能になります。

特にWebRequestが使えない環境や、よりカスタマイズされたHTTP通信が必要な場合に、このWinAPIを使ったアプローチは非常に有効です。


©from-garage 2022 All Rights Reserved.

powered by