Search code examples
pythonpython-3.xzeromqpyzmqmql5

MQL5 Zmq PUB socket not being received by Python zmq SUB socket


I'm trying to set up a PUB socket in MQL5 and a SUB socket in Python that will receive messages.

I have this in MQL5:

#include <Zmq/Zmq.mqh>

Context context("helloworld");
Socket socket(context,ZMQ_PUB);

string BROKER;

int OnInit()
{
   if (socket.bind("tcp://*:5556"))
   {
       Print("Error");
   }
   else
       Print("Bound");
   BROKER = AccountInfoString(ACCOUNT_COMPANY);
   return(INIT_SUCCEEDED);
}

void OnTick()
{  
   MqlTick last_tick; 
   string str;
   if(SymbolInfoTick(Symbol(),last_tick)) 
   { 
      StringConcatenate(str, BROKER, ",", Symbol(), ",", last_tick.time_msc, ",", last_tick.ask, ",", last_tick.bid, ",", last_tick.last, ",", last_tick.volume); 
   } 
   else 
      str = "FAIL";

   Print(str);
   ZmqMsg reply(str);
   socket.send(reply);
}

And this in Python:

import zmq
import random
import sys
import time

context = zmq.Context()
socket = context.socket(zmq.SUB)

ports = [5556]
for port in ports:
    print(port)
    socket.connect("tcp://localhost:{}".format(port))
socket.setsockopt_string(zmq.SUBSCRIBE, '')

print('connected')

f = open('metatrader-1.csv', 'a')
while True:
    msg = socket.recv()
    print(msg)
    f.write(str(msg) + '\n')

This problem is that this does not seem to receive anything on the Python side, the recv call just blocks forever. The OnTick method is fired in MT, since the prints can be seen.

How can I get this to work?

Note that if I switch to a REP/REQ pair, it works.

MQL5:

#include <Zmq/Zmq.mqh>


Context context("helloworld");
Socket socket(context,ZMQ_REQ);

string BROKER;

int OnInit()
{
   if (socket.connect("tcp://localhost:5555"))
   {
      Print("Error");
   }
   else
      Print("Bound");
   BROKER = AccountInfoString(ACCOUNT_COMPANY);
   return(INIT_SUCCEEDED);
}

void OnTick()
{  
   MqlTick last_tick; 
   string str;
   if(SymbolInfoTick(Symbol(),last_tick)) 
   { 
      StringConcatenate(str, BROKER, ",", Symbol(), ",", last_tick.time_msc, ",", last_tick.ask, ",", last_tick.bid, ",", last_tick.last, ",", last_tick.volume); 
   } 
   else 
      str = "FAIL";

   Print(str);
   ZmqMsg reply(str);
   socket.send(reply);
   socket.recv(reply);
}

Python:

import zmq
import random
import sys
import time

context = zmq.Context()
socket = context.socket(zmq.REP)

ports = [5555]
for port in ports:
    print(port)
    socket.bind("tcp://*:{}".format(port))
#socket.setsockopt_string(zmq.SUBSCRIBE, '')

print('connected')

f = open('metatrader-1.csv', 'a')
while True:
    msg = socket.recv()
    socket.send_string('ack')
    print(msg)
    f.write(str(msg) + '\n')

But this has some drawbacks so I'd rather not use this if I can help it.


Solution

  • Your socket option must be placed before the socket connection, so your code will be:

    import zmq
    import random
    import sys
    import time
    
    context = zmq.Context()
    socket = context.socket(zmq.SUB)
    
    ports = [5556]
    socket.setsockopt(zmq.SUBSCRIBE, b"")  # Note.
    
    for port in ports:
        print(port)
        socket.connect("tcp://localhost:{}".format(port))
    
    print('connected')
    
    f = open('metatrader-1.csv', 'a')
    while True:
        msg = socket.recv()
        print(msg)
        f.write(str(msg) + '\n')
    

    Also, this part:

    if (socket.bind("tcp://*:5556"))
    {
       Print("Error");
    }
    else
       Print("Bound");
    

    Should be the other way around. This will actually print an error when the socket is successfully bound.