HOWTO: Get Quotes from MT4's DDE Server into Python


jack

Administrator
Staff member
For anyone interested in getting quotes from MT4 into Python for use with your own trading applications, see this simple way to do it:

In this HOWTO, I'll be using Pepperstone's MT4 client, which can be downloaded here.

Firstly, the pywin default DDE client seems to have trouble talking with MT4, and I've read on another site that it relates to MT4 only supporting the 'advise' method of getting data from DDE. This tripped me up long ago and it was frustrating enough that I just abandoned this method of quote collection and used MQL4 within MT4 to export the data I needed.

I recently revisited DDE and using some Google-fu, I found an alternate DDE client that appears to work fine with MT4, so I wanted to provide a HOWTO resource plus a bit of example code:

(This HOWTO has been written assuming you already know the basics of Python and have Python v2.x installed.)

Steps:

1) Enable DDE Server in MT4. Go to Tools > Options > Server Tab > Enable DDE server


2) You must get the alternate DDE client. I won't host it here, since it's someone else's code (ActiveState recipe,) but here's a direct link to it: EDIT: After finding a few bugs in the original sources work, I have forked it on github and below is the link to said fork:
https://gist.github.com/FXGears/351a447f47d9356ada6b
(It's not perfect, but I worked around an issue that makes it fail with MT4 after running for a bit.)

Save this file in your project's directory.

3) Create a new python script to test it out. This one for example:

Code:
import dde_client as ddec
import time


# Connect to MT4
# Must register BID and ASK as topics separately..
QUOTE_client = ddec.DDEClient('MT4', 'QUOTE')

# Register desired symbols..
symbols = ['EURUSD', 'AUDUSD', 'USDJPY', 'USDCAD']
for i in symbols:
    QUOTE_client.advise(i)

# Prove it worked:
columns = ['Symbol', 'DATE', 'TIME', 'BID', 'ASK']


while 1:
    time.sleep(1)
    to_display = []
    for item in symbols:
        current_quote = QUOTE_client.request(item).split()
        current_quote.insert(0, item)
        to_display.append(current_quote)

    print '\t'.join(columns)
    for line in to_display:
        print ' '.join(line)
Which outputs something like this:
Code:
Symbol    DATE        TIME    BID    ASK
EURUSD    2015/04/20    06:15    1.07976    1.07979
AUDUSD    2015/04/20    06:15    0.78069    0.78076
USDJPY    2015/04/20    06:15    118.860    118.863
USDCAD    2015/04/20    06:15    1.21979    1.21989
Notes on the example code:
* If you broker uses different symbol naming conventions (like "EURUSD.x" instead of "EURUSD" as seen within MT4,) you'll need to edit this.
* Notice that you register the QUOTE topic in general first, then ask to be advised when a given 'item' (such as EURUSD) is updated on this topic. You may then 'request' the current value of an item within the topic.
* The DDE Client is written using direct calls to Window's C libraries.. which means that outside of the python code, the library wants to print to console, and thus every time there's a price change you'll notice a line get printed with said change outside of the quote table that prints every second. I'm working on a way to squelch this without losing the ability to get updates as they happen instead of when the user pulls them with 'request'
 
Last edited:

jack

Administrator
Staff member
People have forked my code 5 times on github from this post.. interesting.

I hope if they found any bugs that they would submit a pull request for me to include.
 

Tarikh Agustia

New Member
hallo mr Jack,
im use you code and great..
and i see method callback in your code, how can i use the callback in my code, so i will not change to code dde_client.py, thanks :)
 

jack

Administrator
Staff member
hallo mr Jack,
im use you code and great..
and i see method callback in your code, how can i use the callback in my code, so i will not change to code dde_client.py, thanks :)
I'm not sure what you mean by callback in your post.

Do you mean the callback functions within the code?

What's wrong with them, or what are you trying to change/do?
 

Nike

New Member
Hi Jack,

Good program. Although when I try to run the line : current_quote = QUOTE_client.request(i).split(" "), I have the error: TypeError: a bytes-like object is required, not 'str', so I have decoded it in utf-8. It works well if I request manually (running the line everytime I want an update).

But when I run:
while 1:
time.sleep(5)
for i in symbols:
current_quote = QUOTE_client.request(i)​

I have the following error:

Traceback (most recent call last):

File "_ctypes/callbacks.c", line 234, in 'calling callback function'

File "C:\Users\Hp\Documents\Python Scripts\dde_client.py", line 213, in _callback
item = create_string_buffer('\000' * 128)

File "C:\Users\Hp\Anaconda3\lib\ctypes\__init__.py", line 63, in create_string_buffer
raise TypeError(init)

TypeError:

Any idea how I could tackle the problem?
 
Last edited:

jack

Administrator
Staff member
I'm unable to reproduce that error on my end. The code still works fine.

Are you using Python 2.7.x? It's written for 2.7

 

Nike

New Member
Looks like you're using Python 3... That's likely your problem.
Yes I'm using python 3.6... Do you know what should I change to make it work ?
Cause it works fine if I request just one time, it's using the while 1: ... that produces the error.
 

jack

Administrator
Staff member
Do you know what should I change to make it work ?
Yes. You should use Python 2.7

;P

(This HOWTO has been written assuming you already know the basics of Python and have Python v2.x installed.)
The DDE client library was written for 2.7 ... I'm not sure just how much of it would need to be altered for Python3. Probably a lot.
 

jack

Administrator
Staff member
I just updated the example / proof script.. since it was ugly code using string concatenation.. looks cleaner now.
 

Nike

New Member
Yes. You should use Python 2.7

;P



The DDE client library was written for 2.7 ... I'm not sure just how much of it would need to be altered for Python3. Probably a lot.
God, I changed line 213 in dde_client for item = create_string_buffer(bytes('\000' * 128, 'ascii')), now QUOTE_client.advise(i) triggers infinite update of the the bid-ask. Without calling the request...
 

Nike

New Member
Also do you have any idea why when I run the request method, I have
Symbol DATE TIME BID ASK
EURUSD N\A
Symbol DATE TIME BID ASK
EURUSD N\A
Symbol DATE TIME BID ASK
EURUSD N\A
The request does not bring anything.
 

Amin Mohammadi

New Member
I'm trying to read data using this library. At first hour it works fine but after some time the data fetching process gets interrupted and the program fails. Is there any limitations on memory or process time?
 

jack

Administrator
Staff member
I'm trying to read data using this library. At first hour it works fine but after some time the data fetching process gets interrupted and the program fails. Is there any limitations on memory or process time?
I setup an instance of this DDE code to run for a day and I did not encounter the same issue. It was stable for nearly 48 hours til I shut it down. Perhaps you're doing a heavier use case than I am (I just had it pulling two symbols for that time, maybe you're pulling all data on all symbols...)

..But I also don't rely on DDE for extended periods of time in production. I've moved on to using ZeroMQ as my messaging layer between various applications and my custom trading platform. I trust it a lot more to do extended / heavy use cases. Maybe that will suit your needs better... DDE is such an old legacy standard anyway (which is effectivly dead in most all business settings save for legacy finance applications.)
 

sarc007

New Member
Hi Jack,

Good program. Although when I try to run the line : current_quote = QUOTE_client.request(i).split(" "), I have the error: TypeError: a bytes-like object is required, not 'str', so I have decoded it in utf-8. It works well if I request manually (running the line everytime I want an update).

But when I run:
while 1:
time.sleep(5)
for i in symbols:
current_quote = QUOTE_client.request(i)​

I have the following error:

Traceback (most recent call last):

File "_ctypes/callbacks.c", line 234, in 'calling callback function'

File "C:\Users\Hp\Documents\Python Scripts\dde_client.py", line 213, in _callback
item = create_string_buffer('\000' * 128)

File "C:\Users\Hp\Anaconda3\lib\ctypes\__init__.py", line 63, in create_string_buffer
raise TypeError(init)

TypeError:

Any idea how I could tackle the problem?
Change the code from item = create_string_buffer('000'*128) to item = create_string_buffer(b'000'*128) the b is for bytes
and also change
for line in to_display:
print ' '.join(line)
to below code
for line in to_display:
str_line = str(line)
print (' '.join(str_line))

since you are using python3.x please make sure print command has brackets i.e. print('XYX') and not print '' as this is used in python2.x
 

Top