Supports MT5 and forex trading.
Signed-off-by: TIANHE <TIANHE@GMAIL.COM>
This commit is contained in:
@@ -4,6 +4,7 @@ Translate a strategy signal into a direct-exchange order call.
|
||||
Supports:
|
||||
- Crypto exchanges: Binance, OKX, Bitget, Bybit, Coinbase, Kraken, KuCoin, Gate, Bitfinex
|
||||
- Traditional brokers: Interactive Brokers (IBKR) for US/HK stocks
|
||||
- Forex brokers: MetaTrader 5 (MT5)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -28,6 +29,9 @@ from app.services.live_trading.bitfinex import BitfinexClient, BitfinexDerivativ
|
||||
# Lazy import IBKR
|
||||
IBKRClient = None
|
||||
|
||||
# Lazy import MT5
|
||||
MT5Client = None
|
||||
|
||||
|
||||
def _signal_to_sides(signal_type: str) -> Tuple[str, str, bool]:
|
||||
"""
|
||||
@@ -169,6 +173,24 @@ def place_order_from_signal(
|
||||
exchange_config=exchange_config,
|
||||
)
|
||||
|
||||
# Check for MT5 client (lazy import to avoid circular dependency)
|
||||
global MT5Client
|
||||
if MT5Client is None:
|
||||
try:
|
||||
from app.services.mt5_trading import MT5Client as _MT5Client
|
||||
MT5Client = _MT5Client
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if MT5Client is not None and isinstance(client, MT5Client):
|
||||
return _place_mt5_order(
|
||||
client=client,
|
||||
signal_type=signal_type,
|
||||
symbol=symbol,
|
||||
amount=qty,
|
||||
exchange_config=exchange_config,
|
||||
)
|
||||
|
||||
raise LiveTradingError(f"Unsupported client type: {type(client)}")
|
||||
|
||||
|
||||
@@ -228,3 +250,56 @@ def _place_ibkr_order(
|
||||
)
|
||||
|
||||
|
||||
def _place_mt5_order(
|
||||
client,
|
||||
*,
|
||||
signal_type: str,
|
||||
symbol: str,
|
||||
amount: float,
|
||||
exchange_config: Optional[Dict[str, Any]] = None,
|
||||
) -> LiveOrderResult:
|
||||
"""
|
||||
Place order via MT5 for forex trading.
|
||||
|
||||
Signal mapping for forex:
|
||||
- open_long / add_long -> BUY
|
||||
- close_long / reduce_long -> SELL
|
||||
- open_short / add_short -> SELL
|
||||
- close_short / reduce_short -> BUY
|
||||
"""
|
||||
sig = (signal_type or "").strip().lower()
|
||||
|
||||
# Determine action based on signal
|
||||
if sig in ("open_long", "add_long"):
|
||||
action = "buy"
|
||||
elif sig in ("close_long", "reduce_long"):
|
||||
action = "sell"
|
||||
elif sig in ("open_short", "add_short"):
|
||||
action = "sell"
|
||||
elif sig in ("close_short", "reduce_short"):
|
||||
action = "buy"
|
||||
else:
|
||||
raise LiveTradingError(f"Unsupported signal_type for MT5: {signal_type}")
|
||||
|
||||
# Place market order
|
||||
result = client.place_market_order(
|
||||
symbol=symbol,
|
||||
side=action,
|
||||
volume=amount,
|
||||
comment="QuantDinger",
|
||||
)
|
||||
|
||||
# Convert MT5Client result to LiveOrderResult format
|
||||
return LiveOrderResult(
|
||||
success=result.success,
|
||||
exchange_order_id=str(result.order_id) if result.order_id else "",
|
||||
filled=result.filled,
|
||||
avg_price=result.price,
|
||||
raw={
|
||||
"status": result.status,
|
||||
"message": result.message,
|
||||
"deal_id": result.deal_id,
|
||||
"raw": result.raw,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user