はじめに
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
解決策:
ANSI版関数からUnicode版関数に変更(A接尾辞からW接尾辞へ)
パラメータ型を修正(特に空のボディの初期化方法)
エラー処理の詳細化
問題2: Wine環境での互換性
Wine環境ではWindowsネイティブAPIとの互換性に問題が生じることがあります。
解決策:
空の文字列には
NULL
ではなく""
を使用パラメータを明示的に初期化
デバッグ情報を詳細に出力
まとめ
Wine環境でのMT5からHTTP通信を行うには、いくつかの特別な配慮が必要です。Unicode版のWinAPI関数を使用し、パラメータの型と初期化に注意することで、安定したHTTP通信が実現できます。
この方法を使えば、MT5のストラテジーテスターでも外部APIからデータを取得できるため、よりリアルなバックテストや、外部データを活用したEAの開発が可能になります。
特にWebRequestが使えない環境や、よりカスタマイズされたHTTP通信が必要な場合に、このWinAPIを使ったアプローチは非常に有効です。