diff --git a/README.md b/README.md index c39ee62..f4cb082 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,23 @@ Backtrader Python client located here: [Python Backtrader - Metaquotes MQL5 ](ht In development: * Historical data load speed +<<<<<<< HEAD * Add error handling to docs * Trades info * Experation * Devitation * Netting/hedging mode switch * Stop limit orders +======= +* Trades info +* Experation +* Devitation +>>>>>>> f5db7b13fe223453c6b2950ab4e5cddea7a80e19 ## Installation 1. Install ZeroMQ for MQL5 [https://github.com/dingmaotu/mql-zmq](https://github.com/dingmaotu/mql-zmq) +<<<<<<< HEAD 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. @@ -49,6 +56,29 @@ The script uses four ZeroMQ sockets: 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: +======= +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. + + + +Configure the script: +>>>>>>> f5db7b13fe223453c6b2950ab4e5cddea7a80e19 ``` { @@ -69,6 +99,7 @@ The idea is to send requests via `System socket` and recieve results/errors via "comment": None } ``` +<<<<<<< HEAD Check out the available combinations of `action` and `actionType`: action | actionType | Description | @@ -80,6 +111,18 @@ POSITIONS | None | Get current open positions | ORDERS | None | Get current open orders | HISTORY | DATA | Get data history | HISTORY | TRADES | Get trades history | +======= + +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 | +-----------|----------------------|----------------------------| +>>>>>>> f5db7b13fe223453c6b2950ab4e5cddea7a80e19 TRADE | ORDER_TYPE_BUY | Buy market | TRADE | ORDER_TYPE_SELL | Sell market | TRADE | ORDER_TYPE_BUY_LIMIT | Buy limit | @@ -88,17 +131,26 @@ 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 | +<<<<<<< HEAD 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: +======= +TRADE | POSITION_ID | Position close by id | +TRADE | POSITION_CLOSE | Position close | +TRADE | ORDER_MODIFY | Order modify | +TRADE | ORDER_CANCEL | Order cancel | + +>>>>>>> f5db7b13fe223453c6b2950ab4e5cddea7a80e19 ``` python import zmq class MTraderAPI: +<<<<<<< HEAD def __init__(self, host=None): self.HOST = host or 'localhost' self.SYS_PORT = 15555 # REP/REQ port @@ -124,6 +176,39 @@ class MTraderAPI: self.data_socket.connect('tcp://{}:{}'.format(self.HOST, self.DATA_PORT)) except zmq.ZMQError: raise zmq.ZMQBindError("Binding ports ERROR") +======= + + 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") +>>>>>>> f5db7b13fe223453c6b2950ab4e5cddea7a80e19 def _send_request(self, data: dict) -> None: """ Send request to server via ZeroMQ System socket """ @@ -145,6 +230,7 @@ class MTraderAPI: raise zmq.NotDone('Data socket timeout ERROR') return msg +<<<<<<< HEAD def live_socket(self, context=None): try: context = context or zmq.Context.instance() @@ -292,3 +378,60 @@ while True: 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. +======= + 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. +>>>>>>> f5db7b13fe223453c6b2950ab4e5cddea7a80e19