From f5db7b13fe223453c6b2950ab4e5cddea7a80e19 Mon Sep 17 00:00:00 2001 From: Nikolai Date: Sun, 26 May 2019 13:28:00 +0300 Subject: [PATCH] Update README.md --- README.md | 214 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 189 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 33540d9..7e3fc0a 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,203 @@ -# Metaquotes MQL5 - Python Backtrader - API +# Metaquotes MQL5 - JSON - API -### Development state: Betta (Code is stable, documentation in development) - -[Report Bug](https://github.com/khramkov/MQL5-JSON-API/issues) or [Request Feature](https://github.com/khramkov/MQL5-JSON-API/issues) +### Development state: stable beta (code is stable) ## Table of Contents * [About the Project](#about-the-project) -* [Installation and usage](#installation-and-usage) +* [Installation](#installation) +* [Documentation](#documentation) +* [Usage](#usage) * [License](#license) -## About The Project +## About the Project -This project was created to work as a broker for Backtrader trading framework. It uses ZeroMQ sockets to communicate. Python side of this project located here: [Python Backtrader - Metaquotes MQL5 ](https://github.com/khramkov/Backtrader-MQL5-API) +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](http://zeromq.org/bindings:_start). -Working: -* Change timeframe and instrument symdol -* Account info -* Balance info -* Historical data -* Live data -* Fetching orders/positions -* Order creation -* Cancel order -* Close position -Not working: +Backtrader Python client located here: [Python Backtrader - Metaquotes MQL5 ](https://github.com/khramkov/MQL5-Backtrader-API) + +In development: +* Historical data load speed * Trades info +* Experation +* Devitation -## Installation and usage +## Installation -1. Inastall ZeroMQ for MQL5 [https://github.com/dingmaotu/mql-zmq](https://github.com/dingmaotu/mql-zmq) -2. Put 'include/json.mqh' from this repo to your host 'include' directoty. -3. Compile the code and attach it to chart in Metatrader 5. -4. This version dosn't support symbol and timeframe changing. You should control it yourself. +1. Install ZeroMQ for MQL5 [https://github.com/dingmaotu/mql-zmq](https://github.com/dingmaotu/mql-zmq) +2. Put 'include/Json.mqh' from this repo to your MetaEditor 'include' directoty. +3. Download and compile JsonAPI script. +4. Check if 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 and Windows 10 in Parallels Desktop container. + +## Documentation + +The script uses 4 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 - pushes last candle when it closes. +4. Streaming socket - pushes last transaction info when it happens. -## License -Distributed under the GNU License. See `LICENSE` for more information. + +Configure the script: + +``` +{ + "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 +} +``` + +action | actionType | Description | +-----------|----------------------|----------------------------| +CONFIG | null | Set script configuration | +ACCOUNT | null | Get account settings | +BALANCE | null | Get current balance | +HISTORY | null | Get symbol history | +POSITIONS | null | Get current open positions | +ORDERS | null | Get current open orders | +-----------|----------------------|----------------------------| +TRADE | ORDER_TYPE_BUY | Buy market | +TRADE | ORDER_TYPE_SELL | Sell market | +TRADE | ORDER_TYPE_BUY_LIMIT | Buy limit | +TRADE | ORDER_TYPE_SELL_LIMIT| Sell limit | +TRADE | ORDER_TYPE_BUY_STOP | Buy stop | +TRADE | ORDER_TYPE_SELL_STOP | Sell stop | +TRADE | POSITION_MODIFY | Position modify | +TRADE | POSITION_PARTIAL | Position close partial | +TRADE | POSITION_ID | Position close by id | +TRADE | POSITION_CLOSE | Position close | +TRADE | ORDER_MODIFY | Order modify | +TRADE | ORDER_CANCEL | Order cancel | + + +``` python +import zmq + +class MTraderAPI: + + HOST = 'localhost' + SYS_PORT = 15555 # REP/REQ port + DATA_PORT = 15556 # PUSH/PULL port + LIVE_PORT = 15557 # PUSH/PULL port + EVENTS_PORT = 15558 # PUSH/PULL port + + # ZeroMQ timeout in seconds + sys_sock_tmout = 1 + data_sock_tmout = 10 + + # initialise ZMQ context + context = zmq.Context() + + # connect to server sockets + try: + sys_socket = context.socket(zmq.REQ) + sys_socket.RCVTIMEO = sys_sock_tmout * 1000 + sys_socket.connect('tcp://{}:{}'.format(HOST, SYS_PORT)) + + data_socket = context.socket(zmq.PULL) + data_socket.RCVTIMEO = data_sock_tmout * 1000 + data_socket.connect('tcp://{}:{}'.format(HOST, DATA_PORT)) + + live_socket = context.socket(zmq.PULL) + live_socket.connect('tcp://{}:{}'.format(HOST, LIVE_PORT)) + + events_socket = context.socket(zmq.PULL) + events_socket.connect('tcp://{}:{}'.format(HOST, EVENTS_PORT)) + except zmq.ZMQError: + raise zmq.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 request + assert msg == 'OK', 'Something wrong on server side' + except AssertionError as err: + raise zmq.NotDone(err) + except zmq.ZMQError: + raise zmq.NotDone("Sending request ERROR") + + def _pull_reply(self): + """ Get reply from server via Data socket with timeout """ + try: + msg = self.data_socket.recv_json() + except zmq.ZMQError: + raise zmq.NotDone('Data socket timeout ERROR') + return msg + + def live_data(self): + """ Catch live data from server """ + try: + candle = self.live_socket.recv_json() + except zmq.ZMQError: + raise zmq.NotDone("Live data ERROR") + return candle + + def streaming_events(self): + """ Catch events from server """ + try: + candle = self.events_socket.recv_json() + except zmq.ZMQError: + raise zmq.NotDone("Streaming events ERROR") + return candle + + def construct_and_send(self, **kwargs) -> dict: + """ Construct request dictionary from default """ + + # default dictionary + request = { + "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 exist + for key, value in kwargs.items(): + if key in request: + request[key] = value + else: + raise KeyError('Unknown key in **kwargs ERROR') + + # send dict to server + self._send_request(request) + + # return server reply + return self._pull_reply() + +``` + +# License +Distributed under the GNU v3 License. See `LICENSE` for more information.