├── datasets ├── README.md ├── trader-darwins │ ├── quotes │ │ └── README.md │ ├── scores │ │ └── README.md │ └── README.md └── community-darwins │ └── README.md ├── research ├── README.md └── fx_market_volatility │ ├── README.md │ └── data │ └── README.md ├── tools ├── R │ ├── README.md │ ├── ZeroMQ_MT4_R_Template.R │ ├── DLABS-BP-MLD-2.R │ └── dlabs-calculate-spread-statistics-from-tick-data.R ├── Cpp │ └── README.md ├── Java │ └── README.md ├── MQL4 │ ├── README.md │ ├── DLabs_CurrencyIndex.mqh │ ├── DLabs_CurrencyIndex.mq4 │ ├── DLabs_TickData_ToCSV_MT4.mq4 │ ├── DLabs_TickData_ToCSV_MT4_v2.0.mq4 │ ├── DLabs_CurrencyPortfolio.mq4 │ └── ZeroMQ_MT4_EA_Template.mq4 ├── MQL5 │ ├── README.md │ ├── DLabs_TickData_ToCSV_MT5.mq5 │ └── DLabs_TickData_ToCSV_MT5_v2.0.mq5 ├── Python │ ├── README.md │ ├── Tick-Data │ │ ├── README.md │ │ ├── dwx_tickdata_download.py │ │ └── dwx_tick_data_io.py │ ├── MetaTrader_Helpers │ │ ├── README.md │ │ ├── Data_Processing │ │ │ ├── README.md │ │ │ └── DWX_HISTORY_IO_v2_0_1_RC8.py │ │ └── Report_Conversion │ │ │ └── MT_TO_PYTHON │ │ │ ├── images │ │ │ ├── README.md │ │ │ └── mt_to_df_example.png │ │ │ ├── README.md │ │ │ └── DWX_MT_TO_PYTHON_v2_RC4.py │ └── ZeroMQ_MT4_Python_Template.py └── dwx_zeromq_connector │ ├── v2.0.1 │ ├── MQL4 │ │ ├── README.md │ │ └── DWX_ZeroMQ_Server_v2.0.1_RC8.mq4 │ ├── Python │ │ ├── README.md │ │ └── DWX_ZeroMQ_Connector_v2_0_1_RC8.py │ ├── EXAMPLES │ │ ├── __init__.py │ │ └── TEMPLATE │ │ │ ├── STRATEGIES │ │ │ ├── __init__.py │ │ │ ├── BASE │ │ │ │ ├── __init__.py │ │ │ │ └── DWZ_ZMQ_Strategy.py │ │ │ └── coin_flip_traders_v1.0.py │ │ │ └── MODULES │ │ │ ├── DWX_ZMQ_Reporting.py │ │ │ └── DWX_ZMQ_Execution.py │ ├── resources │ │ └── images │ │ │ ├── expert-inputs.png │ │ │ ├── 1200x628-twitter-4.jpg │ │ │ ├── dwx-zeromq-infographic-1.png │ │ │ ├── ZeroMQ_Server_Publishing_Symbol_Data.gif │ │ │ └── InAction_ZeroMQ_Server_Publishing_Symbol_Data.gif │ └── README.md │ ├── mql-zmq-master.zip │ ├── LICENSE │ └── README.md ├── .gitattributes ├── webinars ├── effects-of-market-volatility-on-trader-performance │ ├── README.md │ └── webinar_fx_volatility.xlsx └── README.md ├── LICENSE └── README.md /datasets/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /research/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/R/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/Cpp/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/Java/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/MQL4/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/MQL5/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/Python/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/Python/Tick-Data/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /research/fx_market_volatility/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/Python/MetaTrader_Helpers/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /research/fx_market_volatility/data/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/MQL4/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/Python/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/Python/MetaTrader_Helpers/Data_Processing/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/EXAMPLES/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.py linguist-vendored=false 3 | -------------------------------------------------------------------------------- /webinars/effects-of-market-volatility-on-trader-performance/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/EXAMPLES/TEMPLATE/STRATEGIES/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/Python/MetaTrader_Helpers/Report_Conversion/MT_TO_PYTHON/images/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/EXAMPLES/TEMPLATE/STRATEGIES/BASE/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/mql-zmq-master.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/tools/dwx_zeromq_connector/mql-zmq-master.zip -------------------------------------------------------------------------------- /webinars/README.md: -------------------------------------------------------------------------------- 1 | This directory contains any supplementary materials or resources made available to attendees during Darwinex Labs webinars. 2 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/resources/images/expert-inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/tools/dwx_zeromq_connector/v2.0.1/resources/images/expert-inputs.png -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/resources/images/1200x628-twitter-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/tools/dwx_zeromq_connector/v2.0.1/resources/images/1200x628-twitter-4.jpg -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/resources/images/dwx-zeromq-infographic-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/tools/dwx_zeromq_connector/v2.0.1/resources/images/dwx-zeromq-infographic-1.png -------------------------------------------------------------------------------- /webinars/effects-of-market-volatility-on-trader-performance/webinar_fx_volatility.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/webinars/effects-of-market-volatility-on-trader-performance/webinar_fx_volatility.xlsx -------------------------------------------------------------------------------- /tools/Python/MetaTrader_Helpers/Report_Conversion/MT_TO_PYTHON/images/mt_to_df_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/tools/Python/MetaTrader_Helpers/Report_Conversion/MT_TO_PYTHON/images/mt_to_df_example.png -------------------------------------------------------------------------------- /datasets/trader-darwins/quotes/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Update (as of December 21, 2018) 3 | 4 | This flat-file repository of DARWIN data has been taken offline. Please contact info@darwinex.com to request DARWIN API access instead. 5 | 6 | # 7 | -------------------------------------------------------------------------------- /datasets/trader-darwins/scores/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Update (as of December 21, 2018) 3 | 4 | This flat-file repository of DARWIN data has been taken offline. Please contact info@darwinex.com to request DARWIN API access instead. 5 | 6 | # 7 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/resources/images/ZeroMQ_Server_Publishing_Symbol_Data.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/tools/dwx_zeromq_connector/v2.0.1/resources/images/ZeroMQ_Server_Publishing_Symbol_Data.gif -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/resources/images/InAction_ZeroMQ_Server_Publishing_Symbol_Data.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwinex/DarwinexLabs/HEAD/tools/dwx_zeromq_connector/v2.0.1/resources/images/InAction_ZeroMQ_Server_Publishing_Symbol_Data.gif -------------------------------------------------------------------------------- /datasets/trader-darwins/README.md: -------------------------------------------------------------------------------- 1 | # Update (as of December 21, 2018) 2 | 3 | This flat-file repository of DARWIN data has been taken offline. Please contact info@darwinex.com to request DARWIN API access instead. 4 | 5 | # 6 | 7 | This directory houses DARWIN data from the Darwinex Trader Community. 8 | 9 | DARWIN Data Structure: 10 | -- 11 | Quotes: 12 | 1) timestamp (Unix EPOCH in milliseconds) 13 | 2) Quote (quoted price of the asset on the exchange, indexed to 100 base) 14 | 15 | Scores: 16 | 1) timestamp (Unix EPOCH in milliseconds) 17 | 2) Dp - D Periods 18 | 3) Ex - Experience 19 | 4) Mc - Market Correlation 20 | 5) Rs - Risk Stability 21 | 6) Ra - Risk Adjustment 22 | 7) Os - Open Strategy 23 | 8) Cs - Close Strategy 24 | 9) R+ - Positive Return Consistency 25 | 10) R- - Negative Return Consistency 26 | 11) Dc - Duration Consistency 27 | 12) La - Loss Aversion 28 | 13) Pf - Performance 29 | 14) Cp – Capacity 30 | 15) Ds - D-Score 31 | 32 | For more detail on each of the above features, please visit: https://www.darwinex.com/education 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Darwinex. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /tools/MQL4/DLabs_CurrencyIndex.mqh: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| DLabs_CurrencyIndex.mqh | 3 | //| Copyright 2017, Darwinex Labs. | 4 | //+------------------------------------------------------------------+ 5 | #property copyright "Copyright 2017, Darwinex Labs." 6 | #property strict 7 | 8 | enum CURRENCY 9 | { 10 | EUR=0, 11 | USD=1, 12 | GBP=2, 13 | JPY=3, 14 | AUD=4, 15 | NZD=5, 16 | CHF=6, 17 | CAD=7 18 | }; 19 | 20 | int N=7; 21 | 22 | string symbols[8][7]= 23 | { 24 | "EURUSD","EURGBP","EURAUD","EURNZD","EURJPY","EURCHF","EURCAD", 25 | "EURUSD","GBPUSD","AUDUSD","NZDUSD","USDJPY","USDCHF","USDCAD", 26 | "EURGBP","GBPUSD","GBPAUD","GBPNZD","GBPJPY","GBPCHF","GBPCAD", 27 | "EURJPY","USDJPY","AUDJPY","NZDJPY","GBPJPY","CHFJPY","CADJPY", 28 | "EURAUD","AUDUSD","AUDJPY","AUDNZD","GBPAUD","AUDCHF","AUDCAD", 29 | "EURNZD","NZDUSD","AUDNZD","NZDJPY","GBPNZD","NZDCHF","NZDCAD", 30 | "EURCHF","NZDCHF","AUDCHF","CHFJPY","GBPCHF","USDCHF","CADCHF", 31 | "EURCAD","USDCAD","AUDCAD","NZDCAD","GBPCAD","CADCHF","CADJPY" 32 | }; 33 | 34 | double weights[8][7]= 35 | { 36 | 1,1,1,1,1,1,1, 37 | -1,-1,-1,-1,1,1,1, 38 | -1,1,1,1,1,1,1, 39 | -1,-1,-1,-1,-1,-1,-1, 40 | -1,1,1,1,-1,1,1, 41 | -1,1,-1,1,-1,1,1, 42 | -1,-1,-1,1,-1,-1,-1, 43 | -1,-1,-1,-1,-1,1,1 44 | }; 45 | 46 | string names[] = {"EUR","USD","GBP","JPY","AUD","NZD","CHF","CAD"}; 47 | 48 | int size=8; 49 | int instrumentsSize=28; -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Darwinex. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /datasets/community-darwins/README.md: -------------------------------------------------------------------------------- 1 | # Update (as of December 21, 2018) 2 | 3 | This flat-file repository of DARWIN data has been taken offline. Please contact info@darwinex.com to request DARWIN API access instead. 4 | 5 | # 6 | 7 | Community Darwins are created by Darwinex Labs, quantitatively leveraging community behaviour. 8 | 9 | Community behaviour is the Darwinex community's intellectual property. Therefore, to protect this intellectual property, Community Darwins are only available to the Darwinex Community for investment. 10 | 11 | The data structure of any datasets in this directory is exactly the same as any other DARWIN asset on the Darwinex Exchange. 12 | 13 | DARWIN Data Structure: 14 | -- 15 | Quotes: 16 | 1) timestamp (Unix EPOCH in milliseconds) 17 | 2) Quote (quoted price of the asset on the exchange, indexed to 100 base) 18 | 19 | Scores: 20 | 1) timestamp (Unix EPOCH in milliseconds) 21 | 2) Dp - D Periods 22 | 3) Ex - Experience 23 | 4) Mc - Market Correlation 24 | 5) Rs - Risk Stability 25 | 6) Ra - Risk Adjustment 26 | 7) Os - Open Strategy 27 | 8) Cs - Close Strategy 28 | 9) R+ - Positive Return Consistency 29 | 10) R- - Negative Return Consistency 30 | 11) Dc - Duration Consistency 31 | 12) La - Loss Aversion 32 | 13) Pf - Performance 33 | 14) Cp – Capacity 34 | 15) Ds - D-Score 35 | 36 | For more detail on each of the above features, please visit: https://www.darwinex.com/education 37 | 38 | Filename Conventions: 39 | -- 40 | Filenames for datasets in this directory have the following format: 41 | 42 | DARWIN-TICKER . TIMEFRAME . DATATYPE . LATEST-UPDATE 43 | 44 | For example, if a file is named "DWC.D1.QUOTES.29.11.2017", it contains: 45 | 46 | 1) Data for DARWIN $DWC 47 | 2) Data Sensitivity / Timeframe is D1 (M1 = 1-Minute, D1 = Daily) 48 | 3) Type of data (QUOTES or SCORES) 49 | 4) Contains data up to the 29th of November, 2017. 50 | 51 | **Please note:** 52 | 1) Data in 1-minute precision for DARWIN $DWC is only available from 21st of May, 2017 onwards. 53 | 2) SCORES data will be made available in future releases. 54 | -------------------------------------------------------------------------------- /tools/Python/ZeroMQ_MT4_Python_Template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Status: DEPRECATED 4 | Please visit DarwinexLabs/tree/master/tools/dwx_zeromq_connector 5 | 6 | Created on Thu Aug 24 16:48:05 2017 7 | @author: Darwinex Labs (www.darwinex.com) 8 | """ 9 | 10 | # IMPORT zmq library 11 | import zmq 12 | 13 | # Sample Commands for ZeroMQ MT4 EA 14 | eurusd_buy_order = "TRADE|OPEN|0|EURUSD|0|50|50|Python-to-MT4" 15 | eurusd_sell_order = "TRADE|OPEN|1|EURUSD|0|50|50|Python-to-MT4" 16 | eurusd_closebuy_order = "TRADE|CLOSE|0|EURUSD|0|50|50|Python-to-MT4" 17 | get_rates = "RATES|GBPUSD" 18 | 19 | # Sample Function for Client 20 | def zeromq_mt4_ea_client(): 21 | 22 | # Create ZMQ Context 23 | context = zmq.Context() 24 | 25 | # Create REQ Socket 26 | reqSocket = context.socket(zmq.REQ) 27 | reqSocket.connect("tcp://localhost:5555") 28 | 29 | # Create PULL Socket 30 | pullSocket = context.socket(zmq.PULL) 31 | pullSocket.connect("tcp://localhost:5556") 32 | 33 | # Send RATES command to ZeroMQ MT4 EA 34 | remote_send(reqSocket, get_rates) 35 | 36 | # Send BUY EURUSD command to ZeroMQ MT4 EA 37 | # remote_send(reqSocket, eurusd_buy_order) 38 | 39 | # Send CLOSE EURUSD command to ZeroMQ MT4 EA. You'll need to append the 40 | # trade's ORDER ID to the end, as below for example: 41 | # remote_send(reqSocket, eurusd_closebuy_order + "|" + "12345678") 42 | 43 | # PULL from pullSocket 44 | remote_pull(pullSocket) 45 | 46 | # Function to send commands to ZeroMQ MT4 EA 47 | def remote_send(socket, data): 48 | 49 | try: 50 | socket.send(data) 51 | msg = socket.recv_string() 52 | print msg 53 | 54 | except zmq.Again as e: 55 | print "Waiting for PUSH from MetaTrader 4.." 56 | 57 | # Function to retrieve data from ZeroMQ MT4 EA 58 | def remote_pull(socket): 59 | 60 | try: 61 | msg = socket.recv(flags=zmq.NOBLOCK) 62 | print msg 63 | 64 | except zmq.Again as e: 65 | print "Waiting for PUSH from MetaTrader 4.." 66 | 67 | # Run Tests 68 | zeromq_mt4_ea_client() 69 | 70 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/EXAMPLES/TEMPLATE/MODULES/DWX_ZMQ_Reporting.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | DWX_ZMQ_Reporting.py 4 | -- 5 | @author: Darwinex Labs (www.darwinex.com) 6 | 7 | Copyright (c) 2019 onwards, Darwinex. All rights reserved. 8 | 9 | Licensed under the BSD 3-Clause License, you may not use this file except 10 | in compliance with the License. 11 | 12 | You may obtain a copy of the License at: 13 | https://opensource.org/licenses/BSD-3-Clause 14 | """ 15 | 16 | from pandas import DataFrame, to_datetime 17 | from time import sleep 18 | 19 | class DWX_ZMQ_Reporting(): 20 | 21 | def __init__(self, _zmq): 22 | self._zmq = _zmq 23 | 24 | ########################################################################## 25 | 26 | def _get_open_trades_(self, _trader='Trader_SYMBOL', 27 | _delay=0.1, _wbreak=10): 28 | 29 | # Reset data output 30 | self._zmq._set_response_(None) 31 | 32 | # Get open trades from MetaTrader 33 | self._zmq._DWX_MTX_GET_ALL_OPEN_TRADES_() 34 | 35 | # While loop start time reference 36 | _ws = to_datetime('now') 37 | 38 | # While data not received, sleep until timeout 39 | while self._zmq._valid_response_('zmq') == False: 40 | 41 | sleep(_delay) 42 | 43 | if (to_datetime('now') - _ws).total_seconds() > (_delay * _wbreak): 44 | break 45 | 46 | # If data received, return DataFrame 47 | if self._zmq._valid_response_('zmq'): 48 | 49 | _response = self._zmq._get_response_() 50 | 51 | if ('_trades' in _response.keys() 52 | and len(_response['_trades']) > 0): 53 | 54 | _df = DataFrame(data=_response['_trades'].values(), 55 | index=_response['_trades'].keys()) 56 | return _df[_df['_comment'] == _trader] 57 | 58 | # Default 59 | return DataFrame() 60 | 61 | ########################################################################## 62 | 63 | -------------------------------------------------------------------------------- /tools/Python/MetaTrader_Helpers/Report_Conversion/MT_TO_PYTHON/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Convert MetaTrader Reports to Pandas Dataframes 3 | 4 | ## Latest version: v2_RC4 5 | 6 | ### @author: Darwinex Labs ([click here to visit Darwinex](http://www.darwinex.com?utm_source=github&utm_campaign=darwinex-labs&utm_medium=python-script&utm_content=mt_reports_to_pandas)) 7 | 8 | ## Purpose 9 | 10 | This script enables traders knowledgable in Python to import 11 | their Account History and Strategy Tester reports directly into 12 | pandas dataframes. 13 | 14 | Leverage the capabilities of Python to conduct more meaningful, 15 | sophisitcated analyses of your track records and backtests. 16 | 17 | ## Dependencies: 18 | 19 | - Python 3.6+ 20 | - BeautifulSoup 4 (bs4) 21 | - pandas (Python Data Analysis Library) 22 | - numpy (Scientific Computing with Python) 23 | 24 | ## Notes: 25 | 26 | The script isolates certain structural nuances of MetaTrader's HTML report, 27 | specified in the __init__() function. 28 | 29 | These are at the mercy of MetaTrader, and should these change in future, the 30 | corresponding variables in the script will require adjustments accordingly. 31 | 32 | ## Tested with: 33 | 34 | MetaTrader 4 Build 1170 (20 Dec 2018) 35 | 36 | ## Usage: 37 | 38 | >> _mt = DWX_MT_TO_PYTHON(_verbose=False, 39 | _type='normal', 40 | _filename='') 41 | 42 | 1) Set _type = 'normal' for Account Histories saved as "Normal" Reports 43 | 2) Set _type = 'detailed' for Account Histories saved as "Detailed" Reports 44 | 3) Set _type = 'backtest' for Strategy Tester reports 45 | 46 | By default, dataframes generated are stored inside a class variable 47 | called '_statement_df'. Set _verbose to True to print this upon generation. 48 | 49 | >> print(_mt._statement_df) 50 | 51 | ![Example MetaTrader to Pandas Conversion](images/mt_to_df_example.png) 52 | 53 | ## Found this code useful? 54 | 55 | [Click here to visit us!](http://www.darwinex.com?utm_source=github&utm_campaign=darwinex-labs&utm_medium=python-script&utm_content=mt_reports_to_pandas) We're always keen to hear your feedback, suggestions and anything in between. 56 | 57 | **Have an idea for a useful script?** 58 | 59 | Send us an email at info@darwinex.com with the subject line **[GitHub User Request for Darwinex Labs]** and a brief description of what you have in mind. 60 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/EXAMPLES/TEMPLATE/MODULES/DWX_ZMQ_Execution.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | DWX_ZMQ_Execution.py 4 | -- 5 | @author: Darwinex Labs (www.darwinex.com) 6 | 7 | Copyright (c) 2019 onwards, Darwinex. All rights reserved. 8 | 9 | Licensed under the BSD 3-Clause License, you may not use this file except 10 | in compliance with the License. 11 | 12 | You may obtain a copy of the License at: 13 | https://opensource.org/licenses/BSD-3-Clause 14 | """ 15 | 16 | from pandas import to_datetime 17 | from time import sleep 18 | 19 | class DWX_ZMQ_Execution(): 20 | 21 | def __init__(self, _zmq): 22 | self._zmq = _zmq 23 | 24 | ########################################################################## 25 | 26 | def _execute_(self, 27 | _exec_dict, 28 | _verbose=False, 29 | _delay=0.1, 30 | _wbreak=10): 31 | 32 | _check = '' 33 | 34 | # Reset thread data output 35 | self._zmq._set_response_(None) 36 | 37 | # OPEN TRADE 38 | if _exec_dict['_action'] == 'OPEN': 39 | 40 | _check = '_action' 41 | self._zmq._DWX_MTX_NEW_TRADE_(_order=_exec_dict) 42 | 43 | # CLOSE TRADE 44 | elif _exec_dict['_action'] == 'CLOSE': 45 | 46 | _check = '_response_value' 47 | self._zmq._DWX_MTX_CLOSE_TRADE_BY_TICKET_(_exec_dict['_ticket']) 48 | 49 | if _verbose: 50 | print('\n[{}] {} -> MetaTrader'.format(_exec_dict['_comment'], 51 | str(_exec_dict))) 52 | 53 | # While loop start time reference 54 | _ws = to_datetime('now') 55 | 56 | # While data not received, sleep until timeout 57 | while self._zmq._valid_response_('zmq') == False: 58 | sleep(_delay) 59 | 60 | if (to_datetime('now') - _ws).total_seconds() > (_delay * _wbreak): 61 | break 62 | 63 | # If data received, return DataFrame 64 | if self._zmq._valid_response_('zmq'): 65 | 66 | if _check in self._zmq._get_response_().keys(): 67 | return self._zmq._get_response_() 68 | 69 | # Default 70 | return None 71 | 72 | ########################################################################## 73 | 74 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/EXAMPLES/TEMPLATE/STRATEGIES/BASE/DWZ_ZMQ_Strategy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | DWX_ZMQ_Strategy.py 4 | -- 5 | @author: Darwinex Labs (www.darwinex.com) 6 | 7 | Copyright (c) 2019 onwards, Darwinex. All rights reserved. 8 | 9 | Licensed under the BSD 3-Clause License, you may not use this file except 10 | in compliance with the License. 11 | 12 | You may obtain a copy of the License at: 13 | https://opensource.org/licenses/BSD-3-Clause 14 | """ 15 | import os 16 | 17 | ############################################################################# 18 | ############################################################################# 19 | _path = '' 20 | os.chdir(_path) 21 | ############################################################################# 22 | ############################################################################# 23 | 24 | from DWX_ZeroMQ_Connector_v2_0_1_RC8 import DWX_ZeroMQ_Connector 25 | from EXAMPLES.TEMPLATE.MODULES.DWX_ZMQ_Execution import DWX_ZMQ_Execution 26 | from EXAMPLES.TEMPLATE.MODULES.DWX_ZMQ_Reporting import DWX_ZMQ_Reporting 27 | 28 | class DWX_ZMQ_Strategy(object): 29 | 30 | def __init__(self, _name="DEFAULT_STRATEGY", # Name 31 | _symbols=[('EURUSD',0.01), # List of (Symbol,Lotsize) tuples 32 | ('AUDNZD',0.01), 33 | ('NDX',0.10), 34 | ('UK100',0.1), 35 | ('GDAXI',0.01), 36 | ('XTIUSD',0.01), 37 | ('SPX500',1.0), 38 | ('STOXX50E',0.10), 39 | ('XAUUSD',0.01)], 40 | _broker_gmt=3, # Darwinex GMT offset 41 | _verbose=False): # Print ZeroMQ messages 42 | 43 | self._name = _name 44 | self._symbols = _symbols 45 | self._broker_gmt = _broker_gmt 46 | 47 | # Not entirely necessary here. 48 | self._zmq = DWX_ZeroMQ_Connector(_verbose=_verbose) 49 | 50 | # Modules 51 | self._execution = DWX_ZMQ_Execution(self._zmq) 52 | self._reporting = DWX_ZMQ_Reporting(self._zmq) 53 | 54 | ########################################################################## 55 | 56 | def _run_(self): 57 | 58 | """ 59 | Enter strategy logic here 60 | """ 61 | 62 | ########################################################################## 63 | -------------------------------------------------------------------------------- /tools/R/ZeroMQ_MT4_R_Template.R: -------------------------------------------------------------------------------- 1 | #+------------------------------------------------------------------+ 2 | #| ZeroMQ_MT4_R_Template.R | 3 | #| Status: DEPRECATED | 4 | #| Copyright 2017, Darwinex Labs | 5 | #| https://www.darwinex.com/ | 6 | #+------------------------------------------------------------------+ 7 | 8 | # Load "rzmq" library. If not installed, run install.packages("rzmq") 9 | library(rzmq) 10 | 11 | # Random placeholder for PULL later. 12 | pull.msg <- "N/A" 13 | 14 | # Function to send commands to ZeroMQ MT4 EA 15 | remote.send <- function(rSocket,data) { 16 | send.raw.string(rSocket, data) 17 | msg <- receive.string(rSocket) 18 | 19 | print(msg) 20 | } 21 | 22 | # Function to PULL data from ZeroMQ MT4 EA PUSH socket. 23 | remote.pull <- function(pSocket) { 24 | 25 | msg <- receive.socket(pSocket, unserialize = FALSE, dont.wait = TRUE) 26 | 27 | if(is.null(msg)) { 28 | msg <- "No data PUSHED yet.." 29 | print(msg) 30 | } else { 31 | msg <- rawToChar(msg) 32 | print(msg) 33 | } 34 | 35 | return(msg) 36 | } 37 | 38 | # CREATE ZeroMQ Context 39 | context = init.context() 40 | 41 | # Initialize ZeroMQ REQ Socket 42 | reqSocket = init.socket(context,"ZMQ_REQ") 43 | 44 | # Initialize ZeroMQ PULL Socket 45 | pullSocket = init.socket(context, "ZMQ_PULL") 46 | 47 | # Connect to REQ Socket on port 5555 48 | connect.socket(reqSocket,"tcp://localhost:5555") 49 | 50 | # Connect to PULL Socket on port 5556 51 | connect.socket(pullSocket,"tcp://localhost:5556") 52 | 53 | # Run Tests 54 | while(TRUE) { 55 | 56 | # REMEMBER: If the data you're pulling isn't "downloaded" in MT4's History Centre, 57 | # it's very likely your PULL will produce no data. 58 | 59 | # So if you're going to be pulling data for a currency pair from MT4, 60 | # make sure its data is downloaded, and chart open just in case. 61 | 62 | # Pull from server 63 | remote.pull(pullSocket) 64 | 65 | f <- file("stdin") 66 | open(f) 67 | 68 | print("Enter Command for MetaTrader 4 ZeroMQ Server, 'q' to quit") 69 | # e.g. RATES|EURUSD -> Retrieves Current Bid/Ask for EURUSD from MT4. 70 | mt4.command <- readLines(f, n=1) 71 | 72 | if(tolower(mt4.command) == "q") { 73 | break 74 | } 75 | 76 | # Send to ZeroMQ MetaTrader 4 Server 77 | if(!grepl("PULL", mt4.command)) 78 | remote.send(reqSocket, mt4.command) 79 | 80 | # Pull from ZeroMQ MetaTrader 4 Server 81 | pull.msg <- remote.pull(pullSocket) 82 | } 83 | -------------------------------------------------------------------------------- /tools/MQL4/DLabs_CurrencyIndex.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| DLabs_CurrencyIndex.mq4 | 3 | //| Copyright 2017, Darwinex Labs. | 4 | //+------------------------------------------------------------------+ 5 | #property copyright "Copyright 2017, Darwinex Labs." 6 | #property strict 7 | 8 | #property script_show_inputs 9 | #property indicator_separate_window 10 | #property indicator_buffers 1 11 | #property indicator_color1 Blue 12 | 13 | #include 14 | 15 | //--- input parameters 16 | input CURRENCY currency=EUR; 17 | input string firstDate="2015.01.01"; 18 | 19 | //--Buffers indexes 20 | double idx[]; 21 | 22 | datetime firstDateTime; 23 | //+------------------------------------------------------------------+ 24 | //| Custom indicator initialization function | 25 | //+------------------------------------------------------------------+ 26 | int OnInit() 27 | { 28 | IndicatorShortName("Dlabs_CurrencyIndex [" + names[currency] + "]"); 29 | 30 | IndicatorBuffers(1); 31 | 32 | SetIndexStyle(0,DRAW_LINE); 33 | SetIndexBuffer(0,idx); 34 | SetIndexLabel(0,"INDEX"); 35 | 36 | firstDateTime=StrToTime(firstDate); 37 | 38 | return(INIT_SUCCEEDED); 39 | } 40 | //+------------------------------------------------------------------+ 41 | //| Custom indicator iteration function | 42 | //+------------------------------------------------------------------+ 43 | int OnCalculate(const int rates_total, 44 | const int prev_calculated, 45 | const datetime &time[], 46 | const double &open[], 47 | const double &high[], 48 | const double &low[], 49 | const double &close[], 50 | const long &tick_volume[], 51 | const long &volume[], 52 | const int &spread[]) 53 | { 54 | 55 | ArraySetAsSeries(idx,false); 56 | ArraySetAsSeries(time,false); 57 | 58 | int limit; 59 | if(prev_calculated==0) 60 | { 61 | idx[0]=100.0; 62 | limit=1; 63 | } else { 64 | limit=prev_calculated-1; 65 | } 66 | 67 | for(int i=limit; ifirstDateTime) 70 | { 71 | double contribution=0; 72 | for(int j=0;j 0) 27 | { 28 | if(FileSize(csv_io_hnd) <= 5) 29 | FileWrite(csv_io_hnd, "time_milliseconds", "bid", "ask", "spread"); 30 | 31 | // Move to end of file (if it's being written to again) 32 | FileSeek(csv_io_hnd, 0, SEEK_END); 33 | } 34 | else 35 | Alert("ERROR Opening/Creating CSV file!"); 36 | //--- 37 | return(INIT_SUCCEEDED); 38 | } 39 | //+------------------------------------------------------------------+ 40 | //| Custom indicator iteration function | 41 | //+------------------------------------------------------------------+ 42 | int OnCalculate(const int rates_total, 43 | const int prev_calculated, 44 | const datetime &time[], 45 | const double &open[], 46 | const double &high[], 47 | const double &low[], 48 | const double &close[], 49 | const long &tick_volume[], 50 | const long &volume[], 51 | const int &spread[]) 52 | { 53 | //--- 54 | 55 | // If CSV file handle is open, write timestamp + Bid/Ask + spread + tick_flags data to it. 56 | if (csv_io_hnd > 0) 57 | { 58 | if(SymbolInfoTick(Symbol(), tick_struct)) 59 | { 60 | Comment("\n[Darwinex Labs] Tick Data | Bid: " + DoubleToString(tick_struct.bid, 5) 61 | + " | Ask: " + DoubleToString(tick_struct.ask, 5) 62 | + " | Spread: " + StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5)) 63 | + "\n\n* Writing tick data to \\MQL5\\Files\\" + Symbol() + "_TickData.csv ..." 64 | + "\n(please remove the indicator from this chart to access CSV under \\MQL5\\Files.)" 65 | ); 66 | FileWrite(csv_io_hnd, tick_struct.time_msc, tick_struct.bid, 67 | tick_struct.ask, StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5))); 68 | } 69 | else 70 | Print("ERROR: SymbolInfoTick() failed to validate tick."); 71 | 72 | } 73 | //--- return value of prev_calculated for next call 74 | return(rates_total); 75 | } 76 | //+------------------------------------------------------------------+ 77 | 78 | void OnDeinit(const int reason) 79 | { 80 | // Close CSV file handle if currently open, before exiting. 81 | if(csv_io_hnd > 0) 82 | FileClose(csv_io_hnd); 83 | 84 | // Clear chart comments 85 | Comment(""); 86 | } -------------------------------------------------------------------------------- /tools/MQL4/DLabs_TickData_ToCSV_MT4.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| DLabs_TickData_ToCSV_MT4.mq4 | 3 | //| Copyright 2018, Darwinex Labs. | 4 | //| https://blog.darwinex.com/category/labs/ | 5 | //+------------------------------------------------------------------+ 6 | #property copyright "Copyright 2018, Darwinex Labs." 7 | #property link "https://blog.darwinex.com/category/labs/" 8 | #property version "1.00" 9 | #property strict 10 | #property indicator_chart_window 11 | 12 | // Variables 13 | int csv_io_hnd; 14 | MqlTick tick_struct; 15 | 16 | //+------------------------------------------------------------------+ 17 | //| Custom indicator initialization function | 18 | //+------------------------------------------------------------------+ 19 | int OnInit() 20 | { 21 | //--- indicator buffers mapping 22 | 23 | // Create CSV file handle in WRITE mode. 24 | csv_io_hnd = FileOpen(Symbol() + "_TickData.csv", FILE_CSV|FILE_READ|FILE_WRITE|FILE_REWRITE, ','); 25 | 26 | // If creation successful, write CSV header, else throw error 27 | if(csv_io_hnd > 0) 28 | { 29 | if(FileSize(csv_io_hnd) <= 5) 30 | FileWrite(csv_io_hnd, "time_milliseconds", "bid", "ask", "spread"); 31 | 32 | // Move to end of file (if it's being written to again) 33 | FileSeek(csv_io_hnd, 0, SEEK_END); 34 | } 35 | else 36 | Alert("ERROR: Opening/Creating CSV file!"); 37 | //--- 38 | return(INIT_SUCCEEDED); 39 | } 40 | //+------------------------------------------------------------------+ 41 | //| Custom indicator iteration function | 42 | //+------------------------------------------------------------------+ 43 | int OnCalculate(const int rates_total, 44 | const int prev_calculated, 45 | const datetime &time[], 46 | const double &open[], 47 | const double &high[], 48 | const double &low[], 49 | const double &close[], 50 | const long &tick_volume[], 51 | const long &volume[], 52 | const int &spread[]) 53 | { 54 | //--- 55 | 56 | // If CSV file handle is open, write time, bid, ask and spread to it. 57 | if(csv_io_hnd > 0) 58 | { 59 | if(SymbolInfoTick(Symbol(), tick_struct)) 60 | { 61 | Comment("\n[Darwinex Labs] Tick Data | Bid: " + DoubleToString(tick_struct.bid, 5) 62 | + " | Ask: " + DoubleToString(tick_struct.ask, 5) 63 | + " | Spread: " + StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5)) 64 | + "\n\n* Writing tick data to \\MQL4\\Files\\" + Symbol() + "_TickData.csv ..." 65 | + "\n(please remove the indicator from this chart to access CSV under \\MQL4\\Files.)" 66 | ); 67 | FileWrite(csv_io_hnd, tick_struct.time_msc, tick_struct.bid, 68 | tick_struct.ask, StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5))); 69 | } 70 | else 71 | Print("ERROR: SymbolInfoTick() failed to validate tick."); 72 | } 73 | 74 | 75 | //--- return value of prev_calculated for next call 76 | return(rates_total); 77 | } 78 | //+------------------------------------------------------------------+ 79 | 80 | void OnDeinit(const int reason) 81 | { 82 | // Close CSV file handle if currently open, before exiting. 83 | if(csv_io_hnd > 0) 84 | FileClose(csv_io_hnd); 85 | 86 | // Clear chart comments 87 | Comment(""); 88 | } -------------------------------------------------------------------------------- /tools/Python/Tick-Data/dwx_tickdata_download.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Created on Mon Oct 29 17:24:20 2018 5 | 6 | Script: dwx_tickdata_download.py (Python 3) 7 | -- 8 | Downloads tick data from the Darwinex tick data server. This code demonstrates 9 | how to download data for one specific date/hour combination, but can be 10 | extended easily to downloading entire assets over user-specified start/end 11 | datetime ranges. 12 | 13 | Requirements: Your Darwinex FTP credentials. 14 | 15 | Result: Dictionary of pandas DataFrame objects by date/hour. 16 | (columns: float([ask, size]), index: millisecond timestamp) 17 | 18 | Example code: 19 | 20 | > td = DWX_Tick_Data(dwx_ftp_user='very_secure_username', 21 | dwx_ftp_pass='extremely_secure_password', 22 | dwx_ftp_hostname='mystery_ftp.server.com', 23 | dwx_ftp_port=21) 24 | 25 | > td._download_hour_(_asset='EURNOK', _date='2018-10-22', _hour='00') 26 | 27 | > td._asset_db['EURNOK-2018-10-22-00'] 28 | 29 | ask size 30 | 2018-10-22 00:00:07.097000+00:00 9.47202 1000000.0 31 | 2018-10-22 00:00:07.449000+00:00 9.47188 750000.0 32 | 2018-10-22 00:01:08.123000+00:00 9.47201 250000.0 33 | 2018-10-22 00:01:10.576000+00:00 9.47202 1000000.0 34 | ... ... 35 | 36 | @author: Darwinex Labs 37 | @twitter: https://twitter.com/darwinexlabs 38 | @web: http://blog.darwinex.com/category/labs 39 | 40 | """ 41 | 42 | from ftplib import FTP 43 | from io import BytesIO 44 | import pandas as pd 45 | import gzip 46 | 47 | class DWX_Tick_Data(): 48 | 49 | def __init__(self, dwx_ftp_user='', 50 | dwx_ftp_pass='', 51 | dwx_ftp_hostname='', 52 | dwx_ftp_port=21): 53 | 54 | # Dictionary DB to hold dictionary objects in FX/Hour format 55 | self._asset_db = {} 56 | 57 | self._ftpObj = FTP(dwx_ftp_hostname) 58 | self._ftpObj.login(dwx_ftp_user, dwx_ftp_pass) 59 | 60 | self._virtual_dl = None 61 | 62 | ######################################################################### 63 | # Function: Downloads and stored currency tick data from Darwinex FTP 64 | # Server. Object stores data in a dictionary, keys being of the 65 | # format: CURRENCYPAIR-YYYY-MM-DD-HH 66 | ######################################################################### 67 | 68 | def _download_hour_(self, _asset='EURUSD', _date='2017-10-01', _hour='22', 69 | _ftp_loc_format='{}/{}_ASK_{}_{}.log.gz', 70 | _verbose=False): 71 | 72 | _file = _ftp_loc_format.format(_asset, _asset, _date, _hour) 73 | _key = '{}-{}-{}'.format(_asset, _date, _hour) 74 | 75 | self._virtual_dl = BytesIO() 76 | 77 | if _verbose is True: 78 | print("\n[INFO] Retrieving file \'{}\' from Darwinex Tick Data Server..".format(_file)) 79 | 80 | try: 81 | self._ftpObj.retrbinary("RETR {}".format(_file), self._virtual_dl.write) 82 | 83 | self._virtual_dl.seek(0) 84 | _log = gzip.open(self._virtual_dl) 85 | 86 | # Get bytes into local DB as list of lists 87 | self._asset_db[_key] = [line.strip().decode().split(',') for line in _log] 88 | 89 | # Construct DataFrame 90 | _temp = self._asset_db[_key] 91 | self._asset_db[_key] = pd.DataFrame({'ask': [l[1] for l in _temp], 92 | 'size': [l[2] for l in _temp]}, 93 | index=[pd.to_datetime(l[0], unit='ms', utc=True) for l in _temp]) 94 | 95 | # Sanitize types 96 | self._asset_db[_key] = self._asset_db[_key].astype(float) 97 | 98 | if _verbose is True: 99 | print('\n[SUCCESS] {} tick data for {} (hour {}) stored in self._asset_db dict object.\n'.format(_asset, _date, _hour)) 100 | 101 | # Case: if file not found 102 | except Exception as ex: 103 | _exstr = "Exception Type {0}. Args:\n{1!r}" 104 | _msg = _exstr.format(type(ex).__name__, ex.args) 105 | print(_msg) 106 | 107 | ######################################################################### 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Important Notice (2019-03-18 12:20 UTC): 2 | 3 | Future updates to the _**DWX_ZeroMQ_Connector**_ project will be served in its own dedicated repository here: https://github.com/darwinex/dwx-zeromq-connector 4 | 5 | 6 | 7 | ## Need help? Join the [Darwinex Collective Slack](https://join.slack.com/t/darwinex-collective/shared_invite/enQtNjg4MjA0ODUzODkyLWFiZWZlMDZjNGVmOGE2ZDBiZGI4ZWUxNjM5YTU0MjZkMTQ2NGZjNGIyN2QxZDY4NjUyZmVlNmU3N2E2NGE1Mjk) for Q&A, debug and more. 8 | 9 | # Darwinex Labs 10 | **Darwinex Labs** is the Prop Investing Arm & Quant Team @ **Darwinex (www.darwinex.com) - The Open Trader Exchange.** 11 | 12 | ## Who we are 13 | 14 | Founded in 2012, Darwinex is a **Broker, Asset Manager and Technology Provider**, authorized and regulated by the Financial Conduct Authority (FCA) in the United Kingdom. 15 | 16 | The **Darwinex® trademark** and the www.darwinex.com domain are owned by **Tradeslide Trading Tech Limited**, a company duly authorised and regulated by the **Financial Conduct Authority (FCA) in the United Kingdom with FRN 586466**. 17 | 18 | ## What we do 19 | 20 | In our quest to democratize the financial landscape, we’ve: 21 | 22 | 1. Engineered the technology necessary to **wrap trading strategies as an asset class**. The **DARWIN asset/wrapper** lets investors **invest in / trade manager talent, alpha and community sentiment in real-time, while fairly compensating for trader IP**. 23 | 24 | 1. Eliminated the startup costs and regulatory overhead associated with starting a hedge fund, **enabling under-capitalized talent to compete at institutional scale under our regulatory umbrella** - the foundation of our **Hedge Fund as a Service** business model. 25 | 26 | 1. Successfully assetized alpha, enabling anyone with access to our community data via the DARWIN API to engineer **investment strategies uncorrelated to any other asset class** in existence. 27 | 28 | We’ve hence created an **ecosystem for traders and investors to pool collective talent, information and capital** for private and social benefit, one that: 29 | 30 | 1. Structurally solves the _“manage peanuts, make peanuts” problem_ faced by under-capitalized talent. 31 | 32 | 1. Facilitates **merit-driven access to investor leverage with no downside risk**. 33 | 34 | ## How we do it 35 | 36 | These outcomes have been achieved via our proprietary data wrapper - **the DARWIN (Dynamic Asset & Risk-Weighted Investment)** asset, that: 37 | 38 | 1. Replicates alpha signal in real-time, our algorithmic Risk Manager transforming it to one with a **fixed target Value-at-Risk (95% confidence)** for investors. 39 | 40 | 1. Enables an **‘apples to apples’ comparison** of all DARWIN assets listed on our Exchange, asset risk comparable to that of a mid-cap stock. 41 | 42 | 1. Encrypts all underlying transactions both historically and in real-time, thus **protecting trader IP from compromise and dissemination, enabling fair compensation on a high-water mark basis.** 43 | 44 | 1. Collects **20% performance fees from investor profits on behalf of the alpha generators** providing the signal, and pays the amount to them under our regulatory umbrella. 45 | 46 | ## The DARWIN Exchange 47 | 48 | This transaction between traders and investors is **facilitated through our proprietary marketplace - The DARWIN Exchange - an: 49 | 50 | 1. Online **“Hedge Fund as a Service”** for traders to list their strategy/data as tradable DARWINs. 51 | 52 | 1. **“Liquid Alt Amazon for investors”** where investors source talent via a one-stop shop solution including Prime Brokerage, Risk Management and Back-Office 53 | 54 | Our technology is bolted on to this Exchange **providing traders with regulatory cover to charge a 20% success fee** on investor profits. 55 | 56 | By doing so, we've built **an ecosystem giving traders equal opportunity**, where **merit alone drives outcomes**. 57 | 58 | Their interests are aligned with ours, as well as those of fellow traders, pooling our collective talent, information and capital for private and social benefit. 59 | 60 | ## Trading Technology 61 | 62 | We offer traders a range of platforms, **access to our liquidity via FIX and a proprietary API offering (DARWIN API)** to trade DARWIN assets on our Exchange. 63 | 64 | We strive to deliver the best, **most competitive execution conditions** at all times, and **100% DMA/STP** access to the **FX, Stock, Commodity, Index and Crypto CFD markets**. 65 | 66 | ## [Click here for execution conditions, platforms, assets & spreads](https://www.darwinex.com/executionconditions?utm_source=github&utm_medium=main-page&utm_content=intro-bottom) 67 | -------------------------------------------------------------------------------- /tools/MQL4/DLabs_TickData_ToCSV_MT4_v2.0.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| DLabs_TickData_ToCSV_MT4_v2.0.mq4 | 3 | //| Copyright 2018, Darwinex Labs. | 4 | //| https://blog.darwinex.com/category/labs/ | 5 | //+------------------------------------------------------------------+ 6 | #property copyright "Copyright 2018, Darwinex Labs." 7 | #property link "https://blog.darwinex.com/category/labs/" 8 | #property version "2.00" 9 | #property strict 10 | #property indicator_chart_window 11 | 12 | // Variables 13 | int csv_io_hnd; 14 | MqlTick tick_struct; 15 | string fileName; 16 | string monthStr, dayStr, yearStr; 17 | 18 | //+------------------------------------------------------------------+ 19 | //| Custom indicator initialization function | 20 | //+------------------------------------------------------------------+ 21 | int OnInit() 22 | { 23 | //--- indicator buffers mapping 24 | //--- 25 | return(INIT_SUCCEEDED); 26 | } 27 | //+------------------------------------------------------------------+ 28 | //| Custom indicator iteration function | 29 | //+------------------------------------------------------------------+ 30 | int OnCalculate(const int rates_total, 31 | const int prev_calculated, 32 | const datetime &time[], 33 | const double &open[], 34 | const double &high[], 35 | const double &low[], 36 | const double &close[], 37 | const long &tick_volume[], 38 | const long &volume[], 39 | const int &spread[]) 40 | { 41 | //--- 42 | ///////////////////////////////////////////// 43 | 44 | // 11-04-2018: Construct fileName with symbol, and date. 45 | 46 | if( Month() < 10 ) 47 | monthStr = "0" + DoubleToStr(Month(),0); 48 | else 49 | monthStr = DoubleToStr(Month(),0); 50 | 51 | if ( Day() < 10 ) 52 | dayStr = "0" + DoubleToStr(Day(),0); 53 | else 54 | dayStr = DoubleToStr(Day(),0); 55 | 56 | yearStr = DoubleToStr(Year(),0); 57 | 58 | fileName = Symbol() + "_TickData_" + yearStr + "-" + monthStr + "-" + dayStr + ".csv"; 59 | 60 | ///////////////////////////////////////////// 61 | 62 | // Create CSV file handle in WRITE mode. 63 | csv_io_hnd = FileOpen(fileName, FILE_CSV|FILE_READ|FILE_WRITE|FILE_REWRITE, ','); 64 | 65 | // If creation successful, write CSV header, else throw error 66 | if(csv_io_hnd > 0) 67 | { 68 | if(FileSize(csv_io_hnd) <= 5) 69 | FileWrite(csv_io_hnd, "time_milliseconds", "bid", "ask", "spread"); 70 | 71 | // Move to end of file (if it's being written to again) 72 | FileSeek(csv_io_hnd, 0, SEEK_END); 73 | } 74 | else 75 | Alert("ERROR: Opening/Creating CSV file!"); 76 | 77 | 78 | ///////////////////////////////////////////// 79 | 80 | // If CSV file handle is open, write time, bid, ask and spread to it. 81 | if(csv_io_hnd > 0) 82 | { 83 | if(SymbolInfoTick(Symbol(), tick_struct)) 84 | { 85 | Comment("\n[Darwinex Labs] Tick Data | Bid: " + DoubleToString(tick_struct.bid, 5) 86 | + " | Ask: " + DoubleToString(tick_struct.ask, 5) 87 | + " | Spread: " + StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5)) 88 | + "\n\n* Writing tick data to \\MQL4\\Files\\" + fileName 89 | + "\n(please remove the indicator from this chart to access CSV under \\MQL4\\Files.)" 90 | ); 91 | FileWrite(csv_io_hnd, tick_struct.time_msc, tick_struct.bid, 92 | tick_struct.ask, StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5))); 93 | 94 | // 11-04-2018: Close file handle so it's accessible to other processes. 95 | FileClose(csv_io_hnd); 96 | } 97 | else 98 | Print("ERROR: SymbolInfoTick() failed to validate tick."); 99 | } 100 | 101 | 102 | //--- return value of prev_calculated for next call 103 | return(rates_total); 104 | } 105 | //+------------------------------------------------------------------+ 106 | 107 | void OnDeinit(const int reason) 108 | { 109 | // Close CSV file handle if currently open, before exiting. 110 | if(csv_io_hnd > 0) 111 | FileClose(csv_io_hnd); 112 | 113 | // Clear chart comments 114 | Comment(""); 115 | } -------------------------------------------------------------------------------- /tools/MQL5/DLabs_TickData_ToCSV_MT5_v2.0.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| DLabs_TickData_ToCSV_MT5_v2.0.mq5 | 3 | //| Copyright 2018, Darwinex Labs. | 4 | //| https://blog.darwinex.com/category/labs/ | 5 | //+------------------------------------------------------------------+ 6 | #property copyright "Copyright 2018, Darwinex Labs." 7 | #property link "https://blog.darwinex.com/category/labs/" 8 | #property version "2.00" 9 | #property indicator_chart_window 10 | 11 | // Variables 12 | int csv_io_hnd; 13 | MqlTick tick_struct; 14 | string fileName; 15 | MqlDateTime date_time; 16 | string monthStr, dayStr, yearStr; 17 | 18 | //+------------------------------------------------------------------+ 19 | //| Custom indicator initialization function | 20 | //+------------------------------------------------------------------+ 21 | int OnInit() 22 | { 23 | //--- indicator buffers mapping 24 | 25 | //--- 26 | return(INIT_SUCCEEDED); 27 | } 28 | //+------------------------------------------------------------------+ 29 | //| Custom indicator iteration function | 30 | //+------------------------------------------------------------------+ 31 | int OnCalculate(const int rates_total, 32 | const int prev_calculated, 33 | const datetime &time[], 34 | const double &open[], 35 | const double &high[], 36 | const double &low[], 37 | const double &close[], 38 | const long &tick_volume[], 39 | const long &volume[], 40 | const int &spread[]) 41 | { 42 | //--- 43 | 44 | ///////////////////////////////////////////// 45 | 46 | // 11-04-2018: Construct fileName with symbol, and date. 47 | TimeToStruct(TimeCurrent(), date_time); 48 | 49 | if( date_time.mon < 10 ) 50 | monthStr = "0" + DoubleToString(date_time.mon,0); 51 | else 52 | monthStr = DoubleToString(date_time.mon,0); 53 | 54 | if ( date_time.day < 10 ) 55 | dayStr = "0" + DoubleToString(date_time.day,0); 56 | else 57 | dayStr = DoubleToString(date_time.day,0); 58 | 59 | yearStr = DoubleToString(date_time.year,0); 60 | 61 | fileName = Symbol() + "_TickData_" + yearStr + "-" + monthStr + "-" + dayStr + ".csv"; 62 | 63 | ///////////////////////////////////////////// 64 | 65 | // Create CSV file handle in WRITE mode. 66 | csv_io_hnd = FileOpen(fileName, FILE_CSV|FILE_READ|FILE_WRITE|FILE_REWRITE, '\t'); 67 | 68 | // If creation successful, write CSV header, else throw error. 69 | if (csv_io_hnd > 0) 70 | { 71 | if(FileSize(csv_io_hnd) <= 5) 72 | FileWrite(csv_io_hnd, "time_milliseconds", "bid", "ask", "spread"); 73 | 74 | // Move to end of file (if it's being written to again) 75 | FileSeek(csv_io_hnd, 0, SEEK_END); 76 | } 77 | else 78 | Alert("ERROR Opening/Creating CSV file!"); 79 | 80 | ///////////////////////////////////////////// 81 | 82 | // If CSV file handle is open, write timestamp + Bid/Ask + spread + tick_flags data to it. 83 | if (csv_io_hnd > 0) 84 | { 85 | if(SymbolInfoTick(Symbol(), tick_struct)) 86 | { 87 | Comment("\n[Darwinex Labs] Tick Data | Bid: " + DoubleToString(tick_struct.bid, 5) 88 | + " | Ask: " + DoubleToString(tick_struct.ask, 5) 89 | + " | Spread: " + StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5)) 90 | + "\n\n* Writing tick data to \\MQL5\\Files\\" + fileName 91 | + "\n(please remove the indicator from this chart to access CSV under \\MQL5\\Files.)" 92 | ); 93 | FileWrite(csv_io_hnd, tick_struct.time_msc, tick_struct.bid, 94 | tick_struct.ask, StringFormat("%.05f", NormalizeDouble(MathAbs(tick_struct.bid - tick_struct.ask), 5))); 95 | 96 | FileClose(csv_io_hnd); 97 | } 98 | else 99 | Print("ERROR: SymbolInfoTick() failed to validate tick."); 100 | 101 | } 102 | //--- return value of prev_calculated for next call 103 | return(rates_total); 104 | } 105 | //+------------------------------------------------------------------+ 106 | 107 | void OnDeinit(const int reason) 108 | { 109 | // Close CSV file handle if currently open, before exiting. 110 | if(csv_io_hnd > 0) 111 | FileClose(csv_io_hnd); 112 | 113 | // Clear chart comments 114 | Comment(""); 115 | } 116 | -------------------------------------------------------------------------------- /tools/Python/MetaTrader_Helpers/Data_Processing/DWX_HISTORY_IO_v2_0_1_RC8.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | 5 | DWX_HISTORY_IO_v2_0_1_RC8.py 6 | -- 7 | @author: Darwinex Labs (www.darwinex.com) 8 | 9 | Copyright (c) 2017-2019, Darwinex. All rights reserved. 10 | 11 | Licensed under the BSD 3-Clause License, you may not use this file except 12 | in compliance with the License. 13 | 14 | You may obtain a copy of the License at: 15 | https://opensource.org/licenses/BSD-3-Clause 16 | 17 | Purpose: 18 | To import historical prices from HST archives into Python 19 | 20 | """ 21 | 22 | import os 23 | import struct 24 | import time 25 | import pandas as pd 26 | 27 | pd.set_option('display.max_columns', 500) 28 | pd.set_option('display.width', 1000) 29 | 30 | class DWX_MT_HISTORY_IO(): 31 | 32 | def __init__(self): 33 | 34 | """ 35 | .hst file format valid as of MT4 574 and later 36 | Refer to: https://www.mql5.com/en/forum/149178 37 | 38 | DATABASE HEADER (148 bytes): 39 | 40 | int version; // 4 bytes 41 | string copyright[64]; // 64 bytes 42 | string symbol[12]; // 12 bytes 43 | int period; // 4 bytes 44 | int digits; // 4 bytes 45 | datetime timesign; // 4 bytes 46 | datetime last_sync; // 4 bytes 47 | int unused[13]; // 52 bytes 48 | 49 | BAR STRUCTURE (60 bytes): 50 | 51 | datetime ctm; // 8 bytes 52 | double open; // 8 bytes 53 | double high; // 8 bytes 54 | double low; // 8 bytes 55 | double close; // 8 bytes 56 | long volume; // 8 bytes 57 | int spread; // 4 bytes 58 | long real_volume; // 8 bytes 59 | 60 | Python STRUCT format characters: 61 | (Ref: https://docs.python.org/3/library/struct.html) 62 | 63 | < = (Byte Order: little-endian, Size: standard) 64 | Q = (C Type: unsigned long long, Python type: integer, Size: 8) 65 | d = (C Type: double, Python type: float, Size: 8) 66 | q = (C Type: long long, Python type: integer, Size: 8) 67 | i = (C Type: int, Python type: integer, Size: 4) 68 | """ 69 | 70 | self._HST_BYTE_FORMAT = '= self._HEADER_BYTES: 104 | 105 | _buf = f.read(self._BAR_BYTES) 106 | _seek += self._BAR_BYTES 107 | 108 | if not _buf: 109 | break 110 | 111 | if _verbose == True: 112 | print('[INFO] Extracting Record {} of {} from {} HST archive'.format(_counter, _num_bars, _symbol)) 113 | 114 | _bar = struct.unpack(self._HST_BYTE_FORMAT, _buf) 115 | _open_time.append(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(_bar[0]))) 116 | _open_price.append(_bar[1]) 117 | _high_price.append(_bar[2]) 118 | _low_price.append(_bar[3]) 119 | _close_price.append(_bar[4]) 120 | _volume.append(_bar[5]) 121 | _spread.append(_bar[6]) 122 | _real_volume.append(_bar[7]) 123 | 124 | _counter += 1 125 | 126 | else: 127 | _buf = f.read(self._HEADER_BYTES) 128 | _seek += self._HEADER_BYTES 129 | 130 | _df = pd.DataFrame.from_dict( 131 | {'open_time': _open_time, 132 | 'open': _open_price, 133 | 'high': _high_price, 134 | 'low': _low_price, 135 | 'close': _close_price, 136 | 'volume': _volume, 137 | 'spread': _spread, 138 | 'real_volume': _real_volume} 139 | ).set_index('open_time') 140 | 141 | if _verbose is True: 142 | print(_df) 143 | 144 | return _df 145 | 146 | ########################################################################## -------------------------------------------------------------------------------- /tools/R/DLABS-BP-MLD-2.R: -------------------------------------------------------------------------------- 1 | # 2 | # Machine Learning on DARWIN Datasets - II 3 | # Import & Manipulate DARWIN Time Series in R 4 | # 5 | # DLABS-BP-MLD-2.R 6 | # 7 | 8 | # 1) Load required libraries 9 | 10 | # 2) Importing datasets from GitHub 11 | 12 | # 3) Inspect data types & localize timezones 13 | 14 | # 4) Creating OHLC DARWIN Time Series 15 | 16 | # 5) Visualizing DARWIN Time Series 17 | 18 | # 6) Efficient data storage practices 19 | 20 | # 7) What's coming up next? 21 | 22 | ################################################# 23 | 24 | # 1) Load required libraries 25 | 26 | # Disable scientific formatting 27 | options(scipen=9999) 28 | 29 | if (!require("pacman")) 30 | install.packages("pacman") 31 | 32 | libs.vector <- c("anytime", 33 | "data.table", 34 | "xts", "zoo", "quantmod", 35 | "plotly", 36 | "microbenchmark") 37 | 38 | pacman::p_load(char = libs.vector, 39 | install=TRUE, 40 | update=FALSE) 41 | 42 | ##### 43 | 44 | # 2) Importing datasets from GitHub 45 | 46 | DWC.M1.QUOTES.29.12.2017.dt <- fread("DWC.M1.QUOTES.29.12.2017.csv", colClasses="numeric") 47 | 48 | ##### 49 | 50 | # 3) Initial data preparation, timezone localization & best practices 51 | 52 | # See blog post 53 | 54 | ##### 55 | 56 | # 4) Creating OHLC DARWIN Time Series 57 | 58 | require(xts) 59 | 60 | Convert.toOHLC <- function(x) { 61 | op <- as.vector(first(x)) 62 | hl <- range(x, na.rm = TRUE) 63 | cl <- as.vector(last(x)) 64 | 65 | xts(cbind(Open = op, High = hl[2], Low = hl[1], Close = cl), end(x)) 66 | } 67 | 68 | Convert.DARWIN.To.D1.OHLC.XTS <- function(darwin.M1.dt, 69 | start.hour = 21, 70 | ts.type="open") 71 | { 72 | # 1) Accept M1 data table and convert to 1-hour periodicity xts object 73 | ret.xts <- xts(x = as.numeric(.subset2(darwin.M1.dt, "quote")), 74 | order.by = anytime(as.numeric(.subset2(darwin.M1.dt, "timestamp")), tz="UTC")) 75 | 76 | # Create H1 xts object 77 | ret.xts <- to.period(x = ret.xts, 78 | period = 'hours', 79 | OHLC = FALSE, 80 | indexAt = 'endof') 81 | 82 | ret.H1.zoo <- zoo(coredata(ret.xts), order.by = index(ret.xts)) 83 | 84 | # Convert to xts 85 | y <- as.xts(ret.H1.zoo) 86 | 87 | # Find first occurence of start.hour 88 | first.y <- which(hour(index(y)) == start.hour)[1] 89 | 90 | # Set first observation to epoch (zero) 91 | .index(y) <- .index(y) - .index(y)[first.y] 92 | 93 | # Get endpoints for y by day 94 | ep <- endpoints(y, "days") 95 | 96 | ret.H1.zoo <- period.apply(ret.H1.zoo, ep, Convert.toOHLC) 97 | 98 | if(grepl("open", ts.type)) { 99 | # Lag the series by 1, making each timestamp the Open Time of the corresponding OHLC record. 100 | return(na.omit(lag(as.xts(ret.H1.zoo), -1))) 101 | } else { 102 | return(as.xts(ret.H1.zoo)) 103 | } 104 | } 105 | 106 | ##### 107 | 108 | # 5) Visualizing DARWIN Time Series 109 | 110 | # Visualize XTS data as Candlestick Chart with Bollinger Bands 111 | ts.visualize.DARWIN.xts <- function(darwin.D1.xts, 112 | chart.type="candlestick") { 113 | 114 | # chart.type = candlesticks | closes | returns 115 | 116 | df <- data.frame(Date=index(darwin.D1.xts),coredata(darwin.D1.xts)) 117 | 118 | bb <- BBands(df[ , c("High", "Low", "Close")]) 119 | df <- cbind(df, bb[, 1:3]) 120 | 121 | if(grepl("candlestick", chart.type)) { 122 | print(df %>% 123 | plot_ly(name="DWC", x = ~Date, type="candlestick", 124 | open = ~Open, close = ~Close, 125 | high = ~High, low = ~Low) %>% 126 | 127 | add_lines(y = ~up , name = "Bollinger Bands", 128 | line = list(color = '#ccc', width = 0.5), 129 | legendgroup = "Bollinger Bands", 130 | hoverinfo = "none") %>% 131 | add_lines(y = ~dn, name = "B Bands", 132 | line = list(color = '#ccc', width = 0.5), 133 | legendgroup = "Bollinger Bands", 134 | showlegend = FALSE, hoverinfo = "none") %>% 135 | add_lines(y = ~mavg, name = "Mv Avg", 136 | line = list(color = '#E377C2', width = 0.5), 137 | hoverinfo = "none") %>% 138 | 139 | layout(title = "DARWIN OHLC Candlestick Chart", 140 | yaxis = list(title="DARWIN Quote"), 141 | legend = list(orientation = 'h', x = 0.5, y = 1, 142 | xanchor = 'center', yref = 'paper', 143 | font = list(size = 10), 144 | bgcolor = 'transparent'))) 145 | 146 | } else if (grepl("closes", chart.type)) { 147 | print(df %>% 148 | plot_ly(x = ~Date, type="scatter", mode="lines", 149 | y = ~Close) %>% 150 | 151 | layout(title = "DARWIN OHLC Line Chart (Close Quotes)", 152 | yaxis=list(title="Closing Quote")) ) 153 | 154 | } else if (grepl("returns", chart.type)) { 155 | 156 | rets <- (df$Close[2:nrow(df)] / df$Close[1:nrow(df)-1]) - 1 157 | 158 | print( 159 | plot_ly(x = df$Date, type="scatter", mode="lines", 160 | y = c(0, cumprod(1+rets)-1)*100) %>% 161 | 162 | layout(title = "DARWIN OHLC Line Chart (C.Returns)", 163 | yaxis=list(title="Cumulative Returns (%)")) ) 164 | } 165 | } 166 | 167 | ##### 168 | 169 | # 6) Efficient data storage practices 170 | 171 | DWC.D1.QUOTES.OHLC.xts <- Convert.DARWIN.To.D1.OHLC.XTS(DWC.M1.QUOTES.dt) 172 | 173 | # saveRDS/readRDS vs. write.csv/read.csv 174 | 175 | test.IO.funcs <- function() { 176 | microbenchmark( 177 | write.zoo(DWC.D1.QUOTES.OHLC.xts, "DWC.D1.QUOTES.OHLC.xts.csv", sep=","), 178 | saveRDS(DWC.D1.QUOTES.OHLC.xts, "DWC.D1.QUOTES.OHLC.xts.rds"), 179 | 180 | readRDS("DWC.D1.QUOTES.OHLC.xts.rds"), 181 | read.table("DWC.D1.QUOTES.OHLC.xts.csv", header=TRUE, sep=",") 182 | ) 183 | } 184 | 185 | -------------------------------------------------------------------------------- /tools/R/dlabs-calculate-spread-statistics-from-tick-data.R: -------------------------------------------------------------------------------- 1 | # 2 | # Filename: dlabs-calculate-spread-statistics-from-tick-data.R 3 | # 4 | # Reads in CSV file containing rows of {time_milliseconds|bid|ask|spread} and 5 | # calculates MIN, MAX, AVERAGE and NUM_TICKS for each second, then store as a new CSV. 6 | # 7 | # Date: 28/03/2018 8 | # Darwinex Labs - https://blog.darwinex.com/category/labs/ 9 | # 10 | 11 | library(data.table) 12 | 13 | # Disable scientific numbers. 14 | options(scipen = 9999) 15 | 16 | # Variables 17 | formatter <- "(_TickData_).*\\.csv$" 18 | working_directory <- "C:\\Users\\INSERT-WINDOWS-USERNAME\\AppData\\Roaming\\MetaQuotes\\Terminal\\LONG-ALPHANUMERIC-STRING\\MQL4\\Files" 19 | # working_directory <- "C:\\Users\\INSERT-WINDOWS-USERNAME\\AppData\\Roaming\\MetaQuotes\\Terminal\\LONG-ALPHANUMERIC-STRING\\MQL5\\Files" 20 | zip_archives_directory <- "ZIP_ARCHIVES" 21 | storage_prefix <- "SPREADS_" 22 | 23 | # This date will be used to filter which CSV files to access 24 | # in the \\MQL\\Files directory. 25 | date_of_interest <- Sys.Date()-1 # Actual 26 | # date_of_interest <- Sys.Date() # Current day's testing only. 27 | 28 | # 1) Set Working Directory to MQL -> Files 29 | setwd(working_directory) 30 | 31 | # 2) Get list of files containg string in "formatter" 32 | csv_list <- list.files(pattern = formatter) # Returns a character ARRAY 33 | 34 | # 35 | # FUNCTION - Calculates and returns a data frame containing 36 | # DATE | MIN | MAX | AVERAGE | NUM_TICKS 37 | # 38 | Calculate_Spreads_by_Second <- function(ts_list) 39 | { 40 | require(data.table) 41 | 42 | # Create empty data table with 5 columns 43 | # for DATETIME | MIN | MAX | AVERAGE | NUM_TICKS 44 | ts_len <- length(ts_list) 45 | retDt <- data.table(DATETIME=numeric(ts_len), 46 | MIN=numeric(ts_len), 47 | MAX=numeric(ts_len), 48 | AVERAGE=numeric(ts_len), 49 | NUM_TICKS=numeric(ts_len), 50 | stringsAsFactors = F) 51 | 52 | for(i in 1:ts_len) 53 | { 54 | retDt[i,1] <- .subset2(ts_list[[i]], 1)[1] 55 | retDt[i,2] <- min(.subset2(ts_list[[i]], 4)) # Column "Spread" is index 4 in tick data CSV {time|bid|ask|spread} 56 | retDt[i,3] <- max(.subset2(ts_list[[i]], 4)) 57 | retDt[i,4] <- sum(.subset2(ts_list[[i]], 4)) / length(.subset2(ts_list[[i]], 4)) 58 | retDt[i,5] <- nrow(ts_list[[i]]) 59 | } 60 | 61 | # Return the data table. 62 | return(retDt) 63 | } 64 | 65 | ## 66 | Write_Spreads_to_CSV <- function(wdir=working_directory, 67 | doi=date_of_interest, 68 | sdir=zip_archives_directory, 69 | sop=storage_prefix, 70 | csv.file=NULL, 71 | data=NULL) { 72 | 73 | # Write this data to a new file with PREFIX "SPREADS_" and the original filename 74 | # Don't write row.names - only takes up more disk space, don't need them here. 75 | print("WRITING TO NEW CSV.. please wait..") 76 | 77 | # storage_directory <- as.Date(Sys.Date()) 78 | storage_directory <- as.Date(doi) 79 | 80 | # Create storage directory if it doesn't exist. 81 | dir.create(file.path(wdir, storage_directory), showWarnings = FALSE) 82 | 83 | # Switch to storage directory. 84 | setwd(file.path(wdir, storage_directory)) 85 | 86 | # Write spread statistics to new CSV. 87 | write.csv(data, paste(sop, csv.file, sep=""), row.names = FALSE) 88 | 89 | # Now ZIP this file. 90 | zip(paste(sop, csv.file, ".zip", sep=""), files=paste(sop, csv.file, sep="")) 91 | 92 | # Delete raw summaries CSV. 93 | print("Permanently DELETING SUMMARIES CSV.. please wait..") 94 | file.remove(paste(sop, csv.file, sep="")) 95 | 96 | # ZIP ORIGINAL tick_spreads_ file (make sure RTools is installed!) 97 | dir.create(file.path(wdir, sdir), showWarnings = FALSE) 98 | 99 | # Switch to ZIP_ARCHIVES directory. 100 | setwd(file.path(wdir, sdir)) 101 | 102 | # Move raw tick data CSV to ZIP_ARCHIVES 103 | file.rename(from = paste(wdir, "\\", csv.file, sep=""), to = paste(wdir, "\\", sdir, "\\", csv.file, sep="")) 104 | 105 | # Compress (zip) original tick data CSV. 106 | print("ZIPPING original tick data CSV.. please wait..") 107 | zip(paste(csv.file, ".zip", sep=""), files=csv.file) 108 | 109 | # DELETE ORIGINAL tick_spreads_ file here. 110 | print("Permanently DELETING original CSV.. please wait..") 111 | file.remove(csv.file) 112 | 113 | # DONE 114 | print("DONE! .. Next file?") 115 | } 116 | 117 | ## 118 | Process_Raw_to_Spreads_CSV <- function(csv_list=list()) { 119 | 120 | # 3) Loop through csv_list array and perform conversion steps. 121 | for(csv in csv_list) { 122 | 123 | # MQL4 stores days with leading zeros, but not months. 124 | curr.date <- as.POSIXlt(date_of_interest) 125 | 126 | if (grepl(curr.date, csv)) 127 | { 128 | setwd(working_directory) 129 | 130 | print(paste("Opening: ", csv, " for processing..")) 131 | 132 | # Load data from CSV into Data Table 133 | dt <- fread(csv, colClasses = "numeric") 134 | 135 | # Split data into lists of spreads by time_milliseconds This allows us to perform 136 | # calculations on each second of data. 137 | dt_list <- split(dt, .subset2(dt, "time_milliseconds")) 138 | 139 | # Loop through each element in dt_list, calculate statistics 140 | # and create "day_spreads_by_second" Data Table. 141 | 142 | print("Processing TICKS for EACH Second/Millisecond.. please wait..") 143 | 144 | day_spreads_by_second <- Calculate_Spreads_by_Second(dt_list) 145 | 146 | # Set column names before writing CSV 147 | # colnames(day_spreads_by_second) <- c("DATETIME", "MIN", "MAX", "AVERAGE", "NUM_TICKS") 148 | 149 | # Write to new CSV 150 | Write_Spreads_to_CSV(wdir=working_directory, 151 | doi=date_of_interest, 152 | sdir=zip_archives_directory, 153 | sop=storage_prefix, 154 | csv.file=csv, 155 | data=day_spreads_by_second) 156 | } 157 | else 158 | { 159 | print("....Nothing to do....") 160 | } 161 | } 162 | } 163 | 164 | 165 | ################################################################################### 166 | ################################################################################### 167 | ################################################################################### 168 | 169 | if(length(csv_list) == 0) { 170 | print("....Nothing to do....") 171 | } else { 172 | Process_Raw_to_Spreads_CSV(csv_list) 173 | } 174 | 175 | -------------------------------------------------------------------------------- /tools/MQL4/DLabs_CurrencyPortfolio.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| DLabs_CurrencyPortfolio.mq4 | 3 | //| Copyright 2018, Darwinex Labs. | 4 | //+------------------------------------------------------------------+ 5 | #property copyright "Copyright 2018, Darwinex Labs." 6 | #property strict 7 | 8 | #property script_show_inputs 9 | #property indicator_separate_window 10 | #property indicator_buffers 1 11 | #property indicator_color1 DodgerBlue 12 | 13 | input string Portfolio_Info = "Enter Symbols & Weights (sep. by commas)"; 14 | 15 | //--- input parameters 16 | input string Portfolio_Assets = "NZDCHF,GBPJPY,AUDUSD,EURCAD"; 17 | 18 | input string Portfolio_Weights = "-1,1,1,-1"; 19 | 20 | input string Analysis_Info = "Enter Analysis Start Date Below"; 21 | input string StartDate = "2017.01.01"; 22 | 23 | //--Buffers indexes 24 | double idx[]; 25 | string symbolArray[]; 26 | string weightArray[]; 27 | string portfolioAllocations = ""; 28 | int numSymbols = 0; 29 | int numWeights = 0; 30 | datetime firstDateTime; 31 | 32 | int yOffset = 20; 33 | 34 | //+------------------------------------------------------------------+ 35 | //| Custom indicator initialization function | 36 | //+------------------------------------------------------------------+ 37 | int OnInit() 38 | { 39 | numSymbols = StringSplit(Portfolio_Assets,StringGetCharacter(",",0),symbolArray); 40 | numWeights = StringSplit(Portfolio_Weights,StringGetCharacter(",",0),weightArray); 41 | 42 | // Check Symbols match Weights 43 | if(numSymbols != numWeights) { 44 | Alert("No. of Symbols MUST MATCH No. of Weights. Exiting.."); 45 | return(INIT_FAILED); 46 | } 47 | 48 | IndicatorShortName("[Darwinex Labs] Currency Portfolio Constructor"); 49 | IndicatorBuffers(1); 50 | 51 | SetIndexStyle(0,DRAW_LINE); 52 | SetIndexBuffer(0,idx); 53 | SetIndexLabel(0,"PORTFOLIO"); 54 | 55 | firstDateTime=StrToTime(StartDate); 56 | 57 | return(INIT_SUCCEEDED); 58 | } 59 | //+------------------------------------------------------------------+ 60 | //| Custom indicator iteration function | 61 | //+------------------------------------------------------------------+ 62 | int OnCalculate(const int rates_total, 63 | const int prev_calculated, 64 | const datetime &time[], 65 | const double &open[], 66 | const double &high[], 67 | const double &low[], 68 | const double &close[], 69 | const long &tick_volume[], 70 | const long &volume[], 71 | const int &spread[]) 72 | { 73 | 74 | PrintOnIndicatorWindow(Portfolio_Assets, Portfolio_Weights, yOffset); 75 | 76 | ArraySetAsSeries(idx,false); 77 | ArraySetAsSeries(time,false); 78 | 79 | int limit; 80 | if(prev_calculated==0) 81 | { 82 | idx[0]=100.0; 83 | limit=1; 84 | } else { 85 | limit=prev_calculated-1; 86 | } 87 | 88 | for(int i=limit; ifirstDateTime) 91 | { 92 | double contribution=0; 93 | for(int j=0;j 0: 62 | 63 | return ['{}/{}/{}'.format(self._path, _symbol, _f) for _f in _fs if 'BID' in _f], ['{}/{}/{}' 64 | .format(self._path, _symbol, _f) for _f in _fs if 'ASK' in _f] 65 | 66 | else: 67 | print('[WARNING] No files found for {} - {} - {}' 68 | .format(_symbol, _date, _hour)) 69 | 70 | return None, None 71 | 72 | ########################################################################## 73 | 74 | def _construct_data_(self, _filename): 75 | 76 | _df = pd.DataFrame([line.strip().decode().split(self._delimiter) 77 | for line in gzip.open(_filename) if len(line) > 10]) 78 | 79 | if 'BID' in _filename: 80 | _df.columns = ['timestamp','bid_price','bid_size'] 81 | elif 'ASK' in _filename: 82 | _df.columns = ['timestamp','ask_price','ask_size'] 83 | 84 | _df.set_index('timestamp', drop=True, inplace=True) 85 | 86 | return _df.apply(pd.to_numeric) 87 | 88 | ########################################################################## 89 | 90 | def _get_symbol_as_dataframe_(self, _symbol='EURUSD', 91 | _date='', 92 | _hour='', 93 | _convert_epochs=True, 94 | _check_integrity=False, 95 | _calc_spread = False, 96 | _reindex=['ask_price','bid_price'], 97 | _precision='tick', 98 | _daily_start=22, 99 | _symbol_digits=5): 100 | 101 | """ 102 | See http://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html 103 | for .resample() rule / frequency strings. 104 | """ 105 | 106 | print('[INFO] Finding symbol files.. please wait..') 107 | 108 | _bid_files, _ask_files = self._find_symbol_files_(_symbol,_date,_hour) 109 | 110 | print('[INFO] Processing BID ({}) / ASK ({}) files.. please wait..'.format(len(_bid_files), len(_ask_files))) 111 | 112 | # BIDS 113 | _bids = pd.concat([self._construct_data_(_bid_files[i]) 114 | for i in range(0, len(_bid_files)) if ((print('\rBIDS: {} / {} - {}' 115 | .format(i+1,len(_bid_files),_bid_files[i]), end="", flush=True) or 1==1))], axis=0, sort=True) 116 | print('') 117 | 118 | # ASKS 119 | _asks = pd.concat([self._construct_data_(_ask_files[i]) 120 | for i in range(0, len(_ask_files)) if ((print('\rASKS: {} / {} - {}' 121 | .format(i+1,len(_ask_files),_ask_files[i]), end="", flush=True) or 1==1))], axis=0, sort=True) 122 | 123 | _df = _asks.merge(_bids, how='outer', left_index=True, right_index=True, copy=False).fillna(method='ffill').dropna() 124 | 125 | # Calculate spread? 126 | if _calc_spread: 127 | _df['spread'] = abs(np.diff(_df[['ask_price','bid_price']])) 128 | 129 | # Convert timestamps? 130 | if _convert_epochs: 131 | _df.index = pd.to_datetime(_df.index, unit='ms') 132 | 133 | # Reindex to selected columns? 134 | if len(_reindex) > 0: 135 | _df = _df.reindex(_reindex, axis=1) 136 | 137 | # Resample? 138 | if _precision != 'tick': 139 | _df['mid_price'] = round((_df.ask_price + _df.bid_price) / 2, _symbol_digits) 140 | 141 | if _precision not in ['B','C','D','W','24H']: 142 | _df = _df.mid_price.resample(rule=_precision).ohlc() 143 | else: 144 | _df = _df.mid_price.resample(rule=_precision, base=_daily_start).ohlc().dropna() 145 | 146 | # Check data integrity? 147 | if _check_integrity: 148 | 149 | print('\n\n[INFO] Checking data integrity..') 150 | self._integrity_check_(_df) 151 | 152 | return _df 153 | 154 | ########################################################################## 155 | 156 | def _integrity_check_(self, _df): 157 | 158 | if isinstance(_df, pd.DataFrame) == False: 159 | 160 | print('[ERROR] Input must be a Pandas DataFrame') 161 | 162 | else: 163 | 164 | _diff = _df.index.to_series().diff() 165 | 166 | print('\n[TEST #1] Data Frequency Statistics\n--') 167 | print(_diff.describe()) 168 | 169 | print('\n[TEST #2] Mode of Gap Distribution\n--') 170 | print(_diff.value_counts().head(1)) 171 | 172 | print('\n[TEST #3] Hourly Spread Distribution\n--') 173 | _df.groupby(_df.index.hour).spread.mean().plot( 174 | xticks=range(0,24), 175 | title='Average Spread by Hour (UTC)') 176 | 177 | ########################################################################## 178 | -------------------------------------------------------------------------------- /tools/Python/MetaTrader_Helpers/Report_Conversion/MT_TO_PYTHON/DWX_MT_TO_PYTHON_v2_RC4.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Created on Mon Feb 04 12:01:07 2019 5 | @author: Darwinex Labs (www.darwinex.com) 6 | 7 | DWX_MT_TO_PYTHON_v2_RC4.py 8 | 9 | Purpose: 10 | 11 | This script enables traders knowledgable in Python to import 12 | their Account History and Strategy Tester reports directly into 13 | pandas dataframes. 14 | 15 | Leverage the capabilities of Python to conduct more meaninful, 16 | sophisitcated analyses of your track records and backtests. 17 | 18 | Dependencies: 19 | 20 | - Python 3.6+ 21 | - BeautifulSoup 4 (bs4) 22 | - pandas (Python Data Analysis Library) 23 | - numpy (Scientific Computing with Python) 24 | 25 | Notes: 26 | 27 | The script isolates certain structural nuances of MetaTrader's HTML report, 28 | specified in the __init__() function. 29 | 30 | These are at the mercy of MetaTrader, and should these change in future, the 31 | corresponding variables in the script will require adjustments accordingly. 32 | 33 | Tested with: 34 | 35 | MetaTrader 4 Build 1170 (20 Dec 2018) 36 | 37 | Usage: 38 | 39 | 1) Set _type = 'normal' for Account Histories saves as "Normal" Reports 40 | 2) Set _type = 'detailed' for Account Histories saves as "Detailed" Reports 41 | 3) Set _type = 'backtest' for Strategy Tester reports 42 | 43 | By default, dataframes generated are stored inside a class variable 44 | called '_statement_df'. Set _verbose to True to print this upon generation. 45 | 46 | """ 47 | 48 | from bs4 import BeautifulSoup 49 | import pandas as pd 50 | import numpy as np 51 | 52 | # Set pandas options 53 | pd.set_option('display.width', 1000) 54 | pd.set_option('display.max_columns', 500) 55 | 56 | class DWX_MT_TO_PYTHON(): 57 | 58 | def __init__(self, _verbose=False, 59 | _type='normal', 60 | _filename=''): 61 | 62 | ############################# 63 | # MetaTrader HTML Variables # 64 | ############################# 65 | 66 | self.STATEMENT_HEADER = ['ticket','open_time','type','size','item', 67 | 'open_price','sl','tp','close_time', 68 | 'close_price','commission','taxes', 69 | 'swap','profit','magic','comment'] 70 | 71 | self.BACKTEST_HEADER = ['id','open_time','type','order','size', 72 | 'open_price','sl','tp','profit','balance'] 73 | 74 | self.TRADE_FIELDS = [(self.STATEMENT_HEADER[_r],_r) for _r in range(0,14)] 75 | 76 | self.BALANCE_FIELDS = [('ticket',0), 77 | ('open_time',1), 78 | ('type',2), 79 | ('item',3), 80 | ('profit',4)] 81 | 82 | self.COMMENT_FIELDS = [('magic',1), 83 | ('comment',2)] 84 | 85 | # Variables to sanitize 86 | self.ST_NUMS_TO_SANITIZE = ['ticket','size','open_price','close_price', 87 | 'commission','taxes','swap', 88 | 'profit','sl','tp','magic'] 89 | 90 | self.BT_NUMS_TO_SANITIZE = ['id','order','size','open_price', 91 | 'sl','tp','profit','balance'] 92 | 93 | self.ST_DATES_TO_SANITIZE = ['open_time','close_time'] 94 | 95 | 96 | self.BT_DATES_TO_SANITIZE = ['open_time'] 97 | 98 | # Number of rows to exclude depending on statement type (normal or detailed) 99 | self.NORMAL_TRUNC = 15 100 | self.DETAILED_TRUNC = 29 101 | self.BACKTEST_TRUNC = 0 102 | 103 | # Backtest rows differ as follows: 104 | self.BT_ROW_NO_PROFIT_LEN = 8 105 | self.BT_ROW_WITH_PROFIT_LEN = 10 106 | 107 | ##################################### 108 | # Store DataFrame for other methods # 109 | ##################################### 110 | self._statement_df = None 111 | self._backtest_df = None 112 | self._verbose = _verbose 113 | 114 | # Transform input file into pandas dataframe 115 | print(self._statement_to_dataframe_(_type=_type, 116 | _filename=_filename)) 117 | 118 | print('\n[INFO] Data stored in self._statement_df.') 119 | 120 | ########################################################################## 121 | 122 | def _statement_to_dataframe_(self, _type='normal', 123 | _filename=''): 124 | 125 | # Check if input type is correct, else return error 126 | if _type not in ['backtest','normal','detailed']: 127 | print('[KERNEL] Invalid input file -> must be one of backtest, normal or detailed.') 128 | return None 129 | 130 | try: 131 | with open (_filename, "r") as myfile: 132 | s=myfile.read() 133 | except FileNotFoundError: 134 | print('[ERROR] No such file exists!') 135 | return None 136 | 137 | # Invoke HTML Parser 138 | _soup = BeautifulSoup(s, 'html.parser') 139 | 140 | if _type.lower() == 'normal': 141 | _table = _soup.find_all('table')[0] 142 | _trunc = self.NORMAL_TRUNC 143 | elif _type.lower() == 'detailed': 144 | _table = _soup.find_all('table')[0] 145 | _trunc = self.DETAILED_TRUNC 146 | elif _type.lower() == 'backtest': 147 | _table = _soup.find_all('table')[1] 148 | _trunc = self.BACKTEST_TRUNC 149 | else: 150 | print('[ERROR] Unrecognized statement type.. must be backtest, normal or detailed.') 151 | return None 152 | 153 | # Count number of rows in track record (ignore last 14) 154 | _x = (len(_table.findAll('tr')) - _trunc) 155 | 156 | # Extract rows 157 | _rows = _table.findAll('tr') 158 | 159 | if _type == 'backtest': 160 | _rows = _rows[1:_x] 161 | 162 | # Create dict DB with empty lists (for dataframe later) 163 | _dict = {_c: [np.nan for _l in range(len(_rows))] for _c in self.BACKTEST_HEADER} 164 | 165 | else: 166 | _rows = _rows[3:_x] 167 | 168 | # Create dict DB with empty lists (for dataframe later) 169 | _dict = {_c: [np.nan for _l in range(len(_rows))] for _c in self.STATEMENT_HEADER} 170 | 171 | # Initialize row counter 172 | _row_counter = 0 173 | 174 | for _row in _rows: 175 | 176 | # Extract values 177 | _values = _row.findAll('td') 178 | 179 | if _type != 'backtest': 180 | 181 | ####################################### 182 | # Balance record (deposit/withdrawal) # 183 | ####################################### 184 | if len(_row) == len(self.BALANCE_FIELDS): 185 | 186 | for _f in self.BALANCE_FIELDS: 187 | _dict[_f[0]][_row_counter] = _values[_f[1]].getText() 188 | 189 | ################ 190 | # Trade record # 191 | ################ 192 | elif len(_row) == len(self.TRADE_FIELDS): 193 | 194 | for _f in self.TRADE_FIELDS: 195 | _dict[_f[0]][_row_counter] = _values[_f[1]].getText() 196 | 197 | ################### 198 | # Comment / Magic # 199 | ################### 200 | elif len(_row) == len(self.COMMENT_FIELDS)+1: 201 | 202 | # Update previous trade's comment/magic fields 203 | for _f in self.COMMENT_FIELDS: 204 | _dict[_f[0]][_row_counter-1] = _values[_f[1]].getText() 205 | 206 | else: 207 | print('[ERROR] Cannot recognize row structure.. please check HTML report to confirm if anything has changed?') 208 | return None 209 | 210 | else: 211 | if len(_values) == self.BT_ROW_WITH_PROFIT_LEN: 212 | _iter_range = range(self.BT_ROW_WITH_PROFIT_LEN) 213 | else: 214 | _iter_range = range(self.BT_ROW_NO_PROFIT_LEN) 215 | 216 | for _i in _iter_range: 217 | _dict[self.BACKTEST_HEADER[_i]][_row_counter] = _values[_i].getText() 218 | 219 | # Update for next iteration 220 | _row_counter += 1 221 | 222 | # Create dataframe 223 | _df = pd.DataFrame(data=_dict).dropna(how='all') 224 | 225 | # Sanitize data types 226 | if _type != 'backtest': 227 | 228 | for _n in self.ST_NUMS_TO_SANITIZE: 229 | _df[_n] = pd.to_numeric(_df[_n].str.replace(' ','')) 230 | 231 | for _d in self.ST_DATES_TO_SANITIZE: 232 | _df[_d] = pd.to_datetime(_df[_d]) 233 | 234 | # Save locally for future use 235 | self._statement_df = _df 236 | 237 | else: 238 | 239 | for _n in self.BT_NUMS_TO_SANITIZE: 240 | _df[_n] = pd.to_numeric(_df[_n].str.replace(' ','')) 241 | 242 | for _d in self.BT_DATES_TO_SANITIZE: 243 | _df[_d] = pd.to_datetime(_df[_d]) 244 | 245 | # Save locally for future use 246 | self._backtest_df = _df 247 | 248 | # Return sanitized dataframe 249 | if self._verbose == True: 250 | return _df 251 | 252 | ########################################################################## 253 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/EXAMPLES/TEMPLATE/STRATEGIES/coin_flip_traders_v1.0.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | coin_flip_traders.py 4 | 5 | An example trading strategy created using the Darwinex ZeroMQ Connector 6 | for Python 3 and MetaTrader 4. 7 | 8 | Source code: 9 | https://github.com/darwinex/DarwinexLabs/tree/master/tools/dwx_zeromq_connector 10 | 11 | The strategy launches 'n' threads (each representing a trader responsible 12 | for trading one instrument) 13 | 14 | Each trader must: 15 | 16 | 1) Execute a maximum of 1 trade at any given time. 17 | 18 | 2) Close existing trades after they have been in execution for 19 | 5 seconds. 20 | 21 | 3) Flip a coin - random.randombits(1) - to decide on a BUY or SELL 22 | 23 | 4) Keep trading until the market is closed (_market_open = False) 24 | -- 25 | 26 | @author: Darwinex Labs (www.darwinex.com) 27 | 28 | Copyright (c) 2019 onwards, Darwinex. All rights reserved. 29 | 30 | Licensed under the BSD 3-Clause License, you may not use this file except 31 | in compliance with the License. 32 | 33 | You may obtain a copy of the License at: 34 | https://opensource.org/licenses/BSD-3-Clause 35 | """ 36 | 37 | import os 38 | 39 | ############################################################################# 40 | ############################################################################# 41 | _path = '' 42 | os.chdir(_path) 43 | ############################################################################# 44 | ############################################################################# 45 | 46 | from EXAMPLES.TEMPLATE.STRATEGIES.BASE.DWX_ZMQ_Strategy import DWX_ZMQ_Strategy 47 | 48 | from pandas import Timedelta, to_datetime 49 | from threading import Thread, Lock 50 | from time import sleep 51 | import random 52 | 53 | class coin_flip_traders(DWX_ZMQ_Strategy): 54 | 55 | def __init__(self, _name="COIN_FLIP_TRADERS", 56 | _symbols=[('EURUSD',0.01), 57 | ('AUDNZD',0.01), 58 | ('GBPUSD',0.01), 59 | ('USDJPY',0.01), 60 | ('AUDUSD',0.01), 61 | ('XTIUSD',0.01), 62 | ('GBPJPY',0.01), 63 | ('NZDCHF',0.01), 64 | ('EURCAD',0.01)], 65 | _delay=0.1, 66 | _broker_gmt=3, 67 | _verbose=False, 68 | 69 | _max_trades=1, 70 | _close_t_delta=5): 71 | 72 | super().__init__(_name, 73 | _symbols, 74 | _broker_gmt, 75 | _verbose) 76 | 77 | # This strategy's variables 78 | self._traders = [] 79 | self._market_open = True 80 | self._max_trades = _max_trades 81 | self._close_t_delta = _close_t_delta 82 | self._delay = _delay 83 | self._verbose = _verbose 84 | 85 | # lock for acquire/release of ZeroMQ connector 86 | self._lock = Lock() 87 | 88 | ########################################################################## 89 | 90 | def _run_(self): 91 | 92 | """ 93 | Logic: 94 | 95 | For each symbol in self._symbols: 96 | 97 | 1) Open a new Market Order every 2 seconds 98 | 2) Close any orders that have been running for 10 seconds 99 | 3) Calculate Open P&L every second 100 | 4) Plot Open P&L in real-time 101 | 5) Lot size per trade = 0.01 102 | 6) SL/TP = 10 pips each 103 | """ 104 | 105 | # Launch traders! 106 | for _symbol in self._symbols: 107 | 108 | _t = Thread(name="{}_Trader".format(_symbol[0]), 109 | target=self._trader_, args=(_symbol,self._max_trades)) 110 | 111 | _t.daemon = True 112 | _t.start() 113 | 114 | print('[{}_Trader] Alright, here we go.. Gerrrronimooooooooooo! ..... xD'.format(_symbol[0])) 115 | 116 | self._traders.append(_t) 117 | 118 | print('\n\n+--------------+\n+ LIVE UPDATES +\n+--------------+\n') 119 | 120 | # _verbose can print too much information.. so let's start a thread 121 | # that prints an update for instructions flowing through ZeroMQ 122 | self._updater_ = Thread(name='Live_Updater', 123 | target=self._updater_, 124 | args=(self._delay,)) 125 | 126 | self._updater_.daemon = True 127 | self._updater_.start() 128 | 129 | ########################################################################## 130 | 131 | def _updater_(self, _delay=0.1): 132 | 133 | while self._market_open: 134 | 135 | try: 136 | # Acquire lock 137 | self._lock.acquire() 138 | 139 | print('\r{}'.format(str(self._zmq._get_response_())), end='', flush=True) 140 | 141 | finally: 142 | # Release lock 143 | self._lock.release() 144 | 145 | sleep(self._delay) 146 | 147 | ########################################################################## 148 | 149 | def _trader_(self, _symbol, _max_trades): 150 | 151 | # Note: Just for this example, only the Order Type is dynamic. 152 | _default_order = self._zmq._generate_default_order_dict() 153 | _default_order['_symbol'] = _symbol[0] 154 | _default_order['_lots'] = _symbol[1] 155 | _default_order['_SL'] = _default_order['_TP'] = 100 156 | _default_order['_comment'] = '{}_Trader'.format(_symbol[0]) 157 | 158 | """ 159 | Default Order: 160 | -- 161 | {'_action': 'OPEN', 162 | '_type': 0, 163 | '_symbol': EURUSD, 164 | '_price':0.0, 165 | '_SL': 100, # 10 pips 166 | '_TP': 100, # 10 pips 167 | '_comment': 'EURUSD_Trader', 168 | '_lots': 0.01, 169 | '_magic': 123456} 170 | """ 171 | 172 | while self._market_open: 173 | 174 | try: 175 | 176 | # Acquire lock 177 | self._lock.acquire() 178 | 179 | ############################# 180 | # SECTION - GET OPEN TRADES # 181 | ############################# 182 | 183 | _ot = self._reporting._get_open_trades_('{}_Trader'.format(_symbol[0]), 184 | self._delay, 185 | 10) 186 | 187 | # Reset cycle if nothing received 188 | if self._zmq._valid_response_(_ot) == False: 189 | continue 190 | 191 | ############################### 192 | # SECTION - CLOSE OPEN TRADES # 193 | ############################### 194 | 195 | for i in _ot.index: 196 | 197 | if abs((Timedelta((to_datetime('now') + Timedelta(self._broker_gmt,'h')) - to_datetime(_ot.at[i,'_open_time'])).total_seconds())) > self._close_t_delta: 198 | 199 | _ret = self._execution._execute_({'_action': 'CLOSE', 200 | '_ticket': i, 201 | '_comment': '{}_Trader'.format(_symbol[0])}, 202 | self._verbose, 203 | self._delay, 204 | 10) 205 | 206 | # Reset cycle if nothing received 207 | if self._zmq._valid_response_(_ret) == False: 208 | break 209 | 210 | # Sleep between commands to MetaTrader 211 | sleep(self._delay) 212 | 213 | ############################## 214 | # SECTION - OPEN MORE TRADES # 215 | ############################## 216 | 217 | if _ot.shape[0] < _max_trades: 218 | 219 | # Randomly generate 1 (OP_BUY) or 0 (OP_SELL) 220 | # using random.getrandbits() 221 | _default_order['_type'] = random.getrandbits(1) 222 | 223 | # Send instruction to MetaTrader 224 | _ret = self._execution._execute_(_default_order, 225 | self._verbose, 226 | self._delay, 227 | 10) 228 | 229 | # Reset cycle if nothing received 230 | if self._zmq._valid_response_(_ret) == False: 231 | continue 232 | 233 | finally: 234 | 235 | # Release lock 236 | self._lock.release() 237 | 238 | # Sleep between cycles 239 | sleep(self._delay) 240 | 241 | ########################################################################## 242 | 243 | def _stop_(self): 244 | 245 | self._market_open = False 246 | 247 | for _t in self._traders: 248 | 249 | # Setting _market_open to False will stop each "trader" thread 250 | # from doing anything more. So wait for them to finish. 251 | _t.join() 252 | 253 | print('\n[{}] .. and that\'s a wrap! Time to head home.\n'.format(_t.getName())) 254 | 255 | # Kill the updater too 256 | self._updater_.join() 257 | 258 | print('\n\n{} .. wait for me.... I\'m going home too! xD\n'.format(self._updater_.getName())) 259 | 260 | # Send mass close instruction to MetaTrader in case anything's left. 261 | self._zmq._DWX_MTX_CLOSE_ALL_TRADES_() 262 | 263 | ########################################################################## 264 | -------------------------------------------------------------------------------- /tools/MQL4/ZeroMQ_MT4_EA_Template.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| Status: DEPRECATED | 3 | //| Please visit DarwinexLabs/tree/master/tools/dwx_zeromq_connector | 4 | //| | 5 | //| ZeroMQ_MT4_EA_Template.mq4 | 6 | //| Copyright 2017, Darwinex Labs | 7 | //| https://www.darwinex.com/ | 8 | //+------------------------------------------------------------------+ 9 | #property copyright "Copyright 2017, Darwinex Labs." 10 | #property link "https://www.darwinex.com/" 11 | #property version "1.00" 12 | #property strict 13 | 14 | // Required: MQL-ZMQ from https://github.com/dingmaotu/mql-zmq 15 | #include 16 | 17 | extern string PROJECT_NAME = "DWX_ZeroMQ_Example"; 18 | extern string ZEROMQ_PROTOCOL = "tcp"; 19 | extern string HOSTNAME = "*"; 20 | extern int REP_PORT = 5555; 21 | extern int PUSH_PORT = 5556; 22 | extern int MILLISECOND_TIMER = 1; // 1 millisecond 23 | 24 | extern string t0 = "--- Trading Parameters ---"; 25 | extern int MagicNumber = 123456; 26 | extern int MaximumOrders = 1; 27 | extern double MaximumLotSize = 0.01; 28 | 29 | // CREATE ZeroMQ Context 30 | Context context(PROJECT_NAME); 31 | 32 | // CREATE ZMQ_REP SOCKET 33 | Socket repSocket(context,ZMQ_REP); 34 | 35 | // CREATE ZMQ_PUSH SOCKET 36 | Socket pushSocket(context,ZMQ_PUSH); 37 | 38 | // VARIABLES FOR LATER 39 | uchar data[]; 40 | ZmqMsg request; 41 | 42 | //+------------------------------------------------------------------+ 43 | //| Expert initialization function | 44 | //+------------------------------------------------------------------+ 45 | int OnInit() 46 | { 47 | //--- 48 | 49 | EventSetMillisecondTimer(MILLISECOND_TIMER); // Set Millisecond Timer to get client socket input 50 | 51 | Print("[REP] Binding MT4 Server to Socket on Port " + REP_PORT + ".."); 52 | Print("[PUSH] Binding MT4 Server to Socket on Port " + PUSH_PORT + ".."); 53 | 54 | repSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, REP_PORT)); 55 | pushSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUSH_PORT)); 56 | 57 | /* 58 | Maximum amount of time in milliseconds that the thread will try to send messages 59 | after its socket has been closed (the default value of -1 means to linger forever): 60 | */ 61 | 62 | repSocket.setLinger(1000); // 1000 milliseconds 63 | 64 | /* 65 | If we initiate socket.send() without having a corresponding socket draining the queue, 66 | we'll eat up memory as the socket just keeps enqueueing messages. 67 | 68 | So how many messages do we want ZeroMQ to buffer in RAM before blocking the socket? 69 | */ 70 | 71 | repSocket.setSendHighWaterMark(5); // 5 messages only. 72 | 73 | //--- 74 | return(INIT_SUCCEEDED); 75 | } 76 | //+------------------------------------------------------------------+ 77 | //| Expert deinitialization function | 78 | //+------------------------------------------------------------------+ 79 | void OnDeinit(const int reason) 80 | { 81 | //--- 82 | Print("[REP] Unbinding MT4 Server from Socket on Port " + REP_PORT + ".."); 83 | repSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, REP_PORT)); 84 | 85 | Print("[PUSH] Unbinding MT4 Server from Socket on Port " + PUSH_PORT + ".."); 86 | pushSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUSH_PORT)); 87 | 88 | } 89 | //+------------------------------------------------------------------+ 90 | //| Expert timer function | 91 | //+------------------------------------------------------------------+ 92 | void OnTimer() 93 | { 94 | //--- 95 | 96 | /* 97 | For this example, we need: 98 | 1) socket.recv(request,true) 99 | 2) MessageHandler() to process the request 100 | 3) socket.send(reply) 101 | */ 102 | 103 | // Get client's response, but don't wait. 104 | repSocket.recv(request,true); 105 | 106 | // MessageHandler() should go here. 107 | ZmqMsg reply = MessageHandler(request); 108 | 109 | // socket.send(reply) should go here. 110 | repSocket.send(reply); 111 | } 112 | //+------------------------------------------------------------------+ 113 | 114 | ZmqMsg MessageHandler(ZmqMsg &request) { 115 | 116 | // Output object 117 | ZmqMsg reply; 118 | 119 | // Message components for later. 120 | string components[]; 121 | 122 | if(request.size() > 0) { 123 | 124 | // Get data from request 125 | ArrayResize(data, request.size()); 126 | request.getData(data); 127 | string dataStr = CharArrayToString(data); 128 | 129 | // Process data 130 | ParseZmqMessage(dataStr, components); 131 | 132 | // Interpret data 133 | InterpretZmqMessage(&pushSocket, components); 134 | 135 | // Construct response 136 | ZmqMsg ret(StringFormat("[SERVER] Processing: %s", dataStr)); 137 | reply = ret; 138 | 139 | } 140 | else { 141 | // NO DATA RECEIVED 142 | } 143 | 144 | return(reply); 145 | } 146 | 147 | // Interpret Zmq Message and perform actions 148 | void InterpretZmqMessage(Socket &pSocket, string& compArray[]) { 149 | 150 | Print("ZMQ: Interpreting Message.."); 151 | 152 | // Message Structures: 153 | 154 | // 1) Trading 155 | // TRADE|ACTION|TYPE|SYMBOL|PRICE|SL|TP|COMMENT|TICKET 156 | // e.g. TRADE|OPEN|1|EURUSD|0|50|50|R-to-MetaTrader4|12345678 157 | 158 | // The 12345678 at the end is the ticket ID, for MODIFY and CLOSE. 159 | 160 | // 2) Data Requests 161 | 162 | // 2.1) RATES|SYMBOL -> Returns Current Bid/Ask 163 | 164 | // 2.2) DATA|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME 165 | 166 | // NOTE: datetime has format: D'2015.01.01 00:00' 167 | 168 | /* 169 | compArray[0] = TRADE or RATES 170 | If RATES -> compArray[1] = Symbol 171 | 172 | If TRADE -> 173 | compArray[0] = TRADE 174 | compArray[1] = ACTION (e.g. OPEN, MODIFY, CLOSE) 175 | compArray[2] = TYPE (e.g. OP_BUY, OP_SELL, etc - only used when ACTION=OPEN) 176 | 177 | // ORDER TYPES: 178 | // https://docs.mql4.com/constants/tradingconstants/orderproperties 179 | 180 | // OP_BUY = 0 181 | // OP_SELL = 1 182 | // OP_BUYLIMIT = 2 183 | // OP_SELLLIMIT = 3 184 | // OP_BUYSTOP = 4 185 | // OP_SELLSTOP = 5 186 | 187 | compArray[3] = Symbol (e.g. EURUSD, etc.) 188 | compArray[4] = Open/Close Price (ignored if ACTION = MODIFY) 189 | compArray[5] = SL 190 | compArray[6] = TP 191 | compArray[7] = Trade Comment 192 | */ 193 | 194 | int switch_action = 0; 195 | 196 | if(compArray[0] == "TRADE" && compArray[1] == "OPEN") 197 | switch_action = 1; 198 | if(compArray[0] == "RATES") 199 | switch_action = 2; 200 | if(compArray[0] == "TRADE" && compArray[1] == "CLOSE") 201 | switch_action = 3; 202 | if(compArray[0] == "DATA") 203 | switch_action = 4; 204 | 205 | string ret = ""; 206 | int ticket = -1; 207 | bool ans = FALSE; 208 | double price_array[]; 209 | ArraySetAsSeries(price_array, true); 210 | 211 | int price_count = 0; 212 | 213 | switch(switch_action) 214 | { 215 | case 1: 216 | InformPullClient(pSocket, "OPEN TRADE Instruction Received"); 217 | // IMPLEMENT OPEN TRADE LOGIC HERE 218 | break; 219 | case 2: 220 | ret = "N/A"; 221 | if(ArraySize(compArray) > 1) 222 | ret = GetBidAsk(compArray[1]); 223 | 224 | InformPullClient(pSocket, ret); 225 | break; 226 | case 3: 227 | InformPullClient(pSocket, "CLOSE TRADE Instruction Received"); 228 | 229 | // IMPLEMENT CLOSE TRADE LOGIC HERE 230 | 231 | ret = StringFormat("Trade Closed (Ticket: %d)", ticket); 232 | InformPullClient(pSocket, ret); 233 | 234 | break; 235 | 236 | case 4: 237 | InformPullClient(pSocket, "HISTORICAL DATA Instruction Received"); 238 | 239 | // Format: DATA|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME 240 | price_count = CopyClose(compArray[1], StrToInteger(compArray[2]), 241 | StrToTime(compArray[3]), StrToTime(compArray[4]), 242 | price_array); 243 | 244 | if (price_count > 0) { 245 | 246 | ret = ""; 247 | 248 | // Construct string of price|price|price|.. etc and send to PULL client. 249 | for(int i = 0; i < price_count; i++ ) { 250 | 251 | if(i == 0) 252 | ret = compArray[1] + "|" + DoubleToStr(price_array[i], 5); 253 | else if(i > 0) { 254 | ret = ret + "|" + DoubleToStr(price_array[i], 5); 255 | } 256 | } 257 | 258 | Print("Sending: " + ret); 259 | 260 | // Send data to PULL client. 261 | InformPullClient(pSocket, StringFormat("%s", ret)); 262 | // ret = ""; 263 | } 264 | 265 | break; 266 | 267 | default: 268 | break; 269 | } 270 | } 271 | 272 | // Parse Zmq Message 273 | void ParseZmqMessage(string& message, string& retArray[]) { 274 | 275 | Print("Parsing: " + message); 276 | 277 | string sep = "|"; 278 | ushort u_sep = StringGetCharacter(sep,0); 279 | 280 | int splits = StringSplit(message, u_sep, retArray); 281 | 282 | for(int i = 0; i < splits; i++) { 283 | Print(i + ") " + retArray[i]); 284 | } 285 | } 286 | 287 | //+------------------------------------------------------------------+ 288 | // Generate string for Bid/Ask by symbol 289 | string GetBidAsk(string symbol) { 290 | 291 | double bid = MarketInfo(symbol, MODE_BID); 292 | double ask = MarketInfo(symbol, MODE_ASK); 293 | 294 | return(StringFormat("%f|%f", bid, ask)); 295 | } 296 | 297 | // Inform Client 298 | void InformPullClient(Socket& pushSocket, string message) { 299 | 300 | ZmqMsg pushReply(StringFormat("%s", message)); 301 | // pushSocket.send(pushReply,true,false); 302 | 303 | pushSocket.send(pushReply,true); // NON-BLOCKING 304 | // pushSocket.send(pushReply,false); // BLOCKING 305 | 306 | } 307 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/README.md: -------------------------------------------------------------------------------- 1 | ## Important Notice (2019-03-18 12:20 UTC): 2 | 3 | Future updates to the _**DWX_ZeroMQ_Connector**_ project will be served in its own dedicated repository here: https://github.com/darwinex/dwx-zeromq-connector 4 | 5 | # DWX ZeroMQ Connector { Python 3 to MetaTrader 4 } 6 | 7 | # Latest version: 2.0.1 [(here)](https://github.com/darwinex/DarwinexLabs/tree/master/tools/dwx_zeromq_connector/v2.0.1) 8 | 9 | ## Table of Contents 10 | * [Introduction](#introduction) 11 | * [Installation](#installation) 12 | * [Configuration](#configuration) 13 | * [Example Usage](#example-usage) 14 | * [Video Tutorials](#video-tutorials) 15 | * [Complete list of available functions](#available-functions) 16 | * [License](#license) 17 | 18 | ## Introduction 19 | In this project, we present a technique employing ZeroMQ (an Open Source, Asynchronous Messaging Library and Concurrency Framework) for building a basic – but easily extensible – high performance bridge between external (non-MQL) programming languages and MetaTrader 4. 20 | 21 | ### IMPORTANT NOTE: Any code provided and/or referenced in this repository is NOT meant to be used "as-is". Users must treat all code as educational content that requires modification / incorporation into existing works, as per their individual requirements. 22 | 23 | **Reasons for writing this post:** 24 | 25 | * Lack of comprehensive, publicly available literature about this topic on the web. 26 | * Traders have traditionally relied on Winsock/WinAPI based solutions that often require revision with both Microsoft™ and MetaQuotes™ updates. 27 | * Alternatives to ZeroMQ include named pipes, and approaches where filesystem-dependent functionality forms the bridge between MetaTrader and external languages. 28 | 29 | **We lay the foundation for a distributed trading system that will:** 30 | 31 | * Consist of one or more trading strategies developed outside MetaTrader 4 (non-MQL), 32 | * Use MetaTrader 4 for acquiring market data, trade execution and management, 33 | * Support multiple non-MQL strategies interfacing with MetaTrader 4 simultaneously, 34 | * Consider each trading strategy as an independent “Client”, 35 | * Consider MetaTrader 4 as the “Server”, and medium to market, 36 | * Permit both Server and Clients to communicate with each other on-demand. 37 | 38 | **Infographic: ZeroMQ-Enabled Distributed Trading Infrastructure (with MetaTrader 4)** 39 | ![DWX ZMQ Infographic 1](v2.0.1/resources/images/dwx-zeromq-infographic-1.png) 40 | 41 | **Why ZeroMQ?** 42 | 43 | * Enables programmers to connect any code to any other code, in a number of ways. 44 | * Eliminates a MetaTrader user’s dependency on just MetaTrader-supported technology (features, indicators, language constructs, libraries, etc.) 45 | * Traders can develop indicators and strategies in C/C#/C++, Python, R and Java (to name a few), and deploy to market via MetaTrader 4. 46 | * Leverage machine learning toolkits in Python and R for complex data analysis and strategy development, while interfacing with MetaTrader 4 for trade execution and management. 47 | * ZeroMQ can be used as a high-performance transport layer in sophisticated, distributed trading systems otherwise difficult to implement in MQL. 48 | * Different strategy components can be built in different languages if required, and seamlessly talk to each other over TCP, in-process, inter-process or multicast protocols. 49 | * Multiple communication patterns and disconnected operation. 50 | 51 | ## Installation 52 | 53 | This project requires the following: 54 | 55 | * **Python**: (minimum v3.6) 56 | * **libzmq**: (minimum v4.2.5) 57 | * **pyzmq**: (minimum v17.1.2) 58 | * **libsodium** (https://github.com/jedisct1/libsodium) 59 | * **mql4-lib** (https://github.com/dingmaotu/mql4-lib) 60 | * **mql-zmq** (https://github.com/dingmaotu/mql-zmq) 61 | 62 | For your convenience, files from the last three items above have been included in this repository with appropriate copyrights referenced within. 63 | 64 | ### Steps: 65 | 66 | 1. Download and unzip **mql-zmq-master.zip** (by GitHub author @dingmaotu) 67 | 1. Copy the contents of **mql-zmq-master/Include/Mql** and **mql-zmq-master/Include/Zmq** into your MetaTrader installation's **MQL4/Include** directory as-is. Your **MQL4/Include** directory should now have two additional folders "Mql" and "Zmq". 68 | 1. Copy **libsodium.dll** and **libzmq.dll** from **mql-zmq-master/Library/MT4** to your MetaTrader installation's **MQL4/Libraries** directory. 69 | 1. Download **DWX_ZeroMQ_Server_vX.Y.Z_RCx.mq4** and place it inside your MetaTrader installation's **MQL4/Experts** directory. 70 | 1. Finally, download **DWX_ZeroMQ_Connector_vX_Y_Z_RCx.py**. 71 | 72 | **Note:** vX_Y_Z_RCx refers to version and release candidate, e.g. v2.0.1_RC8. 73 | 74 | ## Configuration 75 | 76 | 1. After completing the steps above, terminate and restart MetaTrader 4. 77 | 1. Open any new chart, e.g. EUR/USD M, then drag and drop **DWX_ZeroMQ_Server_vX.Y.Z_RCx**. 78 | 1. Switch to the EA's Inputs tab and customize values as necessary: 79 | 80 | ![EA Inputs](v2.0.1/resources/images/expert-inputs.png) 81 | 1. Note: Setting **Publish_MarketData** to **True** will cause MetaTrader 4 to begin publishing BID/ASK tick data in real-time for all symbols specified in the array **Publish_Symbols** contained in the .mq4 script. 82 | 1. Simply modify the Publish_Symbols[] aarray's contents to add/remove required symbols as necessary and re-compile. 83 | 1. The default list of symbols is: 84 | 85 | ``` 86 | string Publish_Symbols[7] = { 87 | "EURUSD","GBPUSD","USDJPY","USDCAD","AUDUSD","NZDUSD","USDCHF" 88 | }; 89 | ``` 90 | ![MetaTrader Publishing Tick Data 1](v2.0.1/resources/images/ZeroMQ_Server_Publishing_Symbol_Data.gif) 91 | 92 | ![MetaTrader Publishing Tick Data 2](v2.0.1/resources/images/InAction_ZeroMQ_Server_Publishing_Symbol_Data.gif) 93 | 94 | ## Example Usage 95 | 96 | ### Initialize Connector: 97 | ``` 98 | _zmq = DWX_ZeroMQ_Connector() 99 | ``` 100 | 101 | ### Construct Trade to send via ZeroMQ to MetaTrader: 102 | ``` 103 | _my_trade = _zmq._generate_default_order_dict() 104 | 105 | Output: 106 | {'_action': 'OPEN', 107 | '_type': 0, 108 | '_symbol': 'EURUSD', 109 | '_price': 0.0, 110 | '_SL': 500, 111 | '_TP': 500, 112 | '_comment': 'DWX_Python_to_MT', 113 | '_lots': 0.01, 114 | '_magic': 123456, 115 | '_ticket': 0} 116 | 117 | _my_trade['_lots'] = 0.05 118 | 119 | _my_trade['_SL'] = 250 120 | 121 | _my_trade['_TP'] = 750 122 | 123 | _my_trade['_comment'] = 'nerds_rox0r' 124 | ``` 125 | 126 | ### Send trade to MetaTrader: 127 | ``` 128 | _zmq._DWX_MTX_NEW_TRADE_(_order=_my_trade) 129 | 130 | # MetaTrader response (JSON): 131 | {'_action': 'EXECUTION', 132 | '_magic': 123456, 133 | '_ticket': 85051741, 134 | '_open_price': 1.14414, 135 | '_sl': 250, 136 | '_tp': 750} 137 | ``` 138 | 139 | ### Get all open trades from MetaTrader: 140 | ``` 141 | _zmq._DWX_MTX_GET_ALL_OPEN_TRADES_() 142 | 143 | # MetaTrader response (JSON): 144 | 145 | {'_action': 'OPEN_TRADES', 146 | '_trades': { 147 | 85051741: {'_magic': 123456, 148 | '_symbol': 'EURUSD', 149 | '_lots': 0.05, 150 | '_type': 0, 151 | '_open_price': 1.14414, 152 | '_pnl': -0.45} 153 | } 154 | } 155 | ``` 156 | 157 | ### Partially close 0.01 lots: 158 | ``` 159 | _zmq._DWX_MTX_CLOSE_PARTIAL_BY_TICKET_(85051741, 0.01) 160 | 161 | # MetaTrader response (JSON): 162 | {'_action': 'CLOSE', 163 | '_ticket': 85051741, 164 | '_response': 'CLOSE_PARTIAL', 165 | '_close_price': 1.14401, 166 | '_close_lots': 0.01} 167 | 168 | # Partially closing a trade renews the ticket ID, retrieve it again. 169 | 170 | _zmq._DWX_MTX_GET_ALL_OPEN_TRADES_() 171 | 172 | # MetaTrader response (JSON): 173 | {'_action': 'OPEN_TRADES', 174 | '_trades': { 175 | 85051856: {'_magic': 123456, 176 | '_symbol': 'EURUSD', 177 | '_lots': 0.04, 178 | '_type': 0, 179 | '_open_price': 1.14414, 180 | '_pnl': -0.36} 181 | } 182 | } 183 | ``` 184 | 185 | ### Close a trade by ticket: 186 | ``` 187 | _zmq._DWX_MTX_CLOSE_TRADE_BY_TICKET_(85051856) 188 | 189 | # MetaTrader response (JSON): 190 | {'_action': 'CLOSE', 191 | '_ticket': 85051856, 192 | '_close_price': 1.14378, 193 | '_close_lots': 0.04, 194 | '_response': 'CLOSE_MARKET', 195 | '_response_value': 'SUCCESS'} 196 | ``` 197 | 198 | ### Close all trades by Magic Number: 199 | ``` 200 | # Before running the following example, 5 trades were executed using the same values as in "_my_trade" above, the magic number being 123456. 201 | 202 | # Check currently open trades. 203 | 204 | _zmq._DWX_MTX_GET_ALL_OPEN_TRADES_() 205 | 206 | # MetaTrader response (JSON): 207 | {'_action': 'OPEN_TRADES', 208 | '_trades': { 209 | 85052022: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14353, '_pnl': 1.15}, 210 | 85052026: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14354, '_pnl': 1.1}, 211 | 85052025: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14354, '_pnl': 1.1}, 212 | 85052024: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14354, '_pnl': 1.1}, 213 | 85052023: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14356, '_pnl': 1} 214 | } 215 | } 216 | 217 | # Close all trades with magic number 123456 218 | _zmq._DWX_MTX_CLOSE_TRADES_BY_MAGIC_(123456) 219 | 220 | # MetaTrader response (JSON): 221 | {'_action': 'CLOSE_ALL_MAGIC', '_magic': 123456, 222 | '_responses': { 223 | 85052026: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 224 | 85052025: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 225 | 85052024: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 226 | 85052023: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 227 | 85052022: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}}, 228 | '_response_value': 'SUCCESS'} 229 | ``` 230 | 231 | ### Close ALL open trades: 232 | ``` 233 | 234 | # Open 5 trades 235 | 236 | _zmq._DWX_MTX_NEW_TRADE_() 237 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090920, '_open_price': 1.15379, '_sl': 500, '_tp': 500} 238 | 239 | _zmq._DWX_MTX_NEW_TRADE_() 240 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090921, '_open_price': 1.15379, '_sl': 500, '_tp': 500} 241 | 242 | _zmq._DWX_MTX_NEW_TRADE_() 243 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090922, '_open_price': 1.15375, '_sl': 500, '_tp': 500} 244 | 245 | _zmq._DWX_MTX_NEW_TRADE_() 246 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090926, '_open_price': 1.15378, '_sl': 500, '_tp': 500} 247 | 248 | _zmq._DWX_MTX_NEW_TRADE_() 249 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090927, '_open_price': 1.15378, '_sl': 500, '_tp': 500} 250 | 251 | # Close all open trades 252 | _zmq._DWX_MTX_CLOSE_ALL_TRADES_() 253 | 254 | # MetaTrader response (JSON): 255 | {'_action': 'CLOSE_ALL', 256 | '_responses': {85090927: {'_symbol': 'EURUSD', 257 | '_magic': 123456, 258 | '_close_price': 1.1537, 259 | '_close_lots': 0.01, 260 | '_response': 'CLOSE_MARKET'}, 261 | 85090926: {'_symbol': 'EURUSD', 262 | '_magic': 123456, 263 | '_close_price': 1.1537, 264 | '_close_lots': 0.01, 265 | '_response': 'CLOSE_MARKET'}, 266 | 85090922: {'_symbol': 'EURUSD', 267 | '_magic': 123456, 268 | '_close_price': 1.1537, 269 | '_close_lots': 0.01, 270 | '_response': 'CLOSE_MARKET'}, 271 | 85090921: {'_symbol': 'EURUSD', 272 | '_magic': 123456, 273 | '_close_price': 1.15369, 274 | '_close_lots': 0.01, 275 | '_response': 'CLOSE_MARKET'}, 276 | 85090920: {'_symbol': 'EURUSD', 277 | '_magic': 123456, 278 | '_close_price': 1.15369, 279 | '_close_lots': 0.01, 280 | '_response': 'CLOSE_MARKET'}}, 281 | '_response_value': 'SUCCESS'} 282 | ``` 283 | 284 | ### Subscribe/Unsubscribe to/from EUR/USD bid/ask prices in real-time: 285 | ``` 286 | _zmq._DWX_MTX_SUBSCRIBE_MARKETDATA_('EURUSD') 287 | 288 | Output: 289 | [KERNEL] Subscribed to EURUSD BID/ASK updates. See self._Market_Data_DB. 290 | 291 | # BID/ASK prices are now being streamed into _zmq._Market_Data_DB. 292 | _zmq._Market_Data_DB 293 | 294 | Output: 295 | {'EURUSD': { 296 | '2019-01-08 13:46:49.157431': (1.14389, 1.14392), 297 | '2019-01-08 13:46:50.673151': (1.14389, 1.14393), 298 | '2019-01-08 13:46:51.010993': (1.14392, 1.14395), 299 | '2019-01-08 13:46:51.100941': (1.14394, 1.14398), 300 | '2019-01-08 13:46:51.205881': (1.14395, 1.14398), 301 | '2019-01-08 13:46:52.283107': (1.14394, 1.14397), 302 | '2019-01-08 13:46:52.377055': (1.14395, 1.14398), 303 | '2019-01-08 13:46:52.777823': (1.14394, 1.14398), 304 | '2019-01-08 13:46:52.870773': (1.14395, 1.14398), 305 | '2019-01-08 13:46:52.985708': (1.14395, 1.14397), 306 | '2019-01-08 13:46:53.080652': (1.14393, 1.14397), 307 | '2019-01-08 13:46:53.196584': (1.14394, 1.14398), 308 | '2019-01-08 13:46:53.294541': (1.14393, 1.14397)}} 309 | 310 | _zmq._DWX_MTX_UNSUBSCRIBE_MARKETDATA('EURUSD') 311 | 312 | Output: 313 | ** 314 | [KERNEL] Unsubscribing from EURUSD 315 | ** 316 | ``` 317 | 318 | ## Video Tutorials 319 | 320 | 1. [How to Interface Python/R Trading Strategies with MetaTrader 4](https://www.youtube.com/watch?v=GGOajzvl860) 321 | 322 | 1. [Algorithmic Trading via ZeroMQ: Trade Execution, Reporting & Management (Python to MetaTrader)](https://youtu.be/3nM0c2kG_Sw) 323 | 324 | 1. [Algorithmic Trading via ZeroMQ: Subscribing to Market Data (Python to MetaTrader)](https://youtu.be/9EaP_7sSzEI) 325 | 326 | 1. [Build Algorithmic Trading Strategies with Python & ZeroMQ: Part 1](https://www.youtube.com/watch?v=4MxjFTQHTfw) 327 | 328 | 1. [Build Algorithmic Trading Strategies with Python & ZeroMQ: Part 2](https://www.youtube.com/watch?v=VtOfF-nhhj8) 329 | 330 | ## Available functions: 331 | 332 | 1. DWX_MTX_NEW_TRADE_(self, _order=None) 333 | 1. DWX_MTX_MODIFY_TRADE_BY_TICKET_(self, _ticket, _SL, _TP) 334 | 1. DWX_MTX_CLOSE_TRADE_BY_TICKET_(self, _ticket) 335 | 1. DWX_MTX_CLOSE_PARTIAL_BY_TICKET_(self, _ticket, _lots) 336 | 1. DWX_MTX_CLOSE_TRADES_BY_MAGIC_(self, _magic) 337 | 1. DWX_MTX_CLOSE_ALL_TRADES_(self) 338 | 1. DWX_MTX_GET_ALL_OPEN_TRADES_(self) 339 | 1. generate_default_order_dict(self) 340 | 1. generate_default_data_dict(self) 341 | 1. DWX_MTX_SEND_MARKETDATA_REQUEST_(self, _symbol, _timeframe, _start, _end) 342 | 1. DWX_MTX_SEND_COMMAND_(self, _action, _type, _symbol, _price, _SL, _TP, _comment, _lots, _magic, _ticket) 343 | 1. DWX_MTX_SUBSCRIBE_MARKETDATA_(self, _symbol, _string_delimiter=';') 344 | 1. DWX_MTX_UNSUBSCRIBE_MARKETDATA_(self, _symbol) 345 | 1. DWX_MTX_UNSUBSCRIBE_ALL_MARKETDATA_REQUESTS_(self) 346 | 347 | ## License 348 | 349 | BSD 3-Clause License 350 | 351 | Copyright (c) 2019, Darwinex. 352 | All rights reserved. 353 | 354 | Redistribution and use in source and binary forms, with or without 355 | modification, are permitted provided that the following conditions are met: 356 | 357 | * Redistributions of source code must retain the above copyright notice, this 358 | list of conditions and the following disclaimer. 359 | 360 | * Redistributions in binary form must reproduce the above copyright notice, 361 | this list of conditions and the following disclaimer in the documentation 362 | and/or other materials provided with the distribution. 363 | 364 | * Neither the name of the copyright holder nor the names of its 365 | contributors may be used to endorse or promote products derived from 366 | this software without specific prior written permission. 367 | 368 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 369 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 370 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 371 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 372 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 373 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 374 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 375 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 376 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 377 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 378 | 379 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/README.md: -------------------------------------------------------------------------------- 1 | ## Important Notice (2019-03-18 12:20 UTC): 2 | 3 | Future updates to the _**DWX_ZeroMQ_Connector**_ project will be served in its own dedicated repository here: https://github.com/darwinex/dwx-zeromq-connector 4 | 5 | # DWX ZeroMQ Connector { Python 3 to MetaTrader 4 } 6 | 7 | # Version: 2.0.1 (RC8) 8 | 9 | ## Table of Contents 10 | * [Introduction](#introduction) 11 | * [Installation](#installation) 12 | * [Configuration](#configuration) 13 | * [Example Usage](#example-usage) 14 | * [Video Tutorials](#video-tutorials) 15 | * [Complete list of available functions](#available-functions) 16 | * [Frequently Asked Questions](#faq) 17 | * [License](#license) 18 | 19 | ## Introduction 20 | In this project, we present a technique employing ZeroMQ (an Open Source, Asynchronous Messaging Library and Concurrency Framework) for building a basic – but easily extensible – high performance bridge between external (non-MQL) programming languages and MetaTrader 4. 21 | 22 | ### IMPORTANT NOTE: Any code provided and/or referenced in this repository is NOT meant to be used "as-is". Users must treat all code as educational content that requires modification / incorporation into existing works, as per their individual requirements. 23 | 24 | ### This release has been tested on: Windows 10 Professional, MetaTrader 4 Build 1170 (20 Dec 2018). 25 | 26 | **Reasons for writing this post:** 27 | 28 | * Lack of comprehensive, publicly available literature about this topic on the web. 29 | * Traders have traditionally relied on Winsock/WinAPI based solutions that often require revision with both Microsoft™ and MetaQuotes™ updates. 30 | * Alternatives to ZeroMQ include named pipes, and approaches where filesystem-dependent functionality forms the bridge between MetaTrader and external languages. 31 | 32 | **We lay the foundation for a distributed trading system that will:** 33 | 34 | * Consist of one or more trading strategies developed outside MetaTrader 4 (non-MQL), 35 | * Use MetaTrader 4 for acquiring market data, trade execution and management, 36 | * Support multiple non-MQL strategies interfacing with MetaTrader 4 simultaneously, 37 | * Consider each trading strategy as an independent “Client”, 38 | * Consider MetaTrader 4 as the “Server”, and medium to market, 39 | * Permit both Server and Clients to communicate with each other on-demand. 40 | 41 | **Infographic: ZeroMQ-Enabled Distributed Trading Infrastructure (with MetaTrader 4)** 42 | ![DWX ZMQ Infographic 1](resources/images/dwx-zeromq-infographic-1.png) 43 | 44 | **Why ZeroMQ?** 45 | 46 | * Enables programmers to connect any code to any other code, in a number of ways. 47 | * Eliminates a MetaTrader user’s dependency on just MetaTrader-supported technology (features, indicators, language constructs, libraries, etc.) 48 | * Traders can develop indicators and strategies in C/C#/C++, Python, R and Java (to name a few), and deploy to market via MetaTrader 4. 49 | * Leverage machine learning toolkits in Python and R for complex data analysis and strategy development, while interfacing with MetaTrader 4 for trade execution and management. 50 | * ZeroMQ can be used as a high-performance transport layer in sophisticated, distributed trading systems otherwise difficult to implement in MQL. 51 | * Different strategy components can be built in different languages if required, and seamlessly talk to each other over TCP, in-process, inter-process or multicast protocols. 52 | * Multiple communication patterns and disconnected operation. 53 | 54 | ## Installation 55 | 56 | This project requires the following: 57 | 58 | * **Python**: (minimum v3.6) 59 | * **libzmq**: (minimum v4.2.5) 60 | * **pyzmq**: (minimum v17.1.2) 61 | * **libsodium** (https://github.com/jedisct1/libsodium) 62 | * **mql4-lib** (https://github.com/dingmaotu/mql4-lib) 63 | * **mql-zmq** (https://github.com/dingmaotu/mql-zmq) 64 | 65 | For your convenience, files from the last three items above have been included in this repository with appropriate copyrights referenced within. 66 | 67 | ### Steps: 68 | 69 | 1. Download and unzip **mql-zmq-master.zip** (by GitHub author @dingmaotu) 70 | 1. Copy the contents of **mql-zmq-master/Include/Mql** and **mql-zmq-master/Include/Zmq** into your MetaTrader installation's **MQL4/Include** directory as-is. Your **MQL4/Include** directory should now have two additional folders "Mql" and "Zmq". 71 | 1. Copy **libsodium.dll** and **libzmq.dll** from **mql-zmq-master/Library/MT4** to your MetaTrader installation's **MQL4/Libraries** directory. 72 | 1. Download **DWX_ZeroMQ_Server_v2.0.1_RCx.mq4** and place it inside your MetaTrader installation's **MQL4/Experts** directory. 73 | 1. Finally, download **DWX_ZeroMQ_Connector_v2_0_1_RCx.py**. 74 | 75 | In the filenames above and below, the "x" in **_RCx** refers to the current release candidate for this version, mentioned at the beginning of this README. 76 | 77 | ## Configuration 78 | 79 | 1. After completing the steps above, terminate and restart MetaTrader 4. 80 | 1. Open any new chart, e.g. EUR/USD M, then drag and drop **DWX_ZeroMQ_Server_v2.0.1_RCx**. 81 | 1. Switch to the EA's Inputs tab and customize values as necessary: 82 | 83 | ![EA Inputs](resources/images/expert-inputs.png) 84 | 1. Note: Setting **Publish_MarketData** to **True** will cause MetaTrader 4 to begin publishing BID/ASK tick data in real-time for all symbols specified in the array **Publish_Symbols** contained in the .mq4 script. 85 | 1. Simply modify the Publish_Symbols[] aarray's contents to add/remove required symbols as necessary and re-compile. 86 | 1. The default list of symbols is: 87 | 88 | ``` 89 | string Publish_Symbols[7] = { 90 | "EURUSD","GBPUSD","USDJPY","USDCAD","AUDUSD","NZDUSD","USDCHF" 91 | }; 92 | ``` 93 | ![MetaTrader Publishing Tick Data 1](resources/images/ZeroMQ_Server_Publishing_Symbol_Data.gif) 94 | 95 | ![MetaTrader Publishing Tick Data 2](resources/images/InAction_ZeroMQ_Server_Publishing_Symbol_Data.gif) 96 | 97 | ## Example Usage 98 | 99 | ### Initialize Connector: 100 | ``` 101 | _zmq = DWX_ZeroMQ_Connector() 102 | ``` 103 | 104 | ### Construct Trade to send via ZeroMQ to MetaTrader: 105 | ``` 106 | _my_trade = _zmq._generate_default_order_dict() 107 | 108 | Output: 109 | {'_action': 'OPEN', 110 | '_type': 0, 111 | '_symbol': 'EURUSD', 112 | '_price': 0.0, 113 | '_SL': 500, 114 | '_TP': 500, 115 | '_comment': 'DWX_Python_to_MT', 116 | '_lots': 0.01, 117 | '_magic': 123456, 118 | '_ticket': 0} 119 | 120 | _my_trade['_lots'] = 0.05 121 | 122 | _my_trade['_SL'] = 250 123 | 124 | _my_trade['_TP'] = 750 125 | 126 | _my_trade['_comment'] = 'nerds_rox0r' 127 | ``` 128 | 129 | ### Send trade to MetaTrader: 130 | ``` 131 | _zmq._DWX_MTX_NEW_TRADE_(_order=_my_trade) 132 | 133 | # MetaTrader response (JSON): 134 | {'_action': 'EXECUTION', 135 | '_magic': 123456, 136 | '_ticket': 85051741, 137 | '_open_price': 1.14414, 138 | '_sl': 250, 139 | '_tp': 750} 140 | ``` 141 | 142 | ### Get all open trades from MetaTrader: 143 | ``` 144 | _zmq._DWX_MTX_GET_ALL_OPEN_TRADES_() 145 | 146 | # MetaTrader response (JSON): 147 | 148 | {'_action': 'OPEN_TRADES', 149 | '_trades': { 150 | 85051741: {'_magic': 123456, 151 | '_symbol': 'EURUSD', 152 | '_lots': 0.05, 153 | '_type': 0, 154 | '_open_price': 1.14414, 155 | '_pnl': -0.45} 156 | } 157 | } 158 | ``` 159 | 160 | ### Partially close 0.01 lots: 161 | ``` 162 | _zmq._DWX_MTX_CLOSE_PARTIAL_BY_TICKET_(85051741, 0.01) 163 | 164 | # MetaTrader response (JSON): 165 | {'_action': 'CLOSE', 166 | '_ticket': 85051741, 167 | '_response': 'CLOSE_PARTIAL', 168 | '_close_price': 1.14401, 169 | '_close_lots': 0.01} 170 | 171 | # Partially closing a trade renews the ticket ID, retrieve it again. 172 | 173 | _zmq._DWX_MTX_GET_ALL_OPEN_TRADES_() 174 | 175 | # MetaTrader response (JSON): 176 | {'_action': 'OPEN_TRADES', 177 | '_trades': { 178 | 85051856: {'_magic': 123456, 179 | '_symbol': 'EURUSD', 180 | '_lots': 0.04, 181 | '_type': 0, 182 | '_open_price': 1.14414, 183 | '_pnl': -0.36} 184 | } 185 | } 186 | ``` 187 | 188 | ### Close a trade by ticket: 189 | ``` 190 | _zmq._DWX_MTX_CLOSE_TRADE_BY_TICKET_(85051856) 191 | 192 | # MetaTrader response (JSON): 193 | {'_action': 'CLOSE', 194 | '_ticket': 85051856, 195 | '_close_price': 1.14378, 196 | '_close_lots': 0.04, 197 | '_response': 'CLOSE_MARKET', 198 | '_response_value': 'SUCCESS'} 199 | ``` 200 | 201 | ### Close all trades by Magic Number: 202 | ``` 203 | # Before running the following example, 5 trades were executed using the same values as in "_my_trade" above, the magic number being 123456. 204 | 205 | # Check currently open trades. 206 | 207 | _zmq._DWX_MTX_GET_ALL_OPEN_TRADES_() 208 | 209 | # MetaTrader response (JSON): 210 | {'_action': 'OPEN_TRADES', 211 | '_trades': { 212 | 85052022: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14353, '_pnl': 1.15}, 213 | 85052026: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14354, '_pnl': 1.1}, 214 | 85052025: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14354, '_pnl': 1.1}, 215 | 85052024: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14354, '_pnl': 1.1}, 216 | 85052023: {'_magic': 123456, '_symbol': 'EURUSD', '_lots': 0.05, '_type': 0, '_open_price': 1.14356, '_pnl': 1} 217 | } 218 | } 219 | 220 | # Close all trades with magic number 123456 221 | _zmq._DWX_MTX_CLOSE_TRADES_BY_MAGIC_(123456) 222 | 223 | # MetaTrader response (JSON): 224 | {'_action': 'CLOSE_ALL_MAGIC', '_magic': 123456, 225 | '_responses': { 226 | 85052026: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 227 | 85052025: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 228 | 85052024: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 229 | 85052023: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}, 230 | 85052022: {'_symbol': 'EURUSD', '_close_price': 1.14375, '_close_lots': 0.05, '_response': 'CLOSE_MARKET'}}, 231 | '_response_value': 'SUCCESS'} 232 | ``` 233 | 234 | ### Close ALL open trades: 235 | ``` 236 | 237 | # Open 5 trades 238 | 239 | _zmq._DWX_MTX_NEW_TRADE_() 240 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090920, '_open_price': 1.15379, '_sl': 500, '_tp': 500} 241 | 242 | _zmq._DWX_MTX_NEW_TRADE_() 243 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090921, '_open_price': 1.15379, '_sl': 500, '_tp': 500} 244 | 245 | _zmq._DWX_MTX_NEW_TRADE_() 246 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090922, '_open_price': 1.15375, '_sl': 500, '_tp': 500} 247 | 248 | _zmq._DWX_MTX_NEW_TRADE_() 249 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090926, '_open_price': 1.15378, '_sl': 500, '_tp': 500} 250 | 251 | _zmq._DWX_MTX_NEW_TRADE_() 252 | {'_action': 'EXECUTION', '_magic': 123456, '_ticket': 85090927, '_open_price': 1.15378, '_sl': 500, '_tp': 500} 253 | 254 | # Close all open trades 255 | _zmq._DWX_MTX_CLOSE_ALL_TRADES_() 256 | 257 | # MetaTrader response (JSON): 258 | {'_action': 'CLOSE_ALL', 259 | '_responses': {85090927: {'_symbol': 'EURUSD', 260 | '_magic': 123456, 261 | '_close_price': 1.1537, 262 | '_close_lots': 0.01, 263 | '_response': 'CLOSE_MARKET'}, 264 | 85090926: {'_symbol': 'EURUSD', 265 | '_magic': 123456, 266 | '_close_price': 1.1537, 267 | '_close_lots': 0.01, 268 | '_response': 'CLOSE_MARKET'}, 269 | 85090922: {'_symbol': 'EURUSD', 270 | '_magic': 123456, 271 | '_close_price': 1.1537, 272 | '_close_lots': 0.01, 273 | '_response': 'CLOSE_MARKET'}, 274 | 85090921: {'_symbol': 'EURUSD', 275 | '_magic': 123456, 276 | '_close_price': 1.15369, 277 | '_close_lots': 0.01, 278 | '_response': 'CLOSE_MARKET'}, 279 | 85090920: {'_symbol': 'EURUSD', 280 | '_magic': 123456, 281 | '_close_price': 1.15369, 282 | '_close_lots': 0.01, 283 | '_response': 'CLOSE_MARKET'}}, 284 | '_response_value': 'SUCCESS'} 285 | ``` 286 | 287 | ### Subscribe/Unsubscribe to/from EUR/USD bid/ask prices in real-time: 288 | ``` 289 | _zmq._DWX_MTX_SUBSCRIBE_MARKETDATA_('EURUSD') 290 | 291 | Output: 292 | [KERNEL] Subscribed to EURUSD BID/ASK updates. See self._Market_Data_DB. 293 | 294 | # BID/ASK prices are now being streamed into _zmq._Market_Data_DB. 295 | _zmq._Market_Data_DB 296 | 297 | Output: 298 | {'EURUSD': { 299 | '2019-01-08 13:46:49.157431': (1.14389, 1.14392), 300 | '2019-01-08 13:46:50.673151': (1.14389, 1.14393), 301 | '2019-01-08 13:46:51.010993': (1.14392, 1.14395), 302 | '2019-01-08 13:46:51.100941': (1.14394, 1.14398), 303 | '2019-01-08 13:46:51.205881': (1.14395, 1.14398), 304 | '2019-01-08 13:46:52.283107': (1.14394, 1.14397), 305 | '2019-01-08 13:46:52.377055': (1.14395, 1.14398), 306 | '2019-01-08 13:46:52.777823': (1.14394, 1.14398), 307 | '2019-01-08 13:46:52.870773': (1.14395, 1.14398), 308 | '2019-01-08 13:46:52.985708': (1.14395, 1.14397), 309 | '2019-01-08 13:46:53.080652': (1.14393, 1.14397), 310 | '2019-01-08 13:46:53.196584': (1.14394, 1.14398), 311 | '2019-01-08 13:46:53.294541': (1.14393, 1.14397)}} 312 | 313 | _zmq._DWX_MTX_UNSUBSCRIBE_MARKETDATA('EURUSD') 314 | 315 | Output: 316 | ** 317 | [KERNEL] Unsubscribing from EURUSD 318 | ** 319 | ``` 320 | 321 | ## Video Tutorials 322 | 323 | 1. [How to Interface Python/R Trading Strategies with MetaTrader 4](https://www.youtube.com/watch?v=GGOajzvl860) 324 | 325 | 1. [Algorithmic Trading via ZeroMQ: Trade Execution, Reporting & Management (Python to MetaTrader)](https://youtu.be/3nM0c2kG_Sw) 326 | 327 | 1. [Algorithmic Trading via ZeroMQ: Subscribing to Market Data (Python to MetaTrader)](https://youtu.be/9EaP_7sSzEI) 328 | 329 | 1. [Build Algorithmic Trading Strategies with Python & ZeroMQ: Part 1](https://www.youtube.com/watch?v=4MxjFTQHTfw) 330 | 331 | 1. [Build Algorithmic Trading Strategies with Python & ZeroMQ: Part 2](https://www.youtube.com/watch?v=VtOfF-nhhj8) 332 | 333 | ## Available functions: 334 | 335 | 1. DWX_MTX_NEW_TRADE_(self, _order=None) 336 | 1. DWX_MTX_MODIFY_TRADE_BY_TICKET_(self, _ticket, _SL, _TP) 337 | 1. DWX_MTX_CLOSE_TRADE_BY_TICKET_(self, _ticket) 338 | 1. DWX_MTX_CLOSE_PARTIAL_BY_TICKET_(self, _ticket, _lots) 339 | 1. DWX_MTX_CLOSE_TRADES_BY_MAGIC_(self, _magic) 340 | 1. DWX_MTX_CLOSE_ALL_TRADES_(self) 341 | 1. DWX_MTX_GET_ALL_OPEN_TRADES_(self) 342 | 1. generate_default_order_dict(self) 343 | 1. generate_default_data_dict(self) 344 | 1. DWX_MTX_SEND_MARKETDATA_REQUEST_(self, _symbol, _timeframe, _start, _end) 345 | 1. DWX_MTX_SEND_COMMAND_(self, _action, _type, _symbol, _price, _SL, _TP, _comment, _lots, _magic, _ticket) 346 | 1. DWX_MTX_SUBSCRIBE_MARKETDATA_(self, _symbol, _string_delimiter=';') 347 | 1. DWX_MTX_UNSUBSCRIBE_MARKETDATA_(self, _symbol) 348 | 1. DWX_MTX_UNSUBSCRIBE_ALL_MARKETDATA_REQUESTS_(self) 349 | 350 | ## FAQ 351 | 352 | 1) **What does the MetaTrader 4 Server EA expect stop losses (SL) & take profits (TP) to be specified in, PIPS or POINTS?** 353 | * Points 354 | 355 | 2) **How do I access the JSON that MetaTrader sends back to Python?** 356 | * Each time you send a command from Python, output JSON from MetaTrader is stored in the DWX_ZeroMQ_Connector object's **_ thread_data_output** variable. You may modify this behaviour by editing the Python script to suit your requirements. 357 | 358 | 3) **Can I use this for HFT trading?** 359 | * No. Please ensure there is at least a 10 millisecond delay between commands sent to MetaTrader. 360 | 361 | 4) **I have a large number of trades open in MetaTrader and when I run ._DWX_GET_OPEN_TRADES_ I don't get any output sent back.** 362 | * Try implementing pagination in both the Python and MQL scripts. This is something we have planned for future releases to offset potential transmission issues when requesting large amounts of data from MetaTrader. 363 | 364 | 5) **I requested historical data for a long date-range from MetaTrader, but got nothing back.** 365 | * Try implementing pagination in both the Python and MQL scripts. This is something we have planned for future releases to offset potential transmission issues when requesting large amounts of data from MetaTrader. 366 | 367 | 6) **What is the latest version and release candidate?** 368 | * v2.0.1_RC8 369 | 370 | ## License 371 | 372 | BSD 3-Clause License 373 | 374 | Copyright (c) 2019, Darwinex. 375 | All rights reserved. 376 | 377 | Redistribution and use in source and binary forms, with or without 378 | modification, are permitted provided that the following conditions are met: 379 | 380 | * Redistributions of source code must retain the above copyright notice, this 381 | list of conditions and the following disclaimer. 382 | 383 | * Redistributions in binary form must reproduce the above copyright notice, 384 | this list of conditions and the following disclaimer in the documentation 385 | and/or other materials provided with the distribution. 386 | 387 | * Neither the name of the copyright holder nor the names of its 388 | contributors may be used to endorse or promote products derived from 389 | this software without specific prior written permission. 390 | 391 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 392 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 393 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 394 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 395 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 396 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 397 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 398 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 399 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 400 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 401 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/Python/DWX_ZeroMQ_Connector_v2_0_1_RC8.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | DWX_ZeroMQ_Connector_v2_0_1_RC8.py 4 | -- 5 | @author: Darwinex Labs (www.darwinex.com) 6 | 7 | Copyright (c) 2017-2019, Darwinex. All rights reserved. 8 | 9 | Licensed under the BSD 3-Clause License, you may not use this file except 10 | in compliance with the License. 11 | 12 | You may obtain a copy of the License at: 13 | https://opensource.org/licenses/BSD-3-Clause 14 | """ 15 | 16 | # IMPORT zmq library 17 | import zmq 18 | from time import sleep 19 | from pandas import DataFrame, Timestamp 20 | from threading import Thread 21 | 22 | class DWX_ZeroMQ_Connector(): 23 | 24 | """ 25 | Setup ZeroMQ -> MetaTrader Connector 26 | """ 27 | def __init__(self, 28 | _ClientID='DLabs_Python', # Unique ID for this client 29 | _host='localhost', # Host to connect to 30 | _protocol='tcp', # Connection protocol 31 | _PUSH_PORT=32768, # Port for Sending commands 32 | _PULL_PORT=32769, # Port for Receiving responses 33 | _SUB_PORT=32770, # Port for Subscribing for prices 34 | _delimiter=';', 35 | _verbose=False): # String delimiter 36 | 37 | # Strategy Status (if this is False, ZeroMQ will not listen for data) 38 | self._ACTIVE = True 39 | 40 | # Client ID 41 | self._ClientID = _ClientID 42 | 43 | # ZeroMQ Host 44 | self._host = _host 45 | 46 | # Connection Protocol 47 | self._protocol = _protocol 48 | 49 | # ZeroMQ Context 50 | self._ZMQ_CONTEXT = zmq.Context() 51 | 52 | # TCP Connection URL Template 53 | self._URL = self._protocol + "://" + self._host + ":" 54 | 55 | # Ports for PUSH, PULL and SUB sockets respectively 56 | self._PUSH_PORT = _PUSH_PORT 57 | self._PULL_PORT = _PULL_PORT 58 | self._SUB_PORT = _SUB_PORT 59 | 60 | # Create Sockets 61 | self._PUSH_SOCKET = self._ZMQ_CONTEXT.socket(zmq.PUSH) 62 | self._PULL_SOCKET = self._ZMQ_CONTEXT.socket(zmq.PULL) 63 | self._SUB_SOCKET = self._ZMQ_CONTEXT.socket(zmq.SUB) 64 | 65 | # Bind PUSH Socket to send commands to MetaTrader 66 | self._PUSH_SOCKET.connect(self._URL + str(self._PUSH_PORT)) 67 | print("[INIT] Ready to send commands to METATRADER (PUSH): " + str(self._PUSH_PORT)) 68 | 69 | # Connect PULL Socket to receive command responses from MetaTrader 70 | self._PULL_SOCKET.connect(self._URL + str(self._PULL_PORT)) 71 | print("[INIT] Listening for responses from METATRADER (PULL): " + str(self._PULL_PORT)) 72 | 73 | # Connect SUB Socket to receive market data from MetaTrader 74 | self._SUB_SOCKET.connect(self._URL + str(self._SUB_PORT)) 75 | 76 | # Initialize POLL set and register PULL and SUB sockets 77 | self._poller = zmq.Poller() 78 | self._poller.register(self._PULL_SOCKET, zmq.POLLIN) 79 | self._poller.register(self._SUB_SOCKET, zmq.POLLIN) 80 | 81 | # Start listening for responses to commands and new market data 82 | self._string_delimiter = _delimiter 83 | 84 | # BID/ASK Market Data Subscription Threads ({SYMBOL: Thread}) 85 | self._MarketData_Thread = None 86 | 87 | # Begin polling for PULL / SUB data 88 | self._MarketData_Thread = Thread(target=self._DWX_ZMQ_Poll_Data_, args=(self._string_delimiter)) 89 | self._MarketData_Thread.start() 90 | 91 | # Market Data Dictionary by Symbol (holds tick data) 92 | self._Market_Data_DB = {} # {SYMBOL: {TIMESTAMP: (BID, ASK)}} 93 | 94 | # Temporary Order STRUCT for convenience wrappers later. 95 | self.temp_order_dict = self._generate_default_order_dict() 96 | 97 | # Thread returns the most recently received DATA block here 98 | self._thread_data_output = None 99 | 100 | # Verbosity 101 | self._verbose = _verbose 102 | 103 | ########################################################################## 104 | 105 | """ 106 | Set Status (to enable/disable strategy manually) 107 | """ 108 | def _setStatus(self, _new_status=False): 109 | 110 | self._ACTIVE = _new_status 111 | print("\n**\n[KERNEL] Setting Status to {} - Deactivating Threads.. please wait a bit.\n**".format(_new_status)) 112 | 113 | ########################################################################## 114 | 115 | """ 116 | Function to send commands to MetaTrader (PUSH) 117 | """ 118 | def remote_send(self, _socket, _data): 119 | 120 | try: 121 | _socket.send_string(_data, zmq.DONTWAIT) 122 | except zmq.error.Again: 123 | print("\nResource timeout.. please try again.") 124 | sleep(0.000000001) 125 | 126 | ########################################################################## 127 | 128 | def _get_response_(self): 129 | return self._thread_data_output 130 | 131 | ########################################################################## 132 | 133 | def _set_response_(self, _resp=None): 134 | self._thread_data_output = _resp 135 | 136 | ########################################################################## 137 | 138 | def _valid_response_(self, _input='zmq'): 139 | 140 | # Valid data types 141 | _types = (dict,DataFrame) 142 | 143 | # If _input = 'zmq', assume self._zmq._thread_data_output 144 | if isinstance(_input, str) and _input == 'zmq': 145 | return isinstance(self._get_response_(), _types) 146 | else: 147 | return isinstance(_input, _types) 148 | 149 | # Default 150 | return False 151 | 152 | ########################################################################## 153 | 154 | """ 155 | Function to retrieve data from MetaTrader (PULL or SUB) 156 | """ 157 | def remote_recv(self, _socket): 158 | 159 | try: 160 | msg = _socket.recv_string(zmq.DONTWAIT) 161 | return msg 162 | except zmq.error.Again: 163 | print("\nResource timeout.. please try again.") 164 | sleep(0.000001) 165 | 166 | return None 167 | 168 | ########################################################################## 169 | 170 | # Convenience functions to permit easy trading via underlying functions. 171 | 172 | # OPEN ORDER 173 | def _DWX_MTX_NEW_TRADE_(self, _order=None): 174 | 175 | if _order is None: 176 | _order = self._generate_default_order_dict() 177 | 178 | # Execute 179 | self._DWX_MTX_SEND_COMMAND_(**_order) 180 | 181 | # MODIFY ORDER 182 | def _DWX_MTX_MODIFY_TRADE_BY_TICKET_(self, _ticket, _SL, _TP): # in points 183 | 184 | try: 185 | self.temp_order_dict['_action'] = 'MODIFY' 186 | self.temp_order_dict['_SL'] = _SL 187 | self.temp_order_dict['_TP'] = _TP 188 | self.temp_order_dict['_ticket'] = _ticket 189 | 190 | # Execute 191 | self._DWX_MTX_SEND_COMMAND_(**self.temp_order_dict) 192 | 193 | except KeyError: 194 | print("[ERROR] Order Ticket {} not found!".format(_ticket)) 195 | 196 | # CLOSE ORDER 197 | def _DWX_MTX_CLOSE_TRADE_BY_TICKET_(self, _ticket): 198 | 199 | try: 200 | self.temp_order_dict['_action'] = 'CLOSE' 201 | self.temp_order_dict['_ticket'] = _ticket 202 | 203 | # Execute 204 | self._DWX_MTX_SEND_COMMAND_(**self.temp_order_dict) 205 | 206 | except KeyError: 207 | print("[ERROR] Order Ticket {} not found!".format(_ticket)) 208 | 209 | # CLOSE PARTIAL 210 | def _DWX_MTX_CLOSE_PARTIAL_BY_TICKET_(self, _ticket, _lots): 211 | 212 | try: 213 | self.temp_order_dict['_action'] = 'CLOSE_PARTIAL' 214 | self.temp_order_dict['_ticket'] = _ticket 215 | self.temp_order_dict['_lots'] = _lots 216 | 217 | # Execute 218 | self._DWX_MTX_SEND_COMMAND_(**self.temp_order_dict) 219 | 220 | except KeyError: 221 | print("[ERROR] Order Ticket {} not found!".format(_ticket)) 222 | 223 | # CLOSE MAGIC 224 | def _DWX_MTX_CLOSE_TRADES_BY_MAGIC_(self, _magic): 225 | 226 | try: 227 | self.temp_order_dict['_action'] = 'CLOSE_MAGIC' 228 | self.temp_order_dict['_magic'] = _magic 229 | 230 | # Execute 231 | self._DWX_MTX_SEND_COMMAND_(**self.temp_order_dict) 232 | 233 | except KeyError: 234 | pass 235 | 236 | # CLOSE ALL TRADES 237 | def _DWX_MTX_CLOSE_ALL_TRADES_(self): 238 | 239 | try: 240 | self.temp_order_dict['_action'] = 'CLOSE_ALL' 241 | 242 | # Execute 243 | self._DWX_MTX_SEND_COMMAND_(**self.temp_order_dict) 244 | 245 | except KeyError: 246 | pass 247 | 248 | # GET OPEN TRADES 249 | def _DWX_MTX_GET_ALL_OPEN_TRADES_(self): 250 | 251 | try: 252 | self.temp_order_dict['_action'] = 'GET_OPEN_TRADES' 253 | 254 | # Execute 255 | self._DWX_MTX_SEND_COMMAND_(**self.temp_order_dict) 256 | 257 | except KeyError: 258 | pass 259 | 260 | # DEFAULT ORDER DICT 261 | def _generate_default_order_dict(self): 262 | return({'_action': 'OPEN', 263 | '_type': 0, 264 | '_symbol': 'EURUSD', 265 | '_price': 0.0, 266 | '_SL': 500, # SL/TP in POINTS, not pips. 267 | '_TP': 500, 268 | '_comment': 'DWX_Python_to_MT', 269 | '_lots': 0.01, 270 | '_magic': 123456, 271 | '_ticket': 0}) 272 | 273 | # DEFAULT DATA REQUEST DICT 274 | def _generate_default_data_dict(self): 275 | return({'_action': 'DATA', 276 | '_symbol': 'EURUSD', 277 | '_timeframe': 1440, # M1 = 1, M5 = 5, and so on.. 278 | '_start': '2018.12.21 17:00:00', # timestamp in MT4 recognized format 279 | '_end': '2018.12.21 17:05:00'}) 280 | 281 | ########################################################################## 282 | """ 283 | Function to construct messages for sending DATA commands to MetaTrader 284 | """ 285 | def _DWX_MTX_SEND_MARKETDATA_REQUEST_(self, 286 | _symbol='EURUSD', 287 | _timeframe=1, 288 | _start='2019.01.04 17:00:00', 289 | _end=Timestamp.now().strftime('%Y.%m.%d %H:%M:00')): 290 | #_end='2019.01.04 17:05:00'): 291 | 292 | _msg = "{};{};{};{};{}".format('DATA', 293 | _symbol, 294 | _timeframe, 295 | _start, 296 | _end) 297 | # Send via PUSH Socket 298 | self.remote_send(self._PUSH_SOCKET, _msg) 299 | 300 | 301 | ########################################################################## 302 | """ 303 | Function to construct messages for sending Trade commands to MetaTrader 304 | """ 305 | def _DWX_MTX_SEND_COMMAND_(self, _action='OPEN', _type=0, 306 | _symbol='EURUSD', _price=0.0, 307 | _SL=50, _TP=50, _comment="Python-to-MT", 308 | _lots=0.01, _magic=123456, _ticket=0): 309 | 310 | _msg = "{};{};{};{};{};{};{};{};{};{};{}".format('TRADE',_action,_type, 311 | _symbol,_price, 312 | _SL,_TP,_comment, 313 | _lots,_magic, 314 | _ticket) 315 | 316 | # Send via PUSH Socket 317 | self.remote_send(self._PUSH_SOCKET, _msg) 318 | 319 | """ 320 | compArray[0] = TRADE or DATA 321 | compArray[1] = ACTION (e.g. OPEN, MODIFY, CLOSE) 322 | compArray[2] = TYPE (e.g. OP_BUY, OP_SELL, etc - only used when ACTION=OPEN) 323 | 324 | For compArray[0] == DATA, format is: 325 | DATA|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME 326 | 327 | // ORDER TYPES: 328 | // https://docs.mql4.com/constants/tradingconstants/orderproperties 329 | 330 | // OP_BUY = 0 331 | // OP_SELL = 1 332 | // OP_BUYLIMIT = 2 333 | // OP_SELLLIMIT = 3 334 | // OP_BUYSTOP = 4 335 | // OP_SELLSTOP = 5 336 | 337 | compArray[3] = Symbol (e.g. EURUSD, etc.) 338 | compArray[4] = Open/Close Price (ignored if ACTION = MODIFY) 339 | compArray[5] = SL 340 | compArray[6] = TP 341 | compArray[7] = Trade Comment 342 | compArray[8] = Lots 343 | compArray[9] = Magic Number 344 | compArray[10] = Ticket Number (MODIFY/CLOSE) 345 | """ 346 | # pass 347 | 348 | ########################################################################## 349 | 350 | """ 351 | Function to check Poller for new reponses (PULL) and market data (SUB) 352 | """ 353 | 354 | def _DWX_ZMQ_Poll_Data_(self, 355 | string_delimiter=';'): 356 | 357 | while self._ACTIVE: 358 | 359 | sockets = dict(self._poller.poll()) 360 | 361 | # Process response to commands sent to MetaTrader 362 | if self._PULL_SOCKET in sockets and sockets[self._PULL_SOCKET] == zmq.POLLIN: 363 | 364 | try: 365 | 366 | msg = self._PULL_SOCKET.recv_string(zmq.DONTWAIT) 367 | 368 | # If data is returned, store as pandas Series 369 | if msg != '' and msg != None: 370 | 371 | try: 372 | _data = eval(msg) 373 | 374 | self._thread_data_output = _data 375 | if self._verbose: 376 | print(_data) # default logic 377 | 378 | except Exception as ex: 379 | _exstr = "Exception Type {0}. Args:\n{1!r}" 380 | _msg = _exstr.format(type(ex).__name__, ex.args) 381 | print(_msg) 382 | 383 | except zmq.error.Again: 384 | pass # resource temporarily unavailable, nothing to print 385 | except ValueError: 386 | pass # No data returned, passing iteration. 387 | except UnboundLocalError: 388 | pass # _symbol may sometimes get referenced before being assigned. 389 | 390 | # Receive new market data from MetaTrader 391 | if self._SUB_SOCKET in sockets and sockets[self._SUB_SOCKET] == zmq.POLLIN: 392 | 393 | try: 394 | msg = self._SUB_SOCKET.recv_string(zmq.DONTWAIT) 395 | 396 | if msg != "": 397 | _symbol, _data = msg.split(" ") 398 | _bid, _ask = _data.split(string_delimiter) 399 | _timestamp = str(Timestamp.now('UTC'))[:-6] 400 | 401 | if self._verbose: 402 | print("\n[" + _symbol + "] " + _timestamp + " (" + _bid + "/" + _ask + ") BID/ASK") 403 | 404 | # Update Market Data DB 405 | if _symbol not in self._Market_Data_DB.keys(): 406 | self._Market_Data_DB[_symbol] = {} 407 | 408 | self._Market_Data_DB[_symbol][_timestamp] = (float(_bid), float(_ask)) 409 | 410 | except zmq.error.Again: 411 | pass # resource temporarily unavailable, nothing to print 412 | except ValueError: 413 | pass # No data returned, passing iteration. 414 | except UnboundLocalError: 415 | pass # _symbol may sometimes get referenced before being assigned. 416 | 417 | ########################################################################## 418 | 419 | """ 420 | Function to subscribe to given Symbol's BID/ASK feed from MetaTrader 421 | """ 422 | def _DWX_MTX_SUBSCRIBE_MARKETDATA_(self, _symbol, _string_delimiter=';'): 423 | 424 | # Subscribe to SYMBOL first. 425 | self._SUB_SOCKET.setsockopt_string(zmq.SUBSCRIBE, _symbol) 426 | 427 | if self._MarketData_Thread is None: 428 | 429 | self._MarketData_Thread = Thread(target=self._DWX_ZMQ_Poll_Data, args=(_string_delimiter)) 430 | self._MarketData_Thread.start() 431 | 432 | print("[KERNEL] Subscribed to {} BID/ASK updates. See self._Market_Data_DB.".format(_symbol)) 433 | 434 | """ 435 | Function to unsubscribe to given Symbol's BID/ASK feed from MetaTrader 436 | """ 437 | def _DWX_MTX_UNSUBSCRIBE_MARKETDATA_(self, _symbol): 438 | 439 | self._SUB_SOCKET.setsockopt_string(zmq.UNSUBSCRIBE, _symbol) 440 | print("\n**\n[KERNEL] Unsubscribing from " + _symbol + "\n**\n") 441 | 442 | 443 | """ 444 | Function to unsubscribe from ALL MetaTrader Symbols 445 | """ 446 | def _DWX_MTX_UNSUBSCRIBE_ALL_MARKETDATA_REQUESTS_(self): 447 | 448 | self._setStatus(False) 449 | self._MarketData_Thread = None 450 | 451 | ########################################################################## 452 | -------------------------------------------------------------------------------- /tools/dwx_zeromq_connector/v2.0.1/MQL4/DWX_ZeroMQ_Server_v2.0.1_RC8.mq4: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------+ 2 | //| DWX_ZeroMQ_Server_v2.0.1_RC8.mq4 3 | //| @author: Darwinex Labs (www.darwinex.com) 4 | //| 5 | //| Copyright (c) 2017-2019, Darwinex. All rights reserved. 6 | //| 7 | //| Licensed under the BSD 3-Clause License, you may not use this file except 8 | //| in compliance with the License. 9 | //| 10 | //| You may obtain a copy of the License at: 11 | //| https://opensource.org/licenses/BSD-3-Clause 12 | //+--------------------------------------------------------------+ 13 | #property copyright "Copyright 2017-2019, Darwinex Labs." 14 | #property link "https://www.darwinex.com/" 15 | #property version "2.0.1" 16 | #property strict 17 | 18 | // Required: MQL-ZMQ from https://github.com/dingmaotu/mql-zmq 19 | 20 | #include 21 | 22 | extern string PROJECT_NAME = "DWX_ZeroMQ_MT4_Server"; 23 | extern string ZEROMQ_PROTOCOL = "tcp"; 24 | extern string HOSTNAME = "*"; 25 | extern int PUSH_PORT = 32768; 26 | extern int PULL_PORT = 32769; 27 | extern int PUB_PORT = 32770; 28 | extern int MILLISECOND_TIMER = 1; 29 | 30 | extern string t0 = "--- Trading Parameters ---"; 31 | extern int MagicNumber = 123456; 32 | extern int MaximumOrders = 1; 33 | extern double MaximumLotSize = 0.01; 34 | extern int MaximumSlippage = 3; 35 | extern bool DMA_MODE = true; 36 | 37 | extern string t1 = "--- ZeroMQ Configuration ---"; 38 | extern bool Publish_MarketData = false; 39 | 40 | string Publish_Symbols[7] = { 41 | "EURUSD","GBPUSD","USDJPY","USDCAD","AUDUSD","NZDUSD","USDCHF" 42 | }; 43 | 44 | /* 45 | string Publish_Symbols[28] = { 46 | "EURUSD","EURGBP","EURAUD","EURNZD","EURJPY","EURCHF","EURCAD", 47 | "GBPUSD","AUDUSD","NZDUSD","USDJPY","USDCHF","USDCAD","GBPAUD", 48 | "GBPNZD","GBPJPY","GBPCHF","GBPCAD","AUDJPY","CHFJPY","CADJPY", 49 | "AUDNZD","AUDCHF","AUDCAD","NZDJPY","NZDCHF","NZDCAD","CADCHF" 50 | }; 51 | */ 52 | 53 | // CREATE ZeroMQ Context 54 | Context context(PROJECT_NAME); 55 | 56 | // CREATE ZMQ_PUSH SOCKET 57 | Socket pushSocket(context, ZMQ_PUSH); 58 | 59 | // CREATE ZMQ_PULL SOCKET 60 | Socket pullSocket(context, ZMQ_PULL); 61 | 62 | // CREATE ZMQ_PUB SOCKET 63 | Socket pubSocket(context, ZMQ_PUB); 64 | 65 | // VARIABLES FOR LATER 66 | uchar _data[]; 67 | ZmqMsg request; 68 | 69 | //+------------------------------------------------------------------+ 70 | //| Expert initialization function | 71 | //+------------------------------------------------------------------+ 72 | int OnInit() 73 | { 74 | //--- 75 | 76 | EventSetMillisecondTimer(MILLISECOND_TIMER); // Set Millisecond Timer to get client socket input 77 | 78 | context.setBlocky(false); 79 | 80 | // Send responses to PULL_PORT that client is listening on. 81 | Print("[PUSH] Binding MT4 Server to Socket on Port " + IntegerToString(PULL_PORT) + ".."); 82 | pushSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PULL_PORT)); 83 | 84 | pushSocket.setSendHighWaterMark(1); 85 | pushSocket.setLinger(0); 86 | 87 | // Receive commands from PUSH_PORT that client is sending to. 88 | Print("[PULL] Binding MT4 Server to Socket on Port " + IntegerToString(PUSH_PORT) + ".."); 89 | pullSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUSH_PORT)); 90 | 91 | pullSocket.setReceiveHighWaterMark(1); 92 | 93 | pullSocket.setLinger(0); 94 | 95 | if (Publish_MarketData == TRUE) 96 | { 97 | // Send new market data to PUB_PORT that client is subscribed to. 98 | Print("[PUB] Binding MT4 Server to Socket on Port " + IntegerToString(PUB_PORT) + ".."); 99 | pubSocket.bind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUB_PORT)); 100 | pubSocket.setSendHighWaterMark(1); 101 | pubSocket.setLinger(0); 102 | } 103 | 104 | //--- 105 | return(INIT_SUCCEEDED); 106 | } 107 | //+------------------------------------------------------------------+ 108 | //| Expert deinitialization function | 109 | //+------------------------------------------------------------------+ 110 | void OnDeinit(const int reason) 111 | { 112 | //--- 113 | 114 | Print("[PUSH] Unbinding MT4 Server from Socket on Port " + IntegerToString(PULL_PORT) + ".."); 115 | pushSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PULL_PORT)); 116 | 117 | Print("[PULL] Unbinding MT4 Server from Socket on Port " + IntegerToString(PUSH_PORT) + ".."); 118 | pullSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUSH_PORT)); 119 | 120 | if (Publish_MarketData == TRUE) 121 | { 122 | Print("[PUB] Unbinding MT4 Server from Socket on Port " + IntegerToString(PUB_PORT) + ".."); 123 | pubSocket.unbind(StringFormat("%s://%s:%d", ZEROMQ_PROTOCOL, HOSTNAME, PUB_PORT)); 124 | } 125 | 126 | // Shutdown ZeroMQ Context 127 | context.shutdown(); 128 | context.destroy(0); 129 | 130 | EventKillTimer(); 131 | } 132 | 133 | //+------------------------------------------------------------------+ 134 | //| Expert tick function | 135 | //+------------------------------------------------------------------+ 136 | void OnTick() 137 | { 138 | /* 139 | Use this OnTick() function to send market data to subscribed client. 140 | */ 141 | if(!IsStopped() && Publish_MarketData == true) 142 | { 143 | for(int s = 0; s < ArraySize(Publish_Symbols); s++) 144 | { 145 | // Python clients can subscribe to a price feed by setting 146 | // socket options to the symbol name. For example: 147 | 148 | string _tick = GetBidAsk(Publish_Symbols[s]); 149 | Print("Sending " + Publish_Symbols[s] + " " + _tick + " to PUB Socket"); 150 | ZmqMsg reply(StringFormat("%s %s", Publish_Symbols[s], _tick)); 151 | pubSocket.send(reply, true); 152 | } 153 | } 154 | } 155 | 156 | //+------------------------------------------------------------------+ 157 | //| Expert timer function | 158 | //+------------------------------------------------------------------+ 159 | void OnTimer() 160 | { 161 | //--- 162 | 163 | /* 164 | Use this OnTimer() function to get and respond to commands 165 | */ 166 | 167 | // Get client's response, but don't block. 168 | pullSocket.recv(request, true); 169 | 170 | if (request.size() > 0) 171 | { 172 | // Wait 173 | // pullSocket.recv(request,false); 174 | 175 | // MessageHandler() should go here. 176 | ZmqMsg reply = MessageHandler(request); 177 | 178 | // Send response, and block 179 | // pushSocket.send(reply); 180 | 181 | // Send response, but don't block 182 | pushSocket.send(reply, true); 183 | } 184 | } 185 | //+------------------------------------------------------------------+ 186 | 187 | ZmqMsg MessageHandler(ZmqMsg &_request) { 188 | 189 | // Output object 190 | ZmqMsg reply; 191 | 192 | // Message components for later. 193 | string components[11]; 194 | 195 | if(_request.size() > 0) { 196 | 197 | // Get data from request 198 | ArrayResize(_data, _request.size()); 199 | _request.getData(_data); 200 | string dataStr = CharArrayToString(_data); 201 | 202 | // Process data 203 | ParseZmqMessage(dataStr, components); 204 | 205 | // Interpret data 206 | InterpretZmqMessage(&pushSocket, components); 207 | 208 | } 209 | else { 210 | // NO DATA RECEIVED 211 | } 212 | 213 | return(reply); 214 | } 215 | 216 | // Interpret Zmq Message and perform actions 217 | void InterpretZmqMessage(Socket &pSocket, string &compArray[]) { 218 | 219 | // Print("ZMQ: Interpreting Message.."); 220 | 221 | // Message Structures: 222 | 223 | // 1) Trading 224 | // TRADE|ACTION|TYPE|SYMBOL|PRICE|SL|TP|COMMENT|TICKET 225 | // e.g. TRADE|OPEN|1|EURUSD|0|50|50|R-to-MetaTrader4|12345678 226 | 227 | // The 12345678 at the end is the ticket ID, for MODIFY and CLOSE. 228 | 229 | // 2) Data Requests 230 | 231 | // 2.1) RATES|SYMBOL -> Returns Current Bid/Ask 232 | 233 | // 2.2) DATA|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME 234 | 235 | // NOTE: datetime has format: D'2015.01.01 00:00' 236 | 237 | /* 238 | compArray[0] = TRADE or RATES 239 | If RATES -> compArray[1] = Symbol 240 | 241 | If TRADE -> 242 | compArray[0] = TRADE 243 | compArray[1] = ACTION (e.g. OPEN, MODIFY, CLOSE) 244 | compArray[2] = TYPE (e.g. OP_BUY, OP_SELL, etc - only used when ACTION=OPEN) 245 | 246 | // ORDER TYPES: 247 | // https://docs.mql4.com/constants/tradingconstants/orderproperties 248 | 249 | // OP_BUY = 0 250 | // OP_SELL = 1 251 | // OP_BUYLIMIT = 2 252 | // OP_SELLLIMIT = 3 253 | // OP_BUYSTOP = 4 254 | // OP_SELLSTOP = 5 255 | 256 | compArray[3] = Symbol (e.g. EURUSD, etc.) 257 | compArray[4] = Open/Close Price (ignored if ACTION = MODIFY) 258 | compArray[5] = SL 259 | compArray[6] = TP 260 | compArray[7] = Trade Comment 261 | compArray[8] = Lots 262 | compArray[9] = Magic Number 263 | compArray[10] = Ticket Number (MODIFY/CLOSE) 264 | */ 265 | 266 | int switch_action = 0; 267 | 268 | if(compArray[0] == "TRADE" && compArray[1] == "OPEN") 269 | switch_action = 1; 270 | if(compArray[0] == "TRADE" && compArray[1] == "MODIFY") 271 | switch_action = 2; 272 | if(compArray[0] == "TRADE" && compArray[1] == "CLOSE") 273 | switch_action = 3; 274 | if(compArray[0] == "TRADE" && compArray[1] == "CLOSE_PARTIAL") 275 | switch_action = 4; 276 | if(compArray[0] == "TRADE" && compArray[1] == "CLOSE_MAGIC") 277 | switch_action = 5; 278 | if(compArray[0] == "TRADE" && compArray[1] == "CLOSE_ALL") 279 | switch_action = 6; 280 | if(compArray[0] == "TRADE" && compArray[1] == "GET_OPEN_TRADES") 281 | switch_action = 7; 282 | if(compArray[0] == "DATA") 283 | switch_action = 8; 284 | 285 | string zmq_ret = ""; 286 | string ret = ""; 287 | int ticket = -1; 288 | bool ans = FALSE; 289 | 290 | switch(switch_action) 291 | { 292 | case 1: // OPEN TRADE 293 | 294 | zmq_ret = "{"; 295 | 296 | // Function definition: 297 | ticket = DWX_OpenOrder(compArray[3], StrToInteger(compArray[2]), StrToDouble(compArray[8]), 298 | StrToDouble(compArray[4]), StrToInteger(compArray[5]), StrToInteger(compArray[6]), 299 | compArray[7], StrToInteger(compArray[9]), zmq_ret); 300 | 301 | // Send TICKET back as JSON 302 | InformPullClient(pSocket, zmq_ret + "}"); 303 | 304 | break; 305 | 306 | case 2: // MODIFY SL/TP 307 | 308 | zmq_ret = "{'_action': 'MODIFY'"; 309 | 310 | // Function definition: 311 | ans = DWX_SetSLTP(StrToInteger(compArray[10]), StrToDouble(compArray[5]), StrToDouble(compArray[6]), 312 | StrToInteger(compArray[9]), StrToInteger(compArray[2]), StrToDouble(compArray[4]), 313 | compArray[3], 3, zmq_ret); 314 | 315 | InformPullClient(pSocket, zmq_ret + "}"); 316 | 317 | break; 318 | 319 | case 3: // CLOSE TRADE 320 | 321 | zmq_ret = "{"; 322 | 323 | // IMPLEMENT CLOSE TRADE LOGIC HERE 324 | DWX_CloseOrder_Ticket(StrToInteger(compArray[10]), zmq_ret); 325 | 326 | InformPullClient(pSocket, zmq_ret + "}"); 327 | 328 | break; 329 | 330 | case 4: // CLOSE PARTIAL 331 | 332 | zmq_ret = "{"; 333 | 334 | ans = DWX_ClosePartial(StrToDouble(compArray[8]), zmq_ret, StrToInteger(compArray[10])); 335 | 336 | InformPullClient(pSocket, zmq_ret + "}"); 337 | 338 | break; 339 | 340 | case 5: // CLOSE MAGIC 341 | 342 | zmq_ret = "{"; 343 | 344 | DWX_CloseOrder_Magic(StrToInteger(compArray[9]), zmq_ret); 345 | 346 | InformPullClient(pSocket, zmq_ret + "}"); 347 | 348 | break; 349 | 350 | case 6: // CLOSE ALL ORDERS 351 | 352 | zmq_ret = "{"; 353 | 354 | DWX_CloseAllOrders(zmq_ret); 355 | 356 | InformPullClient(pSocket, zmq_ret + "}"); 357 | 358 | break; 359 | 360 | case 7: // GET OPEN ORDERS 361 | 362 | zmq_ret = "{"; 363 | 364 | DWX_GetOpenOrders(zmq_ret); 365 | 366 | InformPullClient(pSocket, zmq_ret + "}"); 367 | 368 | break; 369 | 370 | case 8: // DATA REQUEST 371 | 372 | zmq_ret = "{"; 373 | 374 | DWX_GetData(compArray, zmq_ret); 375 | 376 | InformPullClient(pSocket, zmq_ret + "}"); 377 | 378 | break; 379 | 380 | default: 381 | break; 382 | } 383 | } 384 | 385 | // Parse Zmq Message 386 | void ParseZmqMessage(string& message, string& retArray[]) { 387 | 388 | //Print("Parsing: " + message); 389 | 390 | string sep = ";"; 391 | ushort u_sep = StringGetCharacter(sep,0); 392 | 393 | int splits = StringSplit(message, u_sep, retArray); 394 | 395 | /* 396 | for(int i = 0; i < splits; i++) { 397 | Print(IntegerToString(i) + ") " + retArray[i]); 398 | } 399 | */ 400 | } 401 | 402 | //+------------------------------------------------------------------+ 403 | // Generate string for Bid/Ask by symbol 404 | string GetBidAsk(string symbol) { 405 | 406 | MqlTick last_tick; 407 | 408 | if(SymbolInfoTick(symbol,last_tick)) 409 | { 410 | return(StringFormat("%f;%f", last_tick.bid, last_tick.ask)); 411 | } 412 | 413 | // Default 414 | return ""; 415 | } 416 | 417 | // Get data for request datetime range 418 | void DWX_GetData(string& compArray[], string& zmq_ret) { 419 | 420 | // Format: DATA|SYMBOL|TIMEFRAME|START_DATETIME|END_DATETIME 421 | 422 | double price_array[]; 423 | datetime time_array[]; 424 | 425 | // Get prices 426 | int price_count = CopyClose(compArray[1], 427 | StrToInteger(compArray[2]), StrToTime(compArray[3]), 428 | StrToTime(compArray[4]), price_array); 429 | 430 | // Get timestamps 431 | int time_count = CopyTime(compArray[1], 432 | StrToInteger(compArray[2]), StrToTime(compArray[3]), 433 | StrToTime(compArray[4]), time_array); 434 | 435 | zmq_ret = zmq_ret + "'_action': 'DATA'"; 436 | 437 | if (price_count > 0) { 438 | 439 | zmq_ret = zmq_ret + ", '_data': {"; 440 | 441 | // Construct string of price|price|price|.. etc and send to PULL client. 442 | for(int i = 0; i < price_count; i++ ) { 443 | 444 | if(i == 0) 445 | zmq_ret = zmq_ret + "'" + TimeToString(time_array[i]) + "': " + DoubleToString(price_array[i]); 446 | else 447 | zmq_ret = zmq_ret + ", '" + TimeToString(time_array[i]) + "': " + DoubleToString(price_array[i]); 448 | 449 | } 450 | 451 | zmq_ret = zmq_ret + "}"; 452 | 453 | } 454 | else { 455 | zmq_ret = zmq_ret + ", " + "'_response': 'NOT_AVAILABLE'"; 456 | } 457 | 458 | } 459 | 460 | // Inform Client 461 | void InformPullClient(Socket& pSocket, string message) { 462 | 463 | ZmqMsg pushReply(StringFormat("%s", message)); 464 | 465 | pSocket.send(pushReply,true); // NON-BLOCKING 466 | 467 | } 468 | 469 | /* 470 | ############################################################################ 471 | ############################################################################ 472 | ############################################################################ 473 | */ 474 | 475 | // OPEN NEW ORDER 476 | int DWX_OpenOrder(string _symbol, int _type, double _lots, double _price, double _SL, double _TP, string _comment, int _magic, string &zmq_ret) { 477 | 478 | int ticket, error; 479 | 480 | zmq_ret = zmq_ret + "'_action': 'EXECUTION'"; 481 | 482 | if(_lots > MaximumLotSize) { 483 | zmq_ret = zmq_ret + ", " + "'_response': 'LOT_SIZE_ERROR', 'response_value': 'MAX_LOT_SIZE_EXCEEDED'"; 484 | return(-1); 485 | } 486 | 487 | double sl = _SL; 488 | double tp = _TP; 489 | 490 | // Else 491 | if(DMA_MODE) { 492 | sl = 0.0; 493 | tp = 0.0; 494 | } 495 | 496 | if(_symbol == "NULL") { 497 | ticket = OrderSend(Symbol(), _type, _lots, _price, MaximumSlippage, sl, tp, _comment, _magic); 498 | } else { 499 | ticket = OrderSend(_symbol, _type, _lots, _price, MaximumSlippage, sl, tp, _comment, _magic); 500 | } 501 | if(ticket < 0) { 502 | // Failure 503 | error = GetLastError(); 504 | zmq_ret = zmq_ret + ", " + "'_response': '" + IntegerToString(error) + "', 'response_value': '" + ErrorDescription(error) + "'"; 505 | return(-1*error); 506 | } 507 | 508 | int tmpRet = OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES); 509 | 510 | zmq_ret = zmq_ret + ", " + "'_magic': " + IntegerToString(_magic) + ", '_ticket': " + IntegerToString(OrderTicket()) + ", '_open_time': '" + TimeToStr(OrderOpenTime(),TIME_DATE|TIME_SECONDS) + "', '_open_price': " + DoubleToString(OrderOpenPrice()); 511 | 512 | if(DMA_MODE) { 513 | 514 | int retries = 3; 515 | while(true) { 516 | retries--; 517 | if(retries < 0) return(0); 518 | 519 | if((_SL == 0 && _TP == 0) || (OrderStopLoss() == _SL && OrderTakeProfit() == _TP)) { 520 | return(ticket); 521 | } 522 | 523 | if(DWX_IsTradeAllowed(30, zmq_ret) == 1) { 524 | if(DWX_SetSLTP(ticket, _SL, _TP, _magic, _type, _price, _symbol, retries, zmq_ret)) { 525 | return(ticket); 526 | } 527 | if(retries == 0) { 528 | zmq_ret = zmq_ret + ", '_response': 'ERROR_SETTING_SL_TP'"; 529 | return(-11111); 530 | } 531 | } 532 | 533 | Sleep(MILLISECOND_TIMER); 534 | } 535 | 536 | zmq_ret = zmq_ret + ", '_response': 'ERROR_SETTING_SL_TP'"; 537 | zmq_ret = zmq_ret + "}"; 538 | return(-1); 539 | } 540 | 541 | // Send zmq_ret to Python Client 542 | zmq_ret = zmq_ret + "}"; 543 | 544 | return(ticket); 545 | } 546 | 547 | // SET SL/TP 548 | bool DWX_SetSLTP(int ticket, double _SL, double _TP, int _magic, int _type, double _price, string _symbol, int retries, string &zmq_ret) { 549 | 550 | int dir_flag = -1; 551 | 552 | if (_type == 0 || _type == 2 || _type == 4) 553 | dir_flag = 1; 554 | 555 | double vpoint = MarketInfo(OrderSymbol(), MODE_POINT); 556 | int vdigits = (int)MarketInfo(OrderSymbol(), MODE_DIGITS); 557 | 558 | if(OrderModify(ticket, OrderOpenPrice(), NormalizeDouble(OrderOpenPrice()-_SL*dir_flag*vpoint,vdigits), NormalizeDouble(OrderOpenPrice()+_TP*dir_flag*vpoint,vdigits), 0, 0)) { 559 | zmq_ret = zmq_ret + ", '_sl': " + DoubleToString(_SL) + ", '_tp': " + DoubleToString(_TP); 560 | return(true); 561 | } else { 562 | int error = GetLastError(); 563 | zmq_ret = zmq_ret + ", '_response': '" + IntegerToString(error) + "', '_response_value': '" + ErrorDescription(error) + "'"; 564 | 565 | if(retries == 0) { 566 | RefreshRates(); 567 | DWX_CloseAtMarket(-1, zmq_ret); 568 | // int lastOrderErrorCloseTime = TimeCurrent(); 569 | } 570 | 571 | return(false); 572 | } 573 | 574 | return(true); 575 | } 576 | 577 | // CLOSE AT MARKET 578 | bool DWX_CloseAtMarket(double size, string &zmq_ret) { 579 | 580 | int error; 581 | 582 | int retries = 3; 583 | while(true) { 584 | retries--; 585 | if(retries < 0) return(false); 586 | 587 | if(DWX_IsTradeAllowed(30, zmq_ret) == 1) { 588 | if(DWX_ClosePartial(size, zmq_ret)) { 589 | // trade successfuly closed 590 | return(true); 591 | } else { 592 | error = GetLastError(); 593 | zmq_ret = zmq_ret + ", '_response': '" + IntegerToString(error) + "', '_response_value': '" + ErrorDescription(error) + "'"; 594 | } 595 | } 596 | 597 | } 598 | 599 | return(false); 600 | } 601 | 602 | // CLOSE PARTIAL SIZE 603 | bool DWX_ClosePartial(double size, string &zmq_ret, int ticket = 0) { 604 | 605 | RefreshRates(); 606 | double priceCP; 607 | 608 | bool close_ret = False; 609 | 610 | if(OrderType() != OP_BUY && OrderType() != OP_SELL) { 611 | return(true); 612 | } 613 | 614 | if(OrderType() == OP_BUY) { 615 | priceCP = DWX_GetBid(OrderSymbol()); 616 | } else { 617 | priceCP = DWX_GetAsk(OrderSymbol()); 618 | } 619 | 620 | // If the function is called directly, setup init() JSON here. 621 | if(ticket != 0) { 622 | zmq_ret = zmq_ret + "'_action': 'CLOSE', '_ticket': " + IntegerToString(ticket); 623 | zmq_ret = zmq_ret + ", '_response': 'CLOSE_PARTIAL'"; 624 | } 625 | 626 | int local_ticket = 0; 627 | 628 | if (ticket != 0) 629 | local_ticket = ticket; 630 | else 631 | local_ticket = OrderTicket(); 632 | 633 | if(size < 0.01 || size > OrderLots()) { 634 | close_ret = OrderClose(local_ticket, OrderLots(), priceCP, MaximumSlippage); 635 | zmq_ret = zmq_ret + ", '_close_price': " + DoubleToString(priceCP) + ", '_close_lots': " + DoubleToString(OrderLots()); 636 | return(close_ret); 637 | } else { 638 | close_ret = OrderClose(local_ticket, size, priceCP, MaximumSlippage); 639 | zmq_ret = zmq_ret + ", '_close_price': " + DoubleToString(priceCP) + ", '_close_lots': " + DoubleToString(size); 640 | return(close_ret); 641 | } 642 | } 643 | 644 | // CLOSE ORDER (by Magic Number) 645 | void DWX_CloseOrder_Magic(int _magic, string &zmq_ret) { 646 | 647 | bool found = false; 648 | 649 | zmq_ret = zmq_ret + "'_action': 'CLOSE_ALL_MAGIC'"; 650 | zmq_ret = zmq_ret + ", '_magic': " + IntegerToString(_magic); 651 | 652 | zmq_ret = zmq_ret + ", '_responses': {"; 653 | 654 | for(int i=OrdersTotal()-1; i >= 0; i--) { 655 | if (OrderSelect(i,SELECT_BY_POS)==true && OrderMagicNumber() == _magic) { 656 | found = true; 657 | 658 | zmq_ret = zmq_ret + IntegerToString(OrderTicket()) + ": {'_symbol':'" + OrderSymbol() + "'"; 659 | 660 | if(OrderType() == OP_BUY || OrderType() == OP_SELL) { 661 | DWX_CloseAtMarket(-1, zmq_ret); 662 | zmq_ret = zmq_ret + ", '_response': 'CLOSE_MARKET'"; 663 | 664 | if (i != 0) 665 | zmq_ret = zmq_ret + "}, "; 666 | else 667 | zmq_ret = zmq_ret + "}"; 668 | 669 | } else { 670 | zmq_ret = zmq_ret + ", '_response': 'CLOSE_PENDING'"; 671 | 672 | if (i != 0) 673 | zmq_ret = zmq_ret + "}, "; 674 | else 675 | zmq_ret = zmq_ret + "}"; 676 | 677 | int tmpRet = OrderDelete(OrderTicket()); 678 | } 679 | } 680 | } 681 | 682 | zmq_ret = zmq_ret + "}"; 683 | 684 | if(found == false) { 685 | zmq_ret = zmq_ret + ", '_response': 'NOT_FOUND'"; 686 | } 687 | else { 688 | zmq_ret = zmq_ret + ", '_response_value': 'SUCCESS'"; 689 | } 690 | 691 | } 692 | 693 | // CLOSE ORDER (by Ticket) 694 | void DWX_CloseOrder_Ticket(int _ticket, string &zmq_ret) { 695 | 696 | bool found = false; 697 | 698 | zmq_ret = zmq_ret + "'_action': 'CLOSE', '_ticket': " + IntegerToString(_ticket); 699 | 700 | for(int i=0; i= 0; i--) { 733 | if (OrderSelect(i,SELECT_BY_POS)==true) { 734 | 735 | found = true; 736 | 737 | zmq_ret = zmq_ret + IntegerToString(OrderTicket()) + ": {'_symbol':'" + OrderSymbol() + "', '_magic': " + IntegerToString(OrderMagicNumber()); 738 | 739 | if(OrderType() == OP_BUY || OrderType() == OP_SELL) { 740 | DWX_CloseAtMarket(-1, zmq_ret); 741 | zmq_ret = zmq_ret + ", '_response': 'CLOSE_MARKET'"; 742 | 743 | if (i != 0) 744 | zmq_ret = zmq_ret + "}, "; 745 | else 746 | zmq_ret = zmq_ret + "}"; 747 | 748 | } else { 749 | zmq_ret = zmq_ret + ", '_response': 'CLOSE_PENDING'"; 750 | 751 | if (i != 0) 752 | zmq_ret = zmq_ret + "}, "; 753 | else 754 | zmq_ret = zmq_ret + "}"; 755 | 756 | int tmpRet = OrderDelete(OrderTicket()); 757 | } 758 | } 759 | } 760 | 761 | zmq_ret = zmq_ret + "}"; 762 | 763 | if(found == false) { 764 | zmq_ret = zmq_ret + ", '_response': 'NOT_FOUND'"; 765 | } 766 | else { 767 | zmq_ret = zmq_ret + ", '_response_value': 'SUCCESS'"; 768 | } 769 | 770 | } 771 | 772 | // GET OPEN ORDERS 773 | void DWX_GetOpenOrders(string &zmq_ret) { 774 | 775 | bool found = false; 776 | 777 | zmq_ret = zmq_ret + "'_action': 'OPEN_TRADES'"; 778 | zmq_ret = zmq_ret + ", '_trades': {"; 779 | 780 | for(int i=OrdersTotal()-1; i>=0; i--) { 781 | found = true; 782 | 783 | if (OrderSelect(i,SELECT_BY_POS)==true) { 784 | 785 | zmq_ret = zmq_ret + IntegerToString(OrderTicket()) + ": {"; 786 | 787 | zmq_ret = zmq_ret + "'_magic': " + IntegerToString(OrderMagicNumber()) + ", '_symbol': '" + OrderSymbol() + "', '_lots': " + DoubleToString(OrderLots()) + ", '_type': " + IntegerToString(OrderType()) + ", '_open_price': " + DoubleToString(OrderOpenPrice()) + ", '_open_time': '" + TimeToStr(OrderOpenTime(),TIME_DATE|TIME_SECONDS) + "', '_SL': " + DoubleToString(OrderStopLoss()) + ", '_TP': " + DoubleToString(OrderTakeProfit()) + ", '_pnl': " + DoubleToString(OrderProfit()) + ", '_comment': '" + OrderComment() + "'"; 788 | 789 | if (i != 0) 790 | zmq_ret = zmq_ret + "}, "; 791 | else 792 | zmq_ret = zmq_ret + "}"; 793 | } 794 | } 795 | zmq_ret = zmq_ret + "}"; 796 | 797 | } 798 | 799 | // CHECK IF TRADE IS ALLOWED 800 | int DWX_IsTradeAllowed(int MaxWaiting_sec, string &zmq_ret) { 801 | 802 | if(!IsTradeAllowed()) { 803 | 804 | int StartWaitingTime = (int)GetTickCount(); 805 | zmq_ret = zmq_ret + ", " + "'_response': 'TRADE_CONTEXT_BUSY'"; 806 | 807 | while(true) { 808 | 809 | if(IsStopped()) { 810 | zmq_ret = zmq_ret + ", " + "'_response_value': 'EA_STOPPED_BY_USER'"; 811 | return(-1); 812 | } 813 | 814 | int diff = (int)(GetTickCount() - StartWaitingTime); 815 | if(diff > MaxWaiting_sec * 1000) { 816 | zmq_ret = zmq_ret + ", '_response': 'WAIT_LIMIT_EXCEEDED', '_response_value': " + IntegerToString(MaxWaiting_sec); 817 | return(-2); 818 | } 819 | // if the trade context has become free, 820 | if(IsTradeAllowed()) { 821 | zmq_ret = zmq_ret + ", '_response': 'TRADE_CONTEXT_NOW_FREE'"; 822 | RefreshRates(); 823 | return(1); 824 | } 825 | 826 | } 827 | } else { 828 | return(1); 829 | } 830 | 831 | return(1); 832 | } 833 | 834 | string ErrorDescription(int error_code) 835 | { 836 | string error_string; 837 | //---- 838 | switch(error_code) 839 | { 840 | //---- codes returned from trade server 841 | case 0: 842 | case 1: error_string="no error"; break; 843 | case 2: error_string="common error"; break; 844 | case 3: error_string="invalid trade parameters"; break; 845 | case 4: error_string="trade server is busy"; break; 846 | case 5: error_string="old version of the client terminal"; break; 847 | case 6: error_string="no connection with trade server"; break; 848 | case 7: error_string="not enough rights"; break; 849 | case 8: error_string="too frequent requests"; break; 850 | case 9: error_string="malfunctional trade operation (never returned error)"; break; 851 | case 64: error_string="account disabled"; break; 852 | case 65: error_string="invalid account"; break; 853 | case 128: error_string="trade timeout"; break; 854 | case 129: error_string="invalid price"; break; 855 | case 130: error_string="invalid stops"; break; 856 | case 131: error_string="invalid trade volume"; break; 857 | case 132: error_string="market is closed"; break; 858 | case 133: error_string="trade is disabled"; break; 859 | case 134: error_string="not enough money"; break; 860 | case 135: error_string="price changed"; break; 861 | case 136: error_string="off quotes"; break; 862 | case 137: error_string="broker is busy (never returned error)"; break; 863 | case 138: error_string="requote"; break; 864 | case 139: error_string="order is locked"; break; 865 | case 140: error_string="long positions only allowed"; break; 866 | case 141: error_string="too many requests"; break; 867 | case 145: error_string="modification denied because order too close to market"; break; 868 | case 146: error_string="trade context is busy"; break; 869 | case 147: error_string="expirations are denied by broker"; break; 870 | case 148: error_string="amount of open and pending orders has reached the limit"; break; 871 | case 149: error_string="hedging is prohibited"; break; 872 | case 150: error_string="prohibited by FIFO rules"; break; 873 | //---- mql4 errors 874 | case 4000: error_string="no error (never generated code)"; break; 875 | case 4001: error_string="wrong function pointer"; break; 876 | case 4002: error_string="array index is out of range"; break; 877 | case 4003: error_string="no memory for function call stack"; break; 878 | case 4004: error_string="recursive stack overflow"; break; 879 | case 4005: error_string="not enough stack for parameter"; break; 880 | case 4006: error_string="no memory for parameter string"; break; 881 | case 4007: error_string="no memory for temp string"; break; 882 | case 4008: error_string="not initialized string"; break; 883 | case 4009: error_string="not initialized string in array"; break; 884 | case 4010: error_string="no memory for array\' string"; break; 885 | case 4011: error_string="too long string"; break; 886 | case 4012: error_string="remainder from zero divide"; break; 887 | case 4013: error_string="zero divide"; break; 888 | case 4014: error_string="unknown command"; break; 889 | case 4015: error_string="wrong jump (never generated error)"; break; 890 | case 4016: error_string="not initialized array"; break; 891 | case 4017: error_string="dll calls are not allowed"; break; 892 | case 4018: error_string="cannot load library"; break; 893 | case 4019: error_string="cannot call function"; break; 894 | case 4020: error_string="expert function calls are not allowed"; break; 895 | case 4021: error_string="not enough memory for temp string returned from function"; break; 896 | case 4022: error_string="system is busy (never generated error)"; break; 897 | case 4050: error_string="invalid function parameters count"; break; 898 | case 4051: error_string="invalid function parameter value"; break; 899 | case 4052: error_string="string function internal error"; break; 900 | case 4053: error_string="some array error"; break; 901 | case 4054: error_string="incorrect series array using"; break; 902 | case 4055: error_string="custom indicator error"; break; 903 | case 4056: error_string="arrays are incompatible"; break; 904 | case 4057: error_string="global variables processing error"; break; 905 | case 4058: error_string="global variable not found"; break; 906 | case 4059: error_string="function is not allowed in testing mode"; break; 907 | case 4060: error_string="function is not confirmed"; break; 908 | case 4061: error_string="send mail error"; break; 909 | case 4062: error_string="string parameter expected"; break; 910 | case 4063: error_string="integer parameter expected"; break; 911 | case 4064: error_string="double parameter expected"; break; 912 | case 4065: error_string="array as parameter expected"; break; 913 | case 4066: error_string="requested history data in update state"; break; 914 | case 4099: error_string="end of file"; break; 915 | case 4100: error_string="some file error"; break; 916 | case 4101: error_string="wrong file name"; break; 917 | case 4102: error_string="too many opened files"; break; 918 | case 4103: error_string="cannot open file"; break; 919 | case 4104: error_string="incompatible access to a file"; break; 920 | case 4105: error_string="no order selected"; break; 921 | case 4106: error_string="unknown symbol"; break; 922 | case 4107: error_string="invalid price parameter for trade function"; break; 923 | case 4108: error_string="invalid ticket"; break; 924 | case 4109: error_string="trade is not allowed in the expert properties"; break; 925 | case 4110: error_string="longs are not allowed in the expert properties"; break; 926 | case 4111: error_string="shorts are not allowed in the expert properties"; break; 927 | case 4200: error_string="object is already exist"; break; 928 | case 4201: error_string="unknown object property"; break; 929 | case 4202: error_string="object is not exist"; break; 930 | case 4203: error_string="unknown object type"; break; 931 | case 4204: error_string="no object name"; break; 932 | case 4205: error_string="object coordinates error"; break; 933 | case 4206: error_string="no specified subwindow"; break; 934 | default: error_string="unknown error"; 935 | } 936 | //---- 937 | return(error_string); 938 | } 939 | 940 | //+------------------------------------------------------------------+ 941 | 942 | double DWX_GetAsk(string symbol) { 943 | if(symbol == "NULL") { 944 | return(Ask); 945 | } else { 946 | return(MarketInfo(symbol,MODE_ASK)); 947 | } 948 | } 949 | 950 | //+------------------------------------------------------------------+ 951 | 952 | double DWX_GetBid(string symbol) { 953 | if(symbol == "NULL") { 954 | return(Bid); 955 | } else { 956 | return(MarketInfo(symbol,MODE_BID)); 957 | } 958 | } 959 | //+------------------------------------------------------------------+ 960 | 961 | 962 | 963 | //+------------------------------------------------------------------+ 964 | --------------------------------------------------------------------------------