Skip to content

Latest commit

History

21 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Metaquotes MQL5 - JSON - API

Development state: stable beta (code is stable)

Table of Contents

About the Project

This project was developed to work as a server for Backtrader Python trading framework. It is based on ZeroMQ sockets and uses JSON format to communicate. But now it has grown to the independent project. You can use it with any language that has ZeroMQ binding.

Backtrader Python client located here: Python Backtrader - Metaquotes MQL5

In development:

  • Historical data load speed
  • Add error handling to docs
  • Trades info
  • Experation
  • Devitation
  • Netting/hedging mode switch
  • Stop limit orders

Installation

  1. Install ZeroMQ for MQL5 https://github.com/dingmaotu/mql-zmq
  2. Put include/Json.mqh from this repo to your MetaEditor include directoty.
  3. Download and compile experts/JsonAPI.mq5 script.
  4. Check if Metatrader 5 automatic trading is allowed.
  5. Attach the script to a chart in Metatrader 5.
  6. Allow DLL import in dialog window.
  7. Check if the ports are free to use. (default:15555,15556, 15557,15558)

Tested on macOS Mojave / Windows 10 in Parallels Desktop container.

Documentation

The script uses four ZeroMQ sockets:

  1. System socket - recives requests from client and replies 'OK'
  2. Data socket - pushes data to client depending on request via System socket.
  3. Live socket - automatically pushes last candle when it closes.
  4. Streaming socket - automatically pushes last transaction info every time it happens.

The idea is to send requests via System socket and recieve results/errors via Data socket. Event handlers should be created for Live socket and Streaming socket because server sends data to theese sockets automatically. See examples in Usage section.

System socket request uses default JSON dictionary:

{
"action": None,
"actionType": None,
"symbol": None,
"chartTF": None,
"fromDate": None,
"toDate": None,
"id": None,
"magic": None,
"volume": None,
"price": None,
"stoploss": None,
"takeprofit": None,
"expiration": None,
"deviation": None,
"comment": None
}

Check out the available combinations of action and actionType:

actionactionTypeDescription
CONFIGNoneSet script configuration
ACCOUNTNoneGet account settings
BALANCENoneGet current balance
POSITIONSNoneGet current open positions
ORDERSNoneGet current open orders
HISTORYDATAGet data history
HISTORYTRADESGet trades history
TRADEORDER_TYPE_BUYBuy market
TRADEORDER_TYPE_SELLSell market
TRADEORDER_TYPE_BUY_LIMITBuy limit
TRADEORDER_TYPE_SELL_LIMITSell limit
TRADEORDER_TYPE_BUY_STOPBuy stop
TRADEORDER_TYPE_SELL_STOPSell stop
TRADEPOSITION_MODIFYPosition modify
TRADEPOSITION_PARTIALPosition close partial
TRADEPOSITION_CLOSE_IDPosition close by id
TRADEPOSITION_CLOSE_SYMBOLPositions close by symbol
TRADEORDER_MODIFYOrder modify
TRADEORDER_CANCELOrder cancel

Example Python API class:

importzmqclassMTraderAPI:
def__init__(self, host=None):
self.HOST=hostor'localhost'self.SYS_PORT=15555# REP/REQ portself.DATA_PORT=15556# PUSH/PULL portself.LIVE_PORT=15557# PUSH/PULL portself.EVENTS_PORT=15558# PUSH/PULL port# ZeroMQ timeout in secondssys_timeout=1data_timeout=10# initialise ZMQ contextcontext=zmq.Context()
# connect to server socketstry:
self.sys_socket=context.socket(zmq.REQ)
self.sys_socket.RCVTIMEO=sys_timeout*1000self.sys_socket.connect('tcp://{}:{}'.format(self.HOST, self.SYS_PORT))
self.data_socket=context.socket(zmq.PULL)
self.data_socket.RCVTIMEO=data_timeout*1000self.data_socket.connect('tcp://{}:{}'.format(self.HOST, self.DATA_PORT))
exceptzmq.ZMQError:
raisezmq.ZMQBindError("Binding ports ERROR")
def_send_request(self, data: dict) ->None:
""" Send request to server via ZeroMQ System socket """try:
self.sys_socket.send_json(data)
msg=self.sys_socket.recv_string()
# terminal received the requestassertmsg=='OK', 'Something wrong on server side'exceptAssertionErroraserr:
raisezmq.NotDone(err)
exceptzmq.ZMQError:
raisezmq.NotDone("Sending request ERROR")
def_pull_reply(self):
""" Get reply from server via Data socket with timeout """try:
msg=self.data_socket.recv_json()
exceptzmq.ZMQError:
raisezmq.NotDone('Data socket timeout ERROR')
returnmsgdeflive_socket(self, context=None):
try:
context=contextorzmq.Context.instance()
socket=context.socket(zmq.PULL)
socket.connect('tcp://{}:{}'.format(self.HOST, self.LIVE_PORT))
exceptzmq.ZMQError:
raisezmq.ZMQBindError("Binding ports ERROR")
returnsocketdefstreaming_socket(self, context=None):
try:
context=contextorzmq.Context.instance()
socket=context.socket(zmq.PULL)
socket.connect('tcp://{}:{}'.format(self.HOST, self.EVENTS_PORT))
exceptzmq.ZMQError:
raisezmq.ZMQBindError("Binding ports ERROR")
returnsocketdefconstruct_and_send(self, **kwargs) ->dict:
""" Construct request dictionary from default """# default dictionaryrequest= {
"action": None,
"actionType": None,
"symbol": None,
"chartTF": None,
"fromDate": None,
"toDate": None,
"id": None,
"magic": None,
"volume": None,
"price": None,
"stoploss": None,
"takeprofit": None,
"expiration": None,
"deviation": None,
"comment": None
}
# update dict values if existforkey, valueinkwargs.items():
ifkeyinrequest:
request[key] =valueelse:
raiseKeyError('Unknown key in **kwargs ERROR')
# send dict to serverself._send_request(request)
# return server replyreturnself._pull_reply()

Usage

All examples will be on Python 3. Lets create an instance of MetaTrader API class:

api=MTraderAPI()

First of all we should configure script symbol and timeframe. Live data stream will be configured to the seme params.

rep=api.construct_and_send(action="CONFIG", symbol="EURUSD", chartTF="M5")
print(rep)

Get information about trading account.

rep=api.construct_and_send(action="ACCOUNT")
print(rep)

Get historical data. fromDate should be in timestamp format. There are some issues:

  • MetaTrader keeps historical data in cache. But when you make a request for the first time, MetaTrader downloads data from a broker. This operation can exceed Data socket timeout. It depends on your broker. Second request will be handeled quickly.
  • Historical data processing code is not optimal. It takes too much time to process more than 50000 candles. Under refactoring now.
rep=api.construct_and_send(action="HISTORY", actionType="DATA", symbol="EURUSD", chartTF="M5", fromDate=1555555555)
print(rep)

Buy market order.

rep=api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_BUY", symbol="EURUSD", "volume": 0.1, "stoploss": 1.1, "takeprofit": 1.3)
print(rep)

Sell limit order. Remember to switch SL/TP depending on BUY/SELL, or you will get 'invalid stops' error.

  • BUY: SL < price < TP
  • SELL: SL > price > TP
rep=api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_SELL_LIMIT", symbol="EURUSD", "volume": 0.1, "price": 1.2, "stoploss": 1.3, "takeprofit": 1.1)
print(rep)

Event handler example for Live socket and Data socket.

importzmqimportthreadingapi=MTraderAPI()
def_t_livedata():
socket=api.live_socket()
whileTrue:
try:
last_candle=socket.recv_json()
exceptzmq.ZMQError:
raisezmq.NotDone("Live data ERROR")
print(last_candle)
def_t_streaming_events():
socket=api.streaming_socket()
whileTrue:
try:
trans=socket.recv_json()
request, reply=trans.values()
exceptzmq.ZMQError:
raisezmq.NotDone("Streaming data ERROR")
print(request)
print(reply)
foriinrange(3):
t=threading.Thread(target=_t_livedata, daemon=True)
t.start()
foriinrange(3):
t=threading.Thread(target=_t_streaming_events, daemon=True)
t.start()
whileTrue:
pass

License

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE for more information.

About

Metaquotes MQL5 - JSON - API

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages