ZeroMQ – Trade Execution in MetaTrader (ZMQ-II)
This post builds on the contents of the previous article in this series, namely ZeroMQ – How to Interface Python/R with MetaTrader 4.
Therein, we proposed a solution to creating trading strategies in ZeroMQ supported programming languages outside the MetaTrader environment, with the latter simply acting as the intermediary to the market.
Leveraging ZeroMQ’s convenient communication patterns such as Request (REQ) / Reply (REP) and PUSH/PULL, we demonstrated how to plan and develop a simple yet powerful, distributed trading architecture that opens doors to more sophisticated trading strategy development otherwise not possible within the confines of the MetaTrader terminal.
In this post we’ll describe how to execute new orders and manage existing ones via ZeroMQ, through any supported programming language such as Python or R.
Our route to market will continue to be MetaTrader 4, all trading decisions being made outside it the entire time.
In technical terms – as before, any trading strategy will behave as a Client and MetaTrader as the Server.
We will also develop a simple message topology for performing these execution and reporting functions, such that the same topology is understood by both Clients (trading strategies) and Servers (MetaTrader).
In particular, we’re interested in:
- The Message Topology
- Opening, Modifying & Closing Trades
- Getting Account/Market Information
- Getting Trade Reports
This post will cover the Message Topology and Opening/Modifying/Closing Trades sections. The next post in this series will describe the latter two.
1) Message Topology
Before moving forward, we first need to define some discrete messaging rules that both our trading strategies and MetaTrader can understand.
In the MQL source code provided in the previous post, these rules will need to be implemented in the InterpretZmqMessage function, the definition for which is:
void InterpretZmqMessage(Socket &pSocket, string& compArray)
These rules will in essence map particular messages to actions, for example:
- “TRADE” -> Open a Market Order on the EUR/USD for 0.01 lots, Stop Loss of x and Take Profit of y.
- “RATES” -> What is the current Bid/Ask for EUR/USD?
- “ACCOUNT” -> What is account’s currenty Equity?
- “HISTORY” -> How many trades are currently open?
.. and so on.
For this post’s implementation, let’s define the following message formats:
Opening, Modifying & Closing Trades:
With the message topology in place, we can now construct messages containing trading/reporting instructions.
The ZeroMQ enabled MetaTrader server will receive and execute the instructions via the REQ/REP socket as defined in the first post, and send information back to requesting clients using either the REQ/REP or PUSH/PULL socket depending on the instruction.
One implementation for example, could use REQ/REP for “critical” messages, e.g. Open/Modify/Close trading instructions and their corresponding results, while “non-critical” instructions such as displaying account equity for reporting purposes could use the PUSH/PULL socket.
At the end of the day, there are a number of ways this logic could be implemented.
Your choice of communication pattern (REQ/REP or PUSH/PULL) will depend entirely on how critical you consider one instruction set vs. another.
Generally, it pays dividends to plan this in advance of any development, as different control flows will have different performance results and come with their own considerations (good or bad problems if you will).
In the next 3 sections, we’ll run through some examples of how each message format above can be used in practice.
The ZMQ EA Template for MetaTrader 4 shared in the last post, is already setup with the appropriate code placeholders in the InterpretZmqMessage() function so readers can implement these easily.
2) Opening, Modifying & Closing Trades
As above, we’ll use the following format for trade instructions:
Let’s break this down further, into what each component of this message could contain:
 = TRADE  = ACTION (e.g. OPEN, MODIFY, CLOSE)  = TYPE (e.g. OP_BUY, OP_SELL, etc - only used when ACTION=OPEN) // ORDER TYPES: // https://docs.mql4.com/constants/tradingconstants/orderproperties // OP_BUY = 0 // OP_SELL = 1 // OP_BUYLIMIT = 2 // OP_SELLLIMIT = 3 // OP_BUYSTOP = 4 // OP_SELLSTOP = 5  = Symbol (e.g. EURUSD, etc.)  = Open/Close Price (ignored if ACTION = MODIFY)  = SL  = TP  = Trade Comment  = Trade Ticket ID
For example, our trading strategy in Python or R could send the following message to the ZeroMQ EA deployed in MetaTrader 4:
Here, our trading strategy is requesting that:
- A trade be executed (OPEN),
- Order Type (1) is OP_SELL (meaning sell at Market)
- Symbol to trade is EURUSD
- Open/Close price is set to 0 (which in your code should default to executing at the available market price).
- Stop loss of 50 pips
- Take Profit of 50 pips
- Trade’s comment to be set to “R-to-MetaTrader4”
- Ticket # is 0 (as ticket ID’s are not specified when executing trades in MetaTrader 4).
Implementing this logic in InterpretZmqMessage() inside the MQL EA provided, you can then populate a call to MetaTrader’s OrderSend() function and send the order to market.
In the case of Pending Orders (Buy Stop, Buy Limit, Sell Stop Sell Limit), the code you write should of course not ignore Open/Close price.
A call needs to then be made to OrderSend() with “price” set to this value, and “cmd” set to the appropriate command as received in the message via ZeroMQ,
i.e. OP_BUYLIMIT (if 2) , OP_SELLLIMIT (if 3), OP_BUYSTOP (if 4) and OP_SELLSTOP (if 5).
Note: The numeric values (1,2,3,4,5) are chosen arbitrarily of course. You may choose any values you consider meaningful, as long as you implement their corresponding logic exactly in MetaTrader as you do in your external trading strategy (in e.g. Python or R).
In this case, your logic in the InterpretZmqMessage() function will need to ignore Open/Close price once again, and instead populate a call to MetaTrader’s OrderModify() function with its “stoploss” and “takeprofit” parameters set to those received in the ZeroMQ message.
Order Type can be ignored as an existing order is being modified.
You will also need to read the Ticket ID field and specify it in OrderModify()’s “ticket” parameter, so MetaTrader knows which trade to modify, in this case “12345678”.
Closing / Deleting Orders
The procedure for closing orders in MetaTrader 4 is fairly straightforward.
All you would need is to pass the Ticket ID received via ZeroMQ to an OrderClose() call, along with parameter values for “lots” specifying lot size to close, the “price” you wish to close at (e.g. Bid or Ask) and the maximum “slippage” you’re willing to tolerate on the transaction.
Deleting existing pending orders is even simpler -> simply pass the Ticket ID received via ZeroMQ to OrderDelete(), populating its “ticket” parameter.
In the next post (ZMQ-III), we’ll take this one step further and describe how to implement Account & Trade Reporting via ZeroMQ.
As always, we would greatly appreciate if you shared this content with your colleagues and networks.
And please do feel free to ask questions / provide feedback / make requests / anything in between, via the comments section below.
Webinar Recording: How to Interface Python/R Trading Strategies with MetaTrader 4
Do you have what it takes? – Join the Darwinex Trader Movement!