import json import uuid from datetime import datetime from time_utils import now_utc7 from log_utils import setup_logger from risk_manager import RiskManager from risk_manager import TradeMode, TradeProposal logger = setup_logger(__name__) class TradeExecutor: def __init__(self, risk_manager: RiskManager): self.risk = risk_manager self._pending_confirmations: dict[str, dict] = {} def prepare_trade(self, pair: str, direction: str, entry: float, sl: float, tp1: float, tp2: float = None, confidence: float = 0.5, source: str = "signal") -> dict: proposal = TradeProposal( pair=pair, direction=direction, entry=entry, sl=sl, tp1=tp1, tp2=tp2, confidence=confidence, source=source, ) result = self.risk.approve_trade(proposal) if not result["approved"]: return {"success": False, "reason": result["reason"]} trade = { "id": str(uuid.uuid4())[:8], "pair": pair, "direction": direction, "entry": entry, "sl": sl, "tp1": tp1, "tp2": tp2, "confidence": confidence, "source": source, "mode": result["mode"], "position_size": result["position_size"], "risk_amount": result["risk_amount"], "rr_ratio": result["rr_ratio"], "status": "pending" if result["mode"] == "semi_auto" else "executed", "opened_at": now_utc7().isoformat(), "closed_at": None, "pnl": None, } if result["mode"] in ("paper", "auto_controlled"): self.risk.open_position(trade) logger.info(f"Trade {trade['id']}: {direction} {pair} @ {entry} ({result['mode']})") trade["success"] = True else: self._pending_confirmations[trade["id"]] = trade trade["success"] = True trade["note"] = "Awaiting confirmation" return trade def confirm_trade(self, trade_id: str, approved: bool) -> dict: trade = self._pending_confirmations.pop(trade_id, None) if not trade: return {"success": False, "reason": "Trade not found or expired"} if not approved: logger.info(f"Trade {trade_id} rejected by user") return {"success": False, "reason": "Rejected by user"} trade["status"] = "executed" self.risk.open_position(trade) logger.info(f"Trade {trade_id} confirmed and executed") return {"success": True, "trade": trade} def close_trade(self, trade_id: str, pnl: float, reason: str = "manual"): self.risk.close_position(trade_id, pnl) logger.info(f"Trade {trade_id} closed at PnL {pnl} ({reason})") def get_pending(self) -> list[dict]: return list(self._pending_confirmations.values()) def execute_by_mode(self, signal: dict, trade_mode: str) -> dict: self.risk.set_mode(trade_mode) return self.prepare_trade( pair=signal.get("pair", ""), direction=signal.get("direction", "buy"), entry=signal.get("entry", 0), sl=signal.get("sl", 0), tp1=signal.get("tp1", 0), tp2=signal.get("tp2"), confidence=signal.get("confidence", 0.5), source=signal.get("source", "signal"), )