Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f3d469f14 | |||
| f53fcb7dc3 | |||
| 843b97e3b5 |
@@ -1 +1,3 @@
|
|||||||
.vscode
|
.vscode
|
||||||
|
.venv
|
||||||
|
__pycache__/
|
||||||
+29
@@ -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"]
|
||||||
-338
@@ -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()
|
|
||||||
@@ -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)
|
|
||||||
+13
-51
@@ -1,55 +1,17 @@
|
|||||||
version: "3"
|
version: '3'
|
||||||
services:
|
services:
|
||||||
metatrader_service:
|
trading-bot:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: docker/DockerFile.xorg
|
dockerfile: Dockerfile
|
||||||
container_name: metatrader
|
ports:
|
||||||
restart: unless-stopped
|
- "5000:5000"
|
||||||
|
volumes:
|
||||||
|
- ./src:/app/src
|
||||||
|
- ./static:/app/static
|
||||||
|
- ./templates:/app/templates
|
||||||
|
- ./model_weights.h5:/app/model_weights.h5
|
||||||
environment:
|
environment:
|
||||||
- DISPLAY=$DISPLAY
|
- FLASK_APP=main.py
|
||||||
privileged: true
|
- FLASK_RUN_HOST=0.0.0.0
|
||||||
volumes:
|
- PYTHONUNBUFFERED=1
|
||||||
- /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
|
|
||||||
|
|||||||
@@ -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" ]
|
|
||||||
@@ -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"]
|
|
||||||
@@ -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"]
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
"""
|
||||||
|
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, json, Response
|
||||||
|
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
|
||||||
|
import threading
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Define input shape for the neural network
|
||||||
|
input_shape = (11,) # 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
|
||||||
|
mt5_initialized = False
|
||||||
|
latest_trade_signals = []
|
||||||
|
|
||||||
|
|
||||||
|
# Web Interface Routes
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
"""Render the main page with the 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.
|
||||||
|
"""
|
||||||
|
global mt5_initialized
|
||||||
|
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
|
||||||
|
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
|
||||||
|
threading.Thread(target=run_trading_bot_web_interface).start()
|
||||||
|
|
||||||
|
# Return an empty response
|
||||||
|
return Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/stop_ml_bot", methods=['GET'])
|
||||||
|
def stop_ml_bot():
|
||||||
|
mt5_connector.stop_mt5_ml_bot()
|
||||||
|
redirect('/dashboard')
|
||||||
|
|
||||||
|
def map_signal_priority(signal_priority):
|
||||||
|
# Define a mapping for string values to integers
|
||||||
|
signal_mapping = {
|
||||||
|
'Both': 1,
|
||||||
|
'Pattern': 2,
|
||||||
|
'RSI': 3
|
||||||
|
# Add more mappings as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use the mapping, default to 0 if not found
|
||||||
|
return signal_mapping.get(signal_priority, 0)
|
||||||
|
# Main Trading Bot Logic
|
||||||
|
|
||||||
|
def run_trading_bot_web_interface():
|
||||||
|
"""
|
||||||
|
Run the trading bot using MetaTrader 5 credentials from the web interface.
|
||||||
|
"""
|
||||||
|
global latest_trade_signals
|
||||||
|
historical_data_df = pd.DataFrame()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
symbol = 'EURUSD'
|
||||||
|
lot_size = 0.01
|
||||||
|
stop_loss = 100
|
||||||
|
take_profit = 200
|
||||||
|
|
||||||
|
# Get the latest historical data
|
||||||
|
historical_data_df = get_historical_data(symbol, historical_data_df)
|
||||||
|
|
||||||
|
# Calculate indicators and detect patterns for the latest data
|
||||||
|
df = calculate_indicators_and_detect_patterns(historical_data_df)
|
||||||
|
|
||||||
|
# Generate trade signals for the latest data
|
||||||
|
df = generate_trade_signals(df)
|
||||||
|
df.to_csv('your_file.csv', sep='\t', index=False)
|
||||||
|
|
||||||
|
# Inside the run_trading_bot_web_interface function
|
||||||
|
latest_trade_signals = df.replace({pd.NA: 'null'}).to_json(orient='records')
|
||||||
|
|
||||||
|
|
||||||
|
# Execute trades
|
||||||
|
for i in range(len(df)):
|
||||||
|
|
||||||
|
signal_priority = df['signal'].iloc[i] # Replace with your actual value
|
||||||
|
mapped_priority = map_signal_priority(signal_priority)
|
||||||
|
|
||||||
|
if mapped_priority != 0:
|
||||||
|
execute_trade(mapped_priority, df, symbol, lot_size, stop_loss, take_profit)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@app.route('/get_latest_trade_signals', methods=['GET'])
|
||||||
|
def get_latest_trade_signals():
|
||||||
|
global latest_trade_signals
|
||||||
|
return json.dumps(latest_trade_signals)
|
||||||
|
|
||||||
|
# Start the Flask app
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True)
|
||||||
@@ -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"'
|
|
||||||
Executable → Regular
+5
-3
@@ -1,7 +1,9 @@
|
|||||||
numpy
|
# requirements.txt
|
||||||
pandas
|
pandas
|
||||||
|
numpy
|
||||||
|
scikit-learn
|
||||||
TA-Lib
|
TA-Lib
|
||||||
matplotlib
|
matplotlib
|
||||||
scikit-learn
|
MetaTrader5
|
||||||
tensorflow
|
tensorflow
|
||||||
pymt5
|
flask
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
def stop_mt5_ml_bot():
|
||||||
|
|
||||||
|
mt5.shutdown()
|
||||||
|
return "Disconnected from MetaTrader 5"
|
||||||
@@ -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 = (5,) # 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')
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
# 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
|
||||||
|
from talib import abstract
|
||||||
|
from src.models import neural_network_model
|
||||||
|
|
||||||
|
def get_historical_data(symbol: str, existing_data: pd.DataFrame = None) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
Retrieve historical data for a given symbol and timeframe from MetaTrader 5.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- symbol (str): The financial instrument symbol (e.g., 'EURUSD').
|
||||||
|
- existing_data (pd.DataFrame): Existing historical data DataFrame.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- pd.DataFrame: DataFrame containing historical data with columns: ['time', 'open', 'high', 'low', 'close', 'tick_volume', 'spread', 'real_volume'].
|
||||||
|
"""
|
||||||
|
print(len(existing_data))
|
||||||
|
if len(existing_data) == 0:
|
||||||
|
# If no existing data, fetch the last 2500 bars
|
||||||
|
rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M1, 0, 2500)
|
||||||
|
df = pd.DataFrame(rates)
|
||||||
|
else:
|
||||||
|
# If existing data is provided, fetch only the latest bar
|
||||||
|
rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M1, 0, 1)
|
||||||
|
new_data = pd.DataFrame(rates)
|
||||||
|
|
||||||
|
# Concatenate the new data to the existing data
|
||||||
|
df = pd.concat([existing_data, new_data])
|
||||||
|
|
||||||
|
# Convert data to DataFrame
|
||||||
|
df['time'] = pd.to_datetime(df['time'], unit='s')
|
||||||
|
df.set_index('time', inplace=True)
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
def calculate_patterns(df: pd.DataFrame) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
Calculate common trade patterns such as double tops & bottoms, pennants, wedges, and bull and bear flags.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- df (pd.DataFrame): The DataFrame containing price and indicator information.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- pd.DataFrame: The DataFrame with added columns for detected patterns.
|
||||||
|
"""
|
||||||
|
# Detect Double Tops & Bottoms
|
||||||
|
df['double_top'] = np.where((df['high'].shift(1) > df['high']) & (df['high'].shift(1) > df['high'].shift(2)), 'Double Top', 'None')
|
||||||
|
df['double_bottom'] = np.where((df['low'].shift(1) < df['low']) & (df['low'].shift(1) < df['low'].shift(2)), 'Double Bottom', 'None')
|
||||||
|
|
||||||
|
# Detect Bull and Bear Flags
|
||||||
|
df['bull_flag'] = np.where((df['close'] > abstract.BBANDS(df['close'], timeperiod=5, nbdevup=2.0, nbdevdn=2.0)[0]) & (df['close'].shift(1) < abstract.BBANDS(df['close'].shift(1), timeperiod=5, nbdevup=2.0, nbdevdn=2.0)[0]), 'Bull Flag', 'None')
|
||||||
|
df['bear_flag'] = np.where((df['close'] < abstract.BBANDS(df['close'], timeperiod=5, nbdevup=2.0, nbdevdn=2.0)[2]) & (df['close'].shift(1) > abstract.BBANDS(df['close'].shift(1), timeperiod=5, nbdevup=2.0, nbdevdn=2.0)[2]), 'Bear Flag', 'None')
|
||||||
|
|
||||||
|
# Assign patterns based on conditions
|
||||||
|
df['pattern'] = 'None'
|
||||||
|
conditions = [
|
||||||
|
(df['double_top'] != 'None'),
|
||||||
|
(df['double_bottom'] != 'None'),
|
||||||
|
(df['bull_flag'] != 'None'),
|
||||||
|
(df['bear_flag'] != 'None')
|
||||||
|
]
|
||||||
|
|
||||||
|
choices = ['Double Top', 'Double Bottom', 'Bull Flag', 'Bear Flag']
|
||||||
|
df['pattern'] = np.select(conditions, choices, default='None')
|
||||||
|
|
||||||
|
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'] = abstract.RSI(df['close'], timeperiod=14)
|
||||||
|
# print(df['close'].values)
|
||||||
|
|
||||||
|
# Example: Detect RSI divergence
|
||||||
|
df['rsi_divergence'] = (df['rsi'] > 70) & (df['close'] < df['close'].shift())
|
||||||
|
|
||||||
|
# Example: Detect TREND signal
|
||||||
|
df['trend_signal'] = 'None'
|
||||||
|
df['short_ma'] = df['close'].rolling(window=50).mean()
|
||||||
|
df['long_ma'] = df['close'].rolling(window=200).mean()
|
||||||
|
|
||||||
|
df.loc[df['short_ma'] > df['long_ma'], 'trend_signal'] = 'Uptrend'
|
||||||
|
df.loc[df['short_ma'] < df['long_ma'], 'trend_signal'] = 'Downtrend'
|
||||||
|
# Add your MACD divergence detection logic here
|
||||||
|
|
||||||
|
# Example: Detect patterns
|
||||||
|
df['pattern'] = 'None'
|
||||||
|
# Add your pattern detection logic here
|
||||||
|
df = calculate_patterns(df)
|
||||||
|
|
||||||
|
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()),
|
||||||
|
# Use the calculated 'trend_signal' column for trend condition
|
||||||
|
(df['trend_signal'] == 'Uptrend'),
|
||||||
|
(df['trend_signal'] == 'Downtrend'),
|
||||||
|
# Additional condition to check if the pattern is valid
|
||||||
|
(df['pattern'] != 'None'),
|
||||||
|
]
|
||||||
|
|
||||||
|
choices = ['Divergence', 'Resistance', 'Support', 'Uptrend', 'Downtrend', 'Pattern']
|
||||||
|
|
||||||
|
# Ensure that the lengths of conditions and choices are the same
|
||||||
|
if len(conditions) == len(choices):
|
||||||
|
df['support_resistance_signal'] = np.select(conditions, choices, default='None')
|
||||||
|
else:
|
||||||
|
# Handle the case where lengths do not match (print an error message for debugging)
|
||||||
|
print("Error: Lengths of conditions and choices do not match.")
|
||||||
|
df['support_resistance_signal'] = '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.
|
||||||
|
"""
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
|
||||||
|
# Additional conditions for Buy trade
|
||||||
|
if (
|
||||||
|
(signal_priority == 3 and row['rsi_divergence'] and row['rsi_value'] < 30 and row['trend_signal'] == 'Downtrend') or
|
||||||
|
(signal_priority == 2 and row['pattern'] == 'Double Bottom' and row['trend_signal'] == 'Downtrend') or
|
||||||
|
(signal_priority == 1 and 40 <= row['rsi_value'] <= 60 and row['pattern'] == 'Bull Flag' and row['trend_signal'] == 'Uptrend') or
|
||||||
|
(signal_priority == 0 and row['rsi_value'] < 30 and row['rsi_divergence'] and row['pattern'] == 'Bull')
|
||||||
|
):
|
||||||
|
# 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_IOC,
|
||||||
|
}
|
||||||
|
elif (
|
||||||
|
(signal_priority == 3 and row['rsi_divergence'] and row['rsi_value'] > 70 and row['trend_signal'] == 'Uptrend') or
|
||||||
|
(signal_priority == 2 and row['pattern'] == 'Double Top' and row['trend_signal'] == 'Uptrend') or
|
||||||
|
(signal_priority == 1 and 40 <= row['rsi_value'] <= 60 and row['pattern'] == 'Bear Flag' and row['trend_signal'] == 'Downtrend') or
|
||||||
|
(signal_priority == 0 and row['rsi_value'] > 70 and row['rsi_divergence'] and row['pattern'] == 'Bear')
|
||||||
|
):
|
||||||
|
# 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_IOC,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
if signal_priority != 0:
|
||||||
|
|
||||||
|
result = mt5.order_send(request)
|
||||||
|
print(result)
|
||||||
|
outcome = 'Win' if result.retcode == mt5.TRADE_RETCODE_DONE else 'Loss'
|
||||||
|
|
||||||
|
# Example trade outcome information
|
||||||
|
trade_outcome = {
|
||||||
|
'pattern': row['pattern'],
|
||||||
|
'divergence_strength': row['strongest_divergence_signal'],
|
||||||
|
'time': pd.Timestamp.now(),
|
||||||
|
'trend_direction': row['trend_signal'],
|
||||||
|
'indicator_used': row['strongest_divergence_signal'],
|
||||||
|
'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: {lot_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)}")
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 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
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
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()
|
||||||
|
|
||||||
|
# Use plt.show(block=True) to make the plot blocking
|
||||||
|
plt.show(block=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error plotting trade signals: {str(e)}")
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
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()
|
||||||
|
|
||||||
|
# Use plt.show(block=True) to make the plot blocking
|
||||||
|
plt.show(block=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error plotting trade signals: {str(e)}")
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
<!-- dashboard.html -->
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Trading Dashboard</title>
|
||||||
|
|
||||||
|
<!-- Include Chart.js from a CDN -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Trading Dashboard</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p><strong>Username:</strong> {{ username }}</p>
|
||||||
|
<p><strong>Account Balance:</strong> {{ account_balance }} <small>{{ account_currency }}</small></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add a canvas element for the chart -->
|
||||||
|
<canvas id="tradeChart" width="800" height="400"></canvas>
|
||||||
|
|
||||||
|
<form action="/stop_ml_bot" method="get">
|
||||||
|
<button type="submit">Stop ML Bot</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Use a button without a form to start the ML Bot -->
|
||||||
|
<button onclick="startBot()">Start ML Bot</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Function to update the chart with new trade signals
|
||||||
|
function updateChart(tradeSignals) {
|
||||||
|
// Parse the JSON-formatted string to an object
|
||||||
|
const parsedTradeSignals = JSON.parse(tradeSignals);
|
||||||
|
|
||||||
|
// Extract relevant data for the chart (modify as needed)
|
||||||
|
const timestamps = parsedTradeSignals.map(signal => signal.time);
|
||||||
|
const prices = parsedTradeSignals.map(signal => signal.close);
|
||||||
|
|
||||||
|
// Get the canvas element
|
||||||
|
const ctx = document.getElementById('tradeChart');
|
||||||
|
|
||||||
|
// Destroy existing chart if it exists
|
||||||
|
if (ctx.chart) {
|
||||||
|
ctx.chart.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the chart
|
||||||
|
const myChart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: timestamps,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Close Price',
|
||||||
|
data: prices,
|
||||||
|
borderColor: 'rgba(75, 192, 192, 1)',
|
||||||
|
borderWidth: 1,
|
||||||
|
fill: false
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'minute' // Adjust as needed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function to periodically update the chart
|
||||||
|
function periodicallyUpdateChart() {
|
||||||
|
// Fetch the latest trade signals from the server
|
||||||
|
fetch('/get_latest_trade_signals')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(tradeSignals => {
|
||||||
|
// Update the chart with the new trade signals
|
||||||
|
updateChart(tradeSignals);
|
||||||
|
|
||||||
|
// Schedule the next update
|
||||||
|
setTimeout(periodicallyUpdateChart, 5000); // Update every 5 seconds
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching trade signals:', error);
|
||||||
|
// Retry the update after an interval
|
||||||
|
setTimeout(periodicallyUpdateChart, 5000); // Retry after 5 seconds
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the initial chart update
|
||||||
|
periodicallyUpdateChart();
|
||||||
|
|
||||||
|
function startBot() {
|
||||||
|
// Send an asynchronous request to start the bot
|
||||||
|
fetch('/start_ml_bot', { method: 'POST' });
|
||||||
|
|
||||||
|
// Optionally, you can add logic here to update the UI or provide feedback to the user
|
||||||
|
console.log('Bot started!');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!-- templates/index.html -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MT5 Connector</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>MetaTrader 5 Connector</h1>
|
||||||
|
<form action="/login" method="post">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input type="text" id="username" name="username" required><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input type="password" id="password" name="password" required><br>
|
||||||
|
|
||||||
|
<label for="server">Server:</label>
|
||||||
|
<input type="text" id="server" name="server" required><br>
|
||||||
|
|
||||||
|
<label for="path">MT5 Path:</label>
|
||||||
|
<input type="text" id="path" name="path" required><br>
|
||||||
|
|
||||||
|
<input type="submit" value="Connect">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user