Drawdown Circuit Breaker
Overview
Evaluate whether the trader should take new trade risk today based on account-level realized P&L and recent terminal trade outcomes. This skill reads trader-memory-core thesis YAML files only. It produces a circuit_breaker_decision artifact that complements the market-side exposure_decision from exposure-coach.
The circuit breaker is a recommendation and recordkeeping tool. It does not replace human judgment, and it does not enforce broker-side blocks or automated order rejection.
When to Use
- Before screening or sizing any new swing trade candidate
- After a losing trade or partial trim to check whether a cooldown is active
- During daily planning when trader-memory-core contains recent closed or partially closed positions
- As a workflow gate before swing-opportunity-daily proceeds to candidate generation
- When reviewing whether daily, weekly, or monthly loss limits have been breached
Prerequisites
- Python 3.9+
- Local trader-memory-core thesis YAML files, usually under
state/theses/ - Account size in dollars
- No API keys or network access required
Workflow
Step 1: Read Trader Memory State
Point the script at the thesis state directory:
bashpython3 skills/drawdown-circuit-breaker/scripts/check_circuit_breaker.py \ --state-dir state/theses \ --account-size 100000 \ --output-dir reports/
The script scans every th_*.yaml file and reads realized P&L from each thesis status_history[] ledger entry. It does not use _index.json for P&L, because the index is a lightweight lookup file and does not contain the required realized-P&L ledger.
After validating each file, the script groups valid theses by the case-sensitive, whitespace-trimmed thesis_id. If two or more valid files share an ID, it excludes the entire duplicate group from P&L and losing-streak calculations, reports every actual source path, and returns PARTIAL + HALTED until the duplicate state is repaired and the decision is rerun. metrics.theses_scanned counts only accepted theses with unique IDs.
If the state directory is missing or is an empty directory, the skill returns TRADING_ALLOWED with data_quality: EMPTY_STATE so a new user is not blocked by the absence of history. If the configured state path exists but is not a directory, the skill fails closed as incomplete state data.
If state exists but a thesis, ledger event, or terminal result must be skipped or conflicts with another recorded value, the skill fails closed with data_quality: PARTIAL, recommendation: HALTED, and an incomplete_state_data rule. Repair the warnings and rerun before taking new risk. The one recoverable exception is a finite terminal outcome.pnl_dollars fallback for a legacy thesis with no realized-P&L ledger entry; it remains visible as PARTIAL but does not by itself override the calculated recommendation. For ACTIVE, PARTIALLY_CLOSED, CLOSED, and INVALIDATED theses, each history event must be an object with a recognized status and parseable at, and the last history status must match the thesis status. ACTIVE and PARTIALLY_CLOSED theses must also carry entry actuals; PARTIALLY_CLOSED must carry a position. Malformed, stale, or skeletal lifecycle history disqualifies terminal fallback and halts. Ledger-shaped events whose realized_pnl is missing, untyped, or non-finite also halt instead of being coerced.
Step 2: Evaluate Circuit Breaker Rules
The default rules are:
| Rule | Default | Triggered State | Release |
|---|---|---|---|
| Max daily loss | 2.0% of account | HALTED | Next ET weekday |
| Losing streak cooldown | 2 terminal losing theses | COOLDOWN | 24 hours after latest loss exit |
| Weekly drawdown halt | 5.0% of account | HALTED | Next Monday ET |
| Monthly drawdown halt | 8.0% of account | HALTED | First day of next month ET |
Day, week, and month boundaries use America/New_York. Date-only producer
timestamps from trader-memory-core are counted on the named ET date. Set
--as-of for deterministic evaluation; date-only --as-of values cover the
full ET day, while timestamp values exclude future events after that time:
bashpython3 skills/drawdown-circuit-breaker/scripts/check_circuit_breaker.py \ --state-dir state/theses \ --account-size 100000 \ --as-of 2026-07-02T12:00:00-04:00 \ --output-dir reports/
Step 3: Override Thresholds When Needed
Override individual thresholds on the CLI:
bashpython3 skills/drawdown-circuit-breaker/scripts/check_circuit_breaker.py \ --account-size 100000 \ --max-daily-loss-pct 1.5 \ --losing-streak-n 3 \ --cooldown-hours 48 \ --weekly-drawdown-pct 4 \ --monthly-drawdown-pct 6
Or provide a JSON config file:
json{ "max_daily_loss_pct": 1.5, "losing_streak_n": 3, "cooldown_hours": 48, "weekly_drawdown_pct": 4.0, "monthly_drawdown_pct": 6.0 }
CLI arguments override config-file values.
Step 4: Interpret the Decision
Use the generated decision as a gate for new trade risk:
| Recommendation | Meaning |
|---|---|
| TRADING_ALLOWED | No circuit breaker rule is active; new trade risk may proceed through the rest of the workflow |
| COOLDOWN | Do not open new positions; continue managing existing positions and review the recent losses |
| HALTED | Stop new entries because a drawdown limit is active or account-state data is incomplete; repair/rerun any data warnings before proceeding |
Existing position management remains a human decision. The circuit breaker is designed to prevent new risk escalation after realized damage, not to force liquidation.
Time-based rules carry an ISO 8601 active_until. The non-time-based incomplete_state_data rule uses active_until: null; its Markdown report says the halt lasts until the state is repaired and the decision is rerun.
Output Format
The script writes circuit_breaker_decision_YYYY-MM-DD_HHMMSS.json and, unless --json-only is set, a matching markdown report.
json{ "schema_version": "1.0", "generated_at": "2026-07-02T16:00:00+00:00", "as_of_date": "2026-07-02", "recommendation": "COOLDOWN", "triggered_rules": [ { "rule": "losing_streak_cooldown", "threshold": 2, "observed": 2, "active_until": "2026-07-02T15:30:00-04:00", "detail": "2 consecutive losing closes; last loss exit 2026-07-01T15:30:00-04:00." } ], "metrics": { "realized_pnl_today": 0.0, "realized_pnl_wtd": -250.0, "realized_pnl_mtd": -250.0, "consecutive_losses": 2, "last_loss_exit_at": "2026-07-01T15:30:00-04:00", "theses_scanned": 12 }, "account_size": 100000.0, "config": { "max_daily_loss_pct": 2.0, "losing_streak_n": 2, "cooldown_hours": 24.0, "weekly_drawdown_pct": 5.0, "monthly_drawdown_pct": 8.0 }, "data_quality": "OK", "warnings": [], "rationale": "Recent losing closes triggered a cooldown. Avoid new entries until the cooldown expires." }
Exchange Calendar Contract
Install requirements.txt before running the checker. Daily, weekly, and
monthly halt dates use actual XNYS sessions. active_until remains compatible:
the halt ends at 00:00 America/New_York on the next eligible session date, not
at that session's opening bell. Use --as-of for deterministic evaluation.
Resources
scripts/check_circuit_breaker.py- Main CLI and rule enginereferences/circuit_breaker_framework.md- Rule definitions, defaults, and data-source notesskills/trader-memory-core/schemas/thesis.schema.json- Source schema for thesis state
Key Principles
- Realized damage only - Use recorded realized P&L, not unrealized P&L or thesis-level cumulative fields for daily calculations.
- Survival first - A circuit breaker exists to prevent escalation after losses.
- Advisory, not automatic execution - The output informs the workflow gate; it does not place, cancel, or block broker orders.
- Fail closed on incomplete state - Empty state allows a new user to begin, but malformed, discarded, conflicting, or non-finite risk data returns
PARTIAL+HALTEDwithout crashing. A finite legacy outcome fallback is reported as recoverablePARTIALand remains non-blocking.

