diff --git a/.gitignore b/.gitignore index 600d2d3..650bf2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -.vscode \ No newline at end of file +.vscode +.venv +__pycache__/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..59ff6ad --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# Use an official Python runtime as a parent image +FROM python:3.10 + +# Set the working directory to C:\app +WORKDIR C:\app + +# Install necessary system packages +# Note: Windows containers don't use apt-get, so we skip this step on Windows + +# Copy the current directory contents into the container at C:\app +COPY . . + +# Install TA-Lib +# COPY app/Tab-Lib-deps/ta-lib-0.4.0-src.tar.gz . +# RUN New-Item -ItemType Directory -Path C:\ta-lib +# RUN tar -zxvf .\ta-lib-0.4.0-src.tar.gz -C C:\ta-lib --strip-components=1 +# RUN Remove-Item .\ta-lib-0.4.0-src.tar.gz -Force + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# Make port 5000 available to the world outside this container +EXPOSE 5000 + +# Define environment variable +ENV NAME trading-bot + +# Run app.py when the container launches +CMD ["flask", "run", "--host=0.0.0.0"] diff --git a/README.md b/README.md old mode 100755 new mode 100644 diff --git a/app/bot.py b/app/bot.py deleted file mode 100644 index 38c0fd5..0000000 --- a/app/bot.py +++ /dev/null @@ -1,338 +0,0 @@ -from keras.optimizers import Adam -from keras.layers import Dense, Dropout -from keras.models import Sequential -import pymt5 -import matplotlib.pyplot as plt -import talib -from sklearn.preprocessing import MinMaxScaler -import numpy as np -import pandas as pd -import time -import os -os.environ["CUDA_VISIBLE_DEVICES"] = "-1" - - -def connect_to_mt5_container(): - server = "localhost" # Change to the appropriate IP or hostname if necessary - port = 15555 # Change to the appropriate port if necessary - login = 123456 # Change to your MetaTrader login number if necessary - password = "your_password" # Change to your MetaTrader password if necessary - - # Connect to MetaTrader 5 - mt5 = pymt5.PyMT5() - mt5.onConnected = onConnected - mt5.onDisconnected = onDisconnected - mt5.onData = onData - - # Wait for the connection to be established - while not onConnected: - time.sleep(0.1) - - # Send login request - login_request = { - 'ver': '3', - 'type': '1', - 'login': str(login), - 'password': password, - 'res': '0' - } - mt5.broadcast(login_request) - - # Wait for the login response - while not onConnected: - time.sleep(0.1) - - # Check if login was successful - if onConnected: - print(f"Connected to MetaTrader 5: {onConnected}") - else: - print("Failed to connect to MetaTrader 5") - - -def onConnected(client_info): - print(f"Connected: {client_info}") - - -def onDisconnected(client_info): - print(f"Disconnected: {client_info}") - - -def onData(data): - print(f"Received data: {data}") - - -def start_mt5_bot(): - # Define the symbols and timeframes - symbol = 'EURUSD' - timeframe = 60 # H1 timeframe (1 hour) - - # Set up initial variables - lot_size = 0.01 - stop_loss = 100 - take_profit = 150 - - # Define TensorFlow neural network model - def create_neural_network_model(input_shape): - model = Sequential() - model.add(Dense(64, activation='relu', input_shape=input_shape)) - model.add(Dropout(0.2)) - model.add(Dense(64, activation='relu')) - model.add(Dropout(0.2)) - model.add(Dense(1, activation='sigmoid')) - return model - - # Define input shape for the neural network - # Adjust the input shape based on your features and data - input_shape = (10,) - - # Create the neural network model - neural_network_model = create_neural_network_model(input_shape) - - # Compile the model - neural_network_model.compile(optimizer=Adam( - learning_rate=0.001), loss='binary_crossentropy') - - def get_historical_data(): - # Retrieve historical data - rates = pymt5.copy_rates_from_pos(symbol, timeframe, 0, 1000) - df = pd.DataFrame(rates) - df['time'] = pd.to_datetime(df['time'], unit='s') - df.set_index('time', inplace=True) - return df - - def calculate_indicators_and_detect_patterns(df): - # Calculate RSI - rsi_period = 14 - df['rsi'] = talib.RSI(df['close'], rsi_period) - - # Calculate MACD - macd_fast_period = 12 - macd_slow_period = 26 - macd_signal_period = 9 - df['macd'], _, df['macd_signal'] = talib.MACD(df['close'], fastperiod=macd_fast_period, - slowperiod=macd_slow_period, signalperiod=macd_signal_period) - - # Detect divergence based on RSI and MACD - df['rsi_divergence'] = np.where( - df['rsi'].diff().shift(-1) * df['macd'].diff().shift(-1) < 0, True, False) - df['macd_divergence'] = np.where( - df['macd'].diff().shift(-1) * df['rsi'].diff().shift(-1) < 0, True, False) - - # Detect support and resistance levels - window = 10 - df['support'] = df['low'].rolling(window).min() - df['resistance'] = df['high'].rolling(window).max() - - # Determine trend direction - df['trend_200'] = df['close'].rolling(window=200).mean() - df['trend_50'] = df['close'].rolling(window=50).mean() - - # Detect double tops and bottoms - df['pattern'] = 'None' - df['top_pattern'] = np.where( - (df['high'].shift(1) < df['high']) & (df['high'].shift(-1) < df['high']) & - (df['high'].shift(2) > df['high']) & ( - df['high'].shift(-2) > df['high']), 'Double Top', 'None' - ) - df.loc[df['top_pattern'] != 'None', 'pattern'] = df['top_pattern'] - df['bottom_pattern'] = np.where( - (df['low'].shift(1) > df['low']) & (df['low'].shift(-1) > df['low']) & - (df['low'].shift(2) < df['low']) & ( - df['low'].shift(-2) < df['low']), 'Double Bottom', 'None' - ) - df.loc[df['bottom_pattern'] != 'None', - 'pattern'] = df['bottom_pattern'] - - return df - - def generate_signals(df): - # Determine trade signals based on divergences, patterns, and trend direction - df['signal'] = 'None' - df['divergence_signal'] = np.where((df['rsi_divergence'] == True) & (df['pattern'] != 'None'), 'Both', - np.where(df['rsi_divergence'] == True, 'RSI', 'Pattern')) - df['strongest_divergence_signal'] = df[[ - 'divergence_signal', 'macd_divergence']].max(axis=1) - df['support_resistance_signal'] = np.where(df['close'] > df['resistance'], 'Resistance', - np.where(df['close'] < df['support'], 'Support', 'None')) - df['trend_signal'] = np.where(df['close'] > df['trend_200'], 'Uptrend', - np.where(df['close'] < df['trend_200'], 'Downtrend', 'None')) - for i in range(1, len(df)): - prev_divergence_signal = df['divergence_signal'].iloc[i - 1] - curr_divergence_signal = df['divergence_signal'].iloc[i] - strongest_divergence_signal = df['strongest_divergence_signal'].iloc[i] - support_resistance_signal = df['support_resistance_signal'].iloc[i] - trend_signal = df['trend_signal'].iloc[i] - - if strongest_divergence_signal != 'None': - df['signal'].iloc[i] = strongest_divergence_signal - elif prev_divergence_signal == curr_divergence_signal and curr_divergence_signal != 'None': - df['signal'].iloc[i] = curr_divergence_signal - else: - df['signal'].iloc[i] = support_resistance_signal - - if trend_signal != 'None' and df['signal'].iloc[i] != 'None': - df['signal'].iloc[i] = trend_signal - - return df - - def execute_trade(signal, df): - # Implement risk management and trade execution logic based on the signals generated - # Update TensorFlow neural network model with trade outcome (loss or win) - - # Calculate risk and position size based on lot size, stop loss, and take profit - risk = lot_size * stop_loss - strongest_divergence_signal = df['strongest_divergence_signal'].iloc[-1] - if strongest_divergence_signal == 'RSI': - risk *= 1.2 # Increase risk by 20% if RSI divergence is the strongest - elif strongest_divergence_signal == 'Pattern': - risk *= 1.5 # Increase risk by 50% if pattern divergence is the strongest - - position_size = risk / (take_profit - stop_loss) - - try: - if signal == 'Buy': - # Place a buy trade - result = pymt5.order_send(symbol, pymt5.OP_BUY, lot_size, 0, stop_loss, take_profit, - "Buy trade", 123456, pymt5.ORDER_TIME_GTC, 0) - outcome = 'Win' if result.retcode == pymt5.TRADE_RETCODE_DONE else 'Loss' - - elif signal == 'Sell': - # Place a sell trade - result = pymt5.order_send(symbol, pymt5.OP_SELL, lot_size, 0, stop_loss, take_profit, - "Sell trade", 123456, pymt5.ORDER_TIME_GTC, 0) - outcome = 'Win' if result.retcode == pymt5.TRADE_RETCODE_DONE else 'Loss' - - # Example trade outcome information - trade_outcome = { - 'pattern': df['pattern'].iloc[-1], - 'divergence_strength': strongest_divergence_signal, - 'time': df.index[-1], - 'trend_direction': df['trend_signal'].iloc[-1], - 'indicator_used': strongest_divergence_signal, - 'outcome': outcome - } - - # Update TensorFlow neural network model with trade outcome - update_neural_network_model(trade_outcome) - - # Example print statements for debugging - print( - f"Executed {signal} trade with position size: {position_size}") - print(f"Trade outcome: {trade_outcome}") - - # Additional logic for trade management, monitoring, etc. - except Exception as e: - print(f"Error executing trade: {str(e)}") - - def update_neural_network_model(trade_outcome): - # Implement code to update the neural network model based on trade outcome - pattern = trade_outcome['pattern'] - divergence_strength = trade_outcome['divergence_strength'] - time = trade_outcome['time'] - trend_direction = trade_outcome['trend_direction'] - indicator_used = trade_outcome['indicator_used'] - outcome = trade_outcome['outcome'] - - # Example update code: Append trade outcome information to a dataset for future training - trade_data = pd.DataFrame({ - 'pattern': [pattern], - 'divergence_strength': [divergence_strength], - 'time': [time], - 'trend_direction': [trend_direction], - 'indicator_used': [indicator_used], - 'outcome': [outcome] - }) - # Append the trade data to the dataset for future training - dataset = pd.read_csv('trade_dataset.csv') # Load existing dataset - updated_dataset = pd.concat([dataset, trade_data], ignore_index=True) - # Save updated dataset - updated_dataset.to_csv('trade_dataset.csv', index=False) - - # Example retraining code: Retrain the neural network model with the updated dataset - # Preprocess data as per your requirements - X_train, y_train = preprocess_data(updated_dataset) - # Example retraining step - neural_network_model.fit(X_train, y_train, epochs=10, batch_size=32) - - # Save the updated model weights - neural_network_model.save_weights('weights/model_weights.h5') - - def preprocess_data(dataset): - # Define the numerical features (if any) - numerical_features = [] # Update with the actual numerical feature column names - - # Define the input features - input_features = dataset[[ - 'pattern', 'divergence_strength', 'trend_direction', 'indicator_used']] - - # Convert categorical features to one-hot encoding - input_features = pd.get_dummies(input_features) - - # Normalize numerical features (if any) - if numerical_features: - scaler = MinMaxScaler() - input_features[numerical_features] = scaler.fit_transform( - input_features[numerical_features]) - - # Extract target labels from the dataset - target_labels = dataset['outcome'] - - # Convert target labels to numerical representation (0s and 1s) - target_labels = target_labels.map({'Loss': 0, 'Win': 1}) - - # Return the preprocessed input features and target labels - return input_features, target_labels - - def visualize_data(df): - plt.figure(figsize=(10, 6)) - plt.plot(df.index, df['close'], label='Close') - # Add visualizations for other indicators, levels, and patterns - plt.scatter(df[df['pattern'] == 'Double Top'].index, df[df['pattern'] == 'Double Top']['high'], - color='red', marker='v', label='Double Top') - plt.scatter(df[df['pattern'] == 'Double Bottom'].index, df[df['pattern'] == 'Double Bottom']['low'], - color='green', marker='^', label='Double Bottom') - plt.legend() - plt.show() - - def run_trading_bot(): - # Connect to MetaTrader 5 container - connect_to_mt5_container() - - while True: - try: - # Get historical data - df = get_historical_data() - - # Calculate indicators and detect patterns - df = calculate_indicators_and_detect_patterns(df) - - # Generate trade signals - df = generate_signals(df) - - # Execute trades - for i in range(1, len(df)): - signal = df['signal'].iloc[i] - if signal != 'None': - execute_trade(signal, df) - - # Visualize data - visualize_data(df) - - except Exception as e: - print(f"Error running trading bot: {str(e)}") - - # Wait for the next iteration - time.sleep(60) # Adjust the time interval as needed - - # Run the trading bot - run_trading_bot() - - # Load TensorFlow neural network model weights - neural_network_model.load_weights('weights/model_weights.h5') - - # Disconnect from MetaTrader 5 - pymt5.shutdown() - - -# Start the MetaTrader 5 bot -start_mt5_bot() diff --git a/bridge/mt5_bridge.py b/bridge/mt5_bridge.py deleted file mode 100644 index b5cc6d6..0000000 --- a/bridge/mt5_bridge.py +++ /dev/null @@ -1,30 +0,0 @@ -import socketio -import time - -# Connect to the trading bot container -sio = socketio.Client() -# Replace with the appropriate URL and port of your trading bot container -sio.connect('http://trading_bot:3000') - -# Handle events from the trading bot container - - -@sio.event -def connect(): - print('Connected to trading bot container') - - -@sio.event -def disconnect(): - print('Disconnected from trading bot container') - - -@sio.event -def send_trade_signal(signal): - print(f'Received trade signal: {signal}') - # Process the trade signal and execute trades through MetaTrader 5 - - -# Main loop to keep the script running -while True: - time.sleep(1) diff --git a/docker-compose.yml b/docker-compose.yml index 1c37341..64b1429 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,55 +1,17 @@ -version: "3" +version: '3' services: - metatrader_service: + trading-bot: build: context: . - dockerfile: docker/DockerFile.xorg - container_name: metatrader - restart: unless-stopped + dockerfile: Dockerfile + ports: + - "5000:5000" + volumes: + - ./src:/app/src + - ./static:/app/static + - ./templates:/app/templates + - ./model_weights.h5:/app/model_weights.h5 environment: - - DISPLAY=$DISPLAY - privileged: true - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - - ./mt5:/mt5 - devices: - - /dev/dri:/dev/dri - ports: - - "5900:5900" - - "15555:15555" - - "15556:15556" - - "15557:15557" - - "15558:15558" - networks: - - trading_network - - trading_bot: - container_name: trading_bot - build: - context: . - dockerfile: docker/DockerFile - volumes: - - ./app:/app - ports: - - 3000:3000 - depends_on: - - metatrader_service - networks: - - trading_network - - mt5_bridge: - container_name: mt5_bridge - build: - context: . - dockerfile: docker/DockerFile.mt5_bridge - volumes: - - ./bridge:/bridge - depends_on: - - metatrader_service - - trading_bot - networks: - - trading_network - -networks: - trading_network: - driver: bridge + - FLASK_APP=main.py + - FLASK_RUN_HOST=0.0.0.0 + - PYTHONUNBUFFERED=1 diff --git a/docker/DockerFile b/docker/DockerFile deleted file mode 100644 index 0a45a37..0000000 --- a/docker/DockerFile +++ /dev/null @@ -1,29 +0,0 @@ -# Use an official Python runtime as the base image -FROM python:3.10 - -# Set the working directory in the container -WORKDIR /app - -# Copy the requirements file to the working directory -COPY app/requirements.txt . - -# Copy the Tab-Lib dependencies to the working directory -COPY app/Tab-Lib-deps/ta-lib-0.4.0-src.tar.gz . - -# Extract and install Tab-Lib -RUN tar -xzf ta-lib-0.4.0-src.tar.gz && \ - rm ta-lib-0.4.0-src.tar.gz && \ - cd ta-lib && \ - ./configure --prefix=/usr && \ - make && \ - make install && \ - cd .. - -# Install the Python dependencies -RUN pip install --no-cache-dir -r requirements.txt - -# Copy the application code to the container -COPY app/ . - -# Run the bot script when the container launches -CMD [ "python", "bot.py" ] diff --git a/docker/DockerFile.mt5_bridge b/docker/DockerFile.mt5_bridge deleted file mode 100644 index be3db68..0000000 --- a/docker/DockerFile.mt5_bridge +++ /dev/null @@ -1,12 +0,0 @@ -FROM python:3.10 - -# Set the working directory in the container -WORKDIR /app - -# Copy the bridge script to the container -COPY bridge/mt5_bridge.py . - -# Install any dependencies required by the bridge script -RUN pip install python-socketio python-engineio requests - -CMD ["python", "mt5_bridge.py"] diff --git a/docker/DockerFile.xorg b/docker/DockerFile.xorg deleted file mode 100644 index 99e133f..0000000 --- a/docker/DockerFile.xorg +++ /dev/null @@ -1,39 +0,0 @@ -# Base docker image. -FROM ubuntu:focal - -# Install Wine and necessary dependencies -RUN dpkg --add-architecture i386 && \ - apt-get update && \ - apt-get install -y --no-install-recommends \ - ca-certificates \ - gnupg \ - software-properties-common \ - wget \ - winbind \ - xauth \ - xvfb \ - cabextract - -# Download and install Wine from WineHQ repository -RUN wget -qO- https://dl.winehq.org/wine-builds/winehq.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/winehq.gpg && \ - add-apt-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ focal main' && \ - apt-get update && \ - apt-get install -y --install-recommends winehq-stable winetricks - -# Create a non-root user -RUN useradd -m -s /bin/bash trader - -# Set the working directory -WORKDIR /home/trader - -# Install X server utilities -RUN apt-get install -y x11-xserver-utils x11vnc xvfb - -# Configure X server -RUN mkdir /tmp/.X11-unix && \ - chown trader:trader /tmp/.X11-unix - -# Set up entrypoint script -COPY mt5/entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] diff --git a/main.py b/main.py new file mode 100644 index 0000000..bf2fe27 --- /dev/null +++ b/main.py @@ -0,0 +1,124 @@ +""" +Main script for running the trading bot with a web interface. + +This script initializes the MetaTrader 5 connection, runs the trading bot, and integrates with a web interface for user credentials. + +Author: Mike Kiwalabye +""" + +import time +from flask import Flask, render_template, request, redirect, globals +from src.connectors import mt5_connector +from src.models import neural_network_model +from src.strategies.trading_strategy import get_historical_data, calculate_indicators_and_detect_patterns, generate_trade_signals, execute_trade +from src.utils.visualization import plot_trade_signals + +app = Flask(__name__) + +# Define input shape for the neural network +input_shape = (10,) # Adjust the input shape based on your features and data + +# Create the neural network model +neural_network_model = neural_network_model.create_neural_network_model(input_shape) + +# Global state to track whether MT5 is initialized +globals.mt5_initialized = False + +# Web Interface Routes + +@app.route('/') +def index(): + """Render the main page with login form.""" + return render_template('index.html') + +@app.route('/login', methods=['POST']) +def login(): + """ + Handle the login form submission. + + If the credentials are valid, start the trading bot with the provided credentials. + + Returns: + - str: HTML response. + """ + if request.method == 'POST': + credentials = { + 'username': request.form['username'], + 'password': request.form['password'], + 'server': request.form['server'], + 'path': request.form['path'] + } + if mt5_connector.connect_to_mt5(credentials): + # Set MT5 initialization state to True + globals.mt5_initialized = True + # Redirect to the main dashboard or another page + return redirect('/dashboard') + else: + return render_template('index.html', error='Invalid credentials. Please try again.') + +@app.route('/dashboard') +def dashboard(): + # Replace these with the actual MetaTrader data retrieval logic + mt5_data = mt5_connector.get_account_info() # Replace with the actual method to get account info + user_data = {'username': mt5_data.name, 'account_balance': mt5_data.balance, 'currency': mt5_data.currency} + username = user_data.get('username', 'N/A') + account_balance = user_data.get('account_balance', 'N/A') + account_currency = user_data.get('currency', 'N/A') + + return render_template('dashboard.html', username=username, account_balance=account_balance, account_currency=account_currency) + +# Flask app routes + +@app.route('/start_ml_bot', methods=['POST']) +def start_ml_bot(): + """ + Handle the request to start the ML bot. + + """ + + # Start the ML bot + run_trading_bot_web_interface() + + +# Main Trading Bot Logic + +def run_trading_bot_web_interface(): + """ + Run the trading bot using MetaTrader 5 credentials from the web interface. + """ + + while True: + try: + symbol = 'EURUSD' + lot_size = 0.01 + stop_loss = 100 + take_profit = 150 + + # Get the latest historical data + latest_data = get_historical_data(symbol).iloc[-1:] + + # Calculate indicators and detect patterns for the latest data + df = calculate_indicators_and_detect_patterns(latest_data) + + # Generate trade signals for the latest data + df = generate_trade_signals(df) + print(df) + # Execute trades + for i in range(len(latest_data)): + signal = df['signal'].iloc[i] + if signal != 'None': + print(df['signal'].array) + execute_trade(signal, df, symbol, lot_size, stop_loss, take_profit) + + # Visualize data + # plot_trade_signals(df) + + except Exception as e: + print(f"Error running trading bot: {str(e)}") + + # Wait for the next iteration + time.sleep(60) # Adjust the time interval as needed + +# Start the Flask app +if __name__ == '__main__': + app.run(debug=True) diff --git a/app/weights/model_weights.h5 b/model_weights.h5 similarity index 100% rename from app/weights/model_weights.h5 rename to model_weights.h5 diff --git a/mt5/entrypoint.sh b/mt5/entrypoint.sh deleted file mode 100644 index fdeaeb1..0000000 --- a/mt5/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# Start the X server -Xvfb :0 -screen 0 1024x768x16 & - -# Set the X display -export DISPLAY=:0 - -# Install necessary dependencies using winetricks -winetricks -q corefonts - -# Run MetaTrader 5 -su - trader -c 'wine "/home/trader/.wine/drive_c/Program Files/MetaTrader 5/terminal64.exe"' diff --git a/app/requirements.txt b/requirements.txt old mode 100755 new mode 100644 similarity index 60% rename from app/requirements.txt rename to requirements.txt index f97bba1..5ce5dc6 --- a/app/requirements.txt +++ b/requirements.txt @@ -1,7 +1,9 @@ -numpy +# requirements.txt pandas +numpy +scikit-learn TA-Lib matplotlib -scikit-learn +MetaTrader5 tensorflow -pymt5 \ No newline at end of file +flask \ No newline at end of file diff --git a/src/connectors/mt5_connector.py b/src/connectors/mt5_connector.py new file mode 100644 index 0000000..3b72c43 --- /dev/null +++ b/src/connectors/mt5_connector.py @@ -0,0 +1,73 @@ +# mt5_connector.py + +""" +Module for connecting to MetaTrader 5 (MT5) using the MetaTrader5 Python package. + +This module provides functions for initializing and connecting to MT5, along with error handling. + +Author: Mike Kiwalabye +""" + +import MetaTrader5 as mt5 + + +def start_mt5(username: str, password: str, server: str, path: str) -> bool: + """ + Initialize and start a connection to MetaTrader 5. + + Parameters: + - username (str): The MT5 account username. + - password (str): The MT5 account password. + - server (str): The MT5 trading server. + - path (str): The file path to the MetaTrader 5 executable. + + Returns: + - bool: True if successfully initialized, False otherwise. + """ + # Ensure that all variables are the correct type + uname = int(username) # Username must be an int + pword = str(password) # Password must be a string + trading_server = str(server) # Server must be a string + filepath = str(path) # Filepath must be a string + + # Connect to MetaTrader 5 + if mt5.initialize(login=uname, password=pword, server=trading_server, path=filepath): + # Login to MT5 + if mt5.login(login=uname, password=pword, server=trading_server): + return True + else: + print("Login Fail") + quit() + return PermissionError + else: + print("MT5 Initialization Failed") + quit() + return ConnectionAbortedError + +def connect_to_mt5(credentials: dict): + """ + Connect to MetaTrader 5. + + Parameters: + - credentials (dict): Dictionary containing 'username', 'password', 'server', and 'path'. + """ + # Start the MetaTrader 5 instance + if start_mt5(credentials['username'], credentials['password'], credentials['server'], credentials['path']): + print("Connected to MetaTrader 5") + return True + else: + print("Failed to connect to MetaTrader 5") + return False + +def get_account_info(): + """ + Get account information from MetaTrader 5. + + Returns: + - dict: Dictionary containing account information (e.g., username, account balance). + """ + + # Fetch account information + account_info = mt5.account_info() + + return account_info \ No newline at end of file diff --git a/src/models/neural_network_model.py b/src/models/neural_network_model.py new file mode 100644 index 0000000..55e3c59 --- /dev/null +++ b/src/models/neural_network_model.py @@ -0,0 +1,147 @@ +# neural_network_model.py +""" +Module for defining and managing the TensorFlow Neural Network model. + +This module provides functions to create, compile, and update a simple feedforward neural network +model for use in a trading bot. + +Author: Mike Kiwalabye +""" + +import pandas as pd +from keras.models import Sequential, load_model +from keras.layers import Dense, Dropout +from keras.optimizers import Adam +from keras.losses import BinaryCrossentropy +from sklearn.preprocessing import MinMaxScaler +import numpy as np + + +def create_neural_network_model(input_shape: tuple) -> Sequential: + """ + Create a simple feedforward neural network model. + + Parameters: + - input_shape (tuple): The shape of the input data. + + Returns: + - Sequential: The Keras Sequential model representing the neural network. + """ + model = Sequential() + model.add(Dense(64, activation='relu', input_shape=input_shape)) + model.add(Dropout(0.2)) + model.add(Dense(64, activation='relu')) + model.add(Dropout(0.2)) + model.add(Dense(1, activation='sigmoid')) + return model + + +def compile_neural_network_model(model: Sequential, learning_rate: float = 0.001) -> None: + """ + Compile the neural network model. + + Parameters: + - model (Sequential): The Keras Sequential model representing the neural network. + - learning_rate (float): The learning rate for the Adam optimizer. + """ + model.compile(optimizer=Adam(learning_rate=learning_rate), loss='binary_crossentropy', metrics=['accuracy']) + + +def preprocess_data(dataset: pd.DataFrame) -> tuple: + """ + Preprocess the dataset for training the neural network model. + + Parameters: + - dataset (pd.DataFrame): The dataset containing trade outcome information. + + Returns: + - tuple: A tuple containing preprocessed input features and target labels. + """ + # Define the numerical features (if any) + numerical_features = [] # Update with the actual numerical feature column names + + # Define the input features + input_features = dataset[[ + 'pattern', 'divergence_strength', 'trend_direction', 'indicator_used']] + + # Convert categorical features to one-hot encoding + input_features = pd.get_dummies(input_features) + + # Normalize numerical features (if any) + if numerical_features: + scaler = MinMaxScaler() + input_features[numerical_features] = scaler.fit_transform( + input_features[numerical_features]) + + # Extract target labels from the dataset + target_labels = dataset['outcome'] + + # Convert target labels to numerical representation (0s and 1s) + target_labels = target_labels.map({'Loss': 0, 'Win': 1}) + + # Return the preprocessed input features and target labels + return input_features, target_labels + + +def update_neural_network_model(trade_outcome: dict, dataset_path: str) -> None: + """ + Update the neural network model based on trade outcome. + + Parameters: + - trade_outcome (dict): Trade outcome information. + - dataset_path (str): The path to the dataset file for updating and saving. + """ + # Load existing model or create a new one if it doesn't exist + try: + model = load_model('model_weights.h5') + except (OSError, ValueError): + # If loading fails, create a new model + input_shape = (4,) # Replace with the actual input shape + model = create_neural_network_model(input_shape) + compile_neural_network_model(model, learning_rate=0.001) + + pattern = trade_outcome['pattern'] + divergence_strength = trade_outcome['divergence_strength'] + time = trade_outcome['time'] + trend_direction = trade_outcome['trend_direction'] + indicator_used = trade_outcome['indicator_used'] + outcome = trade_outcome['outcome'] + + # Example update code: Append trade outcome information to a dataset for future training + trade_data = pd.DataFrame({ + 'pattern': [pattern], + 'divergence_strength': [divergence_strength], + 'time': [time], + 'trend_direction': [trend_direction], + 'indicator_used': [indicator_used], + 'outcome': [outcome] + }) + + try: + # Load existing dataset if it exists + dataset = pd.read_csv(dataset_path) + except (FileNotFoundError, pd.errors.EmptyDataError): + # Create an empty dataset if the file doesn't exist or is empty + dataset = pd.DataFrame() + + # Concatenate the trade data to the dataset for future training + updated_dataset = pd.concat([dataset, trade_data], ignore_index=True) + + # Save updated dataset + updated_dataset.to_csv(dataset_path, index=False) + + # Example retraining code: Retrain the neural network model with the updated dataset + # Preprocess data as per your requirements + X_train, y_train = preprocess_data(updated_dataset) # Implement the preprocess_data function + + # Convert labels to NumPy array and ensure the correct data type + y_train = np.array(y_train).astype(float) # Convert to float + + # Ensure labels have the correct shape + y_train = y_train.reshape(-1) + + # Example retraining step + model.fit(X_train, y_train, epochs=10, batch_size=32) + + # Save the updated model weights + model.save_weights('model_weights.h5') diff --git a/src/strategies/trading_strategy.py b/src/strategies/trading_strategy.py new file mode 100644 index 0000000..00644ca --- /dev/null +++ b/src/strategies/trading_strategy.py @@ -0,0 +1,193 @@ +# trading_strategy.py +""" +Module for defining the trading strategy used by the trading bot. + +This module provides functions for generating trade signals based on various indicators, +divergences, patterns, and trend directions. + +Author: Mike Kiwalabye +""" + +import MetaTrader5 as mt5 +import pandas as pd +import numpy as np +from sklearn.preprocessing import MinMaxScaler +import talib +from src.models import neural_network_model + +def get_historical_data(symbol: str) -> pd.DataFrame: + """ + Retrieve historical data for a given symbol and timeframe from MetaTrader 5. + + Parameters: + - symbol (str): The financial instrument symbol (e.g., 'EURUSD'). + + Returns: + - pd.DataFrame: DataFrame containing historical data with columns: ['time', 'open', 'high', 'low', 'close', 'tick_volume', 'spread', 'real_volume']. + """ + # Retrieve historical data + rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M1, 0, 1000) + + # Convert data to DataFrame + df = pd.DataFrame(rates) + + # Convert the 'time' column to datetime + df['time'] = pd.to_datetime(df['time'], unit='s') + + # Set the 'time' column as the index + df.set_index('time', inplace=True) + + return df + +def calculate_indicators_and_detect_patterns(df: pd.DataFrame) -> pd.DataFrame: + """ + Generate trade signals based on divergences, patterns, and trend direction. + + Parameters: + - df (pd.DataFrame): The DataFrame containing indicators, patterns, and trend information. + + Returns: + - pd.DataFrame: The DataFrame with added columns for trade signals. + """ + # Calculate indicators and detect patterns + # Add your indicator calculation and pattern detection logic here + + # Example: Calculate RSI + df['rsi'] = talib.RSI(df['close'], timeperiod=14) + + # Example: Detect RSI divergence + df['rsi_divergence'] = (df['rsi'] > 70) & (df['close'] < df['close'].shift()) + + # Example: Detect MACD divergence + # Add your MACD divergence detection logic here + + # Example: Detect patterns + df['pattern'] = 'None' + # Add your pattern detection logic here + + return df + +def generate_trade_signals(df: pd.DataFrame) -> pd.DataFrame: + """ + Generate trade signals based on divergences, patterns, and trend direction. + + Parameters: + - df (pd.DataFrame): The DataFrame containing indicators, patterns, and trend information. + + Returns: + - pd.DataFrame: The DataFrame with added columns for trade signals. + """ + # Determine trade signals based on divergences, patterns, and trend direction + df['signal'] = 'None' + df['divergence_signal'] = np.where((df['rsi_divergence'] == True) & (df['pattern'] != 'None'), 'Both', + np.where(df['rsi_divergence'] == True, 'RSI', 'Pattern')) + df['strongest_divergence_signal'] = df[['divergence_signal']].max(axis=1) + + # Additional conditions for trade signals + conditions = [ + (df['strongest_divergence_signal'] != 'None'), + # Placeholder for 'resistance' calculation - replace this with your actual logic + (df['close'] > df['close'].rolling(window=10).max()), + (df['close'] < df['close'].rolling(window=10).min()), + # Placeholder for 'trend_200' calculation - replace this with your actual logic + (df['close'] > df['close'].rolling(window=200).mean()), + (df['close'] < df['close'].rolling(window=200).mean()) + ] + + choices = ['Divergence', 'Resistance', 'Support', 'Uptrend', 'Downtrend'] + df['support_resistance_signal'] = np.select(conditions, choices, default='None') + + # Iterate over the data points + for i in range(1, len(df)): + strongest_divergence_signal = df['strongest_divergence_signal'].iloc[i] + support_resistance_signal = df['support_resistance_signal'].iloc[i] + trend_signal = df['trend_signal'].iloc[i] + + if strongest_divergence_signal != 'None': + df.loc[df.index[i], 'signal'] = strongest_divergence_signal + elif support_resistance_signal != 'None': + df.loc[df.index[i], 'signal'] = support_resistance_signal + elif trend_signal != 'None': + df.loc[df.index[i], 'signal'] = trend_signal + + return df + +def execute_trade(signal_priority, df, symbol, lot_size, stop_loss, take_profit): + """ + Execute a trade based on the provided signal and trading parameters. + + Parameters: + - signal_priority (int): The priority assigned to the trade signal. + - df (pd.DataFrame): The DataFrame containing trade-related information. + - symbol (str): The financial instrument symbol (e.g., 'EURUSD'). + - lot_size (float): The size of the trading position. + - stop_loss (float): The stop-loss level. + - take_profit (float): The take-profit level. + """ + # Initialize outcome and request + outcome = None + request = {} + + # Calculate risk and position size based on lot size, stop loss, and take profit + risk_multiplier = 1.2 if 'RSI' in df['strongest_divergence_signal'].iloc[-1] else 1.5 + risk = lot_size * stop_loss * risk_multiplier + position_size = risk / (take_profit - stop_loss) + + try: + if signal_priority == 3: + # Place a buy trade + request = { + 'action': mt5.TRADE_ACTION_DEAL, + 'symbol': symbol, + 'volume': lot_size, + 'type': mt5.ORDER_TYPE_BUY, + 'price': mt5.symbol_info_tick(symbol).ask, + 'sl': mt5.symbol_info_tick(symbol).ask - stop_loss * mt5.symbol_info(symbol).point, + 'tp': mt5.symbol_info_tick(symbol).ask + take_profit * mt5.symbol_info(symbol).point, + 'deviation': 0, + 'magic': 123456, + 'comment': "Buy trade", + 'type_time': mt5.ORDER_TIME_GTC, + 'type_filling': mt5.ORDER_FILLING_RETURN, + } + elif signal_priority == 2: + # Place a sell trade + request = { + 'action': mt5.TRADE_ACTION_DEAL, + 'symbol': symbol, + 'volume': lot_size, + 'type': mt5.ORDER_TYPE_SELL, + 'price': mt5.symbol_info_tick(symbol).bid, + 'sl': mt5.symbol_info_tick(symbol).bid + stop_loss * mt5.symbol_info(symbol).point, + 'tp': mt5.symbol_info_tick(symbol).bid - take_profit * mt5.symbol_info(symbol).point, + 'deviation': 0, + 'magic': 123456, + 'comment': "Sell trade", + 'type_time': mt5.ORDER_TIME_GTC, + 'type_filling': mt5.ORDER_FILLING_RETURN, + } + + result = mt5.order_send(request) + outcome = 'Win' if result.retcode == mt5.TRADE_RETCODE_DONE else 'Loss' + + # Example trade outcome information + trade_outcome = { + 'pattern': df['pattern'].iloc[-1], + 'divergence_strength': df['strongest_divergence_signal'].iloc[-1], + 'time': df.index[-1], + 'trend_direction': df['trend_signal'].iloc[-1], + 'indicator_used': df['strongest_divergence_signal'].iloc[-1], + 'outcome': outcome + } + + # Update TensorFlow neural network model with trade outcome + neural_network_model.update_neural_network_model(trade_outcome, 'tradedata.csv') + + # Example print statements for debugging + print( + f"Executed trade with signal priority: {signal_priority}, position size: {position_size}") + print(f"Trade outcome: {trade_outcome}") + + # Additional logic for trade management, monitoring, etc. + except Exception as e: + print(f"Error executing trade: {str(e)}") \ No newline at end of file diff --git a/src/utils/data_processing.py b/src/utils/data_processing.py new file mode 100644 index 0000000..a8031f7 --- /dev/null +++ b/src/utils/data_processing.py @@ -0,0 +1,68 @@ +# data_processing.py +""" +Module for processing and preprocessing data for the trading bot. + +This module provides functions for loading, cleaning, and preprocessing historical price data. + +Author: Mike Kiwalabye +""" + +import pandas as pd +from sklearn.preprocessing import MinMaxScaler + +def load_data(file_path: str) -> pd.DataFrame: + """ + Load historical price data from a CSV file. + + Parameters: + - file_path (str): The path to the CSV file containing historical price data. + + Returns: + - pd.DataFrame: The DataFrame containing historical price data. + """ + # Load data from CSV file + df = pd.read_csv(file_path) + + # Ensure the 'time' column is in datetime format + df['time'] = pd.to_datetime(df['time']) + + return df + +def clean_data(df: pd.DataFrame) -> pd.DataFrame: + """ + Clean the historical price data by handling missing values and removing duplicates. + + Parameters: + - df (pd.DataFrame): The DataFrame containing historical price data. + + Returns: + - pd.DataFrame: The cleaned DataFrame. + """ + # Handle missing values (if any) + df.dropna(inplace=True) + + # Remove duplicate rows (if any) + df.drop_duplicates(inplace=True) + + return df + +def preprocess_data(df: pd.DataFrame, numerical_features: list = []) -> pd.DataFrame: + """ + Preprocess the historical price data by normalizing numerical features and encoding categorical features. + + Parameters: + - df (pd.DataFrame): The DataFrame containing historical price data. + - numerical_features (list): A list of column names corresponding to numerical features. + + Returns: + - pd.DataFrame: The preprocessed DataFrame. + """ + # Convert categorical features to one-hot encoding + df = pd.get_dummies(df) + + # Normalize numerical features (if any) + if numerical_features: + scaler = MinMaxScaler() + df[numerical_features] = scaler.fit_transform(df[numerical_features]) + + return df \ No newline at end of file diff --git a/src/utils/visualization.py b/src/utils/visualization.py new file mode 100644 index 0000000..55af840 --- /dev/null +++ b/src/utils/visualization.py @@ -0,0 +1,61 @@ +# visualization.py +""" +Module for visualizing data for the trading bot. + +This module provides functions for visualizing historical price data and trade signals. + +Author: Mike Kiwalabye +""" + +import matplotlib.pyplot as plt +import pandas as pd + +def plot_price_data(df: pd.DataFrame, title: str = 'Price Chart') -> None: + """ + Plot the historical price data. + + Parameters: + - df (pd.DataFrame): The DataFrame containing historical price data. + - title (str): The title of the plot. + + Returns: + - None + """ + plt.figure(figsize=(10, 6)) + plt.plot(df.index, df['close'], label='Close') + + # Add visualizations for other indicators, levels, and patterns + # (Add more visualizations as needed) + + plt.title(title) + plt.xlabel('Time') + plt.ylabel('Price') + plt.legend() + plt.show() + +def plot_trade_signals(df: pd.DataFrame, title: str = 'Trade Signals') -> None: + """ + Plot trade signals on the historical price chart. + + Parameters: + - df (pd.DataFrame): The DataFrame containing historical price data with trade signals. + - title (str): The title of the plot. + + Returns: + - None + """ + plt.figure(figsize=(10, 6)) + plt.plot(df.index, df['close'], label='Close') + + # Plot trade signals + buy_signals = df[df['signal'] == 'Buy'] + sell_signals = df[df['signal'] == 'Sell'] + + plt.scatter(buy_signals.index, buy_signals['close'], color='green', marker='^', label='Buy Signal') + plt.scatter(sell_signals.index, sell_signals['close'], color='red', marker='v', label='Sell Signal') + + plt.title(title) + plt.xlabel('Time') + plt.ylabel('Price') + plt.legend() + plt.show() \ No newline at end of file diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..8c9deef --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,26 @@ + + + + +
+ + +Username: {{ username }}
+Account Balance: {{ account_balance }} {{ account_currency }}
+