◆ DEEP GUIDE

Swing Deck Deep Guide

The power-user reference. About a 45-minute read top-to-bottom; mostly used as a lookup once you've been around the dashboard a few weeks.

If you haven't read Swing Deck Guide Reference yet, start there. This doc assumes you know what a card looks like, what a trap chip does, and that "the math cleared" is different from "the AI told you to."


Part 1 — How it ticks

1. The five-layer architecture

Swing Deck is a stack. Each layer answers a different question. They run in order; outputs feed downstream.

Layer What it answers Output
L1 — 11-point technical Is the tape worth trading? Composite score 0–100, grade A–F
L2 — 13 risk pillars (E1–E13) Does the math allow this trade? Pass / refuse, per pillar
L3 — 12-point war calibration When VIX > 25, what changes? Tighter caps, defensive overlays
L4 — 13-point options If options are open, is this strategy worth it? Long-call vs spread vs CSP, strike, DTE
AI surfaces (10 coaches) What does this mean? Triggered narration, structured output

The framework refuses. The dashboard narrates. AI explains. You decide. That order is the brand.

1.5 The two-sleeve architecture

The stack above governs the swing sleeve. Swing Deck actually runs two books in one account, with opposite risk philosophies:

Swing sleeveInvestor sleeve (◈ INVEST ROOM)
HoldDays–weeksYears
EntrySetup + score/R:R gateUnderwritten thesis + valuation band
Sizing1% risk off stop distanceValuation band (cheap→none at EXTREME)
StopHard stop, alwaysNone — exit only on thesis break
Exit triggerStop hit / score collapseINTACT → REVIEW → BROKEN
GovernorE1–E13 pillarsHard-max (35% sleeve / 10% name)

Capital carve-out — the two sleeves are sized off separate capital so neither distorts the other: swing_capital = NAV − max(INVESTOR_SLEEVE_CAPITAL, barbell_market_value).

Anchors enter the investor sleeve either curated (hand-picked, e.g. EOG/ICE) or via a broad quality/durability screen, then must clear the durability pillars (moat / balance sheet / reinvestment runway / patent-cliff read from the FDA Orange Book + SEC EDGAR feed — discovery-only, never sizes or gates). You underwrite with a written thesis after answering a devil's-advocate skeptic pass.

Protected holds — barbell names scored and tracked but locked out of swing actions (no auto-exit, options, or stop alerts; 🔒 HOLD).

Scoring itself runs through a lens architecture — independent frames (trend, momentum, structure, flow, value-quality) that each render a verdict and can issue a legible veto (a plain-English block with a reason, not a silent score cut). Every decision is logged to a corpus the framework calibrates against — does an 80 actually beat a 65? — so the gates tighten when confidence drifts.

2. The audit cycle, every 5 minutes

The daemon (audit_framework.py --watch) runs the cycle on a 5-min interval. One cycle, ~30–90 s for 7–10 tickers:

  1. Macro fetch — VIX, oil ($USD), brent ($USD), ^IRX. Determines war mode (VIX > 25), oil shock (Oil > $100), armor yield warn (^IRX < 4%).
  2. Regime classificationregime_engine resolves the active regime label (OIL_SHOCK, KINETIC, WAR, NORMAL) plus per-sleeve cap overrides.
  3. Per-ticker pass — for each symbol in portfolio.txt + candidates.txt: - Skip if cash armor (USFR/SGOV). - Fetch df_d (daily) + df_w (weekly) from yfinance/Polygon. - compute_daily(df_d) → indicators (EMA stack, RSI, ADX, MACD, ATR, BB, OBV, sweep_clean). - compute_weekly(df_w) → weekly trend + EMA alignment. - detect_catalyst(t) → earnings calendar, headline news, Fed events. - compute_structure(df_d) → primary_S, primary_R, levels_above, levels_below from 10 S/R sources collapsed via 0.15 × ATR confluence merging. - score_11pt(...) → composite, grade, bias, sl_val, tp1/tp2/tp3, rr. - score_12pt_war(...) if war mode. - score_13pt_options(...) if options engine enabled. - compute_composite(...) → final score with regime modifiers. - Pillar gate checks — sleeve caps, sector caps, hard-cash floor, drawdown. - Trap contradiction detection — see below. - Enrichment fetches: insider Form 4, float/short, RS-rank, gamma walls, comparable setups.
  4. System-level rebalance — if portfolio drawdown > 7% (E3) or hard-cash floor breached (E5) or armor yield drops (E12), portfolio_rebalancer writes rotation orders.
  5. Atomic JSON write to audit_output.json. Dashboard polls every 5 s.
  6. Alerts engine detects threshold transitions and emails on state changes (entry-eligible, near-stop, exit-review, etc.).

Heartbeat is written at every step (_write_audit_state) so the dashboard's audit signal can show progress and detect a stalled cycle.

3. Trap contradiction detection — the safety net

After scoring, the framework runs _detect_trap_contradictions(ind_d, ind_w, r11). Trap flags fire when two server-computed signals disagree in ways the methodology calls bagholder traps. Pre-v6.6.9 the framework had ALL the data to surface these but didn't — the user had to mentally reconcile contradictory chips. Trap surfacing is the methodology's "math floor not motivation" job.

Trap flag Fires when Translation
⚠ Trap: chase-the-top Strong weekly trend (ADX-W ≥ 30) AND overbought weekly RSI (≥ 75) Move already happened; you'd pay the top
⚠ Trap: bull-stack distribution Bullish EMA stack but Falling OBV with high vol Price up, smart money out
⚠ Trap: weak rally HH/HL but vol_ratio < 0.7 Rally on no volume — usually fails

Trap chips override the score. If you see one, the framework is refusing the trade even when the gate technically opened. Don't override.


Part 2 — The math

4. Layer 1 — The 11-point technical score

Each cell scores 0–10. Sum = 0–100 (Point 9 is the composite, not a separate scoring cell). Grades: A ≥ 90 · B 80–89 · C 70–79 · D 60–69 · F < 60.

# Cell What it reads
1 Trend Confirmation HH/HL on daily, EMA stack alignment, weekly bonus
2 Momentum & Strength RSI band (40–65 sweet spot), ADX (≥ 30 strong), MACD bullish + hist rising
3 Volatility Context BB width, ATR%, EMA21/price proximity, loose-trend bonus
4 Volume & Flow vol_ratio (today vs 20-day avg), OBV trend, sweep_clean
5 Whale Sentiment MarketAux news + AI confirmation coach output
6 Manipulation Detection Sweeps + OBV + vol confirmation; book-aware penalty (v6.7.43)
7 Trade Execution Price vs EMA21, structure type, entry quality near support
8 Position Sizing Vol-adjusted (0–5) + stop-tightness (0–5); cap-breach floor at conc=2
10 Macro Correlation VIX leg (0–3) + sector trend leg (0–3) + per-ticker RS leg (0–4)
11 Hidden Tape Call/put gamma ratio + put sweep + max-pain magnet + squeeze potential

Modifiers apply after the base sum:

Composite = max(0, min(100, base + modifiers)).

The cells are not equally weighted — they're equally scored. The 11-pt model trusts that a clean signal across cells matters more than any one cell's strength. That's why a single dead cell (e.g. macro at 5) doesn't tank the score, but five mediocre cells will.

5. Layer 2 — The 13 risk pillars (E1–E13)

Pillars 1–7 are baseline (always active). Pillars 8–13 are war-time / regime overlays.

Detailed mechanics per pillar:

E1 — Armor Cap (15% per asset)

Refuses: any single position > 15% of portfolio. Cash positions exempt. Structural — overrides "I'm so sure about this one."

E2 — Stop Discipline (Trade-the-Structure, v6.7.47)

Refuses: holding without a real broker stop. The stop sits at max(primary_S × 0.999, entry − 2×ATR) — tighter of structural support or 2×ATR floor. The 2×ATR floor preserves Pillar 7's spirit; structural support snaps closer when the framework can see real geometry. Mental stops negotiate. Broker stops don't.

E3 — Red-Line (7% portfolio drawdown)

Refuses: holding through a 7% drop from cost-basis high-water mark. Daemon writes 50%-cash pivot orders automatically. Multi-window thresholds added v5.6: today vs prior close ≤ −4%, week peak ≤ −7%, 30-day rolling ≤ −10%.

E4 — Score Gate (composite ≥ 80)

Refuses: new entries on tickers below B-grade (composite < 80). Gate is hard. Most setups don't clear it. By design.

E5 — Pre-Market Firewall (S&P futures −1.5%)

Refuses: new long entries when ES futures down > 1.5% at 7:25 AM MST. Exits and stop-raises still allowed.

E6 — Sector Cap (40% per theme)

Refuses: > 40% in any single sleeve (semis, AI infra, defense, pharma). Engine tags tickers at audit time. Stops the NVDA + AVGO + AMD + SMCI + MRVL "pretend diversification."

E7 — Weekly Confirmation

Refuses: new entries when weekly EMAs aren't aligned. Half the structural support is missing. Daily trend without weekly = lower-conviction setup.

E8 — Hard-Cash Floor (10%)

Refuses: going below 10% in USFR/SGOV/cash. The sizer refuses to allocate the last 10%. "If you're 100% invested, you can't add."

E9 — Earnings Proximity (≤ 3 sessions)

Refuses: new entries within 3 trading sessions of earnings. Holds and exits unaffected.

E10 — Defensive Oil Cap (15%, war regime)

Refuses: energy > 15% in war regimes (VIX > 25).

E11 — Hardware War Cap (30%)

Refuses: semis + compute infra > 30% in inversion regimes (natgas/oil/uranium term-structure inverted).

E12 — Velocity Exception (35%)

Refuses: sleeve > 35% even on momentum names. Velocity Track tickers can exceed the 25% Sports-Cars cap up to 35%, only while Velocity flag is active AND Armor Cash > 15%.

E13 — Diplomatic Decay (48h post-truce)

Refuses: full-conviction sizing within 48 h of a named truce/summit (Ukraine, Gaza, Taiwan). Sports-Cars score penalized −10. Geopolitics matters to your portfolio whether you like it or not.

The pillars are gates, not rules. They don't generate trades. They filter out trades your technical scanner wants to take but shouldn't.

6. Layer 3 — War mode (VIX > 25)

Activates automatically. What flips:

What you do differently:

Back to baseline triggers when VIX < 25 for 5 consecutive cycles, or when the regime engine clears the WAR label via macro re-classification.

7. Layer 4 — 13-point options (Level 2 only)

Activates when OPTIONS_ENGINE_ENABLED = True in config and the ticker has a Tradier chain available.

The 13 cells (each 0–10):

  1. Structure & Trend — same logic as L1 Point 1 with options-aware loose-trend bucket
  2. Volatility regime — VIX-driven; tighter when VIX > 25
  3. IV Rank — ≥ 30 floor, ≤ 80 cap; deep-ITM filter when in VIX trap
  4. Theta Budget — portfolio theta ≥ MIN_PORTFOLIO_THETA before short premium
  5. Delta Target — −0.30 to 0 for short puts; matches strategy
  6. DTE Preference — 14-day default; 7–60 acceptable
  7. Liquidity — bid-ask < 10%, OI > threshold
  8. Strategy Fit — long call vs vertical spread vs CSP, picked by IV regime
  9. Sentiment Alignment — MarketAux / AI thesis must match strategy direction
  10. Macro Correlation — same VIX/sector/RS structure as L1
  11. Hidden Tape — gamma exposure ratio, dealer GEX, max pain
  12. Manipulation — options-specific sweep + book skew detection
  13. Energy Penalty — applies when oil shock + high-energy ticker

Strategy picker logic:

When to ignore the recommendation:

The options layer is opt-in. If you trade equities only, you can leave it disabled — the dashboard hides the panel. If you trade options, the layer is your structured second opinion. It doesn't pick the trade. It scores it.


Part 3 — The UI, deep

8. Card anatomy — every signal, threshold, edge case

A ticker card has six zones. Top to bottom:

Header

Score row

Whale toolkit (3-col)

Live-data chips (when present)

Execution levels

Position sizer

11-point audit grid (bottom row)

A 10-cell grid showing the sub-scores that roll up into the composite. Labeled 11-POINT AUDIT (10 sub-scores + 1 composite). Cells: Trend, Momo, Vol, VPos, Whale, Manip, Exec, Size, Macro, Tape — each 0–10, color-coded. Scan for amber/red cells to know which axis is the weak link. See §8.1 for the full mapping to internal sub-score fields and what each measures.

Flags

8.1 Entry signal badges — types and decision paths

Section 8 covered card zones. This section covers the badges that change state cycle-to-cycle — what each one means, what triggers it, and how it interacts with the rest of the gate stack. New users: read this once; you'll recognize every badge thereafter. Existing users: this is the canonical reference for "what's the difference between ARMED and ready_vetoed."

The single-line read

Every audit row produces five state badges that together answer "is this tradeable right now?" Read them top-to-bottom. Each is independent. A green ARMED at the bottom with a yellow bias at the top is the card saying yes and no at the same time — and yes-and-no resolves to no in this framework.

The five chips, in reading order
1. Score 85 / B ← "How good is the setup at all?" (0–100, A–F)
2. Bias ACCUMULATE ← "What would the framework do with this row?"
3. Structure HH / HL ← "What shape is the price action?"
4. Setup pullback_to_ma ← "If actionable, what kind of setup?"
5. Trigger ● ARMED — 5/5 ← "Are all per-setup criteria met right now?"

Five paths to ARMED (and what each means)

The dashboard surfaces five different entry-trigger paths. They are not interchangeable. Each one has its own color-coded path card so you can tell them apart at a glance.

PATH 1 ● ARMED — 5/5 Generic Wheel-of-Setups trigger
  • Above both EMA21 and EMA50
  • Not extended (dist21 < 2 ATR)
  • MACD bullish
  • RSI in 45–70 sweet spot
  • Volume ≥ 1.0× avg

The default trigger. Used by pullback_to_ma, breakout, trend_continuation, reversal setups. Per-setup criterion lists vary (breakout needs vol ≥ 1.5×; pullback needs dist ≤ 0.6 ATR). When 5/5 passes + score ≥ ENTRY_SCORE_GATE (80) + R:R ≥ 2.0 + weekly aligned + no trap flags → alert chimes. Held-aware tier resolver downgrades to info-tier if you already own shares.

Path 2 — Episodic Pivot entry-trigger (Kullamägi setup, currently shadow-only)

The EP overlay re-classifies a row's setup_type to episodic_pivot_pending when a catalyst day exists in the last 5 sessions: ≥ 4% move, ≥ 2× volume, close in upper half of range, and the catalyst close cleared the prior 20-day resistance.

When EP is detected AND today's bar breaks the consolidation high AND today closes in the upper half AND today's volume ≥ 1.2× the trailing 20-day avg, swing_ops.ep_entry_trigger.ready=True. Distinct chip — ⚡ EP ARMED — distinct alert type (entry_armed_ep).

Currently SHADOW-ONLY. config.EP_ENTRY_ALERTS_ENABLED = False by design. The framework emits the trigger and the shadow corpus records every fire with fired-entry geometry (entry = today's close, stop = catalyst-day low, TP = 3R/6R/10R Kullamägi ladder). Alerts stay silent until ≥ 30 resolved outcomes at ≥ 50% win rate and ≥ 2.5R avg winner (the v7.9.x §A.7 promotion gate). When the corpus clears those thresholds, the flag flips and entry_armed_ep alerts go live.

Read this as: the EP trigger is being calibrated in production right now, against your own basket, in real time. When it graduates it will fire alerts on the next live EP detection.

Path 3 — Bull Flag entry-trigger (similar shadow path)

Same shape as EP, different pattern: a prior trend ≥ 30% over 60d, then a tight flag pullback < 25% over 3-21 sessions. When the flag breaks with confirming volume, swing_ops.bull_flag.entry_ready fires the trigger. 🚩 BF ARMED chip, shadow-only until corpus promotion.

Path 4 — Consolidation Breakout Pending overlay

PATH 4 🔋 CBP — 4/4 Consolidation Breakout Pending — the coiled spring
  • Score ≥ 80 (quality clears gate)
  • Price within 2% below resistance
  • ATR contracting vs 22d avg
  • Volume not collapsing (≥ 0.7×)
  • Awaiting breakout above resistance — the trigger event

Not technically an "ARMED" state — it's a queue state. When a row's base setup would be ranging or consolidation but the score has cleared the gate AND price is within 2% of resistance AND ATR is contracting AND volume isn't collapsing, the setup_type re-classifies to consolidation_breakout_pending. Not an entry alert; a watch chip. Use it to set a stop-buy at the resistance level. When CBP rows actually break, they transition to breakout setup_type next cycle and Path 1 takes over.

Path 5 — No trigger / framework-refuses paths

Several non-ARMED states are still informative and end up in the shadow corpus for calibration:

Both cohorts are watched in tools/shadow_corpus_summary.py. If ready_vetoed ever drifts above 30% win rate, the gates are too aggressive. If score_cleared_setup_blocked ever drifts above 50%, the setup classifier is too restrictive. Both currently sit at 0% win rate — the framework's refusals are calibrated correctly.

How to read the WAIT message

When a trigger is partial — say, 3/5 on a generic breakout — the card shows "WAIT — N/M criteria met" with the failing criterion named in the tooltip. The named criterion is the only thing standing between you and ARMED. Read it specifically:

Each is a different reason to wait. The badge names the specific lever you're waiting for.

Bias badges — what the framework wants you to do

The bias chip is what the framework would do with this row if it had your portfolio. Six states:

A bias flip from HOLD to DE-RISK 50% mid-session is a regime-warning state. The framework saw something change between cycles that materially affected the trade quality. Re-read the flags row — something there is new.

Structure badges

Three primary states plus one overlay:

The chip color codes match the bias: green for HH/HL, neutral/amber for Ranging, red for LH/LL.

Structure must agree with setup type for the trigger to be tradeable. A breakout setup on Ranging structure is contradictory — the breakout overlay needs HH/HL underneath. The framework's hysteresis guards against this misalignment but if you see it, the row is in transition and you're catching it mid-classification.

Setup type badges (eight kinds, ordered by tradeability)

Setup Structure prereq Entry trigger gate What it is
pullback_to_maHH/HL0 ≤ dist21 ≤ 0.6 ATR + MACD bull + weekly aligned + clean sweep + RSI > 40 + vol ≥ 1.2×Price pulled back to 21-EMA in confirmed uptrend.
breakoutHH/HLdist21 > 1.5 ATR + vol > 1.2× + MACD bullPrice > 1.5 ATR above EMA21 on volume.
trend_continuationHH/HLabove EMA21 + EMA50 + dist21 < 2 ATR + MACD bull + RSI 45-70 + vol ≥ 1.0×Trend intact, not extended, momentum confirmed.
reversalLH/LL → reclaimreclaimed EMA21 + MACD turned bullish + clean tape + RSI > 45 + vol ≥ 1.2×Lowest-conviction tradeable.
consolidation_breakout_pendingRanging + CBP criteria(queue state, not entry)Coiled spring — watch for break above resistance.
consolidation / rangingRanging(not actionable)Sideways. Wait for break.
downtrendLH/LL(not actionable, veto)Downtrend confirmed. Avoid longs entirely.
episodic_pivot / bull_flagoverlayPath 2/3 (specific)Special overlays — distinct from generic setups.

R:R chip — the gate-pass color and what to do when chip and gate disagree

The R:R chip shows the displayed ratio from the current entry / SL / TP1 levels. Color:

A separate R:R RELEASE chip appears when chip math says one thing but the gate sees another — typically because the canonical R:R reader uses different anchors (e.g., max(structural, breakout) on HH/HL setups). When this chip is present, the framework is being transparent that two valid R:R readings exist and they disagree. Trust the gate read for the entry decision; trust the chip read for sizing math. The chip will name the lever (lower entry / raise TP1 / tighten SL) that would close the divergence.

When the chip reads red but the gate reads green, the setup is technically pass-the-gate but the displayed execution levels don't reflect that pass — you'd need a different entry price OR a different TP target to make the chip and gate agree. Don't tighten the stop to force agreement. That's the gate-bypass anti-pattern; see the DXCM walkthrough in the Reference Guide.

Trap flags — when the framework vetoes against the score

Trap flags are the framework's veto layer. They sit on top of the score and override even a clean 85-score+5/5-ARMED combination. The flag list is open-ended (new traps add over time) but the canonical set as of v7.8.161:

Trap flags trump the score. A score-90 row with a ⚠ Trap: chase-the-top flag is a no-go regardless of how good the other badges look. Match this against your own willpower honestly: if you'd be tempted to override the trap, you've already lost the discipline argument.

Whale toolkit (3-col) — the auxiliary state

Three secondary badges in the middle of the card give institutional-flow context:

The 11-point audit sub-score grid (bottom row of the card)

At the very bottom of each card sits a 10-cell grid labeled 11-POINT AUDIT (10 sub-scores + 1 composite). Each cell is 0–10, color-coded by value. The composite at the top of the card is what these roll up to; the sub-score grid is how it got there. Scan for amber/red cells to know which part of the setup is the weak link.

Cell Sub-score (internal field) What it measures
Trendtrend_confirmationDaily structure (HH/HL) + weekly alignment
Momomomentum_strengthRSI/MACD combination — overbought penalty above 75
Volvix_contextVIX regime fit — calm/elevated/war modifiers
VPosvolume_nodesVolume confirmation — vol_ratio vs 20d avg
Whalesentiment_sweepsNews + insider + sweep detection — institutional flow read
Manipmanipulation_trapTrap detector — chase, bull-stack, distribution patterns
Execexecution_guardSpread, liquidity, slippage envelope
Sizeposition_sizingR:R floor + cap headroom check
Macromacro_correlationRegime fit (STAGFLATION / OIL_SHOCK / etc) and sector bias
Tapehidden_tapeGamma walls + dark-pool prints (Pillar 11)

The composite is computed from these cells with modifier weights (SL breach −20, energy-tech inversion −10, etc.). Grades: A=90+, B=80–89, C=70–79, F<70. Read the cells before reading the composite — the composite hides which axis is failing; the grid surfaces it.

The 13 risk pillars (E1–E13) — backend gate layer, not card chips

The 13 pillars are the framework's backend exposure-and-gate layer. They don't render as a row of chips on every card. They show up as:

The canonical pillar set as of v7.8.161:

Pillar Name What it gates
E1Armor Cap15% per-asset defensive exposure cap
E2Stop DisciplineTrade-the-Structure SL anchoring
E3Red-Line7% portfolio drawdown auto-cash pivot
E4Score GateComposite ≥ 80 to initiate
E5Pre-Market FirewallS&P futures −1.5% kill switch
E6Sector Cap40% per-theme exposure cap
E7Weekly ConfirmationWeekly trend must align with daily
E8Hard-Cash Floor10% USD/SGOV minimum
E9Earnings Proximity≤ 3 sessions window block
E10Defensive Oil Cap15% in war regime
E11Hardware War Cap30% during energy inversion
E12Velocity Exception35% max sleeve
E13Diplomatic Decay48h post-truce cooldown

A pillar violation produces a flag, not a chip. When you see a trap flag on a row, look up which pillar it's the surface form of — the trap is the symptom; the pillar is the rule being enforced.

When two badges disagree — ARMED + AUDIT GATE REFUSES

The most important disagreement pattern to learn. The trigger chip can be GREEN while a RED banner says the audit gate refuses. Below is the canonical example — the actual modal from DXCM on 2026-05-20.

Swing Ops modal — trigger ready but gate refuses
DXCM Swing Ops ● READY — 4/5 (4 req)
  • Volume ≥ 1.5× avg (breakout thrust)
  • MACD bullish
  • Price > 1.5 ATR above EMA21
  • RSI < 75 (room to run)
  • Weekly trend aligned
⚠ AUDIT GATE REFUSES — Setup armed but not approved
• Score 78/100 (Grade C) — below the 80 floor
Trigger mechanics passed. Composite gate refused. Framework default: no-trade.

What this means: the entry mechanics fired, but the composite gate doesn't approve. Score below 80, R:R below 2.0, weekly unaligned, or a trap flag firing — any one will produce this disagreement. The trigger is necessary, not sufficient. Framework's safest default: no-trade when these disagree.

Other disagreement patterns

Same-field-different-writers is a real bug class the framework has hit 15+ times across v7.x. The architecture now enforces canonical readers (rrForDisplay, rr_canonical_from_row, etc.) so chips and alerts agree by construction. But when you see disagreement on screen:

The AND-gate decision pipeline

A useful mental model: every entry decision passes through a series of gates. The trigger gives the trade idea. The score gate, the R:R gate, the weekly gate, the trap-flag gate, the pillar-cap gate, the sizer cap, and the bias each get a separate veto. Any one of them blocks, the whole trade blocks.

The decision pipeline — every entry passes through these gates
Trigger 5/5 ARMED Score gate ≥ 80 R:R gate ≥ 2.0 Weekly aligned Trap flags none firing Pillar caps E1–E13 limits Sizer 25% per-asset Bias ACC or HOLD EXECUTE alert chimes / order eligible Any one box failing → trade blocked. AND-gated, not OR-gated.

What changed in v7.x for entry badges

If you're returning from a pre-v7.5 build:

9. The modal — every tab

Click any card. Modal opens. Header has:

Tabs:

Critical UX rule (v6.7.17): the modal is sacred during a session. Any code path that mounts a coach panel must respect _isModalOpen() or the LLM-loaded coach content gets destroyed. If you're hacking on the dashboard and a coach disappears mid-session, that's the rule you broke.

10. The 10 AI coaches — when, why, what

Coach Trigger Payload it gets What it produces
Pillar Coach Pillar violation/warn Violated pillar + position state + caps "You're at X% in Y; pillar Z says reduce to W"
Exit Coach Score < 70 OR SL approached Position state + new score + signals Sell vs reduce vs wait, with reasoning
Entry Coach ARMED state Full entry context, pillars, R:R, weekly Thesis vs risk in one read
Devil's Advocate Conviction high (≥ 90 + ARMED) Bull thesis + position context Bear case, adversarial — that's the point
Thesis Drift 14d-old AI thesis vs today Both theses + signal diff Where the read changed and why
Position History Audit Held position with score drop Trade history + audit timeline Why this position is no longer scoring
Comparable Setups Today's tape matches prior Comparables + outcome stats What happened last time
Catalyst Interpreter News / earnings / Fed detected Catalyst details + ticker context What it means for this ticker
Whale Confirmation Flow contradicts price Whale data + price action Smart money is in or out
Trap & Structure Trap detector fires Structure state + contradiction details Walks the contradiction in detail

Coach payload contract: each coach receives structured inputs (no free-text). Each produces structured output that the dashboard renders into a fixed panel. No chat box. Ever. The coaches are bound to framework events, summoned by triggers, never by free-form questions.

Threshold-citation guard (v6.6.40+): all coaches inject _THRESHOLD_CITATION_GUARD into their prompts so they cite payload thresholds (entry-score gate, R:R floor, sleeve cap, sector cap, stop %) rather than hardcoding them. This is why a coach saying "the 80/100 entry gate" vs "a 80/100 entry gate" matters: the first is canonical, the second is the AI hallucinating a threshold the framework didn't pass it.

Cache TTL: 5 minutes per coach per ticker. Stale-coach badge fires on the panel when audit data refreshes without a re-classify; user clicks ↻ Regenerate to refresh.

Cost tracking: every coach call logs to ai_thesis_log.jsonl with token counts + model + cost. Cost-meter incident (2026-04-28) caught the bug where the cost calc was missing web_search_requests fees — that's now part of the canonical accounting ($10/1000 web_search calls).


Part 4 — Execution

11. Broker integration — preflight, brackets, ladder

The 8-check preflight chain

Every BUY order runs broker_guard.preflight_new_order() before E*TRADE sees it. Strictly ordered, fail-fast:

# Guard Refuses
0 guard_writes_enabled Master kill switch (set BROKER_WRITES_ENABLED=false to disable all writes)
1 guard_circuit Recent failures > threshold; circuit breaker open
2 guard_rate_limit E*TRADE rate-limit (50/min default)
3 guard_audit_freshness Audit > 15 min old (G6 stale_audit)
4 G1 trade_dollar_cap Notional > MAX_TRADE_USD
— preview returns here —
5 G2 daily_order_budget Rolling 24h order count > MAX_DAILY_ORDERS
6 G4 symbol_cooldown Re-order on same symbol within cooldown
7 G3 new_order_token HMAC token from preceding preview, binds (cid, account, symbol, qty, action, price_type, prices)

G5 — R:R minimum runs inside G1 (v6.5; v6.6.4 limit-price-aware). Refuses BUY when audit R:R < BROKER_RR_MIN (default 2.0). For LIMIT orders, recomputes R:R from planned fill price. Set BROKER_RR_MIN=0 to disable.

Stages: - stage="preview" → skips G2 + G3 (no order placed) - stage="place" → full chain - stage="cancel" → only 0–2 (kill switch never blocks a cancel)

Bracket order flow

Dashboard sends a preview first to get a preview_id. Then place_bracket_order submits the BRACKETED body with two legs:

  1. Entry leg — STOP_LIMIT or LIMIT, GOOD_FOR_DAY
  2. SL leg — STOP or STOP_LIMIT, GOOD_UNTIL_CANCEL (stays after entry fills)

The SL leg auto-opposites the entry action (BUY → SELL, SELL_SHORT → BUY_TO_COVER).

E*TRADE returns order IDs for both legs. Reconciler tracks status.

TP ladder reconciler

broker_tp_ladder.py manages the 40/40/20 scale-out. State file: broker_ladder_state.json. Per ticker:

  1. Snapshot original_qty so 40/40/20 is computed against the starting position even if partials fill.
  2. TP1 placed immediately at the recommended price.
  3. TP2 + TP3 queued internally (NOT placed at broker yet).
  4. Reconcile cycle: query broker for current leg status: - OPEN → keep waiting - FILLED → mark done, advance + place next leg - CANCELLED → user cancelled at broker; mark paused (don't auto-advance)
  5. Source of truth = broker. Local state file is a CACHE reconciled every cycle. Orphan orders we don't recognize are ignored.

Recovery from broker failures

12. Calibration & customization — what to touch, what not to

settings.json (canonical)

Key Default When to change
BROKER_RR_MIN 2.0 Rare. Set 0 to disable G5.
BROKER_MODE live Switch to dryrun for testing without firing real orders.
BROKER_WRITES_ENABLED true Set false for read-only analyst mode.
LICENSE_KEY required Set once, don't touch.
OPTIONS_ENGINE_ENABLED false Enable for L4 options scoring. Requires Tradier chain access.

settings.local.json overrides

Per-user / per-machine tweaks that don't ship to the repo. Same shape as settings.json. Wins over settings.json for any key that's set.

Env vars (for Docker / cloud)

Things you should NOT touch

The framework has been calibrated. If you find yourself wanting to lower the gate to "let more setups through," that's the urge the framework is built to refuse. Tighten your watchlist instead.


Part 5 — Operating reality

13. Edge cases & recovery

Symptom Most likely cause Fix
Audit signal red, > 15 min stale Provider timeout, framework wedged Check topbar; hit refresh; restart daemon if persistent
Topbar broker chip red E*TRADE token expired Settings → re-authorize
All cards grey yfinance rate-limited or down Wait one cycle; check audit_dashboard.log
Coach panel says "loading" forever LLM provider timeout Click ↻ Regenerate; check API key in env
Alerts not firing alerts_engine daemon stopped ps aux | grep alerts; restart
Bracket order rejected (code 100) COID > 20 chars OR genuine service outage Check client_order_id in log; if < 20, retry after backoff
Score moved 5+ points cycle-to-cycle Live data refresh or sentiment swing Look at the cell breakdown; if no clear cause, restart for clean state
TP ladder paused User cancelled at broker Modal → review legs → resume or reset
Modal coach disappeared mid-session Code path didn't respect modal-open guard Bug; reload the page
Version banner says "force upgrade" App version below MIN_VERSION git pull && restart

The "everything turned grey" moment: usually one of three things — provider degradation, license check failing, or the framework process died. Check audit_dashboard.log first. The log will tell you which one.

14. Case studies library

These are the calibration moments that shaped the framework. Each is ~½ page. Read them once; the patterns recur.

VRT TRAP→NEUTRAL (the inverted swept-low rule)

What fired: Original swept-low rule classified VRT as TRAP whenever today swept the prior-day low and close > primary support. VRT had been chronically misclassified TRAP on routine pullbacks.

Root cause: A swept-low that closes above support is a bullish reversal, not a trap. The rule was inverted from the bullish-swept-high TRAP logic without flipping the inequality.

Fix: Geometry classifier now reads swept-low + close > S as NEUTRAL (bullish potential reversal, not failure).

Lesson: When a rule fires on every routine pullback, re-read the rule. It's probably inverted.

Companion case: VRT also produced a published blog post about a separate moment — score 60/D / NO ENTRY — TRAP on VRT despite "trend continuation" being structurally true. R:R was 1.5 (below 2.0 floor); ADX 22; volume 0.52 × normal; RSI 69 daily / 81 weekly; 5 wick sweeps. Framework's read: "Someone is painting this chart sideways-up while distributing — don't be the bagholder who buys $328 on 0.5 × volume after a 30% run." User didn't pull the trigger. Hindsight: would have been right as an investment, not a trade. Trade-quality and structure are separate questions.

AAPL EXTREME→SCHEDULED (10b5-1 distinction)

What fired: insider_transactions._classify_signal() originally flagged any portfolio with ≥ 5 sells, 0 buys, ≥ $5M as EXTREME_SELLING. AAPL fired EXTREME with $25M of sells.

Root cause: ≥ 80% of that volume was 10b5-1 pre-planned — insiders committed months ago while not in possession of MNPI. Today's volume reflects a calendar, not a decision.

Fix (v6.5.0a-4): SCHEDULED_SELLING short-circuit before discretionary classifiers:

if is_predominantly_10b5_1 and sell_count > buy_count:
    return "SCHEDULED_SELLING"

The same false-positive is now an amber/neutral chip when ≥ 80% of dollar volume carries the 10b5-1 flag.

Lesson: Insider data needs context. The number alone is misleading; the type of sale carries the signal.

NVDA TRAP→CHASE (auto-rule override)

What fired: structure_classifier.auto_classify_state() returned TRAP on NVDA from a swept-low pattern.

Root cause: The level wasn't a structural support; price was extended > 1.5 × ATR from S/R; momentum-only setup. That's CHASE, not TRAP.

AI synthesizer override: Flipped the verdict from TRAP to CHASE based on geometry the rules didn't capture.

Lesson: "The auto-rule is the prior, not the verdict — the AI then has a chance to override based on geometry the rules don't capture." Rules give the prior; AI gives the verdict. Both ship to the user's panel with rule reasoning AND AI override note.

PLTR insider sell ($435M Thiel)

What fired: PLTR's insider chip read 🚨 INSIDER SELL ($435M).

Detection: 227 sales vs 0 buys in 6 months. Peter Thiel sold 4M shares in 90 days. Director Moore sold 16k on April 15.

Pattern caught on 3 of 6 calibration tickers: PLTR (Thiel), GOOGL (Pichai 2.53M), NVDA (953,976 shares + 15:0 ratio).

Lesson: Insider-selling detection landed on 3 of 6 calibration tickers — the #1 signal AI consistently catches that the 11-pt framework misses. Drove the v6.5 priority bump making Form 4 ingestion canonical.

MU 2026-05-05 (the trap chip in action)

Setup: - Score: 80/B · HOLD — Bullish · 80 - R:R: 2.45 (clears 2:1 floor) - Entry $640.20 · SL $599.75 (struct, ~6% drawdown) · TP1 $739.42 (3×ATR — no structural cap) - Trap flag: ⚠ Trap: chase-the-top - Weekly: ADX 41.5 (extreme trend) · RSI 80.3 (extreme overbought)

Temptation: every momentum signal is screaming yes. Score is a clean 80. R:R is fine. Daily HH/HL. Bullish news. Call-dominant gamma.

Framework's read: Trap detector caught what the score gate missed. Strong weekly trend on overbought RSI = bagholder pattern. The move has happened. compute_structure couldn't find a primary_R for MU — so the framework can't see where price would actually pause. TP1 sits at $739 because there's no closer ceiling, but that's a 15.5% straight-line run. With weekly RSI at 80, improbable.

Decision: Wait. Watchlist alert at $610–620 — that's where MU pulls back to a tight-structural setup with a real edge.

Lesson: This is the VRT pattern restated. Clean structure, wrong moment. The first override of a ⚠ Trap: flag is the most expensive trade you'll ever make.

The cost-meter incident (meta-case)

What happened: An hour after the v6.4.0 launch, a 12¢ Anthropic charge didn't reconcile with the dashboard's recorded cost.

Two bugs compounding: 1. Whale retry-burn — the Whale Confirmation Coach panel's Retry button fired a fresh paid Anthropic call instead of using the 5-min cache. Stale-cache fallback was missing on the retry path. 2. Missing web_search tool fee — cost calc counted Anthropic input/output/cache tokens but not the $10/1000 server_tool_use.web_search_requests line. Each call invoked web_search 6–8× → recorded cost was ~50% under reality.

Fix: Cost calc reads usage.server_tool_use.web_search_requests from the API response; falls back to scanning content for server_tool_use blocks; adds web_search_requests × $0.01. Whale Retry now uses the 5-min cache; 90s timeout. Per-coach cost capture across all 7 v5.7 AI coaches.

Lesson: "You found a real bug an hour after launch by being annoyed at a 12¢ charge. That's the kind of pressure-testing that catches things." Cost discrepancies are signals.


15. Bug class catalog (the recurring patterns)

These are the bug classes the framework has hit more than once. Each one was caught and shipped a fix; each one tends to recur in new code unless you grep for the pattern.

Pattern Where it shows up Fix
Timezone anchoring time.time() vs getmtime(), daily reset boundaries Use ZoneInfo("America/New_York"); never bare datetime
Atomic writes JSON state files Always temp + os.replace; never write directly
Cross-surface fix-leakage Same scoring logic copy-pasted (11pt vs 13pt) Run diagnostic recipe against BOTH surfaces
Baseline-migration leak Hardcoded constants downstream of new baseline var Grep for the hardcoded value before shipping
Same field, different formula closed filter, win definition, profit factor Rename or unify
Modal-render guard staleness render() blocks during modal-open, data freezes Add targeted in-place rebuild path
DOM-bound observer survival innerHTML rewrite destroys observed subtree Disconnect-and-null at every site that replaces target
Brand-contract sad-path leaks Banners are easy; empty states / toasts / chip tooltips are slow drips Run brand-contract grep end-to-end, not just on banners
DOMContentLoaded race vs render() Component depends on data not yet loaded Cache + v5states:ready re-fire pattern
Hardcoded sticky offsets top: 44px breaks when sticky parents wrap Use var(--Xheight) driven by ResizeObserver
Stacking-context trap Descendant of finite-z-index sticky parent Bump parent's z-index above all floating descendants
Disambiguation tooltips encoding the bug they prevent "NOT to be confused with Pillar N" Scan disambiguation text when fixing labels
Undocumented vendor caps E*TRADE COID 20-char limit Probe boundaries (1, 5, 10, 15, 20, 21, 25)
Symptom-text describing failure category, not cause Generic error text masks specific cause Diff request bytes between working and broken paths
Gate-vs-element-class divergence Class predicate gate (.contains('show')) misses elements Audit every element supposed to flow through the gate

The pattern that's hit most often: same field name carrying different formulas across surfaces. Always rename or unify. Don't trust two surfaces to "do the same thing" because they share a name.


Where to file bugs

If something looks off — a chip that shouldn't be there, a coach that won't load, a score that doesn't match the cell breakdown, a broker order that errors out — write it down before you investigate. A 30-second note ("MU showed Hidden Tape 5 in modal but 6 in compact") becomes an hour-saver six weeks later when you're debugging the same thing.

Three places to write it:

  1. audit_dashboard.log — the framework's own log. Most issues leave a fingerprint here. tail -50 audit_dashboard.log is the first move.
  2. broker_orders.log — broker-specific issues (preflight refusals, place errors, reconciler diffs).
  3. The bug-class catalog above — if it's a pattern that's recurred, add a row.

Swing Deck is a trading framework. Not financial advice.

See Swing Deck Guide Reference for the everyday user manual. See FRAMEWORK_AND_RISK_PILLARS.md for the internal calibration reference (per-cell scoring rubrics, historical case calibration, implementation notes).