Release notes — Swing Deck v5.0 → v7.8
Reverse-chronological log of every meaningful release since v5.0. Each section covers what shipped, why, and where to find it in the dashboard. Latest first. v7.8 release window: June 1, 2026 — currently in dev branch (80+ commits stacked locally), public .dmg ships when GitHub Actions quota refreshes.
v7.8 — Portfolio Truth · Standalone Mac app · 24/7 daemons · broker hardening · May 2026
The largest single architectural push in the project's life. v7.8 reframes Swing Deck from "a dashboard you open in a browser tab" to "a Mac app whose brain runs 24/7." Four major themes, ~80 patch releases on top, all gated for the June 1, 2026 public-release window (CI quota frozen until then).
-
Portfolio Truth (Phases 1–4) — complete restructure of how the dashboard understands broker state. Phase 1 adds the
broker_state_fetcherdaemon: polls E*TRADE every 30s, writesbroker_snapshot.jsonas the single source of broker truth. Account-picker heuristic prefers INDIVIDUAL over ROTHIRA via symbol-overlap with positions.json (catches the bug where the daemon was polling the wrong account). Reconcile pill in the hero strip shows broker-vs-dashboard drift (positions, cash, open orders) with severity color-coding. Manual cash-edit modal logs reason + provenance.
Phase 2 ships transaction auto-routing:transactions_router.pyclassifies BUY/SELL/DIV/INT/FEE into the right ledger automatically; DEPOSIT/WITHDRAW/SPLIT queue for confirmation with drawdown-baseline auto-adjusted viaportfolio_rebalancer.adjust_drawdown_baseline(the daemon no longer captures inflated peaks during user shares-typed-but-cash-not-yet-deducted edits). Phase 3 Open Orders strip was built then removed in v7.8.38 as redundant with the existing PENDING @ ETRADE widget. Phase 4 ships paranoid-mode order verify:order_verify_tracker.pyschedules T+45s / +90s / +120s checks after every place_ok event to catch ghosted orders before they go unnoticed. -
Standalone Mac app — full PWA manifest + apple-touch-icon + meta tags. Safari "File → Add to Dock" or Chrome "Install Swing Deck" both create a native standalone window with own Dock icon, no URL bar, no tabs, native macOS traffic lights, system notifications attributed to "Swing Deck". The Tauri scaffold (
tauri-app/) was also built — abandoned for personal use after a 4-patch cascade trying to fix browser-API divergences (window.open, navigation, window.confirm all behave differently in WKWebView vs a real browser). Kept committed as a future option for customer distribution once the dashboard is refactored to be wrapper-agnostic. -
24/7 background daemons (LaunchAgent) —
./packaging/launchd/install.shwrites a macOS user-scope LaunchAgent template, substitutes user paths viased,plutil -lints the result, andlaunchctl loads it. The agent runscontrol_server.py(which auto-spawns the audit watcher + index fetcher as managed subprocesses) 24/7 starting at login, with auto-respawn on crash (KeepAlive.Crashed=truebutSuccessfulExit=falseso SIGTERM still stops cleanly). SetsAUTO_SHUTDOWN_ON_CLOSE=falseto disable the "no dashboard heartbeat → server exits" watchdog — pre-market alerts at 4am AZ now fire regardless of whether a dashboard window is open.uninstall.shfor clean removal. -
Broker hardening (v7.8.42 / .43 / .44 / .45 / .46 / .73 / .75) — six real bugs caught + pinned. FEED DEGRADED false alarm: equity-tier providers (Tradier / Polygon / E*TRADE) were being polluted by macro symbol fetches (^VIX, CL=F, etc.) those tiers structurally can't quote — gated by
_is_index_futures_forex(symbol). compute_daily insufficient-history guard: a degraded-feed morning let a 0-1 row history frame reachcompute_daily, which crashed the entire audit cycle withIndexError; now self-guards via_MIN_DAILY_BARS=30. Portfolio-loop per-ticker try/except: candidates loop already isolated; portfolio loop didn't — one bad ticker could abort the whole cycle. Now mirrors candidates loop with full traceback logging. Audit cadence floor:AUTO_REFRESH_MIN=0meant cycles ran back-to-back hammering the data tiers (~556 cycles/day); bumped to 1-min floor (~2.75-min effective cadence, ~⅓ the load). Finnhub false alarm: pattern-cluster-banner-style WAF flag on the next-tier provider; now treats all non-Tradier tiers as fallback (any single-call failure goes silent rather than tripping the badge).
E*TRADE route deprecation (v7.8.73):/v1/accounts/listreturned Tomcat 404 for weeks while balance/portfolio worked — live probe showed.jsonsuffix variant returns 200. The reconcile daemon was silently failing every 5 minutes; now hits the right route. OAuth stale-pool 404 (v7.8.75): long-running control_server processes drift into a connection-pool state where/oauth/request_tokenreturns Tomcat 404 while a fresh Python process gets 200 with valid token. Same bug class as v6.7.39 (which fixed/orders/previewcode-100 via fresh Session retry); v7.8.75 setsConnection: closeon bothstart_oauth()andcomplete_oauth()proactively so the bug can't surface to the user. - Calibration upgrades — consolidation_breakout_pending overlay re-classifies high-quality coiled-spring rows the base classifier would mislabel as ranging/consolidation. score-momentum disconnect chip flags held +3R winners whose composite score barely moved (MU 2026-05-13 repro: +11% on the day, score 69 → 72 — the methodology paradox the user shouldn't psychologically over-weight). post_earnings_whale_read synthesizer combines 6 votes (OBV / dark_pool / options_flow / gap_followthrough / hidden_tape / distribution_trap) into one green/red/amber chip with HARD override on bull-stack trap + retail-feed caveat baked into the rationale. Two-tier oil regime separates acute OIL_SHOCK (RoC-gated) from chronic OIL_ELEVATED (digested plateau) — the previous single-tier flag was over-weighting an old shock that was already digested. INTL/US divergence chip in the regime band: ALIGNED / WATCH / UNKNOWN verdict comparing overnight international breadth vs US futures (read-only context chip; not wired into scoring).
-
Pattern-cluster banner disambiguation (v7.8.76) — two completely different concepts shared the "comparable setups" name:
comparable_setupsper-ticker data field (server-emitted historical analogs, looking backward) vscomparable-setups-bannerclient-side detector (portfolio-wide pattern co-occurrence, looking across tickers right now). Banner renamed topattern-cluster-banner+v5DetectPatternClusters+v5RenderPatternClusters. Cluster thresholds extracted to named constants (PATTERN_CLUSTER_THRESHOLD=2,STATE_CLUSTER_THRESHOLD=3) so a small-portfolio user can tune in one place instead of editing 5 hardcoded gates.
Net effect of v7.8: Swing Deck is now a proper standalone Mac app whose brain runs 24/7. The dashboard window becomes optional viewing surface, not a critical-path runtime. Broker truth is real-time and reconciled. Six bug-class fixes captured in pin tests so they can't regress (file-wide regex pins + AST introspection in tests/test_etrade_endpoint_pinning.py and tests/test_audit_loop_isolation.py). 16+ commits stacked locally; public release waits on CI quota refresh.
v7.4 → v7.7 — R:R framework cleanup · calibration upgrades · Control Panel + Daemons infrastructure · April → early May 2026
Four-line arc that solidified the methodology layer and the operational dashboard before v7.8 added the new architectural surfaces on top. v7.4 closed the long-running R:R fix-revert-refix loop. v7.5 added the data-quality envelope. v7.6 modernized the Control Panel. v7.7 unified the Daemons grid.
-
v7.4 — Card audit pass — user-flagged R:R values that didn't match displayed Entry/Stop/TP1. Three parallel agents swept all card surfaces and returned ~85 findings (28 P0 / 31 P1 / 26 P2). 17 fixes shipped in one commit: R:R chip switched to
_rrFromLevelsat 4 display sites (audit card SWING LEVELS + sizer + modal drow + strike-order — gating logic keeps_rrCanonical); Python_round_half_up_1helper aligns banker's vs JS half-up rounding (AAPL pill 1.2 vs chip 1.3 mismatch); modal "below the 65 floor" → 80 (canonicalconfig.ENTRY_SCORE_GATE); "All criteria met" dynamic from passed/total; many smaller chips made held-state aware. 5 new bug-class lessons captured: R:R fix-revert-refix cycle signals bad design (needrrForDisplay({forGate})API); threshold-citation rot on calibration changes; hardcoded-text-masquerading-as-logic; held-state gating is fractal across every action-verb chip; concurrent agents in same worktree create edit storms. -
v7.5 — R:R structural minimum-floor + data-quality envelope — closes the v6.8.1 → v6.8.22 → v7.4 fix-revert-refix arc on structural R:R. New
STRUCTURAL_RR_MIN_FLOOR=1.0, tuple-return reasons from the readers, and a_rr_data_quality{severity, fallback, label}JSON envelope that surfaces to the dashboard chip + LLM thesis so the user sees why canonical fell through instead of mysteriously-tiny values (V's 0.03 was the pre-fix repro). Bug class captured: min-viable-R floor for structural readers — when a writer can emit arbitrarily small numbers and a consumer treats them as gating signals, the writer needs two thresholds (output gate + data-quality floor below which it returns None and lets canonical fall through). v7.5 also fixed the v7.1.79 alerts-test-fixture rot (single-cyclegate_stateseed in_prev, not multi-cycle history). -
v7.6 — Control Panel infrastructure refresh — the v7.x infra had outpaced the Control Panel UI surfaces. v7.6.1 fixed stuck-loading E*TRADE/Tradier panels + hardcoded version footer drift. v7.6.2 added a Broker Safety status row (writes_enabled / circuit / dollar_cap / R:R floor / audit_freshness — single source of truth per the v7.1.74 lesson) + Tax/Wash
<details>collapse that auto-opens Nov-Jan + a brand-contract grep shipped as a reproducible artifact attools/grep_brand_contract.sh(3rd hit of bug class, finally addressed properly). -
v7.7 — Daemons grid + Data State panel — unified Daemons grid covers all five background daemons (audit watcher · index fetcher · trigger layer · drawdown daemon · alerts engine) via one multiplexed
/daemons/statusendpoint (single poll covers all five — same pattern worth reapplying elsewhere). Data State panel via/data-statecoversaudit_output.json·positions.json·audit_decision_history/·setup_fires_log.jsonletc. with timestamp + size + freshness chip. Net +1,428/-70 across 9 files; +343 new test lines.
v7.0 → v7.3 — v7.0 milestone · UX feature wave · cash bookkeeping triple · March → April 2026
Mid-arc work that established the v7.x framing. v7.0 closed the v6.x line and captured 6 bug-class lessons. v7.1 shipped a UX feature wave (charts, alerts, calendar, screener, compare). v7.2 added per-setup expectancy tracking. v7.3 fixed a series of cash-bookkeeping bugs that surfaced once positions.json became the canonical share-state source.
- v7.0.0 milestone + 12-version session arc — closed the v6.x line. 6 bug-class lessons captured: kwarg-default drift, .macro/.meta path drift, interpolation null-leak, resolver pattern, throttle-on-success, same-field-different-writers (5th hit). These now appear in the project's bug catalog and inform future code review.
- v7.1 — UX feature wave — five swing features shipped in one session: sparkline + modal candles (TradingView Lightweight Charts), entry_armed alert tier (4th tone A5 → C6 → E6 so the user can hear-distinguish ARMED from HOLD/WATCH from across the room), 14-day earnings calendar, 5-preset screener, multi-ticker compare overlay tab. v7.1 also kicked off two important sub-arcs: the bulk-TP 9-version chain (v7.1.55-63 — "Set TPs for all positions" + 8 sequential bug fixes that captured 3 new bug classes: rate-limit-as-side-channel, COID-uniqueness-under-truncation, working-tree-dance-restore-trap) and the STOP-order + visibility cascade (v7.1.64-74 — plain STOP "Stop on Quote" wired end-to-end + visibility-of-broker-truth chips at every stop-displaying action surface; captured 5 more bug classes including the let-not-on-window 5th hit and localStorage-as-source-of-truth-drifts-from-broker-reality).
-
v7.2 — Setup Performance subtab —
_classifySetup()priority-ordered classifier tags BUY journal entries with one of 8 setup types (bull_flag · catalyst_play · earnings_runup · breakout_from_base · pullback_at_support · oversold_bounce · trend_continuation · other). New 🎯 Setup Performance subtab in ③ Performance shows per-type win rate, expectancy in R, profit factor, best/worst ticker. Sample bands: <5 hidden, 5-15 developing, 15-30 solid, 30+ established — the UI is honest about which numbers you can trust yet. -
v7.2.1 — drawdown daemon fix (held vs recommended shares) — the daemon was firing phantom 7%-drop alerts on a $91-102K "portfolio" that didn't exist. Root cause:
_get_portfolio_equity()readaudit_output.json["shares"]as held shares, but that's the framework's RECOMMENDED position size (~$11k × 9 tickers), not what the user actually held. Fix: newpositions.jsonsynced from the dashboard'slocalStorage.wt_pos3viaPOST /portfolio/positions/syncwith throttle + first-online-detect hook. Daemon now reads canonical real-held-shares + cash. Bug class captured: audit_output fields are framework PRESCRIPTIONS, not user STATE — any server-side consumer mixing them is bugged. Grep sweep identified candidates for future fixes (sleeve_drift, position_concentration, gamma_firewall, hard_asset_floor enforcement, entry-related alert types). -
v7.3.2-4 — cash bookkeeping triple — sequel to v7.2.1. Three-layer fix for the daemon capturing inflated peaks during shares-typed-but-cash-not-yet edits: L1 atomic input (
upd()auto-deducts/credits cash on shares 0↔N transitions with an undoable toast); L2 daemon jump guard (DRAWDOWN_IMPLAUSIBLE_JUMP_PCT=0.10, skip week_high update on >10% poll-to-poll,current_valuestays honest); L3 manual override (POST /drawdown-status/reset+ dashboard ↻ chip). Bug class captured: bookkeeping-moment leakage — any long-running daemon tracking a reference number from user-editable inputs needs all three layers (atomic input, mid-transition guard, manual override). - v7.3.5 — data quality triple — three writers, three silent data-quality bugs fixed + dashboard chips: max_pain stale (15%-distance + outside-OI-range checks), score_history widened from 6 → 31 elem to align with score_max_30min window, comparable_setups deduped (cycle-counting → transition-counting; AAPL 1596 → 26 cycle entries, AMAT 623 → 1). Three new bug-class patterns captured: multi-axis quality reads, visible-slice/summary-stat window mismatch, cycle-counting in event-driven append-only logs.
v6.6 → v6.7 — Dashboard display contract · swing-candidate scanner funnel · threshold-citation hardening · early April 2026
Two minors that connected the v6.5 critical-gaps batch to the v7.x architecture. v6.6 unified how analytics surfaces read state. v6.7 brought the scanner funnel + hardened the AI coach narratives against threshold drift.
- v6.6 — Dashboard display contract — every analytics surface (Goal Math, Reality vs Projection, Inst Metrics, Trade Journal, Options Book) reads from one canonical source so the same trade renders the same number everywhere. Pre-fix, the same trade could show different P&L on different cards depending on which writer was canonical. Plus a repo-wide pillar canonicalization sweep aligning all surfaces to the §22 reference card. Bug class: same-field-different-writers (the recurring pattern that the v7.x line keeps catching new hits of).
-
v6.7 — Swing-candidate scanner funnel (Layers 1-3) — the scanner that fires at 07:00 + 12:30 ET on weekdays across a ~1,000-name Russell 1000 universe. Setups with a composite ≥75 auto-promote into your watchlist (capped at 5/day so the list stays curated). The audit cycle scores them within ~6 minutes; the alert layer fires when score crosses your entry gate. Threshold-citation hardening across all 7 narrative coaches (pillar / entry / exit / whale_confirmation / trap / catalyst / position_audit) so the LLM narrative cites the canonical gate value (
ENTRY_SCORE_GATE,STRUCTURAL_RR_MIN_FLOOR, etc.) instead of hallucinating one.
v6.5.2 → v6.5.15 — Hardening sweep + UX optimization passes · 2026-04-29 → 2026-04-30
Fourteen patch releases on top of v6.5.1 across two days. No new AI surfaces in the run, no breaking changes. The work split into four themes: (1) a 36-bug sweep across the framework; (2) Windows-installer firefighting; (3) UX cleanup that removed four duplicate panes and tightened the topbar; (4) two systematic audit passes (tooltip coverage + Performance section A+B optimization) that brought every dashboard card surface to consistent quality.
- v6.5.2 — 36-bug sweep — closed 36 framework bugs across three repos in one session. Two recurring classes worth naming: timezone anchoring (mixing
time.time()againstos.path.getmtime()on Windows MST resolved 7 hours apart, breaking staleness checks) and atomic file writes (replacedopen(..., 'w')patterns withos.fdopen+os.replaceon critical paths to prevent partial-write races). Plus Stripe webhook idempotency, dashboard concurrency, broker-token chmod race. - v6.5.4 — release notes click target — the version chip in the dashboard topbar now links to /releases, an auto-populated changelog page that reads from GitHub release tags directly. One source of truth for release notes; no manual changelog maintenance.
- v6.5.5 — v6.5.11 — Windows-installer firefighting — six patches resolving Windows-specific bugs that the Mac dev workflow couldn't surface. Replaced
'python3'hardcode in subprocess invocations withsys.executable(Windows shipspython.exe, notpython3). Swappedps-based shell commands forpsutil(cross-platform). Replacedsignal.SIGKILLwithpsutil.Process.kill()(SIGKILL doesn't exist on Windows). Addedolder-than-usguard + shell blocklist to the cleanup logic, then disabled it on Windows entirely after self-kill cascades persisted in the user's VM. License-key validator off-by-one fixed (SWING-XXXX-XXXX-XXXX= 20 chars including dashes, not 16). Staleness check now usesmax(time.time(), os.path.getmtime(__file__))as baseline so the Mac dev workflow doesn't false-positive its own newly-saved files. - v6.5.12 — UX cleanup — four duplicate panes removed (Trader/Investor/Armor Yield/Scenario Modeler — all were duplicating per-ticker card data or the regime bar). Index quotes ticker tape (was a 6-cell grid ~80px tall, now a horizontal flex single-line ~25px). Action Strip + Quick Filter merged into one labeled Triage row (the visual split was wasted chrome — same chips, one row, dashed line as cluster divider). Topbar collapsed Help and System into dropdowns (Glossary / Quick Start / Dashboard Guide / Framework / Email Support — visible chip count ~22 → ~12). Options Sleeve empty-state collapsed from 3-section panel (~250px) to a single-line ticker-tape banner (~40px) when no positions; full panel returns automatically when positions exist. Trap coach narrative log fix (was reading from
parsed.ai_readwhen the actual fields lived at top-levelparsed; forward-only fix, old log entries stay verdict-only). - v6.5.13 — tooltip-audit pass (six surfaces) — systematic per-card tooltip audit. Roughly 122 tooltips added across all six card surfaces: Audit Card compact view (11), Shared Detail Modal (41 incl. canonical descriptions on all 11-pt and 12-pt pillar rows), Candidate Card compact view (11), Position Sizer (7), Sector Rotation (4 + new Rotate OUT chip rendered —
weakSecwas computed but never rendered), Options 13-pt cards (48 incl. canonical descriptions on all 13 OPTS pillar cells). Bugs surfaced and fixed in the same pass: "Standard Standard — 25%" double-word typo in the sizer, "Pillar 6" → "Pillar 4" attribution correction in the sector cap footer (the framework constantSECTOR_CAPis Pillar 4, not 6). Audit Card compact summary trimmed (RSI + ADX moved to modal; raw numbers don't drive triage decisions). WATCHLIST chip converted from::afterpseudo to a real span so it can carrydata-tip. - v6.5.14 — Performance section A+B optimization — visual/UX + content-relevance audit of all six ③ PERFORMANCE subsections (Velocity Panel, Projection, Performance Tracker, Inst Metrics, Trade Journal, Options Book). Roughly 24 new tooltips. Velocity Panel restructured: conditional render — compact 5-cell mini-strip when fewer than 3/5 conditions met (non-actionable cards stay tight ~140px); full inline detail when ≥3/5 (decision-time cards keep verbose context ~250px). Removed redundant per-card chrome (the "Only 1 Action Required" footer duplicated Condition 4's inline detail; Position % and Red-Line % cells in the metric grid duplicated the "X.X% of Y% cap" line above the bar). Bugs fixed: Projection panel's @ 20YR card hardcoded "(16.8%)" while the calculation used the dynamic
requiredRate— now displays the actual rate; Inst Metrics tooltip wording corrected on the static Required Path row. - v6.5.15 — Control Panel pass + Gamma Firewall generalization + Options Coach — light A+B audit on ④ CONTROL PANEL (sparsest tooltip coverage in the dashboard before this pass — only 6
data-tipattrs across 170 lines). 16 new tooltips on section labels, process control buttons (Start / Stop / Restart for both audit_framework.py and fetch_indexes.py with consequence text), and config file buttons. Last 4 nativetitle=attrs swept todata-tip=for full consistency — the dashboard now has zero nativetitle=attrs.
Gamma Firewall generalized beyond NVDA — the panel previously hardcoded NVDA shares + cost as the worst-case single-name. Now walks the portfolio dynamically and auto-targets the largest equity position by current dollar value (skips cash + hard assets). Panel hides when no equity holdings. Tooltips and the UNPROTECTED warning interpolate the dynamic ticker.
Options Coach (new surface) — ninth bound AI surface. Six trigger conditions: OPT score ≥80 + qualifying setup, VIX regime crossing 25, earnings inside DTE window, VIX Trap detected, armor floor breach on a CSP setup, strategy/equity grade mismatch. Hard scope: Level 1-2 single-leg strategies only. The coach actively redirects any Level 3 (spreads) or Level 4 (naked) inquiry back to the single-leg equivalent — the framework's "math floor not motivation" stance shows up at the surface boundary too.
Net effect by end of v6.5.15: every audited card surface has tooltips on every badge, chip, and data point that informs a decision; zero native title= attrs (full data-tip consistency); two recurring framework bugs catalogued (timezone anchoring + atomic writes) so future patches don't reintroduce them. The honest enumeration count is now ten AI surfaces — Options Coach (v6.6) is the tenth, after AI Thesis · Devil's Advocate · Pillar Coach · Exit Coach · Entry Coach · Position Audit · Catalyst Interpreter · Whale Confirmation Coach (v6.3) · Trap & Structure Coach (v6.4).
v6.5.1 — Hardening + E*TRADE quotes · 2026-04-28
Twelve commits on top of v6.5.0. No breaking changes, no new AI surfaces. Mostly hardening — quote-cascade redundancy, audit-cadence calibration, market-session-aware budget rollover — plus a few user-visible polish items that were sitting in the v6.5 backlog.
- E*TRADE as second broker-grade quote tier — the price cascade is now Tradier → E*TRADE → Finnhub → Alpha Vantage → Stooq → yfinance. Configure either broker and you get a primary tier; configure both and you get redundancy across broker outages. The gamma walls chip auto-falls-back to E*TRADE when Tradier degrades, and both broker tiers show up in the Risk Pulse data-feed chip.
- Audit cadence calibrated — watchdog interval moved from 4min to 10min, dashboard staleness thresholds from 6/11 min to 10/20 min, and the cycle timing is now logged. Fixes the "57-min stale audit" symptom that surfaced on bad-provider days when the prior cadence was tight enough to false-alarm.
- AI history modal — full narrative — whale and trap coach history rows now show the full reasoning text (
recommendation,why,geometry_read, etc.) instead of just verdict + agreement %. The verdict alone hides the work; this puts it back in the modal where a trader can review it. - LLM usage chip + daily budget anchored to ET — both reset at midnight Eastern (the market session boundary), not midnight UTC. The chip reads the same budget the engine enforces, so what you see is what you've spent against today's cap.
- Typed E*TRADE error UX, refined — broker errors resolve to plain-English messages and drive category-aware circuit cooldowns: 5min for service-unavailable, 10min for auth, 1min for rate-limit, 30min for generic hard rejects. The trader sees what happened and what to do, not a Python traceback.
- Comparable Setups Phase 1 — per-card chip showing how many prior snapshots of this ticker matched the current state within tolerance, plus the dominant forward bias from those matches:
📊 N prior · DOMINANT_BIAS X%. Walks the ticker's audit history; no LLM call. Phase 2 (cross-ticker matching) is on the v6.6+ list.
The rest of v6.5.1 is internal: server-side single-instance gate on broker writes (409 stale_session for writes from a stale tab), a topbar live-dot fix that anchors to the actual audit timestamp, and a Risk Pulse cleanup so non-broker users don't see a false "FEED DOWN: Tradier" flag.
v6.5 — Critical-gaps batch · 2026-04-28
v6.5 closes the structural and contextual gaps the framework needed before the v6.6 synthesis layer can stack signals cleanly. Six chips and a hard R:R floor — all deterministic, all surface-bound, none of them new AI surfaces. The eight-surface count is unchanged.
- Structural R:R — replaces the hardcoded 1.5 placeholder with per-ticker math. Reward target comes from the nearest 10-source S/R level above (or below, on shorts); risk comes from the chandelier stop. Levels merge via 0.15× ATR confluence, same primitive the Trap & Structure Coach uses. Broker preview gate refuses to advance any order whose computed R:R is below 2.0.
- Form 4 10b5-1 distinction — new
SCHEDULED_SELLINGchip (cyan / 📅) on the insider activity surface. When ≥80% of the 90-day insider dollar-volume sits under disclosed 10b5-1 plans, the chip flips fromEXTREME_SELLINGtoSCHEDULED_SELLING. Pre-scheduled plan sales are calendar-driven, not bearish; surfacing them differently stops the framework from treating routine plan execution as an exit signal. - Float / short-interest chip (v6.5.0b) — short % of float and days-to-cover surfaced per ticker. Squeeze threshold flagged automatically. Source: yfinance + FINRA semi-monthly reports.
- RS-rank chip (v6.5.0c) — relative strength score 0–100 vs SPY plus the ticker's sector ETF. IBD-style weighting: 1mo 40% / 3mo 20% / 6mo 20% / 1y 20%. Improving / deteriorating trend glyph next to the score.
- Gamma walls chip (v6.5.0d) — dealer GEX walls computed from real Greeks and OI across 2–60 DTE option chains via Tradier. Surfaces top call wall, top put wall, the zero-gamma flip level, and a regime classifier (
LONG_GAMMA/SHORT_GAMMA/MIXED_GAMMA). The walls behave as dynamic S/R; inSHORT_GAMMAthe regime amplifies moves through them. - Comparable Setups Phase 1 (v6.5.1) — per-ticker historical pattern matcher. Walks this ticker's audit history and counts prior snapshots whose state matches the current one within tolerance (score ±5, same vix_regime, same macro regime). Surfaces match count and the dominant forward bias from those matches. Phase 2 (cross-ticker matching) is on the v6.6+ list.
- AI history modal — full narrative — whale and trap coach history rows now show the full reasoning text, not just the verdict + agreement %. The verdict alone hides the work; this puts it back where the trader can review it.
All v6.5 chips are deterministic and surface-bound. No LLM calls. They feed the eight existing AI surfaces as additional context (the trap coach already reads gamma walls and float/short into its reasoning) and are also visible directly on every position card.
v6.4 — Trap & Structure Coach · 2026-04-27
The Trap & Structure Coach was the eighth-shipped of ten per-ticker AI surfaces and the first to issue a direct entry verdict (OK / WAIT / AVOID), not just narrate state. It classifies the current bar into one of five structural states — TRAP (failed breakout/breakdown), FADE (rejection at level), CHASE (extended without consolidation), CLEAN (orderly trend), NEUTRAL — using ten local-compute support/resistance level sources collapsed via 0.15× ATR confluence merging. Read the full surface spec on the AI surfaces page.
Calibration on 8 tickers showed zero hallucinations and a 37.5% AI-override rate vs. the framework alone — meaning roughly 1 in 3 framework-valid breakouts get re-classified as structural traps before the trader fires. For the launch story, see Hidden Tape v2 ships.
Also in v6.4: Founding 100 program opens at $14.50/mo (50% off Pro forever) for the first 100 customers. Live counter on /pricing.
v6.4.2 — Operational hardening
- Daily LLM budget cap —
LLM_DAILY_BUDGET_USDis a hard ceiling on AI spend across all eight surfaces. Exceeding it returns a structured 429 with the daily total, the cap, and the next reset. Setting the cap to0is a kill switch — the dashboard works, the AI surfaces just refuse to fire until the cap is raised. - Typed E*TRADE error UX — broker errors now resolve to plain-English messages ("E*TRADE order service is temporarily unavailable on their side") instead of raw stack traces. Categories drive the circuit breaker: 5min cooldown for transient outages, 10min for auth failures, longer for hard rejections. The trader sees what happened and what to do about it, not a Python traceback.
v6.5.0a — SEC Form 4 insider activity · 2026-04-26
Per-ticker insider activity from SEC EDGAR Form 4 filings, surfaced as a chip on every position card. The chip color-codes the most recent 90 days of insider transactions: EXTREME_SELLING / HEAVY_SELLING / MODERATE_SELLING / NEUTRAL / MODERATE_BUYING / HEAVY_BUYING. Click for the top-5 transaction list with venue, side, dollar amount, and 10b5-1 plan flag.
The fetcher uses the public SEC EDGAR API (free, no key required) with a 24-hour ticker cache plus indefinite per-accession cache. Calibration on PLTR identified a $435M insider-selling pattern the framework alone would have missed entirely — this surface is what catches that.
v6.3 — Whale Confirmation Coach · 2026-04-25
The seventh per-ticker AI surface and the first that goes outside the framework's input boundary on purpose. The framework's whale-sentiment sub-score (Point 5 of the 11-point indicator framework) was previously a closed loop — framework computes it from public price/volume/options data, then narrates those same scores back. v6.3 closes the loop: the AI runs web_search against SEC filings, reputable financial press, and dark-pool reports to verify or contradict what the framework "thinks" institutional flow looks like. Verdicts: CONFIRMED / CONFLICT / WEAK SIGNAL.
Hard requirement: every external claim must cite a URL. Server-side validator strips any URL not present in the web_search result set before returning. Override rate observed in calibration: 37.5% — the AI corrected the framework's whale read on 3 of 8 tickers. See the surface spec.
v6.2 — Dryrun mode + broker safety · 2026-04-22
BROKER_MODE=dryrun short-circuits all place and cancel calls so they log the intent but never hit the broker; preview still runs against real prod for full validation. Combined with the 8-layer pre-flight check in broker_guard.py (audit-freshness, confirm-token, position-size cap, risk-budget cap, options-level enforcement, halt check, dry-run window for new accounts, idempotency keys), this is what "discipline as a process" looks like at the broker boundary.
Also in v6.2: circuit breaker on the order pipeline — trips after 3 consecutive 5xx and locks for 30 min cooldown so a flaky upstream can't be hammered into a worse state.
v6.1 — Atomic order placement · 2026-04-19
Native order placement against E*TRADE: equity entries (BUY / SELL / SELL_SHORT / BUY_TO_COVER), single-leg options (BUY_OPEN / SELL_OPEN / BUY_CLOSE / SELL_CLOSE), multi-leg shapes (vertical spreads, iron condors, butterflies, calendars, diagonals), bracketed entry+SL combos, and TP ladder management. Every order goes through the same preview→confirm→place flow with idempotency keys and tamper-evident logging.
Everything below this line shipped in v5.0 → v5.7 (April 2026). It's still the foundation of everything above — the v6.x releases add to v5.x, they don't replace it.
v5.0 — Status Light: one signal per position
v4.x showed an 11-cell radar chart per ticker — technically correct, mentally exhausting. v5.0 keeps that grid (click any card to expand), but shows one color per ticker above it. Glance 30 positions in 2 seconds.
| STATE | MEANING | ACTION |
|---|---|---|
| 🟢 ARMED | Filter pass + price-action trigger fired + all pillars clear | Enter now (or queue the order) |
| 🔵 HOLD | Owned, score ≥ 75, no pillar breach | Nothing — position is healthy |
| 🟡 WATCH | Qualified by filter, no trigger yet | Wait — the moment hasn't arrived |
| 🟠 TIGHTEN | Owned but degrading — score 60-74 or pillar caution | Raise stop or reduce size |
| 🔴 EXIT | Owned + hard pillar breach OR score < 60 | Rotate to cash |
| ⚫ COLD | Filter fail — don't open | Skip |
Hover the status badge to see the 4-layer tooltip: macro regime, sentiment (VIX), indicator score, price-action triggers. Each layer's stance explained in one line.
Price-Action Triggers — 11 primitives
Every 60 seconds during market hours (30s during the 9:30-10:30 ET opening window), the engine runs every ticker in your portfolio through 11 price-action primitives. Each fire is logged, scored 0-100, and surfaced through the status-light system.
The 11 primitives:
| PRIMITIVE | FIRES WHEN |
|---|---|
| inside_day | Today's range is fully inside yesterday's — coil |
| n_day_breakout | Close above prior 20-day high |
| volume_expansion | Today's volume ≥ 1.5× the 20-day average |
| pullback_bounce | Tagged 20-EMA within 3 bars and bouncing on volume |
| vwap_reclaim | Intraday: lost session VWAP, then reclaimed |
| bearish_divergence | Price HH + RSI LH — exhaustion |
| bullish_divergence | Price LL + RSI HL — reversal precursor |
| liquidity_sweep_reclaim | Punched a swing high/low on ≥1.4× vol, snapped back |
| bull_flag | Sharp pole + tight flag + volume break |
| engulfing_reversal | Today's body engulfs yesterday's with ≥1.3× volume |
| gap_and_go | Open > yday high, close in upper third, ≥1.2× volume |
| key_level_reclaim | Broke support, stayed below ≥2 bars, reclaimed on volume |
Every fire also has a strength score 0-100 and a one-line narrative. The minimum strength to promote WATCH → ARMED is 20 (auto-tuned per-primitive after 10+ samples; see Per-User Calibration).
Expand any ticker card to see which primitives fired today with their narratives. Every fire is also logged to trigger_fires.log in your swing-audit directory for audit.
Focus Mode
The FOCUS chip in the action strip collapses the dashboard to only positions that need action: ARMED, TIGHTEN, EXIT. Most of the time, that list is empty.
Use this as your default screen during market hours. Discipline is doing nothing when nothing needs doing.
Click again to restore the full view. State is persisted to localStorage.
AI Thesis (BYOK)
Every ticker card now has a collapsible ✨ AI THESIS panel. Expand it and the engine generates a one-paragraph thesis grounded in your live Swing Deck data: filter score, which triggers fired today, active pillar violations, macro regime, historical primitive hit rate, and consensus from other Premium users.
BYOK — bring your own LLM key. No data leaves your machine except to the provider you choose with your own key. Supports:
- OpenAI —
gpt-4o-miniis the default (cheap, fast, high-quality) - Anthropic — Claude 3.5 Haiku or Sonnet
- Local (Ollama / LM Studio) — fully private, free, point
AI_THESIS_BASE_URLatlocalhost:11434/v1
Configuration
Add to your .env:
AI_THESIS_ENABLED=true AI_THESIS_PROVIDER=openai AI_THESIS_API_KEY=sk-... AI_THESIS_MODEL=gpt-4o-mini
For Anthropic:
AI_THESIS_PROVIDER=anthropic AI_THESIS_API_KEY=sk-ant-... AI_THESIS_MODEL=claude-3-5-haiku-latest
For Ollama (free, local):
AI_THESIS_PROVIDER=openai AI_THESIS_API_KEY=ollama AI_THESIS_BASE_URL=http://localhost:11434/v1 AI_THESIS_MODEL=llama3.2
Feedback buttons
Every generated thesis has a row of buttons: 👍 👎 shorter longer +macro +price ↻. Each click tunes the prompt for you — length, focus weights, tone. After 50 interactions your thesis reads differently from anyone else's. The preferences are stored in ai_thesis_prefs.json (gitignored, local only).
Reset any time at POST /thesis/ai/reset. Check current state at GET /thesis/ai/status.
Daily Briefing Email
Every weekday at 6:25 AM MST (1 hour before US market open), the local dashboard computes your portfolio state and POSTs it to api.swing-deck.com, which formats and sends an email via Resend.
The email shows:
- Portfolio score + grade
- Macro regime (standard / defensive) + current VIX + oil
- State counts (ARMED / HOLD / WATCH / TIGHTEN / EXIT / COLD)
- Every ARMED ticker with the fired primitive
- Every TIGHTEN ticker with the reason
- Every EXIT ticker with the pillar breach
If nothing needs action: "◆ ALL POSITIONS HEALTHY · NOTHING REQUIRES ACTION". Discipline is doing nothing when nothing needs doing.
Configuration
Requires a valid license with alerts_email entitlement. Controlled by:
DAILY_BRIEFING_ENABLED=true
Test it manually without waiting for 6:25 AM:
curl http://localhost:8001/briefing/preview # see the JSON payload curl http://localhost:8001/briefing/send-now # fire it now
Per-User Calibration Warmup
Over your first 30 days, the engine learns which price-action primitives work for you. Every fire is logged with entry price. At +5 bars (≈1 trading week), each fire is graded: win if price is up ≥ 2%, loss if down ≥ 2%.
Once a primitive has ≥10 graded outcomes, the auto-tuner adjusts its strength gate:
- Hit rate ≥ 60% → lower the gate (let more fires through; they work)
- Hit rate ≤ 40% → raise the gate (demand higher conviction)
- Otherwise → leave unchanged
Bounded [10..50] so the engine never fully silences or floods you. Check progress at:
curl http://localhost:8001/calibration/status | python3 -m json.tool
Why this is a moat: by day 31, your engine is tuned to your portfolio, your regime exposure, and your broker's fill quality. Switching to a competitor means starting over — pure switching cost.
Public Track Record
The owner's local dashboard publishes a daily snapshot of notable fires to swing-deck.com/track-record. Every ARMED / EXIT / TIGHTEN fire is publicly auditable with date, ticker, primitive, strength, and narrative.
This is a trust moat. Anyone can verify what the system said at the time — no retroactive editing. The publish is owner-only; regular users don't publish.
To opt in as the owner:
# In your local .env TRACK_RECORD_PUBLISH=true # On Railway (for your swingdeck-api) TRACK_RECORD_OWNER_KEY=SWING-XXXX-XXXX-XXXX
Consensus Network
Every 60 seconds, your local dashboard submits anonymized trigger fires to /api/consensus/submit. The payload contains ticker + primitive + strength + state — nothing else. No portfolio size, no P&L, no IP.
User identity is hashed via SHA-256 of the license key. Aggregate reads are gated by a k-anonymity floor of 5 users — we never return data that could identify an individual.
Query the crowd:
GET /api/consensus/ticker/NVDA GET /api/consensus/top
Example response — "73% of Premium users are ARMED on NVDA right now, top primitive n_day_breakout." Available in the AI thesis context automatically once there are ≥5 users firing on that ticker.
Mobile PWA
Every page on swing-deck.com is now a Progressive Web App. On iOS Safari or Android Chrome, tap Share → Add to Home Screen. The site installs as a standalone app with:
- Full-screen launch (no browser chrome)
- Offline cache of the marketing site + blog after first visit
- Dark theme-color that extends into the OS status bar
- Home-screen icon
Works best for checking the track record + blog on mobile. The dashboard itself (localhost:8001) is still desktop-only.
v5 Environment Variables Reference
Full list of new env vars in v5.0. All optional — defaults work out of the box.
| VAR | DEFAULT | NOTES |
|---|---|---|
| TRIGGER_LAYER_ENABLED | true | Master switch for the price-action engine |
| TRIGGER_LOOP_SEC | 60 | Base cadence during market hours |
| TRIGGER_OPENING_SEC | 30 | 9:30-10:30 ET cadence |
| DAILY_BRIEFING_ENABLED | true | 6:25 AM MST weekday email |
| TRACK_RECORD_PUBLISH | false | Owner-only; keep false for users |
| AI_THESIS_ENABLED | true | Master switch for thesis panel |
| AI_THESIS_PROVIDER | openai | openai / anthropic |
| AI_THESIS_API_KEY | (required) | BYOK. Empty = thesis panel shows config error |
| AI_THESIS_MODEL | gpt-4o-mini | Any model your provider serves |
| AI_THESIS_BASE_URL | (empty) | Override for Ollama/self-hosted |
See also: v5.0 API endpoints · Troubleshooting