from pathlib import Path import json import os import sys from sqlalchemy.exc import SQLAlchemyError ROOT = Path(__file__).resolve().parents[1] os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://user:pass@localhost:5432/ai_ict_test") if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from src.services.execution import AccountRiskState, ExecutionService from src.services.market_state import MarketStateService, get_market_truth_supervisor def build_account_risk_state_from_env(env=None) -> AccountRiskState: env = os.environ if env is None else env return AccountRiskState( kill_switch=_get_bool(env, "EXECUTION_KILL_SWITCH", False), daily_pnl_pct=_get_float(env, "EXECUTION_DAILY_PNL_PCT", 0.0), consecutive_losses=_get_int(env, "EXECUTION_CONSECUTIVE_LOSSES", 0), open_positions=_get_int(env, "EXECUTION_OPEN_POSITIONS", 0), same_side_open_positions=_get_int(env, "EXECUTION_SAME_SIDE_OPEN_POSITIONS", 0), max_daily_loss_pct=_get_float(env, "EXECUTION_MAX_DAILY_LOSS_PCT", 0.02), max_consecutive_losses=_get_int(env, "EXECUTION_MAX_CONSECUTIVE_LOSSES", 3), max_open_positions=_get_int(env, "EXECUTION_MAX_OPEN_POSITIONS", 1), max_same_side_positions=_get_int(env, "EXECUTION_MAX_SAME_SIDE_POSITIONS", 1), ) def main(env=None) -> int: env = os.environ if env is None else env output_json = _get_bool(env, "EXECUTION_OUTPUT_JSON", False) service = ExecutionService( market_state_service=MarketStateService(market_truth_supervisor=get_market_truth_supervisor()) ) signal_id = 1 execution_mode = "paper" account_state = build_account_risk_state_from_env(env=env) try: decision = service.prepare_execution(signal_id=signal_id, execution_mode=execution_mode, account_state=account_state) if decision is None: payload = { "passed": True, "signal_id": signal_id, "execution_mode": execution_mode, "status": "signal_not_found", "decision": None, "execution_id": None, "settlement_state": None, } _print_summary(payload=payload, output_json=output_json) return 0 execution_id = service.create_execution(signal_id=signal_id, execution_mode=execution_mode, account_state=account_state) settled = None if execution_id is not None: settled = service.settle_latest_execution_for_signal(signal_id=signal_id, execution_mode=execution_mode) except SQLAlchemyError as exc: payload = { "passed": False, "signal_id": signal_id, "execution_mode": execution_mode, "error_type": exc.__class__.__name__, "error": str(exc), } if output_json: print(json.dumps(payload, default=str, ensure_ascii=False, sort_keys=True)) else: print(f"Database unavailable for execution sample: {exc.__class__.__name__}") return 2 payload = { "passed": True, "signal_id": signal_id, "execution_mode": execution_mode, "status": decision.status, "decision": _decision_to_dict(decision), "execution_id": execution_id, "settlement_state": ((settled or {}).get("meta") or {}).get("lifecycle_state"), } _print_summary(payload=payload, output_json=output_json) return 0 def _print_summary(payload: dict, output_json: bool) -> None: if output_json: print(json.dumps(payload, default=str, ensure_ascii=False, sort_keys=True)) return if payload["decision"] is None: print("Signal not found") return decision = payload["decision"] print( f"Execution allowed: {decision['allowed']}; status: {decision['status']}; " f"reasons: {decision['reasons']}; execution_id: {payload['execution_id']}; " f"settlement_state: {payload['settlement_state']}" ) def _decision_to_dict(decision) -> dict: return { "allowed": bool(decision.allowed), "status": decision.status, "reasons": list(decision.reasons), "execution_mode": decision.execution_mode, "requires_confirmation": bool(decision.requires_confirmation), "risk_pct": decision.risk_pct, "size": decision.size, "entry_price": decision.entry_price, "meta": dict(decision.meta or {}), } def _get_float(env, key: str, default: float) -> float: value = env.get(key) if value is None or value == "": return default return float(value) def _get_int(env, key: str, default: int) -> int: value = env.get(key) if value is None or value == "": return default return int(value) def _get_bool(env, key: str, default: bool) -> bool: value = env.get(key) if value is None or value == "": return default return str(value).strip().lower() in {"1", "true", "yes", "on"} if __name__ == "__main__": raise SystemExit(main())