工作中需要將 MT4 的數據讀取並且存儲到 Redis 數據庫中去,同時 MT4 讀取 Redis 當中的數據用於下單的切換賬戶。 MT4 支持使用 MQL 進行開發,通過調用標準的系統 DLL 實現系統調用,因此技術實現並不是太難,只需要按 MQL 的接口要求編寫相應的 CPP 代碼 編譯成 DLL 即可實現所需要的功能。
在 Windows 平台上,連接 Redis 的客戶端選用 Hiredis 作為連接客戶端 lib。
MT4 連接 Redis 頭文件定義, MT4RedisPlugin.h
#define MT4_REDIS_ERR -1 /* 錯誤 */
#define MT4_REDIS_OK 0 /* 正常 */
#define MT4_REDIS_ERR_IO 1 /* 讀取或寫入錯誤 */
#define MT4_REDIS_ERR_EOF 3 /* 文件結尾 */
#define MT4_REDIS_ERR_PROTOCOL 4 /* 協議錯誤 */
#define MT4_REDIS_ERR_OTHER 2 /* 其他錯誤 */
#define MT4_REDIS_CMD_SUCCESSED 0 /* 成功 */
#define MT4_REDIS_CMD_FAILED -1 /* 失敗 */
//---
#define MT4_EXPFUNC __declspec(dllexport)
//+------------------------------------------------------------------+
//| 測試 Redis 是否正常工作 |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall RedisTest(const char* server, const int port);
//+------------------------------------------------------------------+
//| 執行 Redis 命令 "GET key" |
//+------------------------------------------------------------------+
MT4_EXPFUNC wchar_t* __stdcall RedisGet(const wchar_t* key);
//+------------------------------------------------------------------+
//| 執行 Redis 命令 "GET key" |
//+------------------------------------------------------------------+
MT4_EXPFUNC wchar_t* __stdcall RedisGetWithTimeout(const wchar_t* key,int timeout);
//+------------------------------------------------------------------+
//| 執行 Redis 命令 "SET key value" |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall RedisSet(const wchar_t* key, const wchar_t* value);
//+------------------------------------------------------------------+
//| 執行 Redis 命令 "SET key value" |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall RedisSetWithTimeout(const wchar_t* key, const wchar_t* value,int timeout);
//+------------------------------------------------------------------+
//| 執行 Redis 命令 |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall RedisCommand(const wchar_t* command);
//+------------------------------------------------------------------+
//| 執行 Redis 命令 |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall RedisCommandWithTimeout(const wchar_t* command,int timeout);
//+------------------------------------------------------------------+
//| char 轉換為 WCHAR 、wchar_t、LPWSTR 等 |
//+------------------------------------------------------------------+
static wchar_t * CStr2WStr(const char *cStr);
//+------------------------------------------------------------------+
//| WCHAR 、wchar_t、LPWSTR 轉換為 char |
//+------------------------------------------------------------------+
static char* WStr2CStr(const wchar_t *wchar);
//+------------------------------------------------------------------+
//| 顯示消息框 |
//+------------------------------------------------------------------+
static void MT4RedisMsgBox(const wchar_t* msg);
使用 Hiredis 讀取 Redis 數據庫
//+------------------------------------------------------------------+
//| 執行 Redis 命令 "GET key" |
//+------------------------------------------------------------------+
MT4_EXPFUNC wchar_t* __stdcall RedisGetWithTimeout(const wchar_t* key,int timeout)
{
// 構造 windows socket 對象
//WSADATA wsaData;
//WSAStartup(MAKEWORD(2,1), &wsaData);
// 設置超時
struct timeval tv;
tv.tv\_sec = timeout/1000;
tv.tv\_usec = timeout \*1000;
redisContext\* context = redisConnectWithTimeout(REDIS\_SERVER,REDIS\_PORT,tv);
//判斷連接是否有錯誤
if (context->err) {
printf("無法連接redis服務器\[%s:%d\]\\n",REDIS\_SERVER,REDIS\_PORT);
redisFree(context);
return NULL;
}
//構造Redis命令
//獲取size
size\_t len = wcslen(key) + 20;
//命令長度
if (len > COMMAND\_BUFFER) {
printf("命令過長,命令字符串必須短於 %d",COMMAND\_BUFFER);
return NULL;
}
// 構建命令
wchar\_t command\[COMMAND\_BUFFER\];
//構造Redis命令
wsprintf(command,L"GET %s",key);
//字符串轉換
const char\* command1 = WStr2CStr(command);
//執行Redis命令
printf("準備執行命令\[%s\]\\n", command1);
// 執行命令
redisReply\* reply = (redisReply\*)redisCommand(context, command1);
//沒有Redis響應
if( NULL == reply)
{
printf("執行命令失敗\[%s\]\\n",command1);
redisFree(context);
return NULL;
}
//響應狀態
if ( reply->type != REDIS\_REPLY\_STRING)
{
printf("執行命令失敗\[%s\]\\n",command1);
freeReplyObject(reply);
redisFree(context);
return NULL;
}
printf("鍵 \\"%s\\" 的值是 \\"%s\\"\\n", key, reply->str);
//字符串轉換
wchar\_t \* result = CStr2WStr(reply->str);
//釋放Reply對象
freeReplyObject(reply);
//釋放Redis連接
redisFree(context);
printf("成功執行命令\[%s\]\\n", command1);
delete command1;
command1 = NULL;
//返回結果字符串
return result;
}
使用 Hiredis 寫入 Redis 數據庫
//+------------------------------------------------------------------+
//| 執行 Redis 命令 "SET key value" |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall RedisSetWithTimeout(const wchar_t* key, const wchar_t* value, int timeout)
{
// 構造 Redis 命令
// 獲取 size
size_t len = wcslen(key)+wcslen(value) + 20;
// 命令長度
if (len > COMMAND_BUFFER) {
printf ("命令過長,命令字符串必須短於 % d",COMMAND_BUFFER);
return MT4_REDIS_CMD_FAILED;
}
// 構建命令
wchar_t command[COMMAND_BUFFER];
// 構造 Redis 命令
wsprintf(command,L"SET %s %s",key,value);
return RedisCommandWithTimeout(command,timeout);
}
使用 Hiredis 執行 Redis 命令
//+------------------------------------------------------------------+
//| 執行 Redis 命令 |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall RedisCommandWithTimeout(const wchar_t* command,int timeout)
{
// 構造 windows socket 對象
//WSADATA wsaData;
//WSAStartup(MAKEWORD(2,1), &wsaData);
// 設置超時
struct timeval tv;
tv.tv\_sec = timeout/1000;
tv.tv\_usec = timeout \*1000;
redisContext\* context = redisConnectWithTimeout(REDIS\_SERVER,REDIS\_PORT,tv);
//判斷連接是否有錯誤
if (context->err) {
printf("無法連接redis服務器\[%s:%d\]\\n",REDIS\_SERVER,REDIS\_PORT);
redisFree(context);
return MT4\_REDIS\_CMD\_FAILED;
}
// 命令長度
size\_t len = wcslen(command) + 1;
if (len > COMMAND\_BUFFER) {
printf("命令過長,命令字符串必須短於 %d",COMMAND\_BUFFER);
redisFree(context);
return MT4\_REDIS\_CMD\_FAILED;
}
//字符串轉換
const char\* command1 = WStr2CStr(command);
//執行Redis命令
printf("準備執行命令\[%s\]\\n", command1);
// 執行命令
redisReply\* reply = (redisReply\*)redisCommand(context, command1);
//沒有Redis響應
if( NULL == reply)
{
printf("執行命令失敗\[%s\]\\n",command1);
redisFree(context);
return MT4\_REDIS\_CMD\_FAILED;
}
//響應狀態
if( !(reply->type == REDIS\_REPLY\_STATUS && \_stricmp(reply->str,"OK")==0))
{
printf("執行命令失敗\[%s\]\\n",command1);
freeReplyObject(reply);
redisFree(context);
return MT4\_REDIS\_CMD\_FAILED;
}
//釋放Reply對象
freeReplyObject(reply);
//釋放Redis連接
redisFree(context);
printf("成功執行命令\[%s\]\\n", command1);
delete command1;
command1 = NULL;
//返回成功狀態
return MT4\_REDIS\_CMD\_SUCCESSED;
}
測試 MQL 代碼
//+------------------------------------------------------------------+
//| DLLSampleTester.mq4 |
//| Copyright ゥ 2005-2014, MetaQuotes Software Corp. |
//| http://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright ゥ 2005-2014, MetaQuotes Software Corp."
#property link "http://www.metaquotes.net/"
#import "MT4RedisPlugin.dll"
string HelloWorld(string);
string RedisGet(string);
string RedisGetWithTimeout(string,int);
int RedisSet(string,string);
int RedisSetWithTimeout(string, string, int);
int RedisCommand(string);
int RedisCommandWithTimeout(string,int);
int RedisTest(string,int);
#import
#define TIME_INDEX 0
#define OPEN_INDEX 1
#define LOW_INDEX 2
#define HIGH_INDEX 3
#define CLOSE_INDEX 4
#define VOLUME_INDEX 5
//+------------------------------------------------------------------+
//| 專家初始化函數 |
//+------------------------------------------------------------------+
int init()
{
Print("GO GO GO");
string hello;
hello = HelloWorld("1234abc");
Print ("結果是", hello);
string test;
test = RedisTest("127.0.0.1",6379);
Print ("測試結果是", test);
int cmd1;
cmd1 = RedisCommand("set KEYabc VALUE123");
Print ("RedisCommand 測試 =", cmd1);
int cmd2;
cmd2 = RedisCommandWithTimeout("set KEYabc VALUE123",10000);
Print ("RedisCommandWithTimeout 測試 =", cmd2);
string key1 = "key1";
string value1 = "value1";
int set1 = RedisSet(key1,value1);
Print ("RedisSet 測試 =", set1);
string key2 = "key2";
string value2 = "value2";
int timeout = 10000;
int set2 = RedisSetWithTimeout(key2,value2,timeout);
Print ("RedisSetWithTimeout 測試 =", set2);
string get1 = RedisGet(key1);
Print ("RedisGet 測試 =", get1);
string get2 = RedisGetWithTimeout(key2,timeout);
Print ("RedisGetWithTimeout 測試 =", get2);
string key3 = "key3";
string get3 = RedisGet(key3);
Print ("RedisGet 測試 =", get3);
string key4 = "key4";
string get4 = RedisGet(key4);
Print ("RedisGet 測試 =", get4);
//---
return(0);
}
//+------------------------------------------------------------------+
//| 陣列函數調用 |
//+------------------------------------------------------------------+
int start()
{
return(0);
}
//+------------------------------------------------------------------+