Commit inicial: Projeto YuClusters
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Prevent interactive prompts
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Add 32-bit architecture for Wine and install dependencies
|
||||
RUN dpkg --add-architecture i386 && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
wine64 \
|
||||
wine32 \
|
||||
xvfb \
|
||||
wget \
|
||||
cabextract \
|
||||
winbind \
|
||||
curl \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set up user for Wine
|
||||
RUN useradd -m -s /bin/bash mt5user
|
||||
USER mt5user
|
||||
WORKDIR /home/mt5user
|
||||
|
||||
# Copy scripts
|
||||
COPY --chown=mt5user:mt5user scripts/ /app/scripts/
|
||||
RUN chmod +x /app/scripts/*.sh
|
||||
|
||||
# Run provisioning steps
|
||||
RUN /app/scripts/05_install_python.sh
|
||||
RUN /app/scripts/06_install_libraries.sh
|
||||
RUN /app/scripts/06b_install_mt5.sh
|
||||
|
||||
# Copy application source
|
||||
COPY --chown=mt5user:mt5user app.py /app/app.py
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
# Start script
|
||||
ENTRYPOINT ["/app/scripts/07_start_wine_flask.sh"]
|
||||
@@ -0,0 +1,102 @@
|
||||
import os
|
||||
from flask import Flask, jsonify, request
|
||||
import MetaTrader5 as mt5
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Basic settings from environment or defaults
|
||||
MT5_PATH = os.getenv("MT5_PATH", "C:\\Program Files\\MetaTrader 5\\terminal64.exe")
|
||||
MT5_SERVER = os.getenv("MT5_SERVER", "")
|
||||
MT5_LOGIN = int(os.getenv("MT5_LOGIN", "0"))
|
||||
MT5_PASSWORD = os.getenv("MT5_PASSWORD", "")
|
||||
|
||||
def init_mt5():
|
||||
# If login is provided, connect with credentials
|
||||
if MT5_LOGIN != 0 and MT5_PASSWORD:
|
||||
if not mt5.initialize(path=MT5_PATH, login=MT5_LOGIN, server=MT5_SERVER, password=MT5_PASSWORD):
|
||||
return False, mt5.last_error()
|
||||
else:
|
||||
# Just initialize whatever is there
|
||||
if not mt5.initialize(path=MT5_PATH):
|
||||
return False, mt5.last_error()
|
||||
return True, None
|
||||
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
success, error = init_mt5()
|
||||
if not success:
|
||||
return jsonify({"status": "error", "message": "Failed to connect to MT5", "error_code": error}), 500
|
||||
|
||||
info = mt5.terminal_info()
|
||||
if info is None:
|
||||
return jsonify({"status": "error", "message": "Failed to get terminal info"}), 500
|
||||
|
||||
return jsonify({
|
||||
"status": "ok",
|
||||
"terminal_connected": info.connected,
|
||||
"trade_allowed": info.trade_allowed,
|
||||
"build": info.build
|
||||
})
|
||||
|
||||
@app.route('/symbol/<ticker>', methods=['GET'])
|
||||
def symbol_info(ticker):
|
||||
init_mt5()
|
||||
info = mt5.symbol_info(ticker)
|
||||
if info is None:
|
||||
return jsonify({"status": "error", "message": f"Symbol {ticker} not found"}), 404
|
||||
|
||||
return jsonify({
|
||||
"symbol": info.name,
|
||||
"bid": info.bid,
|
||||
"ask": info.ask,
|
||||
"spread": info.spread,
|
||||
"trade_mode": info.trade_mode
|
||||
})
|
||||
|
||||
@app.route('/order', methods=['POST'])
|
||||
def place_order():
|
||||
init_mt5()
|
||||
data = request.json
|
||||
|
||||
# Very basic order payload (can be extended with full Swagger spec later)
|
||||
# Expects: {"symbol": "EURUSD", "action": "buy", "volume": 1.0}
|
||||
symbol = data.get("symbol")
|
||||
action = data.get("action")
|
||||
volume = float(data.get("volume", 0.0))
|
||||
|
||||
if action == "buy":
|
||||
type = mt5.ORDER_TYPE_BUY
|
||||
price = mt5.symbol_info_tick(symbol).ask
|
||||
else:
|
||||
type = mt5.ORDER_TYPE_SELL
|
||||
price = mt5.symbol_info_tick(symbol).bid
|
||||
|
||||
order_request = {
|
||||
"action": mt5.TRADE_ACTION_DEAL,
|
||||
"symbol": symbol,
|
||||
"volume": volume,
|
||||
"type": type,
|
||||
"price": price,
|
||||
"deviation": 20,
|
||||
"magic": 234000,
|
||||
"comment": "python api",
|
||||
"type_time": mt5.ORDER_TIME_GTC,
|
||||
"type_filling": mt5.ORDER_FILLING_IOC,
|
||||
}
|
||||
|
||||
result = mt5.order_send(order_request)
|
||||
|
||||
if result is None:
|
||||
return jsonify({"status": "error", "message": "Order failed entirely", "error": mt5.last_error()}), 500
|
||||
|
||||
# Translate MT5 Return Codes to friendly API responses
|
||||
# Mapping can be expanded as needed
|
||||
if result.retcode == mt5.TRADE_RETCODE_DONE:
|
||||
return jsonify({"status": "ok", "retcode": result.retcode, "deal": result.deal, "message": "Order placed successfully"})
|
||||
elif result.retcode == mt5.TRADE_RETCODE_MARKET_CLOSED:
|
||||
return jsonify({"status": "error", "retcode": result.retcode, "message": "Market is closed"}), 400
|
||||
else:
|
||||
return jsonify({"status": "error", "retcode": result.retcode, "message": "Order failed", "comment": result.comment}), 400
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Installing Python for Windows via Wine ==="
|
||||
# We need to install the Windows version of Python inside Wine for MT5 library compatibility
|
||||
WINEPREFIX=$HOME/.wine wine msiexec /i https://www.python.org/ftp/python/3.10.11/python-3.10.11-amd64.msi /quiet InstallAllUsers=1 PrependPath=1 Include_test=0
|
||||
|
||||
# Verify python installation
|
||||
WINEPREFIX=$HOME/.wine wine python --version
|
||||
echo "Python installed successfully."
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Installing Python Libraries via Wine ==="
|
||||
# Ensure pip is up to date
|
||||
WINEPREFIX=$HOME/.wine wine python -m pip install --upgrade pip
|
||||
|
||||
# Install Flask and MetaTrader5
|
||||
# The user explicitly warned to be careful with the case sensitivity of MetaTrader5!
|
||||
WINEPREFIX=$HOME/.wine wine python -m pip install Flask MetaTrader5
|
||||
|
||||
echo "Libraries installed successfully."
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Downloading and Installing MetaTrader 5 ==="
|
||||
export WINEPREFIX=$HOME/.wine
|
||||
export WINEDLLOVERRIDES="mscoree,mshtml="
|
||||
|
||||
# Start Xvfb temporarily for the installation
|
||||
# Some silent Windows installers still crash if there's no display available
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
XVFB_PID=$!
|
||||
export DISPLAY=:99
|
||||
sleep 2
|
||||
|
||||
# Download MT5 setup from MetaQuotes official CDN
|
||||
wget -O mt5setup.exe "https://download.mql5.com/cdn/web/metaquotes.software.corp/mt5/mt5setup.exe"
|
||||
|
||||
# Install silently ( /auto )
|
||||
echo "Installing MT5 silently (this may take a minute)..."
|
||||
wine mt5setup.exe /auto
|
||||
|
||||
# Wait for background installation tasks to complete and shutdown Wine safely
|
||||
wineserver -w
|
||||
kill $XVFB_PID || true
|
||||
rm mt5setup.exe
|
||||
|
||||
echo "MT5 installed successfully."
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Starting MT5 Gateway ==="
|
||||
export WINEPREFIX=$HOME/.wine
|
||||
export WINEDLLOVERRIDES="mscoree,mshtml="
|
||||
|
||||
# Start Xvfb in background
|
||||
Xvfb :0 -screen 0 1024x768x16 &
|
||||
export DISPLAY=:0
|
||||
|
||||
# Wait for X11
|
||||
sleep 2
|
||||
|
||||
# We start the Flask server via Python in Wine
|
||||
# The MT5 logic inside app.py will initialize MT5
|
||||
echo "Starting Flask API Bridge..."
|
||||
wine python /app/app.py
|
||||
Reference in New Issue
Block a user