dwx-zeromq-connector
dwx-zeromq-connector copied to clipboard
dwx zeromq on MT5
I made some modifications and It's working on Ubuntu 20.04 and MT5.
Download the dependencies and remove funcions that are duplicated. Be happy!!!
//+--------------------------------------------------------------+
//| DWX_ZeroMQ_Server_v2.0.2_RC8.mq4
//| @author: Darwinex Labs (www.darwinex.com)
//|
//| Copyright (c) 2017-2019, Darwinex. All rights reserved.
//|
//| Licensed under the BSD 3-Clause License, you may not use this file except
//| in compliance with the License.
//|
//| You may obtain a copy of the License at:
//| https://opensource.org/licenses/BSD-3-Clause
//+--------------------------------------------------------------+
#property copyright "Copyright 2017-2019, Darwinex Labs."
#property link "https://www.darwinex.com/"
#property version "2.0.2"
#property strict
// Aqui tem algumas constantes que são equivalentes
#include <MT4Orders.mqh> // если есть #include <Trade/Trade.mqh>, вставить эту строчку ПОСЛЕ
#include <MQL4_to_MQL5.mqh> // ТОЛЬКО для данного примера
//MT5|Expert!0
#include <mql4compat.mqh>
//+------------------------------------------------------------------+
//| InitMQL4.mqh |
//| Copyright DC2008 |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "keiji"
#property copyright "DC2008"
#property link "https://www.mql5.com"
//--- Declaration of constants
#define OP_BUY 0 //Buy
#define OP_SELL 1 //Sell
#define OP_BUYLIMIT 2 //Pending order of BUY LIMIT type
#define OP_SELLLIMIT 3 //Pending order of SELL LIMIT type
#define OP_BUYSTOP 4 //Pending order of BUY STOP type
#define OP_SELLSTOP 5 //Pending order of SELL STOP type
//---
#define MODE_OPEN 0
#define MODE_CLOSE 3
#define MODE_VOLUME 4
#define MODE_REAL_VOLUME 5
#define MODE_TRADES 0
#define MODE_HISTORY 1
#define SELECT_BY_POS 0
#define SELECT_BY_TICKET 1
//---
#define DOUBLE_VALUE 0
#define FLOAT_VALUE 1
#define LONG_VALUE INT_VALUE
//---
#define CHART_BAR 0
#define CHART_CANDLE 1
//---
#define MODE_ASCEND 0
#define MODE_DESCEND 1
//---
#define MODE_LOW 1
#define MODE_HIGH 2
#define MODE_TIME 5
#define MODE_BID 9
#define MODE_ASK 10
#define MODE_POINT 11
#define MODE_DIGITS 12
#define MODE_SPREAD 13
#define MODE_STOPLEVEL 14
#define MODE_LOTSIZE 15
#define MODE_TICKVALUE 16
#define MODE_TICKSIZE 17
#define MODE_SWAPLONG 18
#define MODE_SWAPSHORT 19
#define MODE_STARTING 20
#define MODE_EXPIRATION 21
#define MODE_TRADEALLOWED 22
#define MODE_MINLOT 23
#define MODE_LOTSTEP 24
#define MODE_MAXLOT 25
#define MODE_SWAPTYPE 26
#define MODE_PROFITCALCMODE 27
#define MODE_MARGINCALCMODE 28
#define MODE_MARGININIT 29
#define MODE_MARGINMAINTENANCE 30
#define MODE_MARGINHEDGED 31
#define MODE_MARGINREQUIRED 32
#define MODE_FREEZELEVEL 33
//---
#define EMPTY -1
// Required: MQL-ZMQ from https://github.com/dingmaotu/mql-zmq
#include <Zmq/Zmq.mqh>
extern string PROJECT_NAME = "DWX_ZeroMQ_MT4_Server";
extern string ZEROMQ_PROTOCOL = "tcp";
extern string HOSTNAME = "*";
extern int PUSH_PORT = 32768;
extern int PULL_PORT = 32769;
extern int PUB_PORT = 32770;
extern int MILLISECOND_TIMER = 1;
extern string t0 = "--- Trading Parameters ---";
extern int MagicNumber = 123456;
extern int MaximumOrders = 1;
extern double MaximumLotSize = 0.01;
extern int MaximumSlippage = 3;
extern bool DMA_MODE = true;
//extern string t1 = "--- ZeroMQ Configuration ---";
bool Publish_MarketData = false;
bool Publish_MarketRates = false;
// Dynamic array initialized at OnInit(). Can be updated by TRACK_PRICES requests from client peers
string Publish_Symbols[];
// CREATE ZeroMQ Context
Context context(PROJECT_NAME);
// CREATE ZMQ_PUSH SOCKET
Socket pushSocket(context, ZMQ_PUSH);
// CREATE ZMQ_PULL SOCKET
Socket pullSocket(context, ZMQ_PULL);
// CREATE ZMQ_PUB SOCKETMT5|Expert!0
Socket pubSocket(context, ZMQ_PUB);
// VARIABLES FOR LATER
uchar _data[];
ZmqMsg request;
/**
* Class definition for an specific instrument: the tuple (symbol,timeframe)
*/
class Instrument{
public:
//--------------------------------------------------------------
/** Instrument constructor */
Instrument(){ _symbol = ""; _name = ""; _timeframe = PERIOD_CURRENT; _last_pub_rate =0;}
//--------------------------------------------------------------
/** Getters */
string symbol() { return _symbol; }
ENUM_TIMEFRAMES timeframe() { return _timeframe; }
string name() { return _name; }
datetime getLastPublishTimestamp() { return _last_pub_rate; }
/** Setters */
void setLastPublishTimestamp(datetime tmstmp) { _last_pub_rate = tmstmp; }
//+------------------------------------------------------------------+
// Get Timeframe from text
string GetTimeframeText(ENUM_TIMEFRAMES tf){
// Standard timeframes
switch(tf){
case PERIOD_M1: return "M1";
case PERIOD_M5: return "M5";
case PERIOD_M15: return "M15";
case PERIOD_M30: return "M30";
case PERIOD_H1: return "H1";
case PERIOD_H4: return "H4";
case PERIOD_D1: return "D1";
case PERIOD_W1: return "W1";
case PERIOD_MN1: return "MN1";
default: return "UNKNOWN";
}
}
//--------------------------------------------------------------
/** Setup instrument with symbol and timeframe descriptions
* @param arg_symbol Symbol
* @param arg_timeframe Timeframe
*/
void setup(string arg_symbol, ENUM_TIMEFRAMES arg_timeframe){
_symbol = arg_symbol;
_timeframe = arg_timeframe;
_name = _symbol + "_" + GetTimeframeText(_timeframe);
_last_pub_rate = 0;
}
//--------------------------------------------------------------
/** Get last N MqlRates from this instrument (symbol-timeframe)
* @param rates Receives last 'count' rates
* @param count Number of requested rates
* @return Number of returned rates
*/
int GetRates(MqlRates& rates[], int count){
// ensures that symbol is setup
if(StringLen(_symbol) > 0){
return CopyRates(_symbol, _timeframe, 0, count, rates);
}
return 0;
}
protected:
string _name; //!< Instrument descriptive name
string _symbol; //!< Symbol
ENUM_TIMEFRAMES _timeframe; //!< Timeframe
datetime _last_pub_rate; //!< Timestamp of the last published OHLC rate. Default = 0 (1 Jan 1970)
};
// Array of instruments whose rates will be published if Publish_MarketRates = True. It is initialized at OnInit() and
// can be updated through TRACK_RATES request from client peers.
Instrument Publish_Instruments[];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
//---
// // Default symbol list. Can be modified through TRACK_PRICES request from client side.
// ArrayResize(Publish_Symbols, 1);
// Publish_Symbols[0] = "EURAUD";
//
// // Default instrument list. Can be modified through TRACK_RATES request from client side.
// ArrayResize(Publish_Instruments, 1);
// Publish_Instruments[0].setup("EURAUD", PERIOD_M1);
EventSetMillisecondTimer(MILLISECOND_TIMER); // Set Millisecond Timer to get client socket input
context.setBlocky(false);
// Send responses to PULL_PORT that client is listening on.
if(!pushSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PULL_PORT))){
Print("[PUSH] ####ERROR#### Binding MT4 Server to Socket on Port " + IntegerToString(PULL_PORT) + "..");
return(INIT_FAILED);
}
else{
Print("[PUSH] Binding MT4 Server to Socket on Port " + IntegerToString(PULL_PORT) + "..");
pushSocket.setSendHighWaterMark(1);
pushSocket.setLinger(0);
}
// Receive commands from PUSH_PORT that client is sending to.
if(!pullSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUSH_PORT))){
Print("[PULL] ####ERROR#### Binding MT4 Server to Socket on Port " + IntegerToString(PUSH_PORT) + "..");
return(INIT_FAILED);
}
else{
Print("[PULL] Binding MT4 Server to Socket on Port " + IntegerToString(PUSH_PORT) + "..");
pullSocket.setReceiveHighWaterMark(1);
pullSocket.setLinger(0);
}
// Send new market data to PUB_PORT that client is subscribed to.
if(!pubSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUB_PORT))){
Print("[PUB] ####ERROR#### Binding MT4 Server to Socket on Port " + IntegerToString(PUB_PORT) + "..");
return(INIT_FAILED);
}
else{
Print("[PUB] Binding MT4 Server to Socket on Port " + IntegerToString(PUB_PORT) + "..");
pubSocket.setSendHighWaterMark(1);
pubSocket.setLinger(0);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
Print("[PUSH] Unbinding MT4 Server from Socket on Port " + IntegerToString(PULL_PORT) + "..");
pushSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PULL_PORT));
Print("[PULL] Unbinding MT4 Server from Socket on Port " + IntegerToString(PUSH_PORT) + "..");
pullSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUSH_PORT));
if (Publish_MarketData == true || Publish_MarketRates == true)
{
Print("[PUB] Unbinding MT4 Server from Socket on Port " + IntegerToString(PUB_PORT) + "..");
pubSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUB_PORT));
}
// Shutdown ZeroMQ Context
context.shutdown();
context.destroy(0);
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
/*
Use this OnTick() function to send market data to subscribed client.
*/
if(!IsStopped())
{
// Python clients can subscribe to a price feed for each tracked symbol
if(Publish_MarketData == true) {
for(int s = 0; s < ArraySize(Publish_Symbols); s++) {
string _tick = GetBidAsk(Publish_Symbols[s]);
// publish: topic=symbol msg=tick_data
ZmqMsg reply(StringFormat("%s %s", Publish_Symbols[s], _tick));
Print("Sending PRICE [" + reply.getData() + "] to PUB Socket");
if(!pubSocket.send(reply, true)){
Print("###ERROR### Sending price");
}
}
}
// Python clients can also subscribe to a rates feed for each tracked instrument
if(Publish_MarketRates == true){
for(int s = 0; s < ArraySize(Publish_Instruments); s++) {
MqlRates curr_rate[];
int count = Publish_Instruments[s].GetRates(curr_rate, 1);
// if last rate is returned and its timestamp is greater than the last published...
if(count > 0 && Publish_Instruments[s].getLastPublishTimestamp() < curr_rate[0].time){
// then send a new pub message with this new rate
string _rates = StringFormat("%u;%f;%f;%f;%f;%d;%d;%d",
curr_rate[0].time,
curr_rate[0].open,
curr_rate[0].high,
curr_rate[0].low,
curr_rate[0].close,
curr_rate[0].tick_volume,
curr_rate[0].spread,
curr_rate[0].real_volume);
ZmqMsg reply(StringFormat("%s %s", Publish_Instruments[s].name(), _rates));
Print("Sending Rates @"+TimeToStr(curr_rate[0].time) + " [" + reply.getData() + "] to PUB Socket");
if(!pubSocket.send(reply, true)){
Print("###ERROR### Sending rate");
}
// updates the timestamp
Publish_Instruments[s].setLastPublishTimestamp(curr_rate[0].time);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Expert timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
/*
Use this OnTimer() function to get and respond to commands
*/
// Get client's response, but don't block.
pullSocket.recv(request, true);
if (request.size() > 0)
{
// Wait
// pullSocket.recv(request,false);
// MessageHandler() should go here.
ZmqMsg reply = MessageHandler(request);
// Send response, and block
// pushSocket.send(reply);
// Send response, but don't block
if(!pushSocket.send(reply, true)){
Print("###ERROR### Sending message");
}
}
}
//+------------------------------------------------------------------+
ZmqMsg MessageHandler(ZmqMsg &_request) {
// Output object
ZmqMsg reply;
// Message components for later.
string components[11];
if(_request.size() > 0) {
// Get data from request
ArrayResize(_data, _request.size());
_request.getData(_data);
string dataStr = CharArrayToString(_data);
// Process data
ParseZmqMessage(dataStr, components);
// Interpret data
InterpretZmqMessage(pushSocket, components);
}
else {
// NO DATA RECEIVED
}
return(reply);
}
// Interpret Zmq Message and perform actions
void InterpretZmqMessage(Socket &pSocket, string &compArray[]) {
// Message Structures:
// 1) Trading
// TRADE|ACTION|TYPE|SYMBOL|PRICE|SL|TP|COMMENT|TICKET
// e.g. TRADE|OPEN|1|EURUSD|0|50|50|R-to-MetaTrader4|12345678
// The 12345678 at the end is the ticket ID, for MODIFY and CLOSE.
// 2) Data Requests
// 2.1) RATES|SYMBOL -> Returns Current Bid/Ask
// 2.2) DATA|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME
// NOTE: datetime has format: D'2015.01.01 00:00'
/*
compArray[0] = TRADE or RATES
If RATES -> compArray[1] = Symbol
If TRADE ->
compArray[0] = TRADE
compArray[1] = ACTION (e.g. OPEN, MODIFY, CLOSE)
compArray[2] = TYPE (e.g. OP_BUY, OP_SELL, etc - only used when ACTION=OPEN)
// ORDER TYPES:
// https://docs.mql4.com/constants/tradingconstants/orderproperties
// OP_BUY = 0
// OP_SELL = 1
// OP_BUYLIMIT = 2
// OP_SELLLIMIT = 3
// OP_BUYSTOP = 4
// OP_SELLSTOP = 5
compArray[3] = Symbol (e.g. EURUSD, etc.)
compArray[4] = Open/Close Price (ignored if ACTION = MODIFY)
compArray[5] = SL
compArray[6] = TP
compArray[7] = Trade Comment
compArray[8] = Lots
compArray[9] = Magic Number
compArray[10] = Ticket Number (MODIFY/CLOSE)
*/
int switch_action = 0;
/* 02-08-2019 10:41 CEST - HEARTBEAT */
if(compArray[0] == "HEARTBEAT")
InformPullClient(pSocket, "{'_action': 'heartbeat', '_response': 'loud and clear!'}");
/* Process Messages */
if(compArray[0] == "TRADE" && compArray[1] == "OPEN")
switch_action = 1;
if(compArray[0] == "TRADE" && compArray[1] == "MODIFY")
switch_action = 2;
if(compArray[0] == "TRADE" && compArray[1] == "CLOSE")
switch_action = 3;
if(compArray[0] == "TRADE" && compArray[1] == "CLOSE_PARTIAL")
switch_action = 4;
if(compArray[0] == "TRADE" && compArray[1] == "CLOSE_MAGIC")
switch_action = 5;
if(compArray[0] == "TRADE" && compArray[1] == "CLOSE_ALL")
switch_action = 6;
if(compArray[0] == "TRADE" && compArray[1] == "GET_OPEN_TRADES")
switch_action = 7;
if(compArray[0] == "DATA")
switch_action = 8;
/* Setup processing variables */
string zmq_ret = "";
string ret = "";
int ticket = -1;
bool ans = false;
/****************************
* PERFORM SOME CHECKS HERE *
****************************/
if (CheckOpsStatus(pSocket, switch_action) == true)
{
switch(switch_action)
{
case 1: // OPEN TRADE
zmq_ret = "{";
// Function definition:
ticket = DWX_OpenOrder(compArray[3], StringToInteger(compArray[2]), StringToDouble(compArray[8]),
StringToDouble(compArray[4]), StringToInteger(compArray[5]), StringToInteger(compArray[6]),
compArray[7], StringToInteger(compArray[9]), zmq_ret);
// Send TICKET back as JSON
InformPullClient(pSocket, zmq_ret + "}");
break;
case 2: // MODIFY SL/TP
zmq_ret = "{'_action': 'MODIFY'";
// Function definition:
ans = DWX_SetSLTP(StringToInteger(compArray[10]), StringToDouble(compArray[5]), StringToDouble(compArray[6]),
StringToInteger(compArray[9]), StringToInteger(compArray[2]), StringToDouble(compArray[4]),
compArray[3], 3, zmq_ret);
InformPullClient(pSocket, zmq_ret + "}");
break;
case 3: // CLOSE TRADE
zmq_ret = "{";
// IMPLEMENT CLOSE TRADE LOGIC HERE
DWX_CloseOrder_Ticket(StrToInteger(compArray[10]), zmq_ret);
InformPullClient(pSocket, zmq_ret + "}");
break;
case 4: // CLOSE PARTIAL
zmq_ret = "{";
ans = DWX_ClosePartial(StrToDouble(compArray[8]), zmq_ret, StrToInteger(compArray[10]));
InformPullClient(pSocket, zmq_ret + "}");
break;
case 5: // CLOSE MAGIC
zmq_ret = "{";
DWX_CloseOrder_Magic(StrToInteger(compArray[9]), zmq_ret);
InformPullClient(pSocket, zmq_ret + "}");
break;
case 6: // CLOSE ALL ORDERS
zmq_ret = "{";
DWX_CloseAllOrders(zmq_ret);
InformPullClient(pSocket, zmq_ret + "}");
break;
case 7: // GET OPEN ORDERS
zmq_ret = "{";
DWX_GetOpenOrders(zmq_ret);
InformPullClient(pSocket, zmq_ret + "}");
break;
case 8: // DATA REQUEST
zmq_ret = "{";
DWX_GetData(compArray, zmq_ret);
InformPullClient(pSocket, zmq_ret + "}");
break;
default:
break;
}
}
}
// CLOSE ORDER (by Ticket)
void DWX_CloseOrder_Ticket(int _ticket, string &zmq_ret) {
bool found = false;
zmq_ret = zmq_ret + "'_action': 'CLOSE', '_ticket': " + IntegerToString(_ticket);
for(int i=0; i<OrdersTotal(); i++) {
if (OrderSelect(i,SELECT_BY_POS)==true && OrderTicket() == _ticket) {
found = true;
if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
DWX_CloseAtMarket(-1, zmq_ret);
zmq_ret = zmq_ret + ", '_response': 'CLOSE_MARKET'";
} else {
zmq_ret = zmq_ret + ", '_response': 'CLOSE_PENDING'";
int tmpRet = OrderDelete(OrderTicket());
}
}
}
if(found == false) {
zmq_ret = zmq_ret + ", '_response': 'NOT_FOUND'";
}
else {
zmq_ret = zmq_ret + ", '_response_value': 'SUCCESS'";
}
}
// Check if operations are permitted
bool CheckOpsStatus(Socket &pSocket, int sFlag) {
if (sFlag <= 6) {
if (!IsTradeAllowed()) {
InformPullClient(pSocket, "{'_response': 'TRADING_IS_NOT_ALLOWED__ABORTED_COMMAND'}");
return(false);
}
else if (!IsExpertEnabled()) {
InformPullClient(pSocket, "{'_response': 'EA_IS_DISABLED__ABORTED_COMMAND'}");
return(false);
}
else if (IsTradeContextBusy()) {
InformPullClient(pSocket, "{'_response': 'TRADE_CONTEXT_BUSY__ABORTED_COMMAND'}");
return(false);
}
else if (!IsDllsAllowed()) {
InformPullClient(pSocket, "{'_response': 'DLLS_DISABLED__ABORTED_COMMAND'}");
return(false);
}
else if (!IsLibrariesAllowed()) {
InformPullClient(pSocket, "{'_response': 'LIBS_DISABLED__ABORTED_COMMAND'}");
return(false);
}
else if (!IsConnected()) {
InformPullClient(pSocket, "{'_response': 'NO_BROKER_CONNECTION__ABORTED_COMMAND'}");
return(false);
}
}
return(true);
}
// OPEN NEW ORDER
int DWX_OpenOrder(string _symbol, int _type, double _lots, double _price, double _SL, double _TP, string _comment, int _magic, string &zmq_ret) {
int ticket, error;
zmq_ret = zmq_ret + "'_action': 'EXECUTION'";
if(_lots > MaximumLotSize) {
zmq_ret = zmq_ret + ", " + "'_response': 'LOT_SIZE_ERROR', 'response_value': 'MAX_LOT_SIZE_EXCEEDED'";
return(-1);
}
double sl = _SL;
double tp = _TP;
// Else
if(DMA_MODE) {
sl = 0.0;
tp = 0.0;
}
if(_symbol == "NULL") {
ticket = OrderSend(Symbol(), _type, _lots, _price, MaximumSlippage, sl, tp, _comment, _magic);
} else {
ticket = OrderSend(_symbol, _type, _lots, _price, MaximumSlippage, sl, tp, _comment, _magic);
}
if(ticket < 0) {
// Failure
error = GetLastError();
zmq_ret = zmq_ret + ", " + "'_response': '" + IntegerToString(error) + "', 'response_value': '" + ErrorDescription(error) + "'";
return(-1*error);
}
int tmpRet = OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
zmq_ret = zmq_ret + ", " + "'_magic': " + IntegerToString(_magic) + ", '_ticket': " + IntegerToString(OrderTicket()) + ", '_open_time': '" + TimeToStr(OrderOpenTime(),TIME_DATE|TIME_SECONDS) + "', '_open_price': " + DoubleToString(OrderOpenPrice());
if(DMA_MODE) {
int retries = 3;
while(true) {
retries--;
if(retries < 0) return(0);
if((_SL == 0 && _TP == 0) || (OrderStopLoss() == _SL && OrderTakeProfit() == _TP)) {
return(ticket);
}
if(DWX_IsTradeAllowed(30, zmq_ret) == 1) {
if(DWX_SetSLTP(ticket, _SL, _TP, _magic, _type, _price, _symbol, retries, zmq_ret)) {
return(ticket);
}
if(retries == 0) {
zmq_ret = zmq_ret + ", '_response': 'ERROR_SETTING_SL_TP'";
return(-11111);
}
}
Sleep(MILLISECOND_TIMER);
}
zmq_ret = zmq_ret + ", '_response': 'ERROR_SETTING_SL_TP'";
zmq_ret = zmq_ret + "}";
return(-1);
}
// Send zmq_ret to Python Client
zmq_ret = zmq_ret + "}";
return(ticket);
}
//+------------------------------------------------------------------+
// SET SL/TP
bool DWX_SetSLTP(int ticket, double _SL, double _TP, int _magic, int _type, double _price, string _symbol, int retries, string &zmq_ret) {
if (OrderSelect(ticket, SELECT_BY_TICKET) == true)
{
int dir_flag = -1;
if (OrderType() == 0 || OrderType() == 2 || OrderType() == 4)
dir_flag = 1;
double vpoint = MarketInfo(OrderSymbol(), MODE_POINT);
int vdigits = (int)MarketInfo(OrderSymbol(), MODE_DIGITS);
if(OrderModify(ticket, OrderOpenPrice(), NormalizeDouble(OrderOpenPrice()-_SL*dir_flag*vpoint,vdigits), NormalizeDouble(OrderOpenPrice()+_TP*dir_flag*vpoint,vdigits), 0, 0)) {
zmq_ret = zmq_ret + ", '_sl': " + DoubleToString(_SL) + ", '_tp': " + DoubleToString(_TP);
return(true);
} else {
int error = GetLastError();
zmq_ret = zmq_ret + ", '_response': '" + IntegerToString(error) + "', '_response_value': '" + ErrorDescription(error) + "', '_sl_attempted': " + NormalizeDouble(OrderOpenPrice()-_SL*dir_flag*vpoint,vdigits) + ", '_tp_attempted': " + NormalizeDouble(OrderOpenPrice()+_TP*dir_flag*vpoint,vdigits);
if(retries == 0) {
RefreshRates();
DWX_CloseAtMarket(-1, zmq_ret);
// int lastOrderErrorCloseTime = TimeCurrent();
}
return(false);
}
}
// return(true);
return(false);
}
//+------------------------------------------------------------------+
// Set list of symbols to get real-time price data
void DWX_SetSymbolList(string& compArray[], string& zmq_ret) {
zmq_ret = zmq_ret + "'_action': 'TRACK_PRICES'";
// Format: TRACK_PRICES|SYMBOL_1|SYMBOL_2|...|SYMBOL_N
string result = "Tracking PRICES from";
int _num_symbols = ArraySize(compArray) - 1;
if(_num_symbols > 0){
ArrayResize(Publish_Symbols, _num_symbols);
for(int s=0; s<_num_symbols; s++){
Publish_Symbols[s] = compArray[s+1];
result += " " + Publish_Symbols[s];
}
zmq_ret = zmq_ret + ", '_data': {'symbol_count':" + IntegerToString(_num_symbols) + "}";
Publish_MarketData = true;
}
else {
Publish_MarketData = false;
ArrayResize(Publish_Symbols, 1);
zmq_ret = zmq_ret + ", '_data': {'symbol_count': 0}";
result += " NONE";
}
Print(result);
}
//+------------------------------------------------------------------+
// Get data for request datetime range
void DWX_GetData(string& compArray[], string& zmq_ret) {
// Format: DATA|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME
double price_array[];
datetime time_array[];
// Get prices
int price_count = CopyClose(compArray[1],
TFMigrate(StringToInteger(compArray[2])),
StringToTime(compArray[3]),
StringToTime(compArray[4]),
price_array);
// Get timestamps
int time_count = CopyTime(compArray[1],
TFMigrate(StringToInteger(compArray[2])), //
StringToTime(compArray[3]), // datetime
StringToTime(compArray[4]), // datetime
time_array); // Array datetime
// int CopyTime(
// string symbol_name, // symbol name
// ENUM_TIMEFRAMES timeframe, // period
// datetime start_time, // start date and time
// datetime stop_time, // stop date and time
// datetime time_array[] // target array to copy open times
//);
zmq_ret = zmq_ret + "'_action': 'DATA'";
if (price_count > 0) {
zmq_ret = zmq_ret + ", '_data': {";
// Construct string of price|price|price|.. etc and send to PULL client.
for(int i = 0; i < price_count; i++ ) {
if(i == 0)
zmq_ret = zmq_ret + "'" + TimeToString(time_array[i]) + "': " + DoubleToString(price_array[i]);
else
zmq_ret = zmq_ret + ", '" + TimeToString(time_array[i]) + "': " + DoubleToString(price_array[i]);
}
zmq_ret = zmq_ret + "}";
}
else {
zmq_ret = zmq_ret + ", " + "'_response': 'NOT_AVAILABLE'";
}
}
//+------------------------------------------------------------------+
// Get historic for request datetime range
void DWX_GetHist(string& compArray[], string& zmq_ret) {
// Format: HIST|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME
MqlRates rates_array[];
// Get prices
int rates_count = CopyRates(compArray[1],
TFMigrate(StringToInteger(compArray[2])),
StringToTime(compArray[3]),
StringToTime(compArray[4]), rates_array);
zmq_ret = zmq_ret + "'_action': 'HIST'";
// if data then forms response as json:
// {'_action: 'HIST',
// '_data':[{'time': 'YYYY:MM:DD,HH:MM:SS', 'open':0.0, 'high':0.0, 'low':0.0, 'close':0.0, 'tick_volume:0, 'spread':0, 'real_volume':0},
// {...},
// ...
// ]
// }
if (rates_count > 0) {
zmq_ret = zmq_ret + ", '_data': [";
// Construct string of rates and send to PULL client.
for(int i = 0; i < rates_count; i++ ) {
if(i == 0)
zmq_ret = zmq_ret + "{'time':'" + TimeToString(rates_array[i].time) + "', 'open':" + DoubleToString(rates_array[i].open) + ", 'high':" + DoubleToString(rates_array[i].high) + ", 'low':" + DoubleToString(rates_array[i].low) + ", 'close':" + DoubleToString(rates_array[i].close) + ", 'tick_volume':" + IntegerToString(rates_array[i].tick_volume) + ", 'spread':" + IntegerToString(rates_array[i].spread) + ", 'real_volume':" + IntegerToString(rates_array[i].real_volume) + "}";
else
zmq_ret = zmq_ret + ", {'time':'" + TimeToString(rates_array[i].time) + "', 'open':" + DoubleToString(rates_array[i].open) + ", 'high':" + DoubleToString(rates_array[i].high) + ", 'low':" + DoubleToString(rates_array[i].low) + ", 'close':" + DoubleToString(rates_array[i].close) + ", 'tick_volume':" + IntegerToString(rates_array[i].tick_volume) + ", 'spread':" + IntegerToString(rates_array[i].spread) + ", 'real_volume':" + IntegerToString(rates_array[i].real_volume) + "}";
}
zmq_ret = zmq_ret + "]";
}
// if NO data then forms response as json:
// {'_action: 'HIST',
// '_response': 'NOT_AVAILABLE'
// }
else {
zmq_ret = zmq_ret + ", " + "'_response': 'NOT_AVAILABLE'";
}
}
//+------------------------------------------------------------------+
// CLOSE ORDER (by Magic Number)
void DWX_CloseOrder_Magic(int _magic, string &zmq_ret) {
bool found = false;
zmq_ret = zmq_ret + "'_action': 'CLOSE_ALL_MAGIC'";
zmq_ret = zmq_ret + ", '_magic': " + IntegerToString(_magic);
zmq_ret = zmq_ret + ", '_responses': {";
for(int i=OrdersTotal()-1; i >= 0; i--) {
if (OrderSelect(i,SELECT_BY_POS)==true && OrderMagicNumber() == _magic) {
found = true;
zmq_ret = zmq_ret + IntegerToString(OrderTicket()) + ": {'_symbol':'" + OrderSymbol() + "'";
if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
DWX_CloseAtMarket(-1, zmq_ret);
zmq_ret = zmq_ret + ", '_response': 'CLOSE_MARKET'";
if (i != 0)
zmq_ret = zmq_ret + "}, ";
else
zmq_ret = zmq_ret + "}";
} else {
zmq_ret = zmq_ret + ", '_response': 'CLOSE_PENDING'";
if (i != 0)
zmq_ret = zmq_ret + "}, ";
else
zmq_ret = zmq_ret + "}";
int tmpRet = OrderDelete(OrderTicket());
}
}
}
zmq_ret = zmq_ret + "}";
if(found == false) {
zmq_ret = zmq_ret + ", '_response': 'NOT_FOUND'";
}
else {
zmq_ret = zmq_ret + ", '_response_value': 'SUCCESS'";
}
}
//+------------------------------------------------------------------+
// Set list of instruments to get OHLC rates
void DWX_SetInstrumentList(string& compArray[], string& zmq_ret) {
zmq_ret = zmq_ret + "'_action': 'TRACK_RATES'";
// Format: TRACK_RATES|SYMBOL_1|TIMEFRAME_1|SYMBOL_2|TIMEFRAME_2|...|SYMBOL_N|TIMEFRAME_N
string result = "Tracking RATES from";
int _num_instruments = (ArraySize(compArray) - 1)/2;
if(_num_instruments > 0){
ArrayResize(Publish_Instruments, _num_instruments);
for(int s=0; s<_num_instruments; s++){
Publish_Instruments[s].setup(compArray[(2*s)+1], (ENUM_TIMEFRAMES)StringToInteger(compArray[(2*s)+2]));
result += " " + Publish_Instruments[s].name();
}
zmq_ret = zmq_ret + ", '_data': {'instrument_count':" + IntegerToString(_num_instruments) + "}";
Publish_MarketRates = true;
}
else {
Publish_MarketRates = false;
ArrayResize(Publish_Instruments, 1);
zmq_ret = zmq_ret + ", '_data': {'instrument_count': 0}";
result += " NONE";
}
Print(result);
}
//+------------------------------------------------------------------+
// Inform Client
void InformPullClient(Socket& pSocket, string message) {
ZmqMsg pushReply(StringFormat("%s", message));
pSocket.send(pushReply,true); // NON-BLOCKING
}
// Parse Zmq Message
void ParseZmqMessage(string& message, string& retArray[]) {
//Print("Parsing: " + message);
string sep = ";";
ushort u_sep = StringGetCharacter(sep,0);
int splits = StringSplit(message, u_sep, retArray);
/*
for(int i = 0; i < splits; i++) {
Print(IntegerToString(i) + ") " + retArray[i]);
}
*/
}
//+------------------------------------------------------------------+
// Generate string for Bid/Ask by symbol
string GetBidAsk(string symbol) {
MqlTick last_tick;
if(SymbolInfoTick(symbol,last_tick))
{
return(StringFormat("%f;%f", last_tick.bid, last_tick.ask));
}
// Default
return "";
}
//+------------------------------------------------------------------+
// CLOSE AT MARKET
bool DWX_CloseAtMarket(double size, string &zmq_ret) {
int error;
int retries = 3;
while(true) {
retries--;
if(retries < 0) return(false);
if(DWX_IsTradeAllowed(30, zmq_ret) == 1) {
if(DWX_ClosePartial(size, zmq_ret)) {
// trade successfuly closed
return(true);
} else {
error = GetLastError();
zmq_ret = zmq_ret + ", '_response': '" + IntegerToString(error) + "', '_response_value': '" + ErrorDescription(error) + "'";
}
}
}
return(false);
}
//+------------------------------------------------------------------+
// CLOSE PARTIAL SIZE
bool DWX_ClosePartial(double size, string &zmq_ret, int ticket = 0) {
RefreshRates();
double priceCP;
bool close_ret = False;
if(OrderType() != OP_BUY && OrderType() != OP_SELL) {
return(true);
}
if(OrderType() == OP_BUY) {
priceCP = DWX_GetBid(OrderSymbol());
} else {
priceCP = DWX_GetAsk(OrderSymbol());
}
// If the function is called directly, setup init() JSON here.
if(ticket != 0) {
zmq_ret = zmq_ret + "'_action': 'CLOSE', '_ticket': " + IntegerToString(ticket);
zmq_ret = zmq_ret + ", '_response': 'CLOSE_PARTIAL'";
}
int local_ticket = 0;
if (ticket != 0)
local_ticket = ticket;
else
local_ticket = OrderTicket();
if(size < 0.01 || size > OrderLots()) {
close_ret = OrderClose(local_ticket, OrderLots(), priceCP, MaximumSlippage);
zmq_ret = zmq_ret + ", '_close_price': " + DoubleToString(priceCP) + ", '_close_lots': " + DoubleToString(OrderLots());
return(close_ret);
} else {
close_ret = OrderClose(local_ticket, size, priceCP, MaximumSlippage);
zmq_ret = zmq_ret + ", '_close_price': " + DoubleToString(priceCP) + ", '_close_lots': " + DoubleToString(size);
return(close_ret);
}
}
//+------------------------------------------------------------------+
// CLOSE ALL ORDERS
void DWX_CloseAllOrders(string &zmq_ret) {
bool found = false;
zmq_ret = zmq_ret + "'_action': 'CLOSE_ALL'";
zmq_ret = zmq_ret + ", '_responses': {";
for(int i=OrdersTotal()-1; i >= 0; i--) {
if (OrderSelect(i,SELECT_BY_POS)==true) {
found = true;
zmq_ret = zmq_ret + IntegerToString(OrderTicket()) + ": {'_symbol':'" + OrderSymbol() + "', '_magic': " + IntegerToString(OrderMagicNumber());
if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
DWX_CloseAtMarket(-1, zmq_ret);
zmq_ret = zmq_ret + ", '_response': 'CLOSE_MARKET'";
if (i != 0)
zmq_ret = zmq_ret + "}, ";
else
zmq_ret = zmq_ret + "}";
} else {
zmq_ret = zmq_ret + ", '_response': 'CLOSE_PENDING'";
if (i != 0)
zmq_ret = zmq_ret + "}, ";
else
zmq_ret = zmq_ret + "}";
int tmpRet = OrderDelete(OrderTicket());
}
}
}
zmq_ret = zmq_ret + "}";
if(found == false) {
zmq_ret = zmq_ret + ", '_response': 'NOT_FOUND'";
}
else {
zmq_ret = zmq_ret + ", '_response_value': 'SUCCESS'";
}
}
//+------------------------------------------------------------------+
// GET OPEN ORDERS
void DWX_GetOpenOrders(string &zmq_ret) {
bool found = false;
zmq_ret = zmq_ret + "'_action': 'OPEN_TRADES'";
zmq_ret = zmq_ret + ", '_trades': {";
for(int i=OrdersTotal()-1; i>=0; i--) {
found = true;
if (OrderSelect(i,SELECT_BY_POS)==true) {
zmq_ret = zmq_ret + IntegerToString(OrderTicket()) + ": {";
zmq_ret = zmq_ret + "'_magic': " + IntegerToString(OrderMagicNumber()) + ", '_symbol': '" + OrderSymbol() + "', '_lots': " + DoubleToString(OrderLots()) + ", '_type': " + IntegerToString(OrderType()) + ", '_open_price': " + DoubleToString(OrderOpenPrice()) + ", '_open_time': '" + TimeToStr(OrderOpenTime(),TIME_DATE|TIME_SECONDS) + "', '_SL': " + DoubleToString(OrderStopLoss()) + ", '_TP': " + DoubleToString(OrderTakeProfit()) + ", '_pnl': " + DoubleToString(OrderProfit()) + ", '_comment': '" + OrderComment() + "'";
if (i != 0)
zmq_ret = zmq_ret + "}, ";
else
zmq_ret = zmq_ret + "}";
}
}
zmq_ret = zmq_ret + "}";
}
//+------------------------------------------------------------------+
// CHECK IF TRADE IS ALLOWED
int DWX_IsTradeAllowed(int MaxWaiting_sec, string &zmq_ret) {
if(!IsTradeAllowed()) {
int StartWaitingTime = (int)GetTickCount();
zmq_ret = zmq_ret + ", " + "'_response': 'TRADE_CONTEXT_BUSY'";
while(true) {
if(IsStopped()) {
zmq_ret = zmq_ret + ", " + "'_response_value': 'EA_STOPPED_BY_USER'";
return(-1);
}
int diff = (int)(GetTickCount() - StartWaitingTime);
if(diff > MaxWaiting_sec * 1000) {
zmq_ret = zmq_ret + ", '_response': 'WAIT_LIMIT_EXCEEDED', '_response_value': " + IntegerToString(MaxWaiting_sec);
return(-2);
}
// if the trade context has become free,
if(IsTradeAllowed()) {
zmq_ret = zmq_ret + ", '_response': 'TRADE_CONTEXT_NOW_FREE'";
RefreshRates();
return(1);
}
}
} else {
return(1);
}
return(1);
}
//+------------------------------------------------------------------+
string ErrorDescription(int error_code)
{
string error_string;
//----
switch(error_code)
{
//---- codes returned from trade server
case 0:
case 1: error_string="no error"; break;
case 2: error_string="common error"; break;
case 3: error_string="invalid trade parameters"; break;
case 4: error_string="trade server is busy"; break;
case 5: error_string="old version of the client terminal"; break;
case 6: error_string="no connection with trade server"; break;
case 7: error_string="not enough rights"; break;
case 8: error_string="too frequent requests"; break;
case 9: error_string="malfunctional trade operation (never returned error)"; break;
case 64: error_string="account disabled"; break;
case 65: error_string="invalid account"; break;
case 128: error_string="trade timeout"; break;
case 129: error_string="invalid price"; break;
case 130: error_string="invalid stops"; break;
case 131: error_string="invalid trade volume"; break;
case 132: error_string="market is closed"; break;
case 133: error_string="trade is disabled"; break;
case 134: error_string="not enough money"; break;
case 135: error_string="price changed"; break;
case 136: error_string="off quotes"; break;
case 137: error_string="broker is busy (never returned error)"; break;
case 138: error_string="requote"; break;
case 139: error_string="order is locked"; break;
case 140: error_string="long positions only allowed"; break;
case 141: error_string="too many requests"; break;
case 145: error_string="modification denied because order too close to market"; break;
case 146: error_string="trade context is busy"; break;
case 147: error_string="expirations are denied by broker"; break;
case 148: error_string="amount of open and pending orders has reached the limit"; break;
case 149: error_string="hedging is prohibited"; break;
case 150: error_string="prohibited by FIFO rules"; break;
//---- mql4 errors
case 4000: error_string="no error (never generated code)"; break;
case 4001: error_string="wrong function pointer"; break;
case 4002: error_string="array index is out of range"; break;
case 4003: error_string="no memory for function call stack"; break;
case 4004: error_string="recursive stack overflow"; break;
case 4005: error_string="not enough stack for parameter"; break;
case 4006: error_string="no memory for parameter string"; break;
case 4007: error_string="no memory for temp string"; break;
case 4008: error_string="not initialized string"; break;
case 4009: error_string="not initialized string in array"; break;
case 4010: error_string="no memory for array\' string"; break;
case 4011: error_string="too long string"; break;
case 4012: error_string="remainder from zero divide"; break;
case 4013: error_string="zero divide"; break;
case 4014: error_string="unknown command"; break;
case 4015: error_string="wrong jump (never generated error)"; break;
case 4016: error_string="not initialized array"; break;
case 4017: error_string="dll calls are not allowed"; break;
case 4018: error_string="cannot load library"; break;
case 4019: error_string="cannot call function"; break;
case 4020: error_string="expert function calls are not allowed"; break;
case 4021: error_string="not enough memory for temp string returned from function"; break;
case 4022: error_string="system is busy (never generated error)"; break;
case 4050: error_string="invalid function parameters count"; break;
case 4051: error_string="invalid function parameter value"; break;
case 4052: error_string="string function internal error"; break;
case 4053: error_string="some array error"; break;
case 4054: error_string="incorrect series array using"; break;
case 4055: error_string="custom indicator error"; break;
case 4056: error_string="arrays are incompatible"; break;
case 4057: error_string="global variables processing error"; break;
case 4058: error_string="global variable not found"; break;
case 4059: error_string="function is not allowed in testing mode"; break;
case 4060: error_string="function is not confirmed"; break;
case 4061: error_string="send mail error"; break;
case 4062: error_string="string parameter expected"; break;
case 4063: error_string="integer parameter expected"; break;
case 4064: error_string="double parameter expected"; break;
case 4065: error_string="array as parameter expected"; break;
case 4066: error_string="requested history data in update state"; break;
case 4099: error_string="end of file"; break;
case 4100: error_string="some file error"; break;
case 4101: error_string="wrong file name"; break;
case 4102: error_string="too many opened files"; break;
case 4103: error_string="cannot open file"; break;
case 4104: error_string="incompatible access to a file"; break;
case 4105: error_string="no order selected"; break;
case 4106: error_string="unknown symbol"; break;
case 4107: error_string="invalid price parameter for trade function"; break;
case 4108: error_string="invalid ticket"; break;
case 4109: error_string="trade is not allowed in the expert properties"; break;
case 4110: error_string="longs are not allowed in the expert properties"; break;
case 4111: error_string="shorts are not allowed in the expert properties"; break;
case 4200: error_string="object is already exist"; break;
case 4201: error_string="unknown object property"; break;
case 4202: error_string="object is not exist"; break;
case 4203: error_string="unknown object type"; break;
case 4204: error_string="no object name"; break;
case 4205: error_string="object coordinates error"; break;
case 4206: error_string="no specified subwindow"; break;
default: error_string="unknown error";
}
//----
return(error_string);
}
double DWX_GetAsk(string symbol) {
if(symbol == "NULL") {
return(Ask);
} else {
return(SymbolInfoDouble(symbol,SYMBOL_ASK));
}
}
//+------------------------------------------------------------------+
double DWX_GetBid(string symbol) {
if(symbol == "NULL") {
return(Bid);
} else {
return(SymbolInfoDouble(symbol,SYMBOL_BID));
}
}
///////////////////// Até aqui está ok
Darwinex could you add MT5 fork of this great project? thx.
@geekaia Please edit your post and use triple-back-ticks for code.
Thank you for this great contribution @geekaia - much appreciated! 👏
We'll test it and credit you with the work once released on GitHub - many thanks for your invaluable contributions guys, this really makes our day! 🙏
p.s. for now, I've updated your post so the code is shown appropriately.
Great contribution!
It's here. https://github.com/geekaia/mt5teste
@integracore2 is this okay to use it in MT5?
It's here. https://github.com/geekaia/mt5teste
I downloaded these files and I moved the dwx mq5 file into the Expert/Advisor folder in my metatrader5, yet...I can't see the EA after refreshing
I use a Mac, and I was thinking if it makes any difference.
Although I had tried using the mq4 for metatrader4, it worked successfully but the mq5 file wouldn't work on metatrader5
And which folders would the remaining 3 "mqh" files be moved to?
@Folarinosuolale i am using mac too i tried dwx-zeromq-connector for mt4 i got error. it says cannot open the file.
Try this https://github.com/darwinex/dwxconnect
I think there's an update here