Refactor code for improved readability and consistency
- Cleaned up whitespace and formatting in various files including http.py, language.py, logger.py, safe_exec.py, and SQL migration scripts. - Consolidated import statements and removed unnecessary blank lines. - Updated logging configuration for better clarity. - Enhanced the safe execution code with improved error handling and logging. - Removed commented-out code and unnecessary variables in backfill_zero_trades.py and other scripts. - Added a pyproject.toml for Ruff and Vulture configuration. - Introduced requirements-dev.txt for development dependencies. - Removed commented-out stock entries in init.sql for cleaner migration scripts.
This commit is contained in:
@@ -4,7 +4,7 @@ MetaTrader 5 Trading API Routes
|
||||
Provides REST API for MT5 trading operations.
|
||||
"""
|
||||
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask import Blueprint, jsonify, request
|
||||
|
||||
from app.utils.logger import get_logger
|
||||
|
||||
@@ -23,7 +23,9 @@ def _ensure_mt5_imports():
|
||||
global MT5Client, MT5Config
|
||||
if MT5Client is None or MT5Config is None:
|
||||
try:
|
||||
from app.services.mt5_trading import MT5Client as _MT5Client, MT5Config as _MT5Config
|
||||
from app.services.mt5_trading import MT5Client as _MT5Client
|
||||
from app.services.mt5_trading import MT5Config as _MT5Config
|
||||
|
||||
MT5Client = _MT5Client
|
||||
MT5Config = _MT5Config
|
||||
except ImportError as e:
|
||||
@@ -45,6 +47,7 @@ def _get_client():
|
||||
|
||||
# ==================== Connection Management ====================
|
||||
|
||||
|
||||
@mt5_bp.route("/status", methods=["GET"])
|
||||
def get_status():
|
||||
"""Get MT5 connection status."""
|
||||
@@ -54,11 +57,9 @@ def get_status():
|
||||
status = client.get_connection_status()
|
||||
return jsonify(status)
|
||||
except ImportError as e:
|
||||
return jsonify({
|
||||
"connected": False,
|
||||
"error": str(e),
|
||||
"hint": "MetaTrader5 library is not installed or not on Windows"
|
||||
})
|
||||
return jsonify(
|
||||
{"connected": False, "error": str(e), "hint": "MetaTrader5 library is not installed or not on Windows"}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Get MT5 status failed: {e}")
|
||||
return jsonify({"connected": False, "error": str(e)})
|
||||
@@ -68,7 +69,7 @@ def get_status():
|
||||
def connect():
|
||||
"""
|
||||
Connect to MT5 terminal.
|
||||
|
||||
|
||||
Request body:
|
||||
{
|
||||
"login": 12345678, // MT5 account number
|
||||
@@ -78,64 +79,53 @@ def connect():
|
||||
}
|
||||
"""
|
||||
global _client
|
||||
|
||||
|
||||
try:
|
||||
_ensure_mt5_imports()
|
||||
|
||||
|
||||
data = request.get_json() or {}
|
||||
|
||||
|
||||
login = data.get("login") or data.get("mt5_login")
|
||||
password = data.get("password") or data.get("mt5_password")
|
||||
server = data.get("server") or data.get("mt5_server")
|
||||
terminal_path = data.get("terminal_path") or data.get("mt5_terminal_path") or ""
|
||||
|
||||
|
||||
if not login or not password or not server:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Missing required fields: login, password, server"
|
||||
}), 400
|
||||
|
||||
return jsonify({"success": False, "error": "Missing required fields: login, password, server"}), 400
|
||||
|
||||
config = MT5Config(
|
||||
login=int(login),
|
||||
password=str(password),
|
||||
server=str(server),
|
||||
terminal_path=str(terminal_path),
|
||||
)
|
||||
|
||||
|
||||
# Create new client with config
|
||||
_client = MT5Client(config)
|
||||
|
||||
|
||||
if _client.connect():
|
||||
account_info = _client.get_account_info()
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "Connected to MT5",
|
||||
"account": account_info
|
||||
})
|
||||
return jsonify({"success": True, "message": "Connected to MT5", "account": account_info})
|
||||
else:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Failed to connect to MT5. Check credentials and ensure terminal is running."
|
||||
}), 400
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"error": "Failed to connect to MT5. Check credentials and ensure terminal is running.",
|
||||
}
|
||||
), 400
|
||||
|
||||
except ImportError as e:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}), 500
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
except Exception as e:
|
||||
logger.error(f"MT5 connect failed: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}), 500
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@mt5_bp.route("/disconnect", methods=["POST"])
|
||||
def disconnect():
|
||||
"""Disconnect from MT5 terminal."""
|
||||
global _client
|
||||
|
||||
|
||||
try:
|
||||
if _client is not None:
|
||||
_client.disconnect()
|
||||
@@ -148,6 +138,7 @@ def disconnect():
|
||||
|
||||
# ==================== Account Queries ====================
|
||||
|
||||
|
||||
@mt5_bp.route("/account", methods=["GET"])
|
||||
def get_account():
|
||||
"""Get account information."""
|
||||
@@ -155,7 +146,7 @@ def get_account():
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
info = client.get_account_info()
|
||||
return jsonify(info)
|
||||
except Exception as e:
|
||||
@@ -170,7 +161,7 @@ def get_positions():
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
symbol = request.args.get("symbol")
|
||||
positions = client.get_positions(symbol=symbol)
|
||||
return jsonify({"success": True, "positions": positions})
|
||||
@@ -186,7 +177,7 @@ def get_orders():
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
symbol = request.args.get("symbol")
|
||||
orders = client.get_orders(symbol=symbol)
|
||||
return jsonify({"success": True, "orders": orders})
|
||||
@@ -202,7 +193,7 @@ def get_symbols():
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
group = request.args.get("group", "*")
|
||||
symbols = client.get_symbols(group=group)
|
||||
return jsonify({"success": True, "symbols": symbols})
|
||||
@@ -213,11 +204,12 @@ def get_symbols():
|
||||
|
||||
# ==================== Trading ====================
|
||||
|
||||
|
||||
@mt5_bp.route("/order", methods=["POST"])
|
||||
def place_order():
|
||||
"""
|
||||
Place an order.
|
||||
|
||||
|
||||
Request body:
|
||||
{
|
||||
"symbol": "EURUSD",
|
||||
@@ -231,28 +223,22 @@ def place_order():
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
data = request.get_json() or {}
|
||||
|
||||
|
||||
symbol = data.get("symbol")
|
||||
side = data.get("side")
|
||||
volume = data.get("volume") or data.get("quantity")
|
||||
order_type = data.get("orderType", "market").lower()
|
||||
price = data.get("price")
|
||||
comment = data.get("comment", "QuantDinger")
|
||||
|
||||
|
||||
if not symbol or not side or not volume:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Missing required fields: symbol, side, volume"
|
||||
}), 400
|
||||
|
||||
return jsonify({"success": False, "error": "Missing required fields: symbol, side, volume"}), 400
|
||||
|
||||
if order_type == "limit":
|
||||
if not price:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Limit order requires price"
|
||||
}), 400
|
||||
return jsonify({"success": False, "error": "Limit order requires price"}), 400
|
||||
result = client.place_limit_order(
|
||||
symbol=symbol,
|
||||
side=side,
|
||||
@@ -267,23 +253,22 @@ def place_order():
|
||||
volume=float(volume),
|
||||
comment=comment,
|
||||
)
|
||||
|
||||
|
||||
if result.success:
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"order_id": result.order_id,
|
||||
"deal_id": result.deal_id,
|
||||
"filled": result.filled,
|
||||
"price": result.price,
|
||||
"status": result.status,
|
||||
"message": result.message,
|
||||
})
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"order_id": result.order_id,
|
||||
"deal_id": result.deal_id,
|
||||
"filled": result.filled,
|
||||
"price": result.price,
|
||||
"status": result.status,
|
||||
"message": result.message,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": result.message
|
||||
}), 400
|
||||
|
||||
return jsonify({"success": False, "error": result.message}), 400
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"MT5 place order failed: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
@@ -293,7 +278,7 @@ def place_order():
|
||||
def close_position():
|
||||
"""
|
||||
Close a position.
|
||||
|
||||
|
||||
Request body:
|
||||
{
|
||||
"ticket": 123456789, // Position ticket
|
||||
@@ -304,38 +289,34 @@ def close_position():
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
data = request.get_json() or {}
|
||||
|
||||
|
||||
ticket = data.get("ticket")
|
||||
volume = data.get("volume")
|
||||
|
||||
|
||||
if not ticket:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Missing required field: ticket"
|
||||
}), 400
|
||||
|
||||
return jsonify({"success": False, "error": "Missing required field: ticket"}), 400
|
||||
|
||||
result = client.close_position(
|
||||
ticket=int(ticket),
|
||||
volume=float(volume) if volume else None,
|
||||
)
|
||||
|
||||
|
||||
if result.success:
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"order_id": result.order_id,
|
||||
"deal_id": result.deal_id,
|
||||
"filled": result.filled,
|
||||
"price": result.price,
|
||||
"message": result.message,
|
||||
})
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"order_id": result.order_id,
|
||||
"deal_id": result.deal_id,
|
||||
"filled": result.filled,
|
||||
"price": result.price,
|
||||
"message": result.message,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": result.message
|
||||
}), 400
|
||||
|
||||
return jsonify({"success": False, "error": result.message}), 400
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"MT5 close position failed: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
@@ -348,12 +329,12 @@ def cancel_order(ticket: int):
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
if client.cancel_order(ticket):
|
||||
return jsonify({"success": True, "message": f"Order {ticket} cancelled"})
|
||||
else:
|
||||
return jsonify({"success": False, "error": "Failed to cancel order"}), 400
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"MT5 cancel order failed: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
@@ -361,11 +342,12 @@ def cancel_order(ticket: int):
|
||||
|
||||
# ==================== Market Data ====================
|
||||
|
||||
|
||||
@mt5_bp.route("/quote", methods=["GET"])
|
||||
def get_quote():
|
||||
"""
|
||||
Get real-time quote.
|
||||
|
||||
|
||||
Query params:
|
||||
- symbol: Trading symbol (e.g., EURUSD)
|
||||
"""
|
||||
@@ -373,14 +355,14 @@ def get_quote():
|
||||
client = _get_client()
|
||||
if not client.connected:
|
||||
return jsonify({"success": False, "error": "Not connected to MT5"}), 400
|
||||
|
||||
|
||||
symbol = request.args.get("symbol")
|
||||
if not symbol:
|
||||
return jsonify({"success": False, "error": "Missing symbol parameter"}), 400
|
||||
|
||||
|
||||
quote = client.get_quote(symbol)
|
||||
return jsonify(quote)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"MT5 get quote failed: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
Reference in New Issue
Block a user