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

Discussion in 'Automated Trading' started by jack, Apr 19, 2015.

  1. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422
    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
    [​IMG]

    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: Oct 4, 2017
  2. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422
    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.
     
  3. Tarikh Agustia

    Tarikh Agustia New Member

    Joined:
    Aug 22, 2017
    Messages:
    1
    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 :)
     
  4. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422
    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?
     
  5. Nike

    Nike New Member

    Joined:
    Oct 4, 2017
    Messages:
    4
    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: Oct 4, 2017
  6. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422
    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

    [​IMG]
     
  7. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422

    Looks like you're using Python 3... That's likely your problem.
     
  8. Nike

    Nike New Member

    Joined:
    Oct 4, 2017
    Messages:
    4
    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.
     
  9. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422
    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.
     
  10. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422
    I just updated the example / proof script.. since it was ugly code using string concatenation.. looks cleaner now.
     
  11. Nike

    Nike New Member

    Joined:
    Oct 4, 2017
    Messages:
    4
    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...
     
  12. Nike

    Nike New Member

    Joined:
    Oct 4, 2017
    Messages:
    4
    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.
     
  13. jack

    jack Administrator Staff Member

    Joined:
    Mar 29, 2013
    Messages:
    2,422
    I feel I've been very clear about the Python version requirement.
     

Share This Page