11 Commits

Author SHA1 Message Date
Ding Li 61861aa68e Documentation update for release 1.4 2017-08-18 10:28:43 +08:00
Ding Li acd272031a Add mql4-lib dependencies directly; change header comments on license; fix ZmqMsg setData method bug 2017-08-18 10:13:15 +08:00
Ding Li e51eceb263 Change licence to Apache 2.0 2017-08-18 10:03:00 +08:00
Ding Li ca488ea853 Fix #6 make vc runtime requirement stand out 2017-08-16 14:06:42 +08:00
Ding Li 4adca2d960 Add notes on using this library on MetaTrader5 32 bit version 2017-08-13 10:36:00 +08:00
Ding Li 01afabc408 Refactored poll support. Add Chapter 2 examples from the official ZMQ guide. 2017-07-18 16:40:22 +08:00
Ding Li 4645013bb2 Fix GlobalHandle bug. Add rebuild method to ZmqMsg. 2017-06-08 18:35:32 +08:00
Ding Li a2c610956c Fix #2 caused by a StringToCharArray bug in MQL 2017-05-27 10:22:18 +08:00
Ding Li 3a231f12b8 Fix send recv method return value check bug 2017-05-26 14:09:57 +08:00
Ding Li b4c30887c9 Fix more format 2017-05-26 13:21:56 +08:00
Ding Li 1720e3c559 Fix format 2017-05-26 13:18:09 +08:00
23 changed files with 1347 additions and 333 deletions
+242
View File
@@ -0,0 +1,242 @@
//+------------------------------------------------------------------+
//| Module: Lang/GlobalVariable.mqh |
//| This file is part of the mql4-lib project: |
//| https://github.com/dingmaotu/mql4-lib |
//| |
//| Copyright 2017 Li Ding <dingmaotu@126.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
//+------------------------------------------------------------------+
//| Encapsulates global variable functions |
//+------------------------------------------------------------------+
class GlobalVariable
{
public:
static int total() {return GlobalVariablesTotal();}
static string name(int index) {return GlobalVariableName(index);}
static void flush() {GlobalVariablesFlush();}
static bool exists(string name) {return GlobalVariableCheck(name);}
static datetime lastAccess(string name) {return GlobalVariableTime(name);}
static bool makeTemp(string name) {return GlobalVariableTemp(name);}
static double get(string name) {return GlobalVariableGet(name);}
static bool get(string name,double &value) {return GlobalVariableGet(name,value);}
static datetime set(string name,double value) {return GlobalVariableSet(name,value);}
static bool setOn(string name,double value,double check) {return GlobalVariableSetOnCondition(name,value,check);}
static bool remove(string name) {return GlobalVariableDel(name);}
static bool removeAll(string prefix=NULL,datetime before=0) {return GlobalVariablesDeleteAll(prefix,before);}
};
//+------------------------------------------------------------------+
//| TempVar is a variable whose life time is the same as the program |
//+------------------------------------------------------------------+
class TempVar
{
private:
string m_name;
bool m_owned;
public:
TempVar(string name,bool create=false):m_name(name),m_owned(create)
{
if(create)
{
GlobalVariable::makeTemp(name);
}
}
~TempVar() {if(m_owned && isValid()) {GlobalVariable::remove(m_name);}}
bool isValid() const {return GlobalVariable::exists(m_name);}
string getName() const {return m_name;}
bool set(double value) {return GlobalVariable::set(m_name,value);}
double get() const {return GlobalVariable::get(m_name);}
bool setOn(double value,double check) {return GlobalVariable::setOn(m_name,value,check);}
datetime lastAccess() const {return GlobalVariable::lastAccess(m_name);}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class AtomicVar: public TempVar
{
public:
AtomicVar(string name,long initial,bool create=false):TempVar(name,create)
{
set(initial);
}
long increment(long by=1);
long decrement(long by=1) {return increment(-by);}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
long AtomicVar::increment(long by)
{
bool success=false;
long value;
do
{
value=(long)get();
success=setOn(value+by,value);
}
while(!success);
return (value+by);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class Semaphore
{
private:
TempVar m_var;
public:
Semaphore(string name,long initial=0);
bool isValid() const {return m_var.isValid();}
bool acquire();
void release();
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Semaphore::Semaphore(string name,long initial)
:m_var(name,initial!=0)
{
if(initial!=0)
{
m_var.set(initial);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool Semaphore::acquire(void)
{
bool success=false;
do
{
long value=(long)m_var.get();
if(value == 0) return false;
success=m_var.setOn(value-1,value);
}
while(!success && !IsStopped());
return success;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Semaphore::release(void)
{
bool success=false;
do
{
long value=(long)m_var.get();
success=m_var.setOn(value+1,value);
}
while(!success && !IsStopped());
}
//+------------------------------------------------------------------+
//| CriticalSection object for making atomic operations |
//| |
//| An exmaple of creating a global context (the creation and destroy|
//| are both enclosed between the SAME critical section): |
//| |
//| enter() |
//| if(refcount==0) create context |
//| else refcontext |
//| increase refcount |
//| leave() |
//| |
//| enter() |
//| decrease refcount |
//| if(refcount==0) context destroy |
//| leave() |
//+------------------------------------------------------------------+
class CriticalSection
{
private:
const string m_name;
public:
CriticalSection(string name):m_name(name){}
bool isValid() const {return m_name!=NULL;}
string getName() const {return m_name;}
void enter() { while(!GlobalVariable::makeTemp(m_name) && !IsStopped())Sleep(100); }
bool tryEnter() { return GlobalVariable::makeTemp(m_name); }
void leave() { GlobalVariable::remove(m_name);}
};
//+------------------------------------------------------------------+
//| A reference counted global pointer (or handle) |
//| Generic type parameter T can be long or int depending the handle |
//| length (64bit or 32bit) |
//| HandleManager should implement 2 static methods: create & destroy|
//+------------------------------------------------------------------+
template<typename T,typename HandleManager>
class GlobalHandle
{
private:
CriticalSection m_cs;
string m_refName;
string m_counterName;
protected:
T m_ref;
public:
GlobalHandle(string sharedKey=NULL):m_cs(sharedKey)
{
m_refName=m_cs.getName()+"_Ref";
m_counterName=m_cs.getName()+"_Count";
if(!m_cs.isValid()) m_ref=HandleManager::create();
else
{
m_cs.enter();
if(!GlobalVariable::exists(m_counterName))
{
GlobalVariable::makeTemp(m_counterName);
GlobalVariable::set(m_counterName,0);
}
if(long(GlobalVariable::get(m_counterName))==0)
{
m_ref=HandleManager::create();
if(!GlobalVariable::exists(m_refName))
{
GlobalVariable::makeTemp(m_refName);
GlobalVariable::set(m_refName,m_ref);
}
}
else
{
m_ref=(T)(GlobalVariable::get(m_refName));
}
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)+1);
m_cs.leave();
}
}
~GlobalHandle()
{
if(!m_cs.isValid()) {HandleManager::destroy(m_ref); return;}
m_cs.enter();
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)-1);
if(long(GlobalVariable::get(m_counterName))==0)
{
HandleManager::destroy(m_ref);
GlobalVariable::remove(m_refName);
GlobalVariable::remove(m_counterName);
}
m_cs.leave();
}
T ref() const {return m_ref;}
};
//+------------------------------------------------------------------+
+105
View File
@@ -0,0 +1,105 @@
//+------------------------------------------------------------------+
//| Module: Lang/Mql.mqh |
//| This file is part of the mql4-lib project: |
//| https://github.com/dingmaotu/mql4-lib |
//| |
//| Copyright 2017 Li Ding <dingmaotu@126.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
#import "stdlib.ex4"
string ErrorDescription(int error_code);
int RGB(int red_value,int green_value,int blue_value);
bool CompareDoubles(double number1,double number2);
string DoubleToStrMorePrecision(double number,int precision);
string IntegerToHexString(int integer_number);
#import
//+------------------------------------------------------------------+
//| Mql language specific methods |
//+------------------------------------------------------------------+
class Mql
{
public:
static int getLastError() {return GetLastError();}
static string getErrorMessage(int errorCode) {return ErrorDescription(errorCode);}
static string doubleToString(double value,int precision) {return DoubleToStrMorePrecision(value,precision);}
static string integerToHexString(int value) {return IntegerToHexString(value);}
static int rgb(int red,int green,int blue) {return RGB(red,green,blue);}
static bool isEqual(double a,double b) {return CompareDoubles(a,b);}
static bool isStopped() {return IsStopped();}
static int getCodePage() {return MQLInfoInteger(MQL_CODEPAGE);}
static ENUM_PROGRAM_TYPE getProgramType() {return(ENUM_PROGRAM_TYPE)MQLInfoInteger(MQL_PROGRAM_TYPE);}
static bool isScript() {return Mql::getProgramType()==PROGRAM_SCRIPT;}
static bool isExpert() {return Mql::getProgramType()==PROGRAM_EXPERT;}
static bool isIndicator() {return Mql::getProgramType()==PROGRAM_INDICATOR;}
static bool isDllAllowed() {return MQLInfoInteger(MQL_DLLS_ALLOWED)!=0;}
static bool isTradeAllowed() {return MQLInfoInteger(MQL_TRADE_ALLOWED)!=0;}
static bool isSignalsAllowed() {return MQLInfoInteger(MQL_SIGNALS_ALLOWED)!=0;}
static bool isDebug() {return MQLInfoInteger(MQL_DEBUG)!=0;}
static bool isProfiling() {return MQLInfoInteger(MQL_PROFILER)!=0;}
static bool isTesting() {return MQLInfoInteger(MQL_TESTER)!=0;}
static bool isOptimizing() {return MQLInfoInteger(MQL_OPTIMIZATION)!=0;}
static bool isVisual() {return MQLInfoInteger(MQL_VISUAL_MODE)!=0;}
static bool isFrameMode() {return MQLInfoInteger(MQL_FRAME_MODE)!=0;}
static ENUM_LICENSE_TYPE getLicenseType() {return(ENUM_LICENSE_TYPE)MQLInfoInteger(MQL_LICENSE_TYPE);}
static bool isFreeLicense() {return Mql::getLicenseType()==LICENSE_FREE;}
static bool isDemoLicense() {return Mql::getLicenseType()==LICENSE_DEMO;}
static bool isFullLicense() {return Mql::getLicenseType()==LICENSE_FULL;}
static bool isTimeLicense() {return Mql::getLicenseType()==LICENSE_TIME;}
static string getProgramName() {return MQLInfoString(MQL_PROGRAM_NAME);}
static string getProgramPath() {return MQLInfoString(MQL_PROGRAM_PATH);}
};
#define ObjectAttr(Type, Private, Public) \
public:\
Type get##Public() const {return m_##Private;}\
void set##Public(Type value) {m_##Private=value;}\
private:\
Type m_##Private\
#define ObjectAttrRead(Type, Private, Public) \
public:\
Type get##Public() const {return m_##Private;}\
private:\
Type m_##Private\
#define ObjectAttrWrite(Type, Private, Public) \
public:\
void set##Public(Type value) {m_##Private=value;}\
private:\
Type m_##Private\
#ifdef _DEBUG
#define Debug(msg) Print(">>> DEBUG: In ",__FUNCTION__,"(",__FILE__,":",__LINE__,") [", msg, "]")
#else
#define Debug(msg)
#endif
//--- Execute some code in the global scope
#define BEGIN_EXECUTE(Name) class __Execute##Name\
{\
public:__Execute##Name()\
{
#define END_EXECUTE(Name) \
}\
}\
__execute##Name;
//+------------------------------------------------------------------+
+200
View File
@@ -0,0 +1,200 @@
//+------------------------------------------------------------------+
//| Module: Lang/Native.mqh |
//| This file is part of the mql4-lib project: |
//| https://github.com/dingmaotu/mql4-lib |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@126.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
// Assume MT5 is 64bit, which is the default.
// Even though MT5 can be 32bit, there is no way to detect this
// by using preprocessor macros. Instead, MetaQuotes provides a
// function called IsX64 to detect this dynamically
// This is just absurd. Why do you want to know the bitness of
// the runtime? To define pointer related entities at compile time!
// All integer types in MQL is uniform on both 32bit or 64bit
// architectures, so it is almost useless to have a runtime function IsX64.
// Why not a __X64__?
#ifdef __MQL5__
#define __X64__
#endif
#ifdef __X64__
#define intptr_t long
#define uintptr_t ulong
#define size_t long
#else
#define intptr_t int
#define uintptr_t uint
#define size_t int
#endif
//--- _WIN32_WINNT version constants
#define _WIN32_WINNT_NT4 0x0400 // Windows NT 4.0
#define _WIN32_WINNT_WIN2K 0x0500 // Windows 2000
#define _WIN32_WINNT_WINXP 0x0501 // Windows XP
#define _WIN32_WINNT_WS03 0x0502 // Windows Server 2003
#define _WIN32_WINNT_WIN6 0x0600 // Windows Vista
#define _WIN32_WINNT_VISTA 0x0600 // Windows Vista
#define _WIN32_WINNT_WS08 0x0600 // Windows Server 2008
#define _WIN32_WINNT_LONGHORN 0x0600 // Windows Vista
#define _WIN32_WINNT_WIN7 0x0601 // Windows 7
#define _WIN32_WINNT_WIN8 0x0602 // Windows 8
#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1
#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 // Windows 10
#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10
//--- define you own for your target platform
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
#import "kernel32.dll"
void RtlMoveMemory(intptr_t dest,const uchar &array[],size_t length);
void RtlMoveMemory(uchar &array[],intptr_t src,size_t length);
void RtlMoveMemory(intptr_t &dest,intptr_t src,size_t length);
int lstrlen(intptr_t psz);
int lstrlenW(intptr_t psz);
uintptr_t lstrcpynW(string &s1,uintptr_t s2,int length);
uintptr_t lstrcpynW(uintptr_t s1,string &s2,int length);
int MultiByteToWideChar(uint codePage,
uint flags,
const intptr_t multiByteString,
int lengthMultiByte,
string &str,
int length
);
uint FormatMessageW(uint dwFlags,
intptr_t lpSource,
uint dwMessageId,
uint dwLanguageId,
ushort &buffer[],
uint nSize,
intptr_t Arguments
);
#import
//--- This is a standard header of the official MetaTrader distribution
#include <WinUser32.mqh>
//+------------------------------------------------------------------+
//| Copy the memory contents pointed by src to array |
//| array parameter should be initialized to the desired size |
//+------------------------------------------------------------------+
void ArrayFromPointer(uchar &array[],intptr_t src,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(array,src,(size_t)size);
}
//+------------------------------------------------------------------+
//| Copy array to the memory pointed by dest |
//+------------------------------------------------------------------+
void ArrayToPointer(const uchar &array[],intptr_t dest,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(dest,array,(size_t)size);
}
//+------------------------------------------------------------------+
//| For void** type, dereference a level to void* |
//+------------------------------------------------------------------+
intptr_t DereferencePointer(intptr_t pointer)
{
intptr_t res=0;
RtlMoveMemory(res,pointer,sizeof(intptr_t));
return res;
}
//+------------------------------------------------------------------+
//| Read a valid wide character string to the MQL environment |
//+------------------------------------------------------------------+
string StringFromPointer(intptr_t psz,int len=0)
{
if(len < 0) return NULL;
if(len==0) {len=lstrlenW(psz);}
string res;
StringInit(res,len+1);
lstrcpynW(res,psz,len+1);
return res;
}
//+------------------------------------------------------------------+
//| Get the pointer address of a string |
//+------------------------------------------------------------------+
uintptr_t StringToPointer(string &s)
{
return lstrcpynW(s,0,0);
}
//+------------------------------------------------------------------+
//| Read a valid utf-8 string to the MQL environment |
//| With this function, there is no need to copy the string to char |
//| array, and convert with CharArrayToString |
//+------------------------------------------------------------------+
string StringFromUtf8Pointer(intptr_t psz,int len)
{
if(len < 0) return NULL;
string res;
int required=MultiByteToWideChar(CP_UTF8,0,psz,len,res,0);
StringInit(res,required);
int resLength = MultiByteToWideChar(CP_UTF8,0,psz,len,res,required);
if(resLength != required)
{
return NULL;
}
else
{
return res;
}
}
//+------------------------------------------------------------------+
//| for null-terminated string |
//+------------------------------------------------------------------+
string StringFromUtf8Pointer(intptr_t psz)
{
if(psz==0) return NULL;
int len=lstrlen(psz);
if(len==0) return NULL;
return StringFromUtf8Pointer(psz, len);
}
//+------------------------------------------------------------------+
//| Convert a utf-8 byte array to a string |
//+------------------------------------------------------------------+
string StringFromUtf8(const uchar &utf8[])
{
return CharArrayToString(utf8, 0, -1, CP_UTF8);
}
//+------------------------------------------------------------------+
//| Convert a string to a utf-8 byte array |
//+------------------------------------------------------------------+
void StringToUtf8(const string str,uchar &utf8[],bool ending=true)
{
if(!ending && str=="") return;
int count=ending ? -1 : StringLen(str);
StringToCharArray(str,utf8,0,count,CP_UTF8);
}
//+------------------------------------------------------------------+
//| Get system defined error code message |
//+------------------------------------------------------------------+
string GetErrorMessage(int errorCode)
{
static ushort buffer[64*1024];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
0,errorCode,0,buffer,ArraySize(buffer),0);
return ShortArrayToString(buffer);
}
//+------------------------------------------------------------------+
+19 -4
View File
@@ -1,11 +1,26 @@
//+------------------------------------------------------------------+
//| AtomicCounter.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: AtomicCounter.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
#include "Common.mqh"
#include <Mql/Lang/Native.mqh>
#import "libzmq.dll"
intptr_t zmq_atomic_counter_new(void);
-116
View File
@@ -1,116 +0,0 @@
//+------------------------------------------------------------------+
//| Common.mqh |
//| This file is part of mql4-lib project (Lang/Native.mqh): |
//| (github.com/dingmaotu/mql4-lib) |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//+------------------------------------------------------------------+
#property strict
#include "Errno.mqh"
// Assume MT5 is 64bit, which is the default.
// Even though MT5 can be 32bit, there is no way to detect this
// by using preprocessor macros. Instead, MetaQuotes provides a
// function called IsX64 to detect this dynamically
// This is just absurd. Why do you want to know the bitness of
// the runtime? To define pointer related entities at compile time!
// All integer types in MQL is uniform on both 32bit or 64bit
// architectures, so it is almost useless to have a runtime function IsX64.
// Why not a __X64__?
#ifdef __MQL5__
#define __X64__
#endif
#ifdef __X64__
#define intptr_t long
#define uintptr_t ulong
#define size_t long
#else
#define intptr_t int
#define uintptr_t uint
#define size_t int
#endif
#import "kernel32.dll"
void RtlMoveMemory(intptr_t dest,const uchar &array[],size_t length);
void RtlMoveMemory(uchar &array[],intptr_t src,size_t length);
int lstrlen(intptr_t psz);
int MultiByteToWideChar(uint codePage,
uint flags,
const intptr_t multiByteString,
int lengthMultiByte,
string &str,
int length
);
#import
//+------------------------------------------------------------------+
//| Copy the memory contents pointed by src to array |
//| array parameter should be initialized to the desired size |
//+------------------------------------------------------------------+
void ArrayFromPointer(uchar &array[],intptr_t src,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(array,src,(size_t)size);
}
//+------------------------------------------------------------------+
//| Copy array to the memory pointed by dest |
//+------------------------------------------------------------------+
void ArrayToPointer(const uchar &array[],intptr_t dest,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(dest,array,(size_t)size);
}
//+------------------------------------------------------------------+
//| Read a valid utf-8 string to the MQL environment |
//| With this function, there is no need to copy the string to char |
//| array, and convert with CharArrayToString |
//+------------------------------------------------------------------+
string StringFromUtf8Pointer(intptr_t psz,int len)
{
if(len < 0) return NULL;
string res;
int required=MultiByteToWideChar(CP_UTF8,0,psz,len,res,0);
StringInit(res,required);
int resLength = MultiByteToWideChar(CP_UTF8,0,psz,len,res,required);
if(resLength != required)
{
return NULL;
}
else
{
return res;
}
}
//+------------------------------------------------------------------+
//| for null-terminated string |
//+------------------------------------------------------------------+
string StringFromUtf8Pointer(intptr_t psz)
{
int len=lstrlen(psz);
return StringFromUtf8Pointer(psz, len);
}
//+------------------------------------------------------------------+
//| Convert a utf-8 byte array to a string |
//+------------------------------------------------------------------+
string StringFromUtf8(const uchar &utf8[])
{
return CharArrayToString(utf8, 0, -1, CP_UTF8);
}
//+------------------------------------------------------------------+
//| Convert a string to a utf-8 byte array |
//+------------------------------------------------------------------+
void StringToUtf8(const string str,uchar &utf8[],bool ending=true)
{
int count=ending ? -1 : StringLen(str);
StringToCharArray(str,utf8,0,count,CP_UTF8);
}
#ifdef _DEBUG
#define Debug(msg) Print(">>> DEBUG: In ",__FUNCTION__,"(",__FILE__,":",__LINE__,") [", msg, "]")
#else
#define Debug(msg)
#endif
//+------------------------------------------------------------------+
+28 -10
View File
@@ -1,12 +1,28 @@
//+------------------------------------------------------------------+
//| Context.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: Context.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#include "Common.mqh"
#property strict
#include <Mql/Lang/Mql.mqh>
#include <Mql/Lang/Native.mqh>
#include <Mql/Lang/GlobalVariable.mqh>
#include "SocketOptions.mqh"
#include "GlobalHandle.mqh"
//--- Context options
#define ZMQ_IO_THREADS 1
@@ -48,16 +64,18 @@ int zmq_ctx_get(intptr_t context,int option);
//| and in a manner not easily recognized by humans, for example: |
//| "__3kewducdxhkd__" |
//+------------------------------------------------------------------+
class Context: public GlobalHandle<intptr_t>
class Context: public GlobalHandle<intptr_t,Context>
{
protected:
int get(int option) {return zmq_ctx_get(m_ref,option);}
bool set(int option,int optval) {return 0==zmq_ctx_set(m_ref,option,optval);}
intptr_t create() override {return zmq_ctx_new();}
void destroy(intptr_t handle) override {if(0!=zmq_ctx_term(handle)) {Debug("failed to terminate context");}}
public:
Context(string shared=NULL):GlobalHandle<intptr_t>(shared) {}
static intptr_t create() {return zmq_ctx_new();}
static void destroy(intptr_t handle) {if(0!=zmq_ctx_term(handle)) {Debug("failed to terminate context");}}
Context(string shared=NULL):GlobalHandle<intptr_t,Context>(shared) {}
bool shutdown() {return 0==zmq_ctx_shutdown(m_ref);}
+18 -1
View File
@@ -1,5 +1,22 @@
//+------------------------------------------------------------------+
//| Errno.mqh |
//| Module: Errno.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
// Following error codes come from Microsoft CRT header errno.h
-125
View File
@@ -1,125 +0,0 @@
//+------------------------------------------------------------------+
//| GlobalHandle.mqh |
//| This file is part of mql4-lib project (Lang/GlobalVariable.mqh): |
//| (github.com/dingmaotu/mql4-lib) |
//| Copyright 2017, Li Ding |
//| dingmaotu@hotmail.com |
//+------------------------------------------------------------------+
#property strict
//+------------------------------------------------------------------+
//| Wraps global variable functions |
//+------------------------------------------------------------------+
class GlobalVariable
{
public:
static int total() {return GlobalVariablesTotal();}
static string name(int index) {return GlobalVariableName(index);}
static void flush() {GlobalVariablesFlush();}
static bool exists(string name) {return GlobalVariableCheck(name);}
static datetime lastAccess(string name) {return GlobalVariableTime(name);}
static bool makeTemp(string name) {return GlobalVariableTemp(name);}
static double get(string name) {return GlobalVariableGet(name);}
static bool get(string name,double &value) {return GlobalVariableGet(name,value);}
static datetime set(string name,double value) {return GlobalVariableSet(name,value);}
static bool setOn(string name,double value,double check) {return GlobalVariableSetOnCondition(name,value,check);}
static bool remove(string name) {return GlobalVariableDel(name);}
static bool removeAll(string prefix=NULL,datetime before=0) {return GlobalVariablesDeleteAll(prefix,before);}
};
//+------------------------------------------------------------------+
//| CriticalSection object for making atomic operations |
//| |
//| An exmaple of creating a global context (the creation and destroy|
//| are both enclosed between the SAME critical section): |
//| |
//| enter() |
//| if(refcount==0) create context |
//| else refcontext |
//| increase refcount |
//| leave() |
//| |
//| enter() |
//| decrease refcount |
//| if(refcount==0) context destroy |
//| leave() |
//+------------------------------------------------------------------+
class CriticalSection
{
private:
const string m_name;
public:
CriticalSection(string name):m_name(name){}
bool isValid() const {return m_name!=NULL;}
string getName() const {return m_name;}
void enter() { while(!GlobalVariable::makeTemp(m_name))Sleep(100); }
bool tryEnter() { return GlobalVariable::makeTemp(m_name); }
void leave() { GlobalVariable::remove(m_name);}
};
//+------------------------------------------------------------------+
//| A reference counted global pointer (or handle) |
//+------------------------------------------------------------------+
template<typename T>
class GlobalHandle
{
private:
CriticalSection m_cs;
string m_refName;
string m_counterName;
protected:
T m_ref;
public:
GlobalHandle(string sharedKey=NULL):m_cs(sharedKey)
{
m_refName=m_cs.getName()+"_Ref";
m_counterName=m_cs.getName()+"_Count";
if(!m_cs.isValid()) m_ref=create();
else
{
m_cs.enter();
if(!GlobalVariable::exists(m_counterName))
{
GlobalVariable::makeTemp(m_counterName);
GlobalVariable::set(m_counterName,0);
}
if(long(GlobalVariable::get(m_counterName))==0)
{
m_ref=create();
if(!GlobalVariable::exists(m_refName))
{
GlobalVariable::makeTemp(m_refName);
GlobalVariable::set(m_refName,m_ref);
}
}
else
{
m_ref=(T)(GlobalVariable::get(m_refName));
}
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)+1);
m_cs.leave();
}
}
~GlobalHandle()
{
if(!m_cs.isValid()) {destroy(m_ref); return;}
m_cs.enter();
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)-1);
if(long(GlobalVariable::get(m_counterName))==0)
{
destroy(m_ref);
GlobalVariable::remove(m_refName);
GlobalVariable::remove(m_counterName);
}
m_cs.leave();
}
T ref() const {return m_ref;}
protected:
virtual T create()=NULL;
virtual void destroy(T handle)=NULL;
};
//+------------------------------------------------------------------+
+55 -23
View File
@@ -1,22 +1,30 @@
//+------------------------------------------------------------------+
//| Socket.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: Socket.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
#include "Common.mqh"
#include <Mql/Lang/Mql.mqh>
#include <Mql/Lang/Native.mqh>
#include "Context.mqh"
#include "SocketOptions.mqh"
#include "ZmqMsg.mqh"
//--- fd is SOCKET on Win32, which is defined as UINT_PTR
struct zmq_pollitem_t
{
intptr_t socket;
uintptr_t fd;
short events;
short revents;
};
//--- Socket types
#define ZMQ_PAIR 0
@@ -64,7 +72,17 @@ struct zmq_pollitem_t
#define ZMQ_POLLPRI 8
#define ZMQ_POLLITEMS_DFLT 16
//--- fd is SOCKET on Win32, which is defined as UINT_PTR
struct PollItem
{
intptr_t socket;
uintptr_t fd;
short events;
short revents;
bool hasInput() const {return(revents&ZMQ_POLLIN)!=0;}
bool hasOutput() const {return(revents&ZMQ_POLLOUT)!=0;}
};
#import "libzmq.dll"
//+------------------------------------------------------------------+
//| Sockets |
@@ -87,7 +105,7 @@ int zmq_msg_recv(zmq_msg_t &msg,intptr_t s,int flags);
//+------------------------------------------------------------------+
//| I/O multiplexing |
//+------------------------------------------------------------------+
int zmq_poll(zmq_pollitem_t &items[],int nitems,long timeout);
int zmq_poll(PollItem &items[],int nitems,long timeout);
//+------------------------------------------------------------------+
//| Message proxying |
//+------------------------------------------------------------------+
@@ -123,8 +141,8 @@ public:
bool send(ZmqMsg &msg,bool nowait=false,bool more=false);
bool recv(ZmqMsg &msg,bool nowait=false);
void register(zmq_pollitem_t &pollitem,bool read=false,bool write=false);
void register(zmq_pollitem_t &pollitems[],int index,bool read=false,bool write=false);
void register(PollItem &pollitem,bool read=false,bool write=false);
void register(PollItem &pollitems[],int index,bool read=false,bool write=false);
//--- monitor socket events
bool monitor(string addr,int events);
@@ -134,7 +152,10 @@ public:
static bool proxySteerable(Socket *frontend,Socket *backend,Socket *capture,Socket *control);
//--- poll
static int poll(zmq_pollitem_t &arr[],long timeout);
static int poll(PollItem &arr[],long timeout);
//--- fill a poll item for this socket
void fillPollItem(PollItem &item,short events);
};
//+------------------------------------------------------------------+
//| |
@@ -187,7 +208,7 @@ bool Socket::recv(uchar &buf[],bool nowait=false)
{
int options=0;
if(nowait) options|=ZMQ_DONTWAIT;
return 0==zmq_recv(m_ref,buf,ArraySize(buf),options);
return -1!=zmq_recv(m_ref,buf,ArraySize(buf),options);
}
//+------------------------------------------------------------------+
//| |
@@ -197,7 +218,7 @@ bool Socket::send(const uchar &buf[],bool nowait=false,bool more=false)
int options=0;
if(nowait) options|=ZMQ_DONTWAIT;
if(more) options|=ZMQ_SNDMORE;
return 0==zmq_send(m_ref,buf,ArraySize(buf),options);
return -1!=zmq_send(m_ref,buf,ArraySize(buf),options);
}
//+------------------------------------------------------------------+
//| |
@@ -207,7 +228,7 @@ bool Socket::sendConst(const uchar &buf[],bool nowait=false,bool more=false)
int options=0;
if(nowait) options|=ZMQ_DONTWAIT;
if(more) options|=ZMQ_SNDMORE;
return 0==zmq_send_const(m_ref,buf,ArraySize(buf),options);
return -1!=zmq_send_const(m_ref,buf,ArraySize(buf),options);
}
//+------------------------------------------------------------------+
//| Send a zmq_msg_t through a socket |
@@ -242,7 +263,7 @@ bool Socket::monitor(string addr,int events)
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Socket::register(zmq_pollitem_t &pollitem,bool read=false,bool write=false)
void Socket::register(PollItem &pollitem,bool read=false,bool write=false)
{
ZeroMemory(pollitem);
pollitem.socket=m_ref;
@@ -252,7 +273,7 @@ void Socket::register(zmq_pollitem_t &pollitem,bool read=false,bool write=false)
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Socket::register(zmq_pollitem_t &pollitems[],int index,bool read=false,bool write=false)
void Socket::register(PollItem &pollitems[],int index,bool read=false,bool write=false)
{
ZeroMemory(pollitems[index]);
pollitems[index].socket=m_ref;
@@ -281,10 +302,21 @@ bool Socket::proxySteerable(Socket *frontend,Socket *backend,Socket *capture,Soc
return 0==zmq_proxy_steerable(frontend_ref, backend_ref, capture_ref, control_ref);
}
//+------------------------------------------------------------------+
//| |
//| poll for events. timeout is milliseconds (-1 for indefinite wait |
//| and 0 for immediate return) |
//+------------------------------------------------------------------+
int Socket::poll(zmq_pollitem_t &arr[],long timeout)
int Socket::poll(PollItem &arr[],long timeout)
{
return zmq_poll(arr,ArraySize(arr),timeout);
}
//+------------------------------------------------------------------+
//| fill a poll item for this socket |
//+------------------------------------------------------------------+
void Socket::fillPollItem(PollItem &item,short events)
{
item.socket=m_ref;
item.fd=0;
item.events=events;
item.revents=0;
}
//+------------------------------------------------------------------+
+19 -6
View File
@@ -1,13 +1,26 @@
//+------------------------------------------------------------------+
//| Socket.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: SocketOptions.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Li Ding"
#property link "dingmaotu@hotmail.com"
#property strict
#include "Common.mqh"
#include <Mql/Lang/Native.mqh>
#import "libzmq.dll"
// We can overload the same function for different data types
+19 -6
View File
@@ -1,13 +1,26 @@
//+------------------------------------------------------------------+
//| Z85.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: Z85.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Li Ding"
#property link "dingmaotu@hotmail.com"
#property strict
#include "Common.mqh"
#include <Mql/Lang/Native.mqh>
#import "libzmq.dll"
// Encode data with Z85 encoding. Returns 0(NULL) if failed
Binary file not shown.
+44 -12
View File
@@ -1,12 +1,26 @@
//+------------------------------------------------------------------+
//| ZmqMsg.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: ZmqMsg.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Li Ding"
#property link "dingmaotu@hotmail.com"
#property strict
#include "Common.mqh"
#include <Mql/Lang/Mql.mqh>
#include <Mql/Lang/Native.mqh>
//+------------------------------------------------------------------+
//| 0MQ Message struct |
//+------------------------------------------------------------------+
@@ -49,12 +63,29 @@ protected:
int get(int property) {return zmq_msg_get(this,property);}
bool set(int property,int value) {return 0==zmq_msg_set(this,property,value);}
intptr_t data() {return zmq_msg_data(this);}
bool setStringData(string data,bool nullterminated=false);
public:
ZmqMsg() {zmq_msg_init(this);}
ZmqMsg(int size) {if(0!=zmq_msg_init_size(this,size)){Debug("Failed to init size msg: insufficient space");}}
ZmqMsg(string data,bool nullterminated=false);
ZmqMsg(string data,bool nullterminated=false) {setStringData(data,nullterminated);}
~ZmqMsg() {if(0!=zmq_msg_close(this)){Debug("Failed to close msg");}}
bool rebuild()
{
if(0!=zmq_msg_close(this)){Debug("Failed to close msg");return false;}
return 0==zmq_msg_init(this);
}
bool rebuild(int size)
{
if(0!=zmq_msg_close(this)){Debug("Failed to close msg");return false;}
return 0==zmq_msg_init_size(this,size);
}
bool rebuild(string data,bool nullterminated=false)
{
if(0!=zmq_msg_close(this)){Debug("Failed to close msg");return false;}
return setStringData(data,nullterminated);
}
size_t size() {return zmq_msg_size(this);}
void getData(uchar &data[]);
@@ -71,13 +102,13 @@ public:
//+------------------------------------------------------------------+
//| Initialize a utf-8 string message |
//+------------------------------------------------------------------+
ZmqMsg::ZmqMsg(string data,bool nullterminated)
bool ZmqMsg::setStringData(string data,bool nullterminated)
{
uchar array[];
StringToUtf8(data,array,nullterminated);
int size=ArraySize(array);
zmq_msg_init_size(this,size);
setData(array);
bool res=(0==zmq_msg_init_size(this,ArraySize(array)));
if(res)setData(array);
return res;
}
//+------------------------------------------------------------------+
//| Get message data as bytes array |
@@ -104,7 +135,8 @@ string ZmqMsg::getData()
void ZmqMsg::setData(const uchar &data[])
{
intptr_t dest=data();
ArrayToPointer(data,dest);
size_t size=size();
ArrayToPointer(data,dest,size);
}
//+------------------------------------------------------------------+
//| Wraps zmq_msg_gets: get metadata associated with the msg |
+198 -17
View File
@@ -1,21 +1,202 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2016 Ding Li
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+59 -13
View File
@@ -7,25 +7,60 @@ ZMQ binding for the MQL language (both 32bit MT4 and 64bit MT5)
This is a complete binding of the [ZeroMQ](http://zeromq.org/) library
for the MQL4/5 language provided by MetaTrader4/5.
Traders with programming abilities have always wanted a messaging solution
like ZeroMQ, simple and powerful, far better than the PIPE trick as
suggested by the official articles. However, bindings for MQL were either outdated or not complete (mostly toy projects and only basic features are implemented). This binding is based on latest 4.2 version of the library, and provides all functionalities as specified in the API documentation.
Traders with programming abilities have always wanted a messaging solution like
ZeroMQ, simple and powerful, far better than the PIPE trick as suggested by the
official articles. However, bindings for MQL were either outdated or not
complete (mostly toy projects and only basic features are implemented). This
binding is based on latest 4.2 version of the library, and provides all
functionalities as specified in the API documentation.
This binding tries to remain compatible between MQL4/5. Users of both versions can use this binding, with a single set of headers. MQL4 and MQL5 are basically the same in that they are merged in recent versions. The difference is in the runtime environment (MetaTrader5 is 64bit by default, while MetaTrader4 is 32bit). The trading system is also different, but it is no concern of this binding.
This binding tries to remain compatible between MQL4/5. Users of both versions
can use this binding, with a single set of headers. MQL4 and MQL5 are basically
the same in that they are merged in recent versions. The difference is in the
runtime environment (MetaTrader5 is 64bit by default, while MetaTrader4 is
32bit). The trading system is also different, but it is no concern of this
binding.
## Files
## Files and Installation
This binding contains three sets of files:
1. The binding itself is in the `Include/Zmq` directory.
1. The binding itself is in the `Include/Zmq` directory. *Note* that there is a
`Mql` directory in `Include`, which is part of
the [mql4-lib](https://github.com/dingmaotu/mql4-lib). Previous `Common.mqh`
and `GlobalHandle.mqh` are actually from this library. At release 1.4, this
becomes a direct reference, with mql4-lib content copied here verbatim. It is
recommended you install the full mql4-lib, as it contains a lot other
features. But for those who want to use mql-zmq alone, it is OK to deploy
only the small subset included here.
2. The testing scripts and zmq guide examples are in `Scripts` directory. The script files are mq4 by default, but you can change the extension to mq5 to use them in MetaTrader5.
2. The testing scripts and zmq guide examples are in `Scripts` directory. The
script files are mq4 by default, but you can change the extension to mq5 to
use them in MetaTrader5.
3. Precompiled DLLs of both 64bit (`Library/MT5`) and 32bit (`Library/MT4`) ZeroMQ and libsodium are provided. Copy the corresponding DLLs to the `Library` folder of your MetaTrader terminal. If you are using MT5 32bit, use the 32bit version from `Library/MT4`. The DLLs require that you have the latest Visual C++ runtime (2015). *Note* that these DLLs are compiled from official sources, without any modification. You can compile your own if you don't trust these binaries. The `libsodium.dll` is copied from the official binary release. If you want to support security mechanisms other than `curve`, or you want to use transports like OpenPGM, you need to compile your own DLL.
3. Precompiled DLLs of both 64bit (`Library/MT5`) and 32bit (`Library/MT4`)
ZeroMQ and libsodium are provided. Copy the corresponding DLLs to the
`Library` folder of your MetaTrader terminal. If you are using MT5 32bit, use
the 32bit version from `Library/MT4`. **The DLLs require that you have the
latest Visual C++ runtime (2015)**.
*Note* that if you are using **MT5 32bit**, you need to comment out the
`__X64__` macro definition at the top of the `Include/Zmq/Common.mqh`. I
assume MT5 is 64 bit, since their is no way to detect 32 bit by native
macros, and to define pointer related values a macro like this is required.
*Note* that these DLLs are compiled from official sources, without any
modification. You can compile your own if you don't trust these binaries. The
`libsodium.dll` is copied from the official binary release. If you want to
support security mechanisms other than `curve`, or you want to use transports
like OpenPGM, you need to compile your own DLL.
## About string encoding
MQL strings are Win32 UNICODE strings (basically 2-byte UTF-16). In this binding all strings are converted to utf-8 strings before sending to the dll layer. The ZmqMsg supports a constructor from MQL strings, the default is _NOT_ null-terminated.
MQL strings are Win32 UNICODE strings (basically 2-byte UTF-16). In this binding
all strings are converted to utf-8 strings before sending to the dll layer. The
ZmqMsg supports a constructor from MQL strings, the default is _NOT_
null-terminated.
## Notes on context creation
@@ -42,11 +77,15 @@ share a process, that is the Terminal. So it is advised to use a single global
context on all your MQL programs. The `shared` parameter of `Context` is used
for sychronization of context creation and destruction. It is better named
globally, and in a manner not easily recognized by humans, for example:
"__3kewducdxhkd__"
`__3kewducdxhkd__`
## Usage
You can find a simple test script in `Scripts/Test`, and you can find examples of the official guide in Scripts/ZeroMQGuideExamples. I intend to translate all examples to this binding, but now only the hello world example is provided. I will gradually add those examples. Of course forking this binding if you are interested and welcome to send pull requests.
You can find a simple test script in `Scripts/Test`, and you can find examples
of the official guide in Scripts/ZeroMQGuideExamples. I intend to translate all
examples to this binding, but now only the hello world example is provided. I
will gradually add those examples. Of course forking this binding if you are
interested and welcome to send pull requests.
Here is a sample from `HelloWorldServer.mq4`:
@@ -91,5 +130,12 @@ void OnStart()
2. Add more examples from the official ZMQ guide.
## Changes
2017-05-26: Released 1.1: add the ability to share a ZMQ context globally in a terminal
2016-12-27: Released 1.0.
* 2017-08-18: Released 1.4: Fix ZmqMsg setData bug; Change License to Apache
2.0; Inlcude mql4-lib dependencies directly.
* 2017-07-18: Released 1.3: Refactored poll support; Add Chapter 2 examples from
the official ZMQ guide.
* 2017-06-08: Released 1.2: Fix GlobalHandle bug; Add rebuild method to ZmqMsg;
Complete all examples in ZMQ Guide Chanpter 1.
* 2017-05-26: Released 1.1: add the ability to share a ZMQ context globally in a terminal
* 2016-12-27: Released 1.0.
Binary file not shown.
@@ -0,0 +1,48 @@
//+------------------------------------------------------------------+
//| TaskSink.mq4 |
//| Copyright 2017, Bear Two Technologies Co., Ltd. |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Bear Two Technologies Co., Ltd."
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Task sink in MQL (adapted from C++ version) |
//| Binds PULL socket to tcp://localhost:5558 |
//| Collects results from workers via that socket |
//| |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
//---
// Prepare our context and socket
Context context;
Socket receiver(context,ZMQ_PULL);
receiver.bind("tcp://*:5558");
// Wait for start of batch
ZmqMsg message;
receiver.recv(message);
// Start our clock now
uint tstart=GetTickCount();
// Process 100 confirmations
string progress="";
for(int i=0; i<100; i++)
{
receiver.recv(message);
if((i/10)*10==i)
progress+=":";
else
progress+=".";
Comment(progress);
}
// Calculate and report duration of batch
uint tend=GetTickCount();
Print(">>> Total elapsed time: ",tend-tstart," msec");
}
//+------------------------------------------------------------------+
@@ -0,0 +1,52 @@
//+------------------------------------------------------------------+
//| TaskWorker.mq4 |
//| Copyright 2017, Bear Two Technologies Co., Ltd. |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Bear Two Technologies Co., Ltd."
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Task worker in MQL (adapted from C++ version) |
//| Connects PULL socket to tcp://localhost:5557 |
//| Collects workloads from ventilator via that socket |
//| Connects PUSH socket to tcp://localhost:5558 |
//| Sends results to sink via that socket |
//| |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Share a single context in the terminal by the key "work"
Context context("work");
//--- Socket to receive messages on
Socket receiver(context,ZMQ_PULL);
receiver.connect("tcp://localhost:5557");
//--- Socket to send messages to
Socket sender(context,ZMQ_PUSH);
sender.connect("tcp://localhost:5558");
//--- Process tasks forever
string progress="";
while(!IsStopped())
{
ZmqMsg message;
receiver.recv(message);
//--- Workload in msecs
int workload=(int)StringToInteger(message.getData());
//--- Do the work
Sleep(workload);
//--- Send results to sink
message.rebuild();
sender.send(message);
// Simple progress indicator for the viewer
progress+=".";
Comment(progress);
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,54 @@
//+------------------------------------------------------------------+
//| MSPoller.mq4 |
//| Copyright 2017, Li Ding |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Li Ding"
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Reading from multiple sockets in MQL (adapted from C++ version) |
//| This version uses zmq_poll() |
//| |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
//---
Context context;
// Connect to task ventilator
Socket receiver(context,ZMQ_PULL);
receiver.connect("tcp://localhost:5557");
// Connect to weather server
Socket subscriber(context,ZMQ_SUB);
subscriber.connect("tcp://localhost:5556");
subscriber.subscribe("10001 ");
// Initialize poll set
PollItem items[2];
receiver.fillPollItem(items[0],ZMQ_POLLIN);
subscriber.fillPollItem(items[1],ZMQ_POLLIN);
// Process messages from both sockets
while(!IsStopped())
{
ZmqMsg message;
//--- MQL Note: To handle Script exit properly, we set a timeout of 500 ms instead of infinite wait
Socket::poll(items,500);
if(items[0].hasInput())
{
receiver.recv(message);
// Process task
}
if(items[1].hasInput())
{
subscriber.recv(message);
// Process weather update
}
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,63 @@
//+------------------------------------------------------------------+
//| RRBroker.mq4 |
//| Copyright 2017, Li Ding |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Li Ding"
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Simple request-reply broker in MQL (adapted from C++ version) |
//| |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
// Prepare our context and sockets
Context context;
Socket frontend(context,ZMQ_ROUTER);
Socket backend(context,ZMQ_DEALER);
frontend.bind("tcp://*:5559");
backend.bind("tcp://*:5560");
// Initialize poll set
PollItem items[2];
frontend.fillPollItem(items[0],ZMQ_POLLIN);
backend.fillPollItem(items[1],ZMQ_POLLIN);
// Switch messages between sockets
while(!IsStopped())
{
ZmqMsg message;
bool more=false; // Multipart detection
Socket::poll(items,500);
if(items[0].hasInput())
{
// Process all parts of the message
do
{
frontend.recv(message);
more=message.more();
backend.send(message,false,more);
}
while(more);
}
if(items[1].hasInput())
{
// Process all parts of the message
do
{
backend.recv(message);
more=message.more();
frontend.send(message,false,more);
}
while(more);
}
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,36 @@
//+------------------------------------------------------------------+
//| RRClient.mq4 |
//| Copyright 2017, Li Ding |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Li Ding"
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Request-reply client in MQL (adapted from C++ version) |
//| Connects REQ socket to tcp://localhost:5559 |
//| Sends "Hello" to server, expects "World" back |
//| |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
Context context;
Socket requester(context,ZMQ_REQ);
requester.connect("tcp://localhost:5559");
for(int request=0; request<10; request++)
{
ZmqMsg message("Hello");
requester.send(message);
ZmqMsg reply;
requester.recv(reply,true);
Print("Received reply ",reply.getData());
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,41 @@
//+------------------------------------------------------------------+
//| RRWorker.mq4 |
//| Copyright 2017, Li Ding |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Li Ding"
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Request-reply service in MQL (adapted from C++ version) |
//| Connects REP socket to tcp://localhost:5560 |
//| Expects "Hello" from client, replies with "World" |
//| |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
Context context;
Socket responder(context,ZMQ_REP);
responder.connect("tcp://localhost:5560");
while(!IsStopped())
{
// Wait for next request from client
ZmqMsg req;
responder.recv(req);
Print("Received request: ",req.getData());
// Do some 'work'
Sleep(1000);
ZmqMsg reply("World");
// Send reply back to client
responder.send(reply);
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,47 @@
//+------------------------------------------------------------------+
//| WUProxy.mq4 |
//| Copyright 2017, Li Ding |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Li Ding"
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
//+------------------------------------------------------------------+
//| Weather proxy device MQL (adapted from C++) |
//| |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
//---
Context context;
// This is where the weather server sits
Socket frontend(context,ZMQ_XSUB);
frontend.connect("tcp://192.168.55.210:5556");
// This is our public endpoint for subscribers
Socket backend(context,ZMQ_XPUB);
backend.bind("tcp://10.1.1.0:8100");
// Subscribe on everything
frontend.subscribe("");
// Shunt messages out to our own subscribers
while(!IsStopped())
{
// Process all parts of the message
ZmqMsg message;
bool more;
do
{
frontend.recv(message);
more=message.more();
backend.send(message,false,more);
}
while(more);
}
}
//+------------------------------------------------------------------+