MQL5 ZeroMQ Wrapper Library
A comprehensive reusable MQL5 wrapper library for ZeroMQ socket operations, designed for real-time communication between MetaTrader 5 and external applications.
Table of Contents
- Overview
- Architecture
- Prerequisites and Installation
- API Reference
- Usage Guide
- Socket Patterns
- Message Protocol
- Complete Examples
- Error Handling
- Best Practices
- Troubleshooting
Overview
This library provides a high-level MQL5 wrapper around the native ZeroMQ (libzmq) library, enabling MetaTrader 5 Expert Advisors and indicators to communicate with external applications via TCP sockets.
For the companion Rust client library, see Rust-ZMQ Library for SUM3API.
Key Features
- Simple API: Object-oriented wrapper class with intuitive methods
- Multiple Socket Types: Support for PUB, SUB, REQ, and REP patterns
- Non-blocking Operations: Configurable blocking/non-blocking send and receive
- UTF-8 Support: Automatic string encoding/decoding
- Resource Management: Automatic cleanup on destruction
Supported Socket Types
| Constant | Value | Description |
|---|---|---|
ZMQ_PUB |
1 | Publisher socket for broadcasting messages |
ZMQ_SUB |
2 | Subscriber socket for receiving broadcasts |
ZMQ_REQ |
3 | Request socket for request/reply pattern (client) |
ZMQ_REP |
4 | Reply socket for request/reply pattern (server) |
Architecture
System Overview
flowchart LR
subgraph MT5["MetaTrader 5"]
EA["ZmqPublisher EA"]
CZmq["CZmq Wrapper"]
DLL["libzmq.dll"]
EA --> CZmq
CZmq --> DLL
end
subgraph Network["ZeroMQ TCP/IP"]
PUB["PUB Socket<br/>tcp://0.0.0.0:5555"]
REP["REP Socket<br/>tcp://0.0.0.0:5556"]
end
subgraph Client["External Client"]
SUB["SUB Socket"]
REQ["REQ Socket"]
APP["Application<br/>(Rust/Go/Java/C++)"]
SUB --> APP
REQ --> APP
end
DLL --> PUB
DLL --> REP
PUB -->|"Tick Data (JSON)"| SUB
REQ <-->|"Order Request/Response"| REP
Communication Flow
sequenceDiagram
participant MT5 as MetaTrader 5
participant PUB as PUB Socket :5555
participant SUB as SUB Socket
participant Client as External Client
participant REQ as REQ Socket
participant REP as REP Socket :5556
Note over MT5,Client: Tick Data Publishing (PUB/SUB)
MT5->>PUB: OnTick() - Create JSON
PUB->>SUB: Broadcast tick data
SUB->>Client: Parse and display
Note over MT5,Client: Order Handling (REQ/REP)
Client->>REQ: Create order request
REQ->>REP: Send JSON request
REP->>MT5: Receive and parse
MT5->>MT5: Execute order
MT5->>REP: Create response
REP->>REQ: Send JSON response
REQ->>Client: Parse result
Pattern Details
Tick Data Publishing (PUB/SUB Pattern)
- EA binds PUB socket to
tcp://0.0.0.0:5555 - External client subscribes via SUB socket
- EA publishes tick data as JSON on every tick
- EA binds PUB socket to
Order Handling (REQ/REP Pattern)
- EA binds REP socket to
tcp://0.0.0.0:5556 - External client sends order requests via REQ socket
- EA processes orders and sends responses
- EA binds REP socket to
Prerequisites and Installation
Required Files
Place the following files in your MetaTrader 5 installation directory:
MQL5/
|-- Libraries/
| |-- libzmq.dll # ZeroMQ core library
| |-- libsodium.dll # Cryptographic dependency for libzmq
|
|-- Include/
| |-- Zmq/
| |-- Zmq.mqh # MQL5 wrapper class
|
|-- Experts/
|-- ZmqPublisher.mq5 # Example Expert Advisor
Installation Steps
Download ZeroMQ Libraries
- Download
libzmq.dll(v4.3.x or later) from ZeroMQ releases - Download
libsodium.dllfrom libsodium releases - Both DLLs must be the same architecture (x64 for 64-bit MT5)
- Download
Copy Files
Copy libzmq.dll --> MQL5/Libraries/ Copy libsodium.dll --> MQL5/Libraries/ Copy Zmq.mqh --> MQL5/Include/Zmq/Enable DLL Imports in MetaTrader 5
- Go to
Tools > Options > Expert Advisors - Enable "Allow DLL imports"
- Disable "Allow DLL imports only for signed DLLs" (or sign the DLLs)
- Go to
Compile Your EA
- Open MetaEditor
- Include the wrapper:
#include <Zmq/Zmq.mqh> - Compile your Expert Advisor
API Reference
Class: CZmq
The main wrapper class for ZeroMQ operations.
Constructor and Destructor
CZmq()
Creates a new CZmq instance. Does not initialize any ZMQ resources.
~CZmq()
Destructor. Automatically calls Shutdown() to clean up resources.
Init
bool Init(int type)
Initializes the ZeroMQ context and creates a socket of the specified type.
Parameters:
| Name | Type | Description |
|---|---|---|
type |
int |
Socket type: ZMQ_PUB, ZMQ_SUB, ZMQ_REQ, or ZMQ_REP |
Returns:
trueif initialization succeededfalseif context or socket creation failed
Example:
CZmq *publisher = new CZmq();
if(!publisher.Init(ZMQ_PUB)) {
Print("Failed to initialize ZMQ publisher");
return INIT_FAILED;
}
Bind
bool Bind(string endpoint)
Binds the socket to a local endpoint. Typically used by server-side sockets (PUB, REP).
Parameters:
| Name | Type | Description |
|---|---|---|
endpoint |
string |
ZMQ endpoint URL (e.g., "tcp://0.0.0.0:5555") |
Returns:
trueif binding succeededfalseif binding failed (check logs for error code)
Endpoint Formats:
| Format | Description |
|---|---|
tcp://*:5555 |
Bind to all interfaces on port 5555 |
tcp://0.0.0.0:5555 |
Same as above |
tcp://127.0.0.1:5555 |
Bind to localhost only |
ipc:///tmp/socket |
Inter-process communication (Unix only) |
Example:
if(!publisher.Bind("tcp://0.0.0.0:5555")) {
Print("Failed to bind to port 5555");
return INIT_FAILED;
}
Connect
bool Connect(string endpoint)
Connects the socket to a remote endpoint. Typically used by client-side sockets (SUB, REQ).
Parameters:
| Name | Type | Description |
|---|---|---|
endpoint |
string |
ZMQ endpoint URL (e.g., "tcp://127.0.0.1:5555") |
Returns:
trueif connection initiated successfullyfalseif connection failed
Example:
CZmq *subscriber = new CZmq();
subscriber.Init(ZMQ_SUB);
if(!subscriber.Connect("tcp://127.0.0.1:5555")) {
Print("Failed to connect to publisher");
}
Send
int Send(string message, bool nonBlocking = true)
Sends a string message through the socket.
Parameters:
| Name | Type | Description |
|---|---|---|
message |
string |
The message to send (UTF-8 encoded) |
nonBlocking |
bool |
If true, returns immediately. If false, blocks until sent. Default: true |
Returns:
- Number of bytes sent on success
-1on failure
Example:
string json = "{\"symbol\":\"EURUSD\",\"bid\":1.1234}";
int bytes = publisher.Send(json, false); // Blocking send
if(bytes < 0) {
Print("Send failed");
}
Receive
string Receive(bool nonBlocking = true)
Receives a message from the socket.
Parameters:
| Name | Type | Description |
|---|---|---|
nonBlocking |
bool |
If true, returns immediately with empty string if no message. If false, blocks until message received. Default: true |
Returns:
- Received message as string on success
- Empty string
""if no message available (non-blocking) or on error
Buffer Size:
- Maximum receive buffer is 4096 bytes
- For larger messages, modify the
buffer[4096]inZmq.mqh
Example:
// Non-blocking receive (polling)
string msg = responder.Receive(true);
if(msg != "") {
Print("Received: ", msg);
}
// Blocking receive (waits for message)
string msg = requester.Receive(false);
Shutdown
void Shutdown()
Closes the socket and terminates the ZMQ context. Should be called during cleanup.
Example:
void OnDeinit(const int reason) {
if(g_publisher != NULL) {
g_publisher.Shutdown();
delete g_publisher;
g_publisher = NULL;
}
}
Usage Guide
Step 1: Include the Library
#include <Zmq/Zmq.mqh>
Step 2: Declare Global Instance
CZmq *g_publisher; // Declare as pointer for proper lifecycle management
Step 3: Initialize in OnInit()
int OnInit() {
g_publisher = new CZmq();
if(!g_publisher.Init(ZMQ_PUB)) {
Print("ZMQ initialization failed");
return INIT_FAILED;
}
if(!g_publisher.Bind("tcp://0.0.0.0:5555")) {
Print("ZMQ bind failed");
return INIT_FAILED;
}
Print("ZMQ Publisher ready on port 5555");
return INIT_SUCCEEDED;
}
Step 4: Use in OnTick()
void OnTick() {
MqlTick tick;
if(SymbolInfoTick(_Symbol, tick)) {
string json;
StringConcatenate(json,
"{\"symbol\":\"", _Symbol,
"\",\"bid\":", DoubleToString(tick.bid, _Digits),
",\"ask\":", DoubleToString(tick.ask, _Digits),
"}");
g_publisher.Send(json);
}
}
Step 5: Cleanup in OnDeinit()
void OnDeinit(const int reason) {
if(g_publisher != NULL) {
g_publisher.Shutdown();
delete g_publisher;
g_publisher = NULL;
}
}
Socket Patterns
PUB/SUB Pattern (One-to-Many Broadcasting)
flowchart LR
PUB["Publisher\n(MT5 EA)"]
SUB1["Subscriber 1\n(Rust App)"]
SUB2["Subscriber 2\n(Go Service)"]
SUB3["Subscriber 3\n(Java Dashboard)"]
PUB -->|"Tick JSON"| SUB1
PUB -->|"Tick JSON"| SUB2
PUB -->|"Tick JSON"| SUB3
Used for real-time data streaming where the publisher broadcasts to all connected subscribers.
MQL5 Side (Publisher):
CZmq *publisher = new CZmq();
publisher.Init(ZMQ_PUB);
publisher.Bind("tcp://0.0.0.0:5555");
// In OnTick
publisher.Send("{\"bid\": 1.1234}");
Rust Client Side (Subscriber):
use zeromq::{Socket, SubSocket};
let mut socket = SubSocket::new();
socket.connect("tcp://127.0.0.1:5555").await?;
socket.subscribe("").await?; // Subscribe to all messages
loop {
let msg = socket.recv().await?;
println!("Received: {:?}", msg);
}
Go Client Side (Subscriber):
package main
import (
"fmt"
zmq "github.com/pebbe/zmq4"
)
func main() {
subscriber, _ := zmq.NewSocket(zmq.SUB)
defer subscriber.Close()
subscriber.Connect("tcp://127.0.0.1:5555")
subscriber.SetSubscribe("") // Subscribe to all messages
for {
msg, _ := subscriber.Recv(0)
fmt.Printf("Received: %s\n", msg)
}
}
REQ/REP Pattern (Request-Reply)
sequenceDiagram
participant Client
participant REQ as REQ Socket
participant REP as REP Socket
participant MT5 as MT5 EA
Client->>REQ: market_buy request
REQ->>REP: Send JSON
REP->>MT5: Receive()
MT5->>MT5: g_trade.Buy()
MT5->>REP: Send response
REP->>REQ: JSON response
REQ->>Client: {success: true, ticket: 12345}
Used for command-response communication, such as order execution.
MQL5 Side (Responder):
CZmq *responder = new CZmq();
responder.Init(ZMQ_REP);
responder.Bind("tcp://0.0.0.0:5556");
// In OnTick (non-blocking poll)
string request = responder.Receive(true);
if(request != "") {
// Process request
string response = ProcessOrderRequest(request);
responder.Send(response, false); // Blocking send required for REP
}
Rust Client Side (Requester):
use zeromq::{Socket, ReqSocket};
let mut socket = ReqSocket::new();
socket.connect("tcp://127.0.0.1:5556").await?;
// Send order request
let request = r#"{"type":"market_buy","symbol":"EURUSD","volume":0.01}"#;
socket.send(request.into()).await?;
// Wait for response
let response = socket.recv().await?;
println!("Response: {:?}", response);
Go Client Side (Requester):
package main
import (
"fmt"
zmq "github.com/pebbe/zmq4"
)
func main() {
requester, _ := zmq.NewSocket(zmq.REQ)
defer requester.Close()
requester.Connect("tcp://127.0.0.1:5556")
// Send order request
request := `{"type":"market_buy","symbol":"EURUSD","volume":0.01}`
requester.Send(request, 0)
// Wait for response
response, _ := requester.Recv(0)
fmt.Printf("Response: %s\n", response)
}
Message Protocol
Tick Data Message (PUB Socket)
Published on every tick from MQL5 to connected subscribers.
{
"symbol": "XAUUSDc",
"bid": 2345.67,
"ask": 2345.89,
"time": 1706400000,
"volume": 100,
"balance": 10000.00,
"equity": 10150.50,
"margin": 500.00,
"free_margin": 9650.50,
"min_lot": 0.01,
"max_lot": 100.00,
"lot_step": 0.01,
"positions": [
{
"ticket": 12345,
"type": "BUY",
"volume": 0.10,
"price": 2340.50,
"profit": 15.25
}
],
"orders": [
{
"ticket": 12346,
"type": "BUY LIMIT",
"volume": 0.05,
"price": 2330.00
}
]
}
Order Request Message (REQ Socket)
Sent from external client to MQL5 for order execution.
Market Order:
{
"type": "market_buy",
"symbol": "XAUUSDc",
"volume": 0.01,
"price": 0
}
Limit Order:
{
"type": "limit_buy",
"symbol": "XAUUSDc",
"volume": 0.01,
"price": 2340.00
}
Close Position:
{
"type": "close_position",
"ticket": 12345
}
Cancel Order:
{
"type": "cancel_order",
"ticket": 12346
}
Download History:
{
"type": "download_history",
"symbol": "XAUUSDc",
"timeframe": "M1",
"start": "2024.01.01",
"end": "2024.01.31",
"mode": "OHLC"
}
Order Response Message (REP Socket)
Sent from MQL5 back to the client.
Success:
{
"success": true,
"ticket": 12347
}
Failure:
{
"success": false,
"error": "Error 10019: Not enough money"
}
Supported Order Types
| Type String | Description |
|---|---|
market_buy |
Execute market buy order |
market_sell |
Execute market sell order |
limit_buy |
Place buy limit pending order |
limit_sell |
Place sell limit pending order |
stop_buy |
Place buy stop pending order |
stop_sell |
Place sell stop pending order |
close_position |
Close existing position by ticket |
cancel_order |
Delete pending order by ticket |
download_history |
Request historical data download |
Complete Examples
Example 1: Simple Tick Publisher
//+------------------------------------------------------------------+
//| SimpleTickPublisher.mq5 |
//+------------------------------------------------------------------+
#include <Zmq/Zmq.mqh>
input string InpAddress = "tcp://0.0.0.0:5555";
CZmq *g_publisher;
int OnInit() {
g_publisher = new CZmq();
if(!g_publisher.Init(ZMQ_PUB)) {
Print("Failed to init ZMQ");
return INIT_FAILED;
}
if(!g_publisher.Bind(InpAddress)) {
Print("Failed to bind");
return INIT_FAILED;
}
Print("Publisher ready on ", InpAddress);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason) {
if(g_publisher != NULL) {
g_publisher.Shutdown();
delete g_publisher;
}
}
void OnTick() {
MqlTick tick;
if(SymbolInfoTick(_Symbol, tick)) {
string json;
StringConcatenate(json,
"{\"symbol\":\"", _Symbol,
"\",\"bid\":", DoubleToString(tick.bid, _Digits),
",\"ask\":", DoubleToString(tick.ask, _Digits),
",\"time\":", IntegerToString(tick.time),
"}");
g_publisher.Send(json);
}
}
Example 2: Order Executor Service
//+------------------------------------------------------------------+
//| OrderExecutor.mq5 |
//+------------------------------------------------------------------+
#include <Zmq/Zmq.mqh>
#include <Trade/Trade.mqh>
input string InpAddress = "tcp://0.0.0.0:5556";
CZmq *g_responder;
CTrade g_trade;
int OnInit() {
g_responder = new CZmq();
if(!g_responder.Init(ZMQ_REP))
return INIT_FAILED;
if(!g_responder.Bind(InpAddress))
return INIT_FAILED;
g_trade.SetDeviationInPoints(10);
Print("Order executor ready on ", InpAddress);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason) {
if(g_responder != NULL) {
g_responder.Shutdown();
delete g_responder;
}
}
void OnTick() {
// Non-blocking receive
string request = g_responder.Receive(true);
if(request == "") return;
Print("Request: ", request);
// Parse and execute (simplified)
string response;
if(StringFind(request, "market_buy") >= 0) {
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(g_trade.Buy(0.01, _Symbol, ask)) {
StringConcatenate(response,
"{\"success\":true,\"ticket\":",
IntegerToString(g_trade.ResultOrder()), "}");
} else {
response = "{\"success\":false,\"error\":\"Buy failed\"}";
}
} else {
response = "{\"success\":false,\"error\":\"Unknown command\"}";
}
g_responder.Send(response, false); // Blocking send for REP
Print("Response: ", response);
}
Example 3: Rust Client (Complete)
// Cargo.toml dependencies:
// zeromq = "0.3"
// tokio = { version = "1", features = ["full"] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
use zeromq::{Socket, SocketRecv, SocketSend};
#[derive(Debug, Deserialize)]
struct TickData {
symbol: String,
bid: f64,
ask: f64,
time: i64,
}
#[derive(Debug, Serialize)]
struct OrderRequest {
#[serde(rename = "type")]
order_type: String,
symbol: String,
volume: f64,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Subscribe to tick data
let (tx, mut rx) = mpsc::channel::<TickData>(100);
tokio::spawn(async move {
let mut socket = zeromq::SubSocket::new();
socket.connect("tcp://127.0.0.1:5555").await.unwrap();
socket.subscribe("").await.unwrap();
loop {
if let Ok(msg) = socket.recv().await {
if let Some(bytes) = msg.get(0) {
if let Ok(json) = std::str::from_utf8(bytes) {
if let Ok(tick) = serde_json::from_str::<TickData>(json) {
let _ = tx.send(tick).await;
}
}
}
}
}
});
// Process ticks
while let Some(tick) = rx.recv().await {
println!("{}: Bid={}, Ask={}", tick.symbol, tick.bid, tick.ask);
}
Ok(())
}
Example 4: Java Client (Complete)
// Maven dependency: org.zeromq:jeromq:0.5.3
import org.zeromq.SocketType;
import org.zeromq.ZContext;
import org.zeromq.ZMQ;
import com.google.gson.Gson;
public class MT5Client {
private ZContext context;
private ZMQ.Socket subscriber;
private ZMQ.Socket requester;
private Gson gson = new Gson();
public MT5Client(int tickPort, int orderPort) {
context = new ZContext();
// Subscriber for tick data
subscriber = context.createSocket(SocketType.SUB);
subscriber.connect("tcp://127.0.0.1:" + tickPort);
subscriber.subscribe("".getBytes());
// Requester for orders
requester = context.createSocket(SocketType.REQ);
requester.connect("tcp://127.0.0.1:" + orderPort);
}
public void startTickStream() {
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
String msg = subscriber.recvStr(ZMQ.DONTWAIT);
if (msg != null) {
TickData tick = gson.fromJson(msg, TickData.class);
System.out.printf("%s: Bid=%.5f, Ask=%.5f%n",
tick.symbol, tick.bid, tick.ask);
}
try { Thread.sleep(1); } catch (InterruptedException e) { break; }
}
}).start();
}
public OrderResponse sendOrder(String type, String symbol, double volume) {
OrderRequest request = new OrderRequest(type, symbol, volume);
requester.send(gson.toJson(request));
String response = requester.recvStr();
return gson.fromJson(response, OrderResponse.class);
}
public void close() {
context.close();
}
// Data classes
static class TickData {
String symbol;
double bid, ask;
long time;
}
static class OrderRequest {
String type, symbol;
double volume;
OrderRequest(String t, String s, double v) { type=t; symbol=s; volume=v; }
}
static class OrderResponse {
boolean success;
Long ticket;
String error;
}
public static void main(String[] args) {
MT5Client client = new MT5Client(5555, 5556);
client.startTickStream();
// Execute a buy order
OrderResponse response = client.sendOrder("market_buy", "EURUSD", 0.01);
System.out.println("Order result: " + response.success);
}
}
Example 5: C++ Client (Complete)
// Requires: libzmq, cppzmq, nlohmann/json
// Compile: g++ -std=c++17 -o mt5_client mt5_client.cpp -lzmq -lpthread
#include <zmq.hpp>
#include <nlohmann/json.hpp>
#include <iostream>
#include <thread>
#include <atomic>
using json = nlohmann::json;
class MT5Client {
private:
zmq::context_t context;
zmq::socket_t subscriber;
zmq::socket_t requester;
std::atomic<bool> running{false};
std::thread tick_thread;
public:
MT5Client(int tick_port = 5555, int order_port = 5556)
: context(1), subscriber(context, zmq::socket_type::sub),
requester(context, zmq::socket_type::req) {
subscriber.connect("tcp://127.0.0.1:" + std::to_string(tick_port));
subscriber.set(zmq::sockopt::subscribe, "");
requester.connect("tcp://127.0.0.1:" + std::to_string(order_port));
}
void start_tick_stream() {
running = true;
tick_thread = std::thread([this]() {
while (running) {
zmq::message_t message;
auto result = subscriber.recv(message, zmq::recv_flags::dontwait);
if (result) {
std::string msg(static_cast<char*>(message.data()), message.size());
json tick = json::parse(msg);
std::cout << tick["symbol"].get<std::string>()
<< ": Bid=" << tick["bid"].get<double>()
<< ", Ask=" << tick["ask"].get<double>() << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
});
}
json send_order(const std::string& type, const std::string& symbol, double volume) {
json request = {{"type", type}, {"symbol", symbol}, {"volume", volume}};
std::string req_str = request.dump();
zmq::message_t req_msg(req_str.begin(), req_str.end());
requester.send(req_msg, zmq::send_flags::none);
zmq::message_t reply;
requester.recv(reply);
std::string reply_str(static_cast<char*>(reply.data()), reply.size());
return json::parse(reply_str);
}
json market_buy(const std::string& symbol, double volume) {
return send_order("market_buy", symbol, volume);
}
json market_sell(const std::string& symbol, double volume) {
return send_order("market_sell", symbol, volume);
}
void stop() {
running = false;
if (tick_thread.joinable()) tick_thread.join();
}
~MT5Client() { stop(); }
};
int main() {
MT5Client client;
client.start_tick_stream();
// Execute a buy order
json response = client.market_buy("EURUSD", 0.01);
std::cout << "Order result: " << response.dump() << std::endl;
// Keep running
std::this_thread::sleep_for(std::chrono::seconds(60));
return 0;
}
Error Handling
ZMQ Error Codes
The library uses zmq_errno() to retrieve error codes. Common errors:
| Error Code | Description | Solution |
|---|---|---|
| 11 | EAGAIN (resource unavailable) | Normal for non-blocking ops when no data |
| 48 | EADDRINUSE (address in use) | Port already bound, use different port |
| 111 | ECONNREFUSED | Remote endpoint not available |
| 156384713 | ETERM (context terminated) | ZMQ context was terminated |
Defensive Programming
// Always check initialization
if(!g_publisher.Init(ZMQ_PUB)) {
Print("ZMQ Init failed");
return INIT_FAILED;
}
// Always check bind/connect
if(!g_publisher.Bind("tcp://0.0.0.0:5555")) {
Print("ZMQ Bind failed, errno: ", zmq_errno());
g_publisher.Shutdown();
return INIT_FAILED;
}
// Handle empty receive gracefully
string msg = g_responder.Receive(true);
if(msg == "") {
// No message available, continue
return;
}
Best Practices
1. Resource Management
Always use pointers and proper cleanup:
CZmq *g_socket = NULL; // Initialize to NULL
int OnInit() {
g_socket = new CZmq();
// ... init and bind
}
void OnDeinit(const int reason) {
if(g_socket != NULL) {
g_socket.Shutdown();
delete g_socket;
g_socket = NULL;
}
}
2. Non-Blocking in OnTick()
Never use blocking operations in OnTick() - they will freeze the terminal:
void OnTick() {
// GOOD: Non-blocking receive
string msg = g_responder.Receive(true);
// BAD: This would freeze the terminal
// string msg = g_responder.Receive(false);
}
3. REQ/REP Pattern Compliance
The REP socket must always send a reply after receiving a request:
void OnTick() {
string request = g_responder.Receive(true);
if(request != "") {
// MUST send response for every request
string response = ProcessRequest(request);
g_responder.Send(response, false); // Use blocking send
}
}
4. Buffer Size Considerations
The default receive buffer is 4096 bytes. For larger messages:
// In Zmq.mqh, modify:
uchar buffer[16384]; // Increase to 16KB
5. JSON Message Construction
Use StringConcatenate for efficient string building:
string json;
StringConcatenate(json,
"{\"symbol\":\"", _Symbol,
"\",\"value\":", DoubleToString(value, 5),
"}");
Troubleshooting
Common Issues
Issue: "dll imports are not allowed"
- Solution: Enable
Allow DLL importsin Tools > Options > Expert Advisors
Issue: "Cannot load library 'libzmq.dll'"
- Solution: Ensure libzmq.dll is in MQL5/Libraries/ folder
- Solution: Ensure libsodium.dll is also present (dependency)
- Solution: Verify DLLs are 64-bit if using 64-bit MT5
Issue: "ZMQ Bind failed"
- Solution: Check if port is already in use
- Solution: Try a different port number
- Solution: Ensure firewall allows the port
Issue: No data received on subscriber
- Solution: Ensure subscriber connects AFTER publisher binds
- Solution: Add a small delay after connect before expecting data
- Solution: Verify network connectivity
Issue: "Request not answered" on REQ socket
- Solution: Ensure REP socket always sends a response for every receive
- Solution: Check for crashes in request processing logic
Debug Logging
Add print statements to trace execution:
void OnTick() {
string request = g_responder.Receive(true);
if(request != "") {
Print("Received request: ", request);
string response = ProcessRequest(request);
Print("Sending response: ", response);
int sent = g_responder.Send(response, false);
Print("Bytes sent: ", sent);
}
}
Version History
| Version | Date | Changes |
|---|---|---|
| 2.00 | 2026-01-27 | Added REP socket support, order handling, account info streaming |
| 1.00 | 2026-01-20 | Initial release with PUB socket support |
License
MIT License
Copyright (c) 2026 Albeos Rembrant
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.
References
Citation
If you use this library in your research or project, please cite:
@software{rembrant2026sum3api,
author = {Rembrant, Albeos},
title = {{SUM3API}: Using Rust, ZeroMQ, and MetaQuotes Language (MQL5) API Combination to Extract, Communicate, and Externally Project Financial Data from MetaTrader 5 (MT5)},
year = {2026},
publisher = {GitHub},
url = {https://github.com/algorembrant/Rust-ZMQ-MT5},
version = {2.00}
}
//end of documentattion