from pathlib import Path import json import os import sys from typing import Optional 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 scripts.run_backtest import ( build_backtest_timeframe_set_from_env, build_backtest_mode_from_env, build_cost_config_from_env, build_replay_config_from_env, build_quality_gate_from_env, build_require_quality_gate_from_env, ) from scripts.run_execution import build_account_risk_state_from_env from scripts.run_signals import build_signal_evidence_keys_from_env from src.main import run_closed_loop def main(env=None) -> int: env = os.environ if env is None else env output_json = _get_bool(env, "PIPELINE_OUTPUT_JSON", False) try: timeframe_set = build_backtest_timeframe_set_from_env(env=env) result = run_closed_loop( instrument_id=1, timeframe=timeframe_set["trigger_tf"], bias_tf=timeframe_set["bias_tf"], setup_tf=timeframe_set["setup_tf"], trigger_tf=timeframe_set["trigger_tf"], backtest_limit=100, run_execution=True, backtest_cost_config=build_cost_config_from_env(env=env), backtest_quality_gate=build_quality_gate_from_env(env=env), backtest_mode=build_backtest_mode_from_env(env=env), backtest_replay_config=build_replay_config_from_env(env=env), signal_required_evidence_keys=build_signal_evidence_keys_from_env(env=env), execution_account_state=build_account_risk_state_from_env(env=env), ) except SQLAlchemyError as exc: if output_json: print( json.dumps( {"passed": False, "error_type": exc.__class__.__name__, "error": str(exc)}, default=str, ensure_ascii=False, sort_keys=True, ) ) else: print(f"Database unavailable for pipeline sample: {exc.__class__.__name__}") return 2 print(json.dumps(result, default=str, ensure_ascii=False, indent=2)) quality_gate = _extract_quality_gate(result) if build_require_quality_gate_from_env(env=env) and (not quality_gate or quality_gate.get("passed") is not True): return 2 return 0 def _extract_quality_gate(result: dict) -> Optional[dict]: if result.get("backtest_quality_gate"): return result["backtest_quality_gate"] latest_backtest = result.get("latest_backtest") or {} summary = latest_backtest.get("summary") or {} return summary.get("quality_gate") 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())