2

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.

Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150
IVlad
  • 43,099
  • 13
  • 111
  • 179

1 Answers1

1

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.

IVlad
  • 43,099
  • 13
  • 111
  • 179
Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150
  • That makes sense, however the behavior remains the same even like that. – IVlad Nov 30 '18 at 16:38
  • Did you test with `socket.setsockopt(zmq.SUBSCRIBE, b"")` instead of `socket.setsockopt_string(zmq.SUBSCRIBE, '')`? – Benyamin Jafari Dec 02 '18 at 08:21
  • Yes, I used it exactly like that. Nothing is being received on the Python side. – IVlad Dec 02 '18 at 09:55
  • 1
    It turns out that my error checking for binding on the MQL side was wrong, so it printed "Bound" when there was actually an error and vice versa (and when I saw it print Error, I was too quick to stop it I guess). `setsockopt_string` works as well. I'm going to accept this, with an edit about the wrong error checking - I hope you don't mind. – IVlad Dec 03 '18 at 22:51
  • Also note that usually there's an error if the port is in use by something else - in my case, 5556 didn't work, but 5555 did. – IVlad Dec 03 '18 at 22:58
  • I use the zmq-mql library.. I have a big problem with both MQL4 (32bit dll) and MQL5 (64bit dll).. in both of them if I run it as an EA, then I cannot change any input values, stop or restart the EA, or even close the MT4/5 terminal (I can try to close it but it hangs and must be force closed).. anyone know how to fix this or at least what avenues I should pursue? – ycomp Jan 25 '19 at 07:03