Delta to LIVE-CUTOVER.md (Alpaca-era): 3-line env flip on the existing
tradier stack, cash-account GFV cap (1 entry/day), equity floor 270,
sandbox-record truncation before first live boot, day-1 naked-stop drill.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Self-contained shakeout stack beside the paper stack: own postgres (host
5434, disposable pgdata-tradier volume), own dashboard (host 8768), loop
with BROKER=tradier (sandbox). Data plane stays Alpaca; Telegram/deadman
disabled so main-stack alerts stay unambiguous; main data dir mounted
read-only (inherits learned_params/overrides.env). Filled local copy is
git-excluded; bootstrap_live_db.sh playbook reused for DB parity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
docs/LIVE-CUTOVER.md: full flip-day procedure — staged pieces, discovered
dependencies (second paper key pair, DB seeding, learned_params copy,
telegram token, ports/dirs), 15-min cutover with the paper stack running
throughout, verification list, rollback. bootstrap_live_db.sh seeds the
live stack's Postgres from the paper nightly dump and truncates the
money-record tables so research history carries over while live P&L
starts at zero.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The gate is damage-control, not an edge source: it can only allocate the
strategy's existing (negative) edge onto least-bad days — best case flat,
never gain. Its value is CONDITIONAL: a holding pattern until an entry edge
(plan 017 L2 depth) is found, at which point it flips to force-multiplier
(good names x good days). Owner's 'it just turns the bot off' critique is
correct at the current no-edge base.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cost-checked (no data spent): DATABENTO_API_KEY reaches XNAS.ITCH TotalView
10-level depth; every real gapper (EHGO/ATPC/MUD/LASE/AEON) covered at
$0.0001-0.0066/fire, full 20-fire sniff <$1 inside free credits. Catalogs
all 13 provisioned-but-unwired keys with a wire-on-demand triage (Databento
= game-changer; 4 news APIs = skip; rest redundant).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
H2 exposed an adjudicator gap: on a -0.21R/fire baseline any keep-or-zero
gate zeroing ~44% of fires clears +0.05 mechanically; its own pre-registered
strata check inverts (choppy beats persistent within both gap buckets =
Simpson's). Queued: random-gate null for keep-or-zero bars. H3 accruing as
designed (n=29, skip-on-no-coverage working).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9 sources (Hasbrouck, Bouchaud x2, Cartea, Dalton, Bellafiore, Neill, CKS,
Cetin-Danilova) read by parallel agents + adversarial synthesis, constrained
to computable data (1-min SIP bars, 15s quotes) and fenced from the 8 refuted
levers. Companion to regime_notes.md. H1 (trigger-erasure fast invalidation,
5-book convergence) is the lead candidate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The session-level metalabel P(our ORB profits today) — the one lever that
survives out-of-sample. Backtested on 266 prod sessions (walk-forward):
trade-always (trained subset): -0.111R/session, 36% hit
meta_v1 GATED (trades best ~32% of days): -0.004R/session, 48% hit
delta +0.107R in-sample, +0.128R OOS (recent 80 sessions), improves across folds
It does NOT make the bot profitable — it takes a bleeding strategy to roughly
flat by sitting out the worst ~2/3 of sessions. threshold_v1 rule gates too
little to matter; meta_v1 (walk-forward GBM) is the signal. Leaky full-fit
(+0.61R) >> walk-forward confirms the discipline was load-bearing; the OOS
holding is the evidence the +0.078R is real, not look-ahead.
Changes:
- research/regime.py: batch minute-bar load per session (64k per-fire round-trips
-> ~266 queries; fixed a >5min build to ~3min) + incremental label pass (skip
already-labeled sessions; --rebuild forces full). Nightly cost now ~one new day.
- docker-compose.yml + truenas-compose.yml: scheduler runs build-features + score
(both models) before `experiments run`, nightly. ALL-SHADOW — does NOT gate live
trading (that stays owner-gated Step 5).
- admin_api.py + admin.html: POST /api/regime/run (detached) + GET /api/regime/status
+ a Run/Status button — the panel had no way to trigger this.
- plans/011 + docs/research/regime_notes.md (6-book distillation) + plans 008/009 docs.
Prod state (shadow, rollback = DROP regime_features/regime_scores + DELETE
experiments WHERE id LIKE 'regime%'): 266 feature rows, 532 scores, 2 experiments
registered.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidated 4-tool review (2 correctness agents + Codex + over-eng audit).
Money-path (regression-tested):
- _flatten_all: moved the fresh-broker close from close_session INTO the
shared function + added a broker-held guard on entry cancellation, so
the daily-loss CIRCUIT BREAKER (which called _flatten_all raw) no
longer strips a just-filled bracket's legs or leaves a sync-gap fill
naked. One guard, all callers.
- restart state rebuild: _fresh_entries cap, _requested_symbols dedup,
and losing benches now rebuild from today's events on boot — a
mid-session restart no longer resets the cap to 0 or re-fires a
resting order into a duplicate (the 2026-07-06 incident).
- POST /orders no longer retries a lost-response URLError (was
re-sending duplicate live orders); GET/DELETE still retry.
- expire_stale_entries now @_locked (raced the trigger thread).
- _session_open: ET conversion instead of a hardcoded 13:30-13:33Z
window that only matched EDT — the fast path armed nothing all winter.
Security:
- admin panel fail-CLOSED on empty ADMIN_TOKEN (was fail-open on a panel
fronting the docker socket); header-only token, constant-time compare.
- admin API responses redact secret override VALUES (live keys would
have echoed after a paper->live switch).
- dashboard: removed wildcard CORS (cross-origin drive-by could drive
trade actions); optional DASHBOARD_TOKEN gate on POST.
Deletions (~805MB + dead code shipped in the prod image): momentum-
rebuilt.zip, archive venv/htmlcov, and schwab/ core/ telemetry/ models/
runtime/providers/ storage/replay.py controlroom/ + three dead
evaluation stubs + package.json — all verified zero-importer.
+4 regression tests. 99 money-path/trigger/exit tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 10 era documents (phase plans, build summaries, redesign writeups, old
runbooks, completed consolidation plan) moved to docs/history/ with a
do-not-follow README.
- Root README: current validated population ($2-20, 10-35% static gap +
squeeze exception, SIP), Deployment section, automated-maintenance
reality, corrected repo map (admin panel, youtube_claims, deploy/).
- RUNBOOK rewritten for the running system: daily rhythm, health checks
per deployment, and the production failure-mode table earned this week
(env drift, pgdata mount traps, uid 10001, zombie approvals, PG_SKIP_
SCHEMA, timezone artifacts).
- deploy/README.md is now the three-mode index: compose (canonical),
systemd (local dev — unit templates now committed), helm (refresh
staged as issue #8). Old k8s doc moved to deploy/helm/README.md.
- docker/README.md: six TrueNAS gotchas, each from a real outage.
- Admin panel improvements staged as GitHub issues #4-#7.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Forensic from the source trader's 2026-07-02 recap: his ENTIRE day (+$8.9k) was CX — a thin
reverse-split name that squeezed $3->$6 on a headline (his "wildcard effect": on dead pre-holiday
days the one mover gets everyone's attention). Our system NEVER SAW IT: zero bars, zero discovery
events. Root cause: discovery ranks by ABSOLUTE SHARE VOLUME (most-actives), and a thin %-squeezer
never cracks a volume-ranked top-N. Every other name he called out we saw and judged the same way
he did (DSY passed/backed-out, CWD too cheap, AHMA rejected) — the gap was pure discovery scope.
- alpaca_paper/client.py: get_movers(top) — /v1beta1/screener/stocks/movers top %-gainers
(verified working with our keys: CLRO +114%, CWD +73% live).
- discover_active_symbols: wildcard gainers are fetched FIRST (warrants/units filtered:
'.'-suffixed + 5-char W-endings), deduped with most-actives, same price-band filter.
Additive + fail-open: a movers failure never breaks the existing actives path.
Discovery/ingestion scope ONLY — every trading gate (confirmation window, quality, VWAP, cap)
still applies unchanged. The immediate value is that shadow tracks + the labeler finally get
BARS for wildcard movers, so the next CX is measurable instead of invisible.
Verified live: universe now leads with gainer-sourced names (CLRO, CWD, USDE, YRD, DSY). 16 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
detect_momentum_ignition (strategy/evaluation/ignition.py): standalone, pure,
binary-rule detector for a vertical squeeze (blue-sky fresh-HOD + price velocity
+ ABSOLUTE volume burst, NOT rvol/float). Stop = last established higher low (per
operator). NOT wired into classify_setup (deliberately). 8 unit tests on
synthetic verticals: fires with the right trigger/stop, and each gate blocks
(not-blue-sky / not-new-HOD / velocity / volume / insufficient-bars / no-ATH).
REALITY CHECK (verify, don't assume): ran it over the REAL PLSM 2026-06-24 bars ->
0 fires, CORRECTLY. PLSM's high was at the 9:35 ET open (it ran +400% PRE-MARKET,
which we don't ingest), and in RTH it was HALTED 228 of ~390 minutes — a
halt-resumption GAP chop (+3.6/-12.5/-17.3/-14.4% jumps), not a smooth vertical.
A consecutive-bar detector structurally cannot see that. So the ignition
opportunity is a PRE-MARKET + halt-resumption phenomenon -> capturing it needs
pre-market bar ingestion + halt-aware gap detection + pre-market/halt trading
(into the FCUV halt-down risk), a far bigger lift than an RTH detector. Documented
front-and-center in docs/IGNITION_SETUP_SCOPE.md as a pre-P1 operator decision.
The P0 detector + tests are a correct foundation regardless.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scope for a SECOND, shadow-only lane to catch PLSM-class vertical squeezes the
pullback-ORB scorer is structurally blind to (PLSM was evaluated 1895x and
blocked ~30% every time — a vertical gives no pullback). Produced by a read-only
4-agent mapping workflow; no code changed.
Key findings: detect on blue-sky fresh-HOD + price velocity + ABSOLUTE volume
burst (NOT RVOL, which zeros out on thin IPOs; NOT float, which is NULL). Emit a
new ShadowSignalEvent that the execution snapshot (query_ready_signals_snapshot,
filters event_type=signal_ready) can never read -> structurally cannot trade.
Score nightly by reusing the labeler + eod_replay forward-outcome machinery.
Pre-register the promotion bar (runner-vs-trap separation, distinct-runner n)
before shadowing. Phased P0 detector -> P1 dark emission -> P2 nightly scoring ->
P3 validation -> P4 tiny live lever only after separation is proven. Strictly
additive; never touches the ORB book or anti-chase guards.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prioritized P0->P2 plan to make the system cleaner + easier to troubleshoot,
produced by a 4-agent read-only mapping pass (no code changed). Key findings:
- Single-Postgres migration is FUNCTIONALLY COMPLETE at the connection layer
(all live code -> storage/db.py -> db_pg -> one DATABASE_URL Postgres; no
live duckdb.connect). Datastore work is cleanup, not architecture.
- The real architectural item is the TWO entry paths. The live (auto) path is
missing five fast-path-only gates: extension, day-extension, halt-guard,
liquidity-sizing, rank — a REAL current gap (47/47 of today's fills were
approved_by="auto"). Root cause: the ready-signal snapshot carries no live
price/day_open/volume/rank. Fix sequenced behind a flag, reversibly.
- Dead-code/cleanup: core/ + telemetry/ packages (zero callers), archive/ tree
+ 232MB zip (gitignored), dead schwab/ integration, an orphaned mem_ test
schema left in prod Postgres.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>