From 413c6e48a003dd1d0b0c6d582df83fb3b01e9e41 Mon Sep 17 00:00:00 2001 From: Nikolai Date: Sun, 26 May 2019 21:42:24 +0300 Subject: [PATCH] 1.12 --- Experts/JsonAPI.mq5 | Bin 0 -> 60678 bytes Experts/api.mq5 | Bin 42620 -> 0 bytes README.md | 304 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 279 insertions(+), 25 deletions(-) create mode 100644 Experts/JsonAPI.mq5 delete mode 100644 Experts/api.mq5 diff --git a/Experts/JsonAPI.mq5 b/Experts/JsonAPI.mq5 new file mode 100644 index 0000000000000000000000000000000000000000..bb3f2556c20d5b887bb1086e5915a87cde8822d7 GIT binary patch literal 60678 zcmeI5dvjICk-*R2TeaUo)>a%5um}%7;w-5GNf=^~SS0K?u9S+0z*>VKNZ4UXzWVO= zZ(6PCd7Q^wUEq?qZoxe|cg{>tPrtio&j0=I`R;sow)T zyBFQ-?q#>%J?Vbvj=H68Med%F?~C1Uy9?d#%^sViej_*E}zxU*`ByFB|k7eASr9bX{DfeE>y;t)6Q0^Vb zH}@ZfCy(TMzk49R9)+j(1H`m?B=dSMzb*+>&qM3S(ocj)TfPb{_d>fPfe{Gk>7_gc zw7|k0{pBl}`91mjD`{~MTHO~MEOmEe{0DNiFAy$ux576q4nu$UkLPV|4nuE`q~(JI zA{*tpjIOxY4&z5$EOpoAj>3FjpxT$e4!U#F)1F-G^UV0C0Ndg5?@Qe!xtGo)g^?b> zRm9GUV4Ag8%+n`yVIx8f;P|zCLj#JjeB9NO{$js7BXm$(RWP+Lqs4XjAwZ*R^}{iw zx}HBv%co&Y9t7OK3TyCKaQ0O26yZ>cS?a#+ZVGMQ=(c2CcVvXCvcliV_qTF&S3dl` zBX@o;_vjlOvjX5|FW}k3wJ%ijLPpw`-+)?a7)n2oC%_h~5TUr;ZO9mFGQN&;wYw(p z+zK!Oh0^c!@b{*)-IfnDuq96cE0hPEJJMoJW^gO~#oc>y_l~sNmZw&w-I81>O(~T` z7-pmC$H%b2TTttg%y&=bh&*^IIIpe!chVLvhW@#F8gRt=L9fcy59Mk<{0;u@%fE-x z7Kx;|i!?9`+;P_MExhwY`n7e{zH3rSF?ch`f&+Q(DDWKI4cz}J^Er~w3z1bw)1Sj< zRr)@VS*}X^mvVhR&;|2Txo{|V{!qcwBI$;RuN|SKb@_)h-st`&b45l*8B}k%F0FnF zTr<}4s?Y)}&T2oC4_G=9N~!mISAPFl#)ltS0o5%>0n^A}mCBC-d`3giXT7)U^2?Dt z^@H?;KDi%gxo_+>`5o=@RGvaZ#8H$do`q5CBSi`thxvuj4xFMA_D~?RvHIqbV?|{t z7(I~Bp3D$zfo0_3f&2}W{QD1CPqebGs!A&3J8uafN0zHLg4qUWk}>OGRr=@~71?

rvLL&1dcP2{#^89%Nv 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 M=OMR2m!GQt1BOhj)Bpeg diff --git a/README.md b/README.md index ce14230..c39ee62 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,294 @@ -# Metaquotes MQL5 - Python Backtrader - API +# Metaquotes MQL5 - JSON - API -### Development state: Alfa (Code not stable yet, documentation in development) - -[Report Bug](https://github.com/khramkov/MQL5-Backtrader-API/issues) or [Request Feature](https://github.com/khramkov/MQL5-Backtrader-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/MQL5-Backtrader-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: -* Account info -* Balance info -* Historical data -* Live data -* Fetching orders/positions -* Order creation -Not working: -* Cancel order -* Close position +Backtrader Python client located here: [Python Backtrader - Metaquotes MQL5 ](https://github.com/khramkov/MQL5-Backtrader-API) + +In development: +* Historical data load speed +* Add error handling to docs * Trades info -* Check socket connection -* Change timeframe and instrument symdol +* Experation +* Devitation +* Netting/hedging mode switch +* Stop limit orders -## 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 `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`. For `Live socket` and `Streaming socket` event handlers should be created because server sends data to theese sockets automatically. See examples in [Usage](#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`: + +action | actionType | Description | +-----------|----------------------|----------------------------| +CONFIG | None | Set script configuration | +ACCOUNT | None | Get account settings | +BALANCE | None | Get current balance | +POSITIONS | None | Get current open positions | +ORDERS | None | Get current open orders | +HISTORY | DATA | Get data history | +HISTORY | TRADES | Get trades history | +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_CLOSE_ID | Position close by id | +TRADE | POSITION_CLOSE_SYMBOL| Positions close by symbol | +TRADE | ORDER_MODIFY | Order modify | +TRADE | ORDER_CANCEL | Order cancel | + +Example Python API class: + +``` python +import zmq + +class MTraderAPI: + def __init__(self, host=None): + self.HOST = host or 'localhost' + self.SYS_PORT = 15555 # REP/REQ port + self.DATA_PORT = 15556 # PUSH/PULL port + self.LIVE_PORT = 15557 # PUSH/PULL port + self.EVENTS_PORT = 15558 # PUSH/PULL port + + # ZeroMQ timeout in seconds + sys_timeout = 1 + data_timeout = 10 + + # initialise ZMQ context + context = zmq.Context() + + # connect to server sockets + try: + self.sys_socket = context.socket(zmq.REQ) + self.sys_socket.RCVTIMEO = sys_timeout * 1000 + self.sys_socket.connect('tcp://{}:{}'.format(self.HOST, self.SYS_PORT)) + + self.data_socket = context.socket(zmq.PULL) + self.data_socket.RCVTIMEO = data_timeout * 1000 + self.data_socket.connect('tcp://{}:{}'.format(self.HOST, self.DATA_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_socket(self, context=None): + try: + context = context or zmq.Context.instance() + socket = context.socket(zmq.PULL) + socket.connect('tcp://{}:{}'.format(self.HOST, self.LIVE_PORT)) + except zmq.ZMQError: + raise zmq.ZMQBindError("Binding ports ERROR") + return socket + + def streaming_socket(self, context=None): + try: + context = context or zmq.Context.instance() + socket = context.socket(zmq.PULL) + socket.connect('tcp://{}:{}'.format(self.HOST, self.EVENTS_PORT)) + except zmq.ZMQError: + raise zmq.ZMQBindError("Binding ports ERROR") + return socket + + 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() +``` +## Usage +All examples will be on Python 3. Lets create an instance of MetaTrader API class: + +``` python +api = MTraderAPI() +``` + +First of all we should configure script `symbol` and `timeframe`. Live data stream will be configured to the seme params. + +``` python +rep = api.construct_and_send(action="CONFIG", symbol="EURUSD", chartTF="M5") +print(rep) +``` + +Get information about trading account. + +``` python +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` timout. 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. + +``` python +rep = api.construct_and_send(action="HISTORY", actionType="DATA", symbol="EURUSD", chartTF="M5", fromDate=1555555555) +print(rep) +``` +Buy market order. + +``` python +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 + +``` python +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`. + +``` python +import zmq +import threading + +api = MTraderAPI() + + +def _t_livedata(): + socket = api.live_socket() + while True: + try: + last_candle = socket.recv_json() + except zmq.ZMQError: + raise zmq.NotDone("Live data ERROR") + print(last_candle) + + +def _t_streaming_events(): + socket = api.streaming_socket() + while True: + try: + trans = socket.recv_json() + request, reply = trans.values() + except zmq.ZMQError: + raise zmq.NotDone("Streaming data ERROR") + print(request) + print(reply) + + +for i in range(3): + t = threading.Thread(target=_t_livedata, daemon=True) + t.start() + +for i in range(3): + t = threading.Thread(target=_t_streaming_events, daemon=True) + t.start() + +while True: + pass +``` ## License -Distributed under the MIT License. See `LICENSE` for more information. +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.