{"id":19014386,"url":"https://github.com/parrondo/mql5-json-api","last_synced_at":"2026-02-07T22:33:57.644Z","repository":{"id":88357637,"uuid":"240108752","full_name":"parrondo/mql5-json-api","owner":"parrondo","description":"MQL5-JSON-API server for trading community","archived":false,"fork":false,"pushed_at":"2020-02-12T20:37:47.000Z","size":96,"stargazers_count":14,"open_issues_count":0,"forks_count":18,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-03T06:25:42.973Z","etag":null,"topics":["backtrader","json-api","json-server","metaquotes","metatrader","mql5","mql5-api","trading","zeromq"],"latest_commit_sha":null,"homepage":"","language":"MQL5","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/parrondo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-12T20:30:59.000Z","updated_at":"2025-05-07T10:14:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"dd636698-5cba-43b3-98fd-11ec829cc139","html_url":"https://github.com/parrondo/mql5-json-api","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/parrondo/mql5-json-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parrondo%2Fmql5-json-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parrondo%2Fmql5-json-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parrondo%2Fmql5-json-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parrondo%2Fmql5-json-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/parrondo","download_url":"https://codeload.github.com/parrondo/mql5-json-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parrondo%2Fmql5-json-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29211127,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T22:22:11.602Z","status":"ssl_error","status_checked_at":"2026-02-07T22:22:10.684Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backtrader","json-api","json-server","metaquotes","metatrader","mql5","mql5-api","trading","zeromq"],"created_at":"2024-11-08T19:29:07.425Z","updated_at":"2026-02-07T22:33:57.629Z","avatar_url":"https://github.com/parrondo.png","language":"MQL5","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Metatrader MQL5 - JSON - API\r\n\r\n### Development state: first stable release\r\n\r\nTested on Windows 10.\r\n\r\nWorking in testing mode on Windows 10.\r\n\r\nStill under development.\r\n\r\n## Table of Contents\r\n* [About the Project](#about-the-project)\r\n* [Installation](#installation)\r\n* [Documentation](#documentation)\r\n* [Usage](#usage)\r\n* [Live data and streaming events](#live-data-and-streaming-events)\r\n* [Error handling](#error-handling)\r\n* [License](#license)\r\n\r\n## About the Project\r\n\r\nThe initial project was a forked from [Metaquotes MQL5 - JSON - API](https://github.com/khramkov/MQL5-JSON-API.git) (all credit to Khramkov efforts, thank you!), but the actual code and its compatibility is far enought to start a new project.\r\n\r\nThis project is a server for the Metatrader trading community. It is based on ZeroMQ sockets and uses JSON format to communicate. We usually use it with Python clients, but you can use it with any programming language that has [ZeroMQ binding](http://zeromq.org/bindings:_start).\r\n\r\n\r\nBacktrader Python client is located here: [Python Metatrader - Backtrader - API ](https://github.com/parrondo/metatrader-backtrader-api)\r\n\r\nIn development:\r\n* Devitation\r\n* Stop limit orders\r\n\r\n## Installation\r\n\r\n1. Install ZeroMQ for MQL5 [https://github.com/dingmaotu/mql-zmq](https://github.com/dingmaotu/mql-zmq)\r\n2. Put `include/JsonAPI` from this repo to your MetaEditor `include` folder.\r\n3. Put `experts/JsonAPI.mq5` from this repo to your MetaEditor `experts` folder.\r\n4. Compile JsonAPI.mq5 \r\n5. Check if Metatrader 5 automatic trading is allowed.\r\n6. Attach the script to a chart in Metatrader 5.\r\n7. Allow DLL import in dialog window.\r\n8. Check if the ports are free to use. (default:`15555`,`15556`, `15557`,`15558`)\r\n\r\n## Documentation\r\n\r\nThe script uses four ZeroMQ sockets:\r\n\r\n1. `System socket` - receives requests from client and replies 'OK'\r\n2. `Data socket` - pushes data to client depending on the request via System socket.\r\n3. `Live socket` - automatically pushes last candle when it closes.\r\n4. `Streaming socket` - automatically pushes last transaction info every time it happens.\r\n\r\nThe idea is to send requests via `System socket` and receive results/errors via `Data socket`. Event handlers should be created for `Live socket` and `Streaming socket` because the server sends data to theese sockets automatically. See examples in [Live data and streaming events](#live-data-and-streaming-events) section.\r\n\r\n`System socket` request uses default JSON dictionary:\r\n\r\n```\r\n{\r\n\t\"action\": null,\r\n\t\"actionType\": null,\r\n\t\"symbol\": null,\r\n\t\"chartTF\": null,\r\n\t\"fromDate\": null,\r\n\t\"toDate\": null,\r\n\t\"id\": null,\r\n\t\"magic\": null,\r\n\t\"volume\": null,\r\n\t\"price\": null,\r\n\t\"stoploss\": null,\r\n\t\"takeprofit\": null,\r\n\t\"expiration\": null,\r\n\t\"deviation\": null,\r\n\t\"comment\": null\r\n}\r\n```\r\nCheck out the available combinations of `action` and `actionType`:\r\n\r\naction     | actionType           | Description                |\r\n-----------|----------------------|----------------------------|\r\nACCOUNT    | null                 | Get account settings       |\r\nBALANCE    | null                 | Get current balance        |\r\nPOSITIONS  | null                 | Get current open positions |\r\nORDERS     | null                 | Get current open orders    |\r\nHISTORY    | DATA                 | Get data history           |\r\nHISTORY    | TRADES               | Get trades history         |\r\nTRADE      | ORDER_TYPE_BUY       | Buy market                 |\r\nTRADE      | ORDER_TYPE_SELL      | Sell market                |\r\nTRADE      | ORDER_TYPE_BUY_LIMIT | Buy limit                  |\r\nTRADE      | ORDER_TYPE_SELL_LIMIT| Sell limit                 |\r\nTRADE      | ORDER_TYPE_BUY_STOP  | Buy stop                   |\r\nTRADE      | ORDER_TYPE_SELL_STOP | Sell stop                  |\r\nTRADE      | POSITION_MODIFY      | Position modify            |\r\nTRADE      | POSITION_PARTIAL     | Position close partial     |\r\nTRADE      | POSITION_CLOSE_ID    | Position close by id       |\r\nTRADE      | POSITION_CLOSE_SYMBOL| Positions close by symbol  |\r\nTRADE      | ORDER_MODIFY         | Order modify               |\r\nTRADE      | ORDER_CANCEL         | Order cancel               |\r\n\r\nPython 3 API class example:\r\n\r\n``` python\r\nimport zmq\r\n\r\nclass MTraderAPI:\r\n    def __init__(self, host=None):\r\n        self.HOST = host or 'localhost'\r\n        self.SYS_PORT = 15555       # REP/REQ port\r\n        self.DATA_PORT = 15556      # PUSH/PULL port\r\n        self.LIVE_PORT = 15557      # PUSH/PULL port\r\n        self.EVENTS_PORT = 15558    # PUSH/PULL port\r\n\r\n        # ZeroMQ timeout in seconds\r\n        sys_timeout = 1\r\n        data_timeout = 10\r\n\r\n        # initialise ZMQ context\r\n        context = zmq.Context()\r\n\r\n        # connect to server sockets\r\n        try:\r\n            self.sys_socket = context.socket(zmq.REQ)\r\n            # set port timeout\r\n            self.sys_socket.RCVTIMEO = sys_timeout * 1000\r\n            self.sys_socket.connect('tcp://{}:{}'.format(self.HOST, self.SYS_PORT))\r\n\r\n            self.data_socket = context.socket(zmq.PULL)\r\n            # set port timeout\r\n            self.data_socket.RCVTIMEO = data_timeout * 1000\r\n            self.data_socket.connect('tcp://{}:{}'.format(self.HOST, self.DATA_PORT))\r\n        except zmq.ZMQError:\r\n            raise zmq.ZMQBindError(\"Binding ports ERROR\")\r\n\r\n    def _send_request(self, data: dict) -\u003e None:\r\n        \"\"\"Send request to server via ZeroMQ System socket\"\"\"\r\n        try:\r\n            self.sys_socket.send_json(data)\r\n            msg = self.sys_socket.recv_string()\r\n            # terminal received the request\r\n            assert msg == 'OK', 'Something wrong on server side'\r\n        except AssertionError as err:\r\n            raise zmq.NotDone(err)\r\n        except zmq.ZMQError:\r\n            raise zmq.NotDone(\"Sending request ERROR\")\r\n\r\n    def _pull_reply(self):\r\n        \"\"\"Get reply from server via Data socket with timeout\"\"\"\r\n        try:\r\n            msg = self.data_socket.recv_json()\r\n        except zmq.ZMQError:\r\n            raise zmq.NotDone('Data socket timeout ERROR')\r\n        return msg\r\n\r\n    def live_socket(self, context=None):\r\n        \"\"\"Connect to socket in a ZMQ context\"\"\"\r\n        try:\r\n            context = context or zmq.Context.instance()\r\n            socket = context.socket(zmq.PULL)\r\n            socket.connect('tcp://{}:{}'.format(self.HOST, self.LIVE_PORT))\r\n        except zmq.ZMQError:\r\n            raise zmq.ZMQBindError(\"Live port connection ERROR\")\r\n        return socket\r\n\r\n    def streaming_socket(self, context=None):\r\n        \"\"\"Connect to socket in a ZMQ context\"\"\"\r\n        try:\r\n            context = context or zmq.Context.instance()\r\n            socket = context.socket(zmq.PULL)\r\n            socket.connect('tcp://{}:{}'.format(self.HOST, self.EVENTS_PORT))\r\n        except zmq.ZMQError:\r\n            raise zmq.ZMQBindError(\"Data port connection ERROR\")\r\n        return socket\r\n\r\n    def construct_and_send(self, **kwargs) -\u003e dict:\r\n        \"\"\"Construct a request dictionary from default and send it to server\"\"\"\r\n\r\n        # default dictionary\r\n        request = {\r\n            \"action\": None,\r\n            \"actionType\": None,\r\n            \"symbol\": None,\r\n            \"chartTF\": None,\r\n            \"fromDate\": None,\r\n            \"toDate\": None,\r\n            \"id\": None,\r\n            \"magic\": None,\r\n            \"volume\": None,\r\n            \"price\": None,\r\n            \"stoploss\": None,\r\n            \"takeprofit\": None,\r\n            \"expiration\": None,\r\n            \"deviation\": None,\r\n            \"comment\": None\r\n        }\r\n\r\n        # update dict values if exist\r\n        for key, value in kwargs.items():\r\n            if key in request:\r\n                request[key] = value\r\n            else:\r\n                raise KeyError('Unknown key in **kwargs ERROR')\r\n\r\n        # send dict to server\r\n        self._send_request(request)\r\n\r\n        # return server reply\r\n        return self._pull_reply()\r\n```\r\n## Usage\r\nAll examples will be on Python 3. Lets create an instance of MetaTrader API class:\r\n\r\n``` python\r\napi = MTraderAPI()\r\n```\r\n\r\nFirst of all we shouldn't configure the script with account parameters because this step is included in the expert parameters.\r\n\r\nGet information about the trading account.\r\n\r\n``` python\r\nrep = api.construct_and_send(action=\"ACCOUNT\")\r\nprint(rep)\r\n```\r\n\r\nGet historical data. `fromDate` should be in timestamp format. The data will be loaded to the last candle if `toDate` is `None`. Notice, that the script sends the last unclosed candle too. You should delete it manually.\r\n\r\n\r\n\r\n``` python\r\nrep = api.construct_and_send(action=\"HISTORY\", actionType=\"DATA\", symbol=\"EURUSD\", chartTF=\"M5\", fromDate=1555555555)\r\nprint(rep)\r\n```\r\n\r\nHistory data reply example:\r\n\r\n```\r\n{'data': [[1560782340, 1.12271, 1.12288, 1.12269, 1.12277, 46.0],[1560782400, 1.12278, 1.12299, 1.12276, 1.12297, 43.0],[1560782460, 1.12296, 1.12302, 1.12293, 1.123, 23.0]]}\r\n```\r\n\r\nBuy market order.\r\n\r\n``` python\r\nrep = api.construct_and_send(action=\"TRADE\", actionType=\"ORDER_TYPE_BUY\", symbol=\"EURUSD\", \"volume\"=0.1, \"stoploss\"=1.1, \"takeprofit\"=1.3)\r\nprint(rep)\r\n```\r\n\r\nSell limit order. Remember to switch SL/TP depending on BUY/SELL, or you will get `invalid stops` error.\r\n\r\n- BUY:  \tSL \u003c price \u003c TP\r\n- SELL: \tSL \u003e price \u003e TP\r\n\r\n``` python\r\nrep = 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)\r\nprint(rep)\r\n```\r\nAll pending orders are set to `Good till cancel` by default. If you want to set an expiration date, pass the date in timestamp format to `expiration` param.\r\n\r\n``` python\r\nrep = api.construct_and_send(action=\"TRADE\", actionType=\"ORDER_TYPE_SELL_LIMIT\", symbol=\"EURUSD\", \"volume\"=0.1, \"price\"=1.2, \"expiration\"=1560782460)\r\nprint(rep)\r\n```\r\n## Live data and streaming events\r\n\r\nEvent handler example for `Live socket` and `Data socket`.\r\n \r\n``` python\r\nimport zmq\r\nimport threading\r\n\r\napi = MTraderAPI()\r\n\r\n\r\ndef _t_livedata():\r\n    socket = api.live_socket()\r\n    while True:\r\n        try:\r\n            last_candle = socket.recv_json()\r\n        except zmq.ZMQError:\r\n            raise zmq.NotDone(\"Live data ERROR\")\r\n        print(last_candle)\r\n\r\n\r\ndef _t_streaming_events():\r\n    socket = api.streaming_socket()\r\n    while True:\r\n        try:\r\n            trans = socket.recv_json()\r\n            request, reply = trans.values()\r\n        except zmq.ZMQError:\r\n            raise zmq.NotDone(\"Streaming data ERROR\")\r\n        print(request)\r\n        print(reply)\r\n\r\n\r\n\r\nt = threading.Thread(target=_t_livedata, daemon=True)\r\nt.start()\r\n\r\nt = threading.Thread(target=_t_streaming_events, daemon=True)\r\nt.start()\r\n\r\nwhile True:\r\n    pass\r\n```\r\n\r\n\r\nThere are only two variants of `Live socket` data. When everything is ok, the script sends data on candle close:\r\n\r\n```\r\n{\"status\":\"CONNECTED\",\"data\":[1560780120,1.12186,1.12194,1.12186,1.12191,15.00000]}\r\n```\r\n\r\nIf the terminal has lost connection to the market:\r\n\r\n```\r\n{\"status\":\"DISCONNECTED\"}\r\n```\r\n\r\nWhen the terminal reconnects to the market, it sends the last closed candle again. So you should update your historical data. Make the `action=\"HISTORY\"` request with `fromDate` equal to your last candle timestamp before disconnect.\r\n\r\n`OnTradeTransaction` function is called when a trade transaction event occurs. `Streaming socket` sends `TRADE_TRANSACTION_REQUEST` data every time it happens. Try to create and modify orders in the MQL5 terminal manually and check the expert logging tab for better understanding. Also see [MQL5 docs](https://www.mql5.com/en/docs/event_handlers/ontradetransaction). \r\n\r\n`TRADE_TRANSACTION_REQUEST` request data:\r\n\r\n```\r\n{\r\n\t'action': 'TRADE_ACTION_DEAL', \r\n\t'order': 501700843, \r\n\t'symbol': 'EURUSD', \r\n\t'volume': 0.1, \r\n\t'price': 1.12181, \r\n\t'stoplimit': 0.0, \r\n\t'sl': 1.1, \r\n\t'tp': 1.13, \r\n\t'deviation': 10, \r\n\t'type': 'ORDER_TYPE_BUY', \r\n\t'type_filling': 'ORDER_FILLING_FOK', \r\n\t'type_time': 'ORDER_TIME_GTC', \r\n\t'expiration': 0, \r\n\t'comment': None, \r\n\t'position': 0, \r\n\t'position_by': 0\r\n}\r\n```\r\n`TRADE_TRANSACTION_REQUEST` result data:\r\n\r\n```\r\n{\r\n\t'retcode': 10009, \r\n\t'result': 'TRADE_RETCODE_DONE', \r\n\t'deal': 501700843, \r\n\t'order': 501700843, \r\n\t'volume': 0.1, \r\n\t'price': 1.12181, \r\n\t'comment': None, \r\n\t'request_id': 8, \r\n\t'retcode_external': 0\r\n}\r\n```\r\n\r\n## Error handling\r\nFirst of all, when you send a command via `System socket`, you should always receive back `\"OK\"` message via `System socket`. It means that your command was received and deserialized. \r\n\r\nAll data that come through `Data socket` have an `error` param. This param will have `true` key if somethng goes wrong. Also, there will be `description` and `function` params. They will hold information about error and the name of the function with error. \r\n\r\nThis information also applies to the trade commands. See [MQL5 docs](https://www.mql5.com/en/docs/constants/errorswarnings/enum_trade_return_codes) for possible server answers.\r\n\r\n## License\r\nThis 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.\r\n\r\nThis 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.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparrondo%2Fmql5-json-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fparrondo%2Fmql5-json-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparrondo%2Fmql5-json-api/lists"}