Back to articles
DocumentationSource: backend/agents/notes/remediation_closed_loop.md

AUTHOR: Tony Mudau

Closed-loop remediation (Phases 1–4)

This document describes the concrete implementation of the remediation plan: wiring closed trades into technical memory, execution guards, performance adaptation, decision snapshots, and where to observe metrics.

Phase 1 — Closed trades → technical memory + idempotent sync

Modules

  • agents/memory_learning.py — SQLite rollups (symbol_trade_stats, setup_outcome_stats), apply_learning_from_closed_trade_id, reprocess_unprocessed_closed_trades, public alias sync_closed_trades_to_technical_memory.
  • agents/common.pytrade_history inserts/updates; close_trade_history_by_ticket triggers learning after a close.

Schema (trade_history)

  • memory_synced INTEGER DEFAULT 0 — primary idempotency flag once a closed row has been rolled into stats and record_trade_outcome (technical file memory) has run.
  • learning_processed — legacy mirror; both are set to 1 on success. Existing rows with learning_processed=1 are backfilled to memory_synced=1 on migration.
  • entry_context_json, entry_mtf_json — optional persisted decision context at entry (not close): core indicators vs H1/D1/HTF fields split from entry_learning so learning reflects the decision environment even if extra_context is trimmed later.

Resolution order when a trade closes

apply_learning_from_closed_trade_id builds the fingerprint using resolve_entry_learning_from_row: prefer entry_context_json + entry_mtf_json, else extra_context.entry_learning.

Who writes entry columns

  • insert_trade_history_row / update_trade_history_for_execution (status open) derive JSON columns from extra_context["entry_learning"] via _serialize_entry_learning_for_columns in common.py.

Orchestration

  • _build_entry_learning_snapshot in orchestration_agent/main.py stores session_label with other fields inside extra_context["entry_learning"] for schedule and execute-now paths.

Phase 2 — Freshness and stale-thesis guards

Module: agents/execution_guards.py

  • evaluate_execution_guards(mt5_tools, proposal, timeframe=..., extra_context=...)
    • Stale bars: last closed bar time vs now; max age = EXEC_GUARD_BAR_AGE_MULT × bar duration (timeframe_seconds).
    • Slippage vs proposal entry: compares live bid/ask to proposal["entry"] against EXEC_GUARD_MAX_SLIPPAGE_PCT.
    • Optional technical agreement: if EXEC_GUARD_REQUIRE_TECH_AGREEMENT is true, a short-window close trend must align with proposal["direction"] (micro trend from recent closes).
  • When MT5 is unavailable, bar/tick checks are skipped (documented in the guard result detail); unit tests inject last_bar_time_utc / execution_bid / execution_ask.
  • record_guard_rejection appends to execution_guard_events for dashboards.
  • guard_rejection_count_since supports rolling counts.

Wiring: agents/execution_agent/main.pyexecute_market_order runs guards before execute_market. On failure it returns {"skipped": True, "reason": ...}, updates trade_history with status skipped_guard, and records a decision snapshot when one was provided.

Scheduler: scheduled_trades.proposal_context_json (JSON) stores proposal_entry, timeframe, extra_context snapshot at schedule time. _scheduler_loop merges this into proposal and extra_context so guards see the original entry and timeframe.

Settings: app/config.pyEXEC_GUARD_*, EXEC_GUARD_REQUIRE_TECH_AGREEMENT.

Phase 3 — Performance-aware adaptation

Module: agents/performance_adaptation.py

  • symbol_adaptation_flags — recent win rate over ADAPT_SYMBOL_LOOKBACK; may set symbol_paused or reduce lot_multiplier vs ADAPT_SYMBOL_WINRATE_THRESHOLD / ADAPT_SYMBOL_LOT_SCALE.
  • on_closed_trade_for_adaptation — called from apply_learning_from_closed_trade_id after rollups: maintains pattern_loss_streaks (per setup_fingerprint) and session_trade_stats. After ADAPT_PATTERN_LOSS_STREAK consecutive losses on the same fingerprint, blocked_until is set for ADAPT_PATTERN_COOLDOWN_HOURS.
  • is_pattern_in_cooldown / effective_min_proposal_confidence — gates used in orchestration before duplicate/execute logic.
  • session_confidence_delta — if a session bucket is weak (enough trades, low win rate, losses ≥ wins), the minimum proposal confidence is raised by ADAPT_SESSION_CONFIDENCE_BUMP.

Orchestration: after portfolio + risk, adaptation may clear proposal (paused symbol, pattern cooldown, or session confidence floor).

Risk: risk_agent/main.py applies symbol_adaptation_flags["lot_multiplier"] again as a final lot scale (orchestration pause already removed bad symbols).

Settings: ADAPT_* keys in app/config.py.

Phase 4 — Decision snapshots (auditable intent)

Module: agents/decision_snapshots.py

  • build_decision_snapshot(...) — portfolio precheck subset, technical confidence/direction/strategy, MTF summary fields, optional reversal guard keys, final proposal confidence, risk block, entry_learning, timestamps.
  • insert_trade_decision_snapshot(trade_history_id, payload) — table trade_decision_snapshots (trade_history_id, optional scheduled_trade_id, symbol, snapshot_json).

Wiring: Orchestration builds a snapshot after adaptation passes and attaches it as extra_context["decision_snapshot"]. ExecutionAgent.execute_market_order persists it after update_trade_history_for_execution returns an id (including guard-skipped paths when a snapshot exists).

Suggested operational metrics

Daily or rolling SQL / exports:

Metric Source
Win rate, expectancy, net PnL by symbol trade_history (status='closed'), symbol_trade_stats
Win rate by strategy mode setup_outcome_stats / fingerprint includes strategy_mode
Win rate by session session_trade_stats
Average adverse excursion Not yet computed in SQL (would need tick/high-water marks per ticket)
Signal → execution latency decision_snapshot.signal_generated_at vs execution_started_at; scheduled path: proposal_context_json.signal_scheduled_at
Stale-thesis / guard rejections execution_guard_events; guard_rejection_count_since

Tests

backend/tests/test_remediation_phases.py — isolated DB via monkeypatch on DB_PATH (use pathlib.Path). Covers memory sync + entry columns, guard staleness/slippage, pattern cooldown, snapshot insert, and sync_closed_trades_to_technical_memory.

Run:

python -m pytest tests/test_remediation_phases.py -v

Related files (quick index)

Area Files
DB path / history CRUD agents/common.py
Learning + sync API agents/memory_learning.py
Guards + metrics table agents/execution_guards.py
Adaptation + pattern/session tables agents/performance_adaptation.py
Snapshots agents/decision_snapshots.py
Cycle + scheduler agents/orchestration_agent/main.py
Execution agents/execution_agent/main.py
Lot scaling agents/risk_agent/main.py
Tunables app/config.py