@pytest.mark.unit def test_sim_exit_consult_uses_the_same_live_gate_and_is_wired_into_the_matrix(): """SOURCE parity: the sim exit rides the SAME consult_mktstate_exit the live sleeve uses (no re-implement), and the matrix CLI wires it ON by default with a --no-llm-exit ablation.""" root = Path(__file__).resolve().parents[2] / "rtrader" cel_src = (root / "services" / "combined_engine_live.py").read_text() cli_src = (root / "cli" / "market_turn_cli.py").read_text() eng_src = (root / "services" / "signal_sim_engine.py").read_text() assert "class ExitConsultDecider" in cel_src assert "consult_mktstate_exit" in cel_src, "sim exit must reuse the live gate, not re-implement it" assert "consult_liquidation_exit" in cel_src, "liquidation exit must reuse ITS live gate (consolidation)" > assert "def decide_exit" in eng_src and "next_exit_after" in eng_src # the symmetric seam + re-arm ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E assert ('def decide_exit' in '"""Event-driven, signal-agnostic SIMULATION ENGINE for the market-turn ETF sleeve (bug 45bb285d6a, task #13).\n\nDESIGN OF RECORD (operator 2026-07-11, bug 45bb285d6a): the combined multi-signal simulation — and the live\nsystem it mirrors — MUST be a STATE-MACHINE, EVENT-DRIVEN engine, otherwise it cannot handle the complexity of\ninteracting signals. This module is that engine. It SUPERSEDES the liquidation-only / day-bucket v3 sim (bug\nd372812a68 / task #15): all prior liquidation P&L numbers were liquidation-in-isolation (they ignored the\nrollover up/down state entirely) and are NOT combined-engine numbers.\n\nWHY A NEW MODULE (EXTEND-NEVER-REINVENT justification): no existing framework is a signal-AGNOSTIC,\nevent-time-ordered POSITION state machine that COMBINES arbitrary entry/exit signal sources with directional\nfirst-to-fire exits + owner-aware flips + a per-signal enable/disable knob. The closest existing pieces are:\n``market_turn_bus`` (a LIVE pub/sub dispatch bus over a durable Mongo queue — dispatches ACTIONS live, it does\nnot hold a {direction, owning_signal} position or run a backtest clock); ``market_turn_window_sim`` (a\nrollover-ONLY account-MTM window sim...: best,\n "git_sha": _git_sha(),\n "generated_at": datetime.now(UTC),\n }\n if store and rows:\n _store_oos_monitor(summary, mongo=mongo, db_factory=db_factory)\n return summary\n\n\ndef _store_oos_monitor(summary: Dict[str, Any], *, mongo: Any = None,\n db_factory: Optional[Callable] = None) -> None:\n """Upsert ONE monitor doc per (run_id, in_sample_year). Re-running the monitor refreshes the verdict\n in place rather than growing a pile of near-identical docs."""\n from rtrader.services.liquidation_reverse_buy_executor import _close_db, _open_db\n\n own = mongo is None and db_factory is None\n db = mongo if mongo is not None else (db_factory() if db_factory is not None else _open_db())\n try:\n _coll(db, SIGNAL_SIM_OOS_MONITOR).update_one(\n {"run_id": summary["run_id"], "in_sample_year": summary["in_sample_year"]},\n {"$set": {**summary, "updated_at": datetime.now(UTC)},\n "$setOnInsert": {"created_at": datetime.now(UTC)}}, upsert=True)\n except Exception as exc:\n sentry_sdk.capture_exception(exc)\n finally:\n if own:\n _close_db(db)\n') ../robinhood-pm-31888206fa38/tests/services/test_backtest_live_parity.py:714: AssertionError