├── .gitignore ├── AI.py ├── LICENSE ├── README.md ├── bot ├── .gitignore ├── AI_client.mq5 ├── Data.py ├── bot.py ├── dt_model.pkl ├── main.py ├── orders.py └── test_order.py ├── data.csv ├── dataGen.mq5 ├── dt_model.pkl └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | output/ 2 | input/ 3 | .idea 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | -------------------------------------------------------------------------------- /AI.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import pandas as pd 3 | from sklearn.tree import DecisionTreeClassifier 4 | from sklearn.model_selection import cross_val_score 5 | 6 | FILENAME = 'data.csv' 7 | 8 | def fix_line(line, is_header=False): 9 | """Function to fix a line. 10 | Args: 11 | line (string): Line to correct. 12 | is_header (bool, optional): 13 | To know if we are fixing the header. Defaults to False. 14 | Returns: 15 | string: 16 | Corrected line. 17 | """ 18 | line = line.replace('\n', '') 19 | line = line.replace('ÿþ', '') 20 | line = line.split(',') 21 | line = [ elem.replace('\x00', '') for elem in line ] 22 | if not is_header: line = [ float(elem) for elem in line ] 23 | return line 24 | 25 | # Openning the file in the correct encoding 26 | with open(FILENAME, encoding="cp1252") as f: 27 | lines = f.readlines() 28 | 29 | # Loading the header 30 | header = fix_line(lines[0], is_header=True) 31 | 32 | # Loading the data 33 | data = [] 34 | for i, line in enumerate(lines[2:-1]): 35 | if i%2 == 0 and line != '': 36 | data.append(fix_line(line)) 37 | 38 | # Creating a pandas dataframe 39 | df = pd.DataFrame(data, columns=header) 40 | 41 | # Correcting the class type 42 | df[header[-1]] = df[header[-1]].astype('int32') 43 | 44 | # Creating the neural network 45 | dt = DecisionTreeClassifier() 46 | 47 | # Setting the data for training 48 | X = df.drop('class', axis=1) 49 | X = X.values 50 | y = df[header[-1]] 51 | 52 | # Training the neural network with DT 53 | dt = dt.fit(X, y) 54 | 55 | # Saving the model 56 | with open("dt_model.pkl", "wb") as f: 57 | pickle.dump(dt, f) 58 | 59 | # To load 60 | #with open("dt_model.pkl", "rb") as f: 61 | # nn = pickle.load(f) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Nuno Miguel Tavares - info@algorithmic.one 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AI ML - MACD, RSI, EMA BOT based on Decision Tree Neural Network 2 | 3 | AI EA bot based on the indicators **MACD**, **RSI** and **EMA** CossOver. 4 | 5 | This MT5 EA uses a **Decision Tree (DTs)** which are a non-parametric supervised learning method used for classification and regression. 6 | The goal is to create a model that predicts the value of a target variable by learning simple decision rules inferred from the data features. A tree can be seen as a piecewise constant approximation. 7 | 8 | - Ref - https://scikit-learn.org/stable/modules/tree.html#decision-treesNeural 9 | - Ref - Network based on Machine Learning in Python from https://scikit-learn.org ! 10 | 11 | ![image](https://user-images.githubusercontent.com/118682909/220060164-f88f5c9a-df7f-4207-a65c-522628115b52.png) 12 | 13 | ### How To 14 | - Install Metatrader 5 15 | - Inside ```MT5 Options``` click ```Allow algorithmic trading``` 16 | - Go to ```Tools``` 17 | - Click on ```Allow algorithmic trading``` 18 | - Add ```Localhost``` for Client 19 | - Install MetaTrader 5 python - ```pip install -r requirements.txt``` 20 | - Install python extension in VSCode 21 | - Add url in MT5 ```localhost``` 22 | - Install Anaconda 23 | - Run 24 | 25 | ### Requirements - PIP version is 22.3.1 26 | ```pip install -r requirements.txt``` 27 | 28 | #### How to Setup 29 | 1. Run dataGen.ex5 to Generate the dataset data.csv with the help of dataGen based on the last 10 years by loading the ex5 with MT5 Tester and doing a backtest. 30 | This will generate the data.csv with the last 10 years of correlated. 31 | 2. Run AI.py so the dt_model which is the decision tree pickle file is generated based on the dataset from step 1. More info on Machine Learning in Python https://scikit-learn.org ! 32 | 3. Start the server AI ```python main.py```. 33 | 34 | ![Capture2](https://user-images.githubusercontent.com/118682909/219991837-606805c3-1529-4fc8-b040-47a2b1a1bbaf.PNG) 35 | 36 | 4. Start on MT5 the client AI_client.ex5 (add the compiled EA to MT5 Chart) that will receive orders based on AI ML using socket on defined port. 37 | 38 | ![Capture1](https://user-images.githubusercontent.com/118682909/219991884-dc71bca7-a9aa-47a2-b84a-6a87decf9281.PNG) 39 | 40 | 5. Done It's running. 41 | 42 | ![Capture3](https://user-images.githubusercontent.com/118682909/220189032-34b18e46-4cf9-4188-be1e-14a5dc030654.PNG) 43 | 44 | #### Orders Candles Intervals 45 | There is a setting for giving intervals between orders. in this case the setting is to be with an interval of 2 candles between orders. 46 | Here is an example of its execution of VAR ```CANDLES_BETWEEN_OPERATIONS = 2``` on ```orders.py``` 47 | 48 | ![image](https://user-images.githubusercontent.com/118682909/220192133-40298b64-57b0-4ed3-82fa-296ad60dacb1.png) 49 | 50 | #### TP and SL 51 | Both TP and SL are configurable and currently are set at 100 as follows 52 | 53 | ![image](https://user-images.githubusercontent.com/118682909/220193360-977a8165-b238-421d-b3db-e632625a9f0d.png) 54 | 55 | This feature is editable on orders.py as follows: 56 | ``` 57 | "sl": price - 100 * point, 58 | "tp": price + 100 * point, 59 | ``` 60 | -------------------------------------------------------------------------------- /bot/.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.pkl 3 | __pycache__/ -------------------------------------------------------------------------------- /bot/AI_client.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| AI_client.mq5 | 3 | //| Copyright 2023, Algorithmic, GMBH | 4 | //| https://www.algorithmic.one | 5 | //+------------------------------------------------------------------+ 6 | #property copyright "Copyright 2023, Algorithmic, GMBH" 7 | #property link "https://www.algorithmic.one" 8 | #property version "1.00" 9 | 10 | #define MAX_BUFF_LEN 2048 11 | #define TIMEOUT 10000 12 | 13 | /* Input parameters */ 14 | input int PORT = 8680; // Connection Port to the Server 15 | input string ADDR = "localhost"; // IP Address to connect to 16 | input int num_data_to_save = 10; // Amount of Data to save 17 | 18 | /* To check whether there is an error or not */ 19 | bool error = false; 20 | 21 | /* Socket variables */ 22 | int socket; // Socket handle 23 | 24 | /* EMA variables */ 25 | int ema_h; // EMA handle 26 | double ema[]; // EMA array 27 | 28 | /* RSI variables */ 29 | int rsi_h; // RSI handle 30 | double rsi[]; // RSI array 31 | 32 | /* MACD variables */ 33 | int macd_h; // MACD handle 34 | double macd[]; // MACD array 35 | double signal[]; // SIGNAL array 36 | 37 | /* Velas */ 38 | MqlRates candles[]; // Velas 39 | 40 | /* Operation */ 41 | enum op_types // constant enumeration 42 | { SELL, BUY }; 43 | 44 | op_types last_operation; 45 | 46 | /* To check if a message has been sent */ 47 | bool sent = false; 48 | 49 | // Function to know if there is a BUY cross 50 | bool buy_cross() { return signal[1] > macd[1] && signal[0] < macd[0]; } 51 | 52 | // Function to know if there is a SELL cross 53 | bool sell_cross() { return signal[1] < macd[1] && signal[0] > macd[0]; } 54 | 55 | void OnInit() { 56 | 57 | // Inicializando rsi, macd y ema 58 | rsi_h = iRSI(_Symbol, _Period, 14, PRICE_CLOSE); 59 | if (rsi_h == INVALID_HANDLE) Print("Error - 3.1: iRSI failure. ", GetLastError()); 60 | 61 | macd_h = iMACD(_Symbol, _Period, 12, 26, 9, PRICE_CLOSE); 62 | if (macd_h == INVALID_HANDLE) Print("Error - 3.2: iMACD failure. ", GetLastError()); 63 | 64 | ema_h = iMA(_Symbol, _Period, 200, 0, MODE_EMA, PRICE_CLOSE); 65 | if (ema_h == INVALID_HANDLE) Print("Error - 3.3: iMA failure. ", GetLastError()); 66 | 67 | if (rsi_h == INVALID_HANDLE || macd_h == INVALID_HANDLE || ema_h == INVALID_HANDLE) { 68 | error = true; 69 | return; 70 | } 71 | 72 | ArraySetAsSeries(rsi, true); 73 | ArraySetAsSeries(macd, true); 74 | ArraySetAsSeries(signal, true); 75 | ArraySetAsSeries(ema, true); 76 | ArraySetAsSeries(candles, true); 77 | 78 | // Initializing the socket 79 | socket = SocketCreate(); 80 | if (socket == INVALID_HANDLE) { 81 | Print("Error - 1: SocketCreate failure. ", GetLastError()); 82 | error = true; 83 | } else { 84 | if (SocketConnect(socket, ADDR, PORT, TIMEOUT)) Print("[INFO]\tConnection stablished"); 85 | else Print("Error - 2: SocketConnect failure. ", GetLastError()); 86 | } 87 | } 88 | 89 | 90 | void OnDeinit(const int reason) { 91 | 92 | if (error) return; 93 | 94 | /* Closing the socket */ 95 | // Creating the message 96 | char req[]; 97 | 98 | Print("[INFO]\tClosing the socket."); 99 | 100 | int len = StringToCharArray("END CONNECTION\0", req)-1; 101 | SocketSend(socket, req, len); 102 | SocketClose(socket); 103 | } 104 | 105 | 106 | void OnTick() { 107 | 108 | if (error) return; 109 | 110 | // Loading the candles, rsi and std values 111 | CopyBuffer(rsi_h, 0, 0, num_data_to_save, rsi); 112 | CopyBuffer(ema_h, 0, 0, num_data_to_save, ema); 113 | CopyBuffer(macd_h, MAIN_LINE, 0, num_data_to_save, macd); 114 | CopyBuffer(macd_h, SIGNAL_LINE, 0, num_data_to_save, signal); 115 | CopyRates(_Symbol, _Period, 0, num_data_to_save, candles); 116 | 117 | // If the previous one is cross we are going to save data in the file 118 | if ((buy_cross() || sell_cross()) && !sent) { 119 | 120 | string data = ""; 121 | 122 | // Saving the EMA 123 | string ema_data = ""; 124 | for(int i = 0; i < num_data_to_save; i++) { 125 | double ema_normalized = NormalizeDouble(ema[i], _Digits); 126 | ema_data += DoubleToString(ema_normalized)+","; 127 | } 128 | 129 | // Saving the RSI 130 | string rsi_data = ""; 131 | for(int i = 0; i < num_data_to_save; i++) { 132 | double rsi_normalized = NormalizeDouble(rsi[i], _Digits); 133 | rsi_data += DoubleToString(rsi_normalized)+","; 134 | } 135 | 136 | // Saving the MACD 137 | string macd_data = ""; 138 | for(int i = 0; i < num_data_to_save; i++) { 139 | double macd_normalized = NormalizeDouble(macd[i], _Digits); 140 | macd_data += DoubleToString(macd_normalized)+","; 141 | } 142 | 143 | // Saving the SIGNAL 144 | string signal_data = ""; 145 | for(int i = 0; i < num_data_to_save; i++) { 146 | double signal_normalized = NormalizeDouble(signal[i], _Digits); 147 | signal_data += DoubleToString(signal_normalized)+","; 148 | } 149 | 150 | // Is the price above or below the EMA? 1 (above) 0 (below) 151 | string above_below = candles[0].close >= ema[0] ? "1," : "0,"; 152 | 153 | // Saving the type of the cross 154 | string cross_type = ""; 155 | if (buy_cross()) {cross_type = "1,"; last_operation = BUY;} 156 | else if (sell_cross()) {cross_type = "0,"; last_operation = SELL;} 157 | 158 | data = ema_data+rsi_data+macd_data+signal_data+above_below+cross_type; 159 | 160 | // Sending data 161 | Print("[INFO]\tSending RSI and STD deviation"); 162 | 163 | char req[]; 164 | int len = StringToCharArray(data, req)-1; 165 | SocketSend(socket, req, len); 166 | 167 | sent = true; 168 | 169 | EventSetTimer(PeriodSeconds()); 170 | } 171 | } 172 | 173 | void OnTimer() { 174 | sent = false; 175 | EventKillTimer(); 176 | } -------------------------------------------------------------------------------- /bot/Data.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | PORT = 8680 4 | ADDR = "localhost" 5 | 6 | def socket_ini(): 7 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | 9 | server_socket.bind((ADDR, PORT)) 10 | server_socket.listen(10) 11 | 12 | connection, addr = server_socket.accept() 13 | print("[INFO]\t", addr, "CONNECTED") 14 | 15 | return connection, server_socket 16 | 17 | 18 | def thread_data(stop_event, data): 19 | msg = "" 20 | 21 | connection, server_socket = socket_ini() 22 | 23 | while not stop_event.is_set() and "END CONNECTION" not in msg: 24 | msg = connection.recv(1024).decode() 25 | 26 | if "END CONNECTION" in msg: 27 | break 28 | 29 | msg_splitted = msg.split(',') 30 | 31 | msg_splitted = [ float(elem) for elem in msg_splitted if elem ] 32 | data['data'] = msg_splitted 33 | data['macd'] = msg_splitted[20:29] # Relates to Amount of Data to save INT on AI_client.mq5 34 | data['signal'] = msg_splitted[30:39] # Relates to Amount of Data to save INT on AI_client.mq5 35 | # Debug on Server Side on Data, EMA, MACD, SIGNAL Being Analysed by SKL DL AI.ML 36 | print(data) 37 | 38 | connection.close() 39 | server_socket.close() 40 | -------------------------------------------------------------------------------- /bot/bot.py: -------------------------------------------------------------------------------- 1 | import MetaTrader5 as mt5 2 | import threading 3 | import Data, orders 4 | 5 | class Bot: 6 | 7 | def __init__(self, lotage: float, time_period: int, market: str): 8 | """Constructor of the bot. It justs fills the needed informartion for the bot. 9 | 10 | Args: 11 | lotage (float): Lotage to be used by the bot. 12 | time_period (int): Time period of the bot, 1 minute, 15 minutes... (in seconds) 13 | market (str): Market to operate in. 14 | """ 15 | self.threads = [] 16 | self.data = {'data': None, 'macd': None, 'signal': None} 17 | self.pill2kill = threading.Event() 18 | self.trading_data = {} 19 | self.trading_data['lotage'] = lotage 20 | self.trading_data['time_period'] = time_period 21 | self.trading_data['market'] = market 22 | 23 | def thread_data(self): 24 | """Function to launch the data thread.""" 25 | t = threading.Thread(target=Data.thread_data, 26 | args=(self.pill2kill, self.data)) 27 | self.threads.append(t) 28 | t.start() 29 | print('Thread - DATA. LAUNCHED') 30 | 31 | def thread_orders_AI(self): 32 | """Function to launch the thread for sending orders.""" 33 | t = threading.Thread(target=orders.thread_orders_AI, 34 | args=(self.pill2kill, self.data, self.trading_data)) 35 | self.threads.append(t) 36 | t.start() 37 | print('Thread AI - ORDERS. LAUNCHED') 38 | 39 | def kill_threads(self): 40 | """Function to kill all the loaded threads.""" 41 | print('Threads - Stopping threads') 42 | self.pill2kill.set() 43 | for thread in self.threads: 44 | thread.join() 45 | 46 | def start(self): 47 | """Function to start all the threads""" 48 | self.thread_data() 49 | self.thread_orders_AI() 50 | 51 | def wait(self): 52 | """Function to make the thread wait.""" 53 | # Input to stop the Server 54 | print('\nPress ENTER to stop the bot\n') 55 | input() 56 | self.kill_threads() 57 | mt5.shutdown() -------------------------------------------------------------------------------- /bot/dt_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tHeStRyNg/ai_mt5_python/10a48af11b1a09a365f215ee66f09bc81c2703f4/bot/dt_model.pkl -------------------------------------------------------------------------------- /bot/main.py: -------------------------------------------------------------------------------- 1 | from bot import Bot 2 | import MetaTrader5 as mt5 3 | 4 | 5 | mt5.initialize() 6 | 7 | ai_bot = Bot(1.00, 60*15, "EURUSD") 8 | 9 | ai_bot.start() 10 | ai_bot.wait() -------------------------------------------------------------------------------- /bot/orders.py: -------------------------------------------------------------------------------- 1 | import MetaTrader5 as mt5 2 | import datetime, pickle 3 | 4 | 5 | AI_FILE = 'dt_model.pkl' 6 | CANDLES_BETWEEN_OPERATIONS = 0 7 | 8 | 9 | def close_position(market: str, lotage: float, result): 10 | """Function to close a position. 11 | 12 | Args: 13 | market (string) 14 | lotage (float) 15 | result: Result of the previous operation 16 | 17 | return: 18 | result after closing the position. 19 | """ 20 | deal = mt5.history_deals_get(ticket=result.deal)[0] 21 | position = mt5.positions_get(ticket=deal.position_id)[0] 22 | close_order = { 23 | 'action': mt5.TRADE_ACTION_DEAL, 24 | 'type': mt5.ORDER_TYPE_BUY, 25 | 'price': mt5.symbol_info_tick(market).ask, 26 | 'symbol': position.symbol, 27 | 'volume': position.volume, 28 | 'position': position.ticket, 29 | } 30 | result = mt5.order_send(close_order) 31 | 32 | return result 33 | 34 | 35 | def open_position(market: str, lotage: float, type): 36 | """Function to open a position. 37 | 38 | Args: 39 | market (string) 40 | lotage (float) 41 | """ 42 | point = mt5.symbol_info(market).point 43 | price = mt5.symbol_info_tick(market).bid 44 | 45 | deviation = 20 46 | operation = { 47 | "action": mt5.TRADE_ACTION_DEAL, 48 | "symbol": market, 49 | "volume": lotage, 50 | "type": type, 51 | "price": price, 52 | "sl": price - 100 * point, 53 | "tp": price + 100 * point, 54 | "deviation": deviation, 55 | "magic": 234000, 56 | "comment": "AI.ML.Py.SKL.DL", 57 | "type_time": mt5.ORDER_TIME_GTC, 58 | "type_filling": mt5.ORDER_FILLING_FOK, 59 | } 60 | 61 | # Sending the buy 62 | result = mt5.order_send(operation) 63 | print("[Thread - orders] 1. order_send(): by {} {} lots at {} with deviation={} points".format(market,lotage,price,deviation)) 64 | if result.retcode != mt5.TRADE_RETCODE_DONE: 65 | print("[Thread - orders] Failed operation: retcode={}".format(result.retcode)) 66 | return None 67 | 68 | return result 69 | 70 | def thread_orders_AI(stop_event, data, trading_data): 71 | """Function executed by a thread. It checks if the conditions to open orders 72 | are okay. 73 | 74 | Args: 75 | stop_event (thread.Event): Event to stop the thread. 76 | data (dict): Dictionary with candles and the indicator. 77 | trading_data (dict): Dictionary with the lotage and the market. 78 | """ 79 | last_operation_time = 0 80 | ep = datetime.datetime(1970,1,1,0,0,0) 81 | 82 | operation_open = False 83 | result = None 84 | 85 | # Loading the Neural network 86 | with open("dt_model.pkl", "rb") as f: 87 | dt = pickle.load(f) 88 | 89 | # Waiting for the data to be loaded 90 | while data['data'] is None: 91 | if stop_event.is_set(): 92 | return 93 | 94 | print("[INFO]\tORDERS RUNNING") 95 | 96 | while not stop_event.is_set(): 97 | cur_time = int((datetime.datetime.utcnow()- ep).total_seconds()) 98 | 99 | if dt.predict([data['data']])[0] >= 0.5 and not operation_open \ 100 | and data['signal'][1] < data['macd'][1] and data['signal'][0] > data['macd'][0]: # Open buy 101 | last_operation_time = cur_time 102 | result = open_position(trading_data["market"], trading_data["lotage"], mt5.ORDER_TYPE_BUY) 103 | operation_open = True 104 | 105 | if dt.predict([data['data']])[0] >= 0.5 and not operation_open \ 106 | and data['signal'][1] > data['macd'][1] and data['signal'][0] < data['macd'][0]: # Open sell 107 | last_operation_time = cur_time 108 | result = open_position(trading_data["market"], trading_data["lotage"], mt5.ORDER_TYPE_SELL) 109 | operation_open = True 110 | 111 | if operation_open and cur_time > last_operation_time+trading_data["time_period"]*CANDLES_BETWEEN_OPERATIONS: 112 | operation_open = False 113 | close_position(trading_data["market"], trading_data["lotage"], result) 114 | 115 | -------------------------------------------------------------------------------- /bot/test_order.py: -------------------------------------------------------------------------------- 1 | import MetaTrader5 as mt5 2 | 3 | 4 | print("MetaTrader5 package author: ", mt5.__author__) 5 | print("MetaTrader5 package version: ", mt5.__version__) 6 | 7 | if not mt5.initialize(): 8 | print("initialize() failed, error code =",mt5.last_error()) 9 | quit() 10 | 11 | symbol = "EURUSD" 12 | symbol_info = mt5.symbol_info(symbol) 13 | if symbol_info is None: 14 | print(symbol, "not found, can not call order_check()") 15 | mt5.shutdown() 16 | quit() 17 | 18 | if not symbol_info.visible: 19 | print(symbol, "is not visible, trying to switch on") 20 | if not mt5.symbol_select(symbol,True): 21 | print("symbol_select({}}) failed, exit",symbol) 22 | mt5.shutdown() 23 | quit() 24 | 25 | lot = 0.1 26 | point = mt5.symbol_info(symbol).point 27 | price = mt5.symbol_info_tick(symbol).ask 28 | deviation = 20 29 | request = { 30 | "action": mt5.TRADE_ACTION_DEAL, 31 | "symbol": symbol, 32 | "volume": lot, 33 | "type": mt5.ORDER_TYPE_BUY, 34 | "price": price, 35 | "sl": price - 100 * point, 36 | "tp": price + 100 * point, 37 | "deviation": deviation, 38 | "magic": 234000, 39 | "comment": "python script open", 40 | "type_time": mt5.ORDER_TIME_GTC, 41 | "type_filling": mt5.ORDER_FILLING_FOK, 42 | } 43 | 44 | result = mt5.order_send(request) 45 | print("1. order_send(): by {} {} lots at {} with deviation={} points".format(symbol,lot,price,deviation)); 46 | if result.retcode != mt5.TRADE_RETCODE_DONE: 47 | print("2. order_send failed, retcode={}".format(result.retcode)) 48 | result_dict=result._asdict() 49 | for field in result_dict.keys(): 50 | print(" {}={}".format(field,result_dict[field])) 51 | if field=="request": 52 | traderequest_dict=result_dict[field]._asdict() 53 | for tradereq_filed in traderequest_dict: 54 | print(" traderequest: {}={}".format(tradereq_filed,traderequest_dict[tradereq_filed])) 55 | print("shutdown() and quit") 56 | mt5.shutdown() 57 | quit() -------------------------------------------------------------------------------- /data.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tHeStRyNg/ai_mt5_python/10a48af11b1a09a365f215ee66f09bc81c2703f4/data.csv -------------------------------------------------------------------------------- /dataGen.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| dataGen.mq5 | 3 | //| Copyright 2023, Algorithmic, GMBH | 4 | //| https://www.algorithmic.one | 5 | //+------------------------------------------------------------------+ 6 | #property copyright "Copyright 2023, Algorithmic, GMBH" 7 | #property link "https://www.algorithmic.one" 8 | #property version "1.00" 9 | 10 | // These are the Input parameters for the file data.csv to be created 11 | 12 | // Input parameters for the file 13 | // Parametros de entrada para un archivo 14 | input string nombre_archivo = "data.csv"; // name of the Achive to be generated 15 | input int num_data_to_save = 10; // Amount of data to store 16 | input int candles_to_close_op = 2; // Number of candles before we closes the operation 17 | 18 | // File handler 19 | int fp = 0; 20 | 21 | // String to write 22 | string data = ""; 23 | 24 | // Array velas 25 | MqlRates candles[]; 26 | 27 | // EMA handler and array 28 | int ema_h; 29 | double ema[]; 30 | 31 | // RSI handler and array 32 | int rsi_h; 33 | double rsi[]; 34 | 35 | // MACD handler and array (SIGNAL as well) 36 | int macd_h; 37 | double macd[]; 38 | double signal[]; 39 | 40 | // To check if the operation is successfully 41 | bool op_abierta = false; 42 | int velas = 0; 43 | double price_open = 0; 44 | enum op_types // constant enumeration 45 | { SELL, BUY }; 46 | 47 | op_types last_operation; 48 | 49 | // Function to know if there is a BUY cross 50 | bool buy_cross() { return signal[1] > macd[1] && signal[0] < macd[0]; } 51 | 52 | // Function to know if there is a SELL cross 53 | bool sell_cross() { return signal[1] < macd[1] && signal[0] > macd[0]; } 54 | 55 | void OnInit() { 56 | // Openning the file 57 | fp = FileOpen(nombre_archivo, FILE_WRITE, 0, CP_ACP); 58 | 59 | // Writing the header of the file 60 | string file_header = ""; 61 | for(int i = 0; i < num_data_to_save; i++) 62 | file_header += "EMA_"+IntegerToString(i)+","; 63 | 64 | for(int i = 0; i < num_data_to_save; i++) 65 | file_header += "RSI_"+IntegerToString(i)+","; 66 | 67 | for(int i = 0; i < num_data_to_save; i++) 68 | file_header += "MACD_"+IntegerToString(i)+","; 69 | 70 | for(int i = 0; i < num_data_to_save; i++) 71 | file_header += "SIGNAL_"+IntegerToString(i)+","; 72 | 73 | file_header += "EMA_ABOVE_BELOW,CROSS_TYPE,"; 74 | 75 | file_header += "class"; 76 | FileWrite(fp, file_header); 77 | 78 | // Handlers 79 | ema_h = iMA(_Symbol, _Period, 200, 0, MODE_EMA, PRICE_CLOSE); 80 | rsi_h = iRSI(_Symbol, _Period, 14, PRICE_CLOSE); 81 | macd_h = iMACD(_Symbol, _Period, 12, 26, 9, PRICE_CLOSE); 82 | 83 | 84 | ArraySetAsSeries(ema, true); 85 | ArraySetAsSeries(rsi, true); 86 | ArraySetAsSeries(macd, true); 87 | ArraySetAsSeries(signal, true); 88 | ArraySetAsSeries(candles, true); 89 | } 90 | 91 | 92 | void OnTick() { 93 | // Loading information 94 | CopyBuffer(ema_h, 0, 0, num_data_to_save, ema); 95 | CopyBuffer(rsi_h, 0, 0, num_data_to_save, rsi); 96 | CopyBuffer(macd_h, MAIN_LINE, 0, num_data_to_save, macd); 97 | CopyBuffer(macd_h, SIGNAL_LINE, 0, num_data_to_save, signal); 98 | CopyRates(_Symbol, _Period, 0, num_data_to_save, candles); 99 | 100 | // If there is a cross we are going to save data in the file 101 | if ((buy_cross() || sell_cross()) && !op_abierta) { 102 | 103 | // Saving the EMA 104 | string ema_data = ""; 105 | for(int i = 0; i < num_data_to_save; i++) { 106 | double ema_normalized = NormalizeDouble(ema[i], _Digits); 107 | ema_data += DoubleToString(ema_normalized)+","; 108 | } 109 | 110 | // Saving the RSI 111 | string rsi_data = ""; 112 | for(int i = 0; i < num_data_to_save; i++) { 113 | double rsi_normalized = NormalizeDouble(rsi[i], _Digits); 114 | rsi_data += DoubleToString(rsi_normalized)+","; 115 | } 116 | 117 | // Saving the MACD 118 | string macd_data = ""; 119 | for(int i = 0; i < num_data_to_save; i++) { 120 | double macd_normalized = NormalizeDouble(macd[i], _Digits); 121 | macd_data += DoubleToString(macd_normalized)+","; 122 | } 123 | 124 | // Saving the SIGNAL 125 | string signal_data = ""; 126 | for(int i = 0; i < num_data_to_save; i++) { 127 | double signal_normalized = NormalizeDouble(signal[i], _Digits); 128 | signal_data += DoubleToString(signal_normalized)+","; 129 | } 130 | 131 | // Is the price above or below the EMA? 1 (above) 0 (below) 132 | string above_below = candles[0].close >= ema[0] ? "1," : "0,"; 133 | 134 | // Saving the type of the cross 135 | string cross_type = ""; 136 | if (buy_cross()) {cross_type = "1,"; last_operation = BUY;} 137 | else if (sell_cross()) {cross_type = "0,"; last_operation = SELL;} 138 | 139 | data = ema_data+rsi_data+macd_data+signal_data+above_below+cross_type; 140 | 141 | op_abierta = true; 142 | velas = Bars(_Symbol, _Period); 143 | 144 | price_open = candles[0].close; 145 | } else if (op_abierta) { 146 | // Checking if we moved enough candles 147 | if (velas+candles_to_close_op <= Bars(_Symbol, _Period)) { 148 | if (last_operation == BUY) { 149 | if (price_open < candles[0].close) FileWrite(fp, data+"1"); 150 | else FileWrite(fp, data+"0"); 151 | } else if (last_operation == SELL) { 152 | if (price_open > candles[0].close) FileWrite(fp, data+"1"); 153 | else FileWrite(fp, data+"0"); 154 | } 155 | 156 | data = ""; 157 | op_abierta = false; 158 | } 159 | } 160 | } 161 | 162 | void OnDeinit(const int reason) { 163 | FileClose(fp); 164 | } -------------------------------------------------------------------------------- /dt_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tHeStRyNg/ai_mt5_python/10a48af11b1a09a365f215ee66f09bc81c2703f4/dt_model.pkl -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | joblib==1.2.0 2 | MetaTrader5==5.0.44 3 | numpy==1.24.2 4 | pandas==1.5.3 5 | python-dateutil==2.8.2 6 | pytz==2022.7.1 7 | scikit-learn==1.2.1 8 | scipy==1.10.1 9 | setuptools==65.5.0 10 | six==1.16.0 11 | sklearn==0.0.post1 12 | threadpoolctl==3.1.0 13 | --------------------------------------------------------------------------------