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 ExecutionService def main(env=None) -> int: env = os.environ if env is None else env output_json = _get_bool(env, "EXECUTION_SETTLEMENT_OUTPUT_JSON", False) service = ExecutionService() signal_id = 1 execution_mode = str(env.get("EXECUTION_SETTLEMENT_MODE") or "paper") settlement_source = str(env.get("EXECUTION_SETTLEMENT_SOURCE") or "backtest_projection").strip().lower() try: observation = build_execution_observation_from_env(env=env) if settlement_source == "manual" else None settled = service.settle_latest_execution_for_signal( signal_id=signal_id, execution_mode=execution_mode, observation=observation, ) 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 settlement sample: {exc.__class__.__name__}") return 2 if settled is None: payload = { "passed": True, "signal_id": signal_id, "execution_mode": execution_mode, "status": "not_found", "execution_id": None, "settlement_state": None, "settlement_source": settlement_source, } _print_summary(payload=payload, output_json=output_json) return 0 payload = { "passed": True, "signal_id": signal_id, "execution_mode": execution_mode, "status": settled["status"], "execution_id": settled["id"], "settlement_state": ((settled.get("meta") or {}).get("lifecycle_state")), "settlement_source": ((settled.get("meta") or {}).get("settlement_source")), } _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["execution_id"] is None: print("Execution or latest backtest result not found") return print( f"Execution settlement updated: id={payload['execution_id']}; status={payload['status']}; " f"lifecycle_state={payload['settlement_state']}; settlement_source={payload['settlement_source']}" ) def build_execution_observation_from_env(env=None) -> dict: env = os.environ if env is None else env return { "source": "manual", "status": env.get("EXECUTION_SETTLEMENT_STATUS"), "fill_state": env.get("EXECUTION_SETTLEMENT_FILL_STATE"), "position_state": env.get("EXECUTION_SETTLEMENT_POSITION_STATE"), "close_reason": env.get("EXECUTION_SETTLEMENT_CLOSE_REASON"), "tp1_hit": _get_bool(env, "EXECUTION_SETTLEMENT_TP1_HIT", False), "tp2_hit": _get_bool(env, "EXECUTION_SETTLEMENT_TP2_HIT", False), "stop_loss_hit": _get_bool(env, "EXECUTION_SETTLEMENT_STOP_LOSS_HIT", False), "invalidated_before_entry": _get_bool(env, "EXECUTION_SETTLEMENT_INVALIDATED_BEFORE_ENTRY", False), "entry_triggered": _get_bool(env, "EXECUTION_SETTLEMENT_ENTRY_TRIGGERED", False), "pnl": _get_float(env, "EXECUTION_SETTLEMENT_PNL", None), } 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"} def _get_float(env, key: str, default): value = env.get(key) if value is None or value == "": return default return float(value) if __name__ == "__main__": raise SystemExit(main())