Commit Graph
350 Commits
Author SHA1 Message Date
momentum devandClaude Opus 5 aae7269538 fix: stop the exit stack amputating every winner
The live exit ran FOUR independent stop ratchets at once: a +5% breakeven
inherited silently from the env default, a 0.25R step-trail, a prior-low
trail, and profit-lock tiers. Stacked, they pinned the stop at or just
above entry within the first minute of any real move, so winners were
capped near +1R while losers still ran to the full stop.

Measured over 62 live Tradier + 172 Alpaca paper round trips:

  - trades whose stock ran >=20% after entry (avg +63.7%) kept 9.3% of it
  - median hold 10 min for winners vs 19 min for losers -- inverted
  - PCLA 2026-09-11: entered 12.8233, stopped 12.82, a 0.03% stop

breakeven_at_pct was the only exit lever in ExitConfig that defaulted ON
from the env (0.05) while its dataclass default and every sibling lever
defaulted to off, so the live stack ran it without ever setting it. Turn
the env default off so "not configured" means "not applied".

Replayed through simulate_exit -- same entries, same initial stops, only
the management varying -- with each stack split 70/30 by date. A 10%
trail running from entry looked best in-sample and went negative out-of-
sample; what survives is a trail armed only after +1R. The 8-20% range
all beat the old config in all four train/test cells (mean R 0.20-0.25 vs
0.125), so the live value of 12% is a plateau pick rather than a fitted
one.

Live config_overrides updated separately (market closed, no open
positions); this commit is the code default and the regression guard.
Risk per trade is unchanged and both safety backstops -- the -10%
catastrophe exit and naked-stop enforcement -- are untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2LepdJfPCfK5Q8uznjFBM
2026-09-13 12:00:29 -05:00
momentum devandClaude Fable 5 40e51c8dd5 feat: morning pick journal — prospective human/LLM forecast experiment
Tests whether a PROSPECTIVE forecaster (owner or local LLM) carries signal
the machine levers never found. Picks land premarket via dashboard panel or
research/llm_picker.py (Ollama); research/pick_scoring.py judges them under
a pre-registered bar (n>=40, >=30 sessions, mean >=+0.5%/pick net, median>0,
both halves positive). Picks after 9:30 ET stored but flagged invalid.
Shadow-only: human_picks is never read by the live loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 10:52:39 -05:00
momentum devandClaude Fable 5 390d6660ea fix: halt-aware flatten — let a working market sell rest instead of churning
A market sell that sits unfilled means the symbol is halted (or session
closed); cancelling and resubmitting it every exit-manager pass fills no
faster and spams the broker — YXT 2026-08-05: 87 market sells across a
10-minute LULD halt, 86 cancelled, plus one exit_naked_stop journal event
every ~4s. On a halt-down the resting sell is also the position's only
protection.

cancel_protective_and_close now returns False (nothing submitted) when a
market sell for the symbol is already working, and only cancels
non-market protective sells before closing. Exit-manager paths
(catastrophe / naked-stop / first-red / trail) journal the exit only when
a close was actually submitted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 19:01:18 -05:00
momentum devandClaude Fable 5 3871bda616 fix: label broker sync events by actual execution broker (tradier stack)
Tradier stack emitted account/positions/orders events hardcoded as
broker_name=alpaca_paper while the executor filters snapshots by
client.broker_name (tradier) — position cap and re-entry block read zero
positions, and the dashboard showed 'Alpaca Paper' over real Tradier values.
Mode label now defaults from BROKER/TRADIER_ENV (tradier_live) instead of
alpaca_paper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 10:52:01 -05:00
momentum devandClaude Fable 5 485cec0372 feat: wide_stop_veto_v1 — skip entries with stops >12% below entry (shadow)
Year-replay stop-width buckets: <8% +0.072R, 8-12% +0.120R, 12-16%
-0.054R, >16% -0.092R — the wide cohort holds every recent dollar
disaster (BIYA 16.7%, FCUV day-2 17.5%), where the exit ladder's blind
spot (BE/trail only arm on the way up) rides the full catastrophe
distance. New stopwidth_gate intervention (fail-open without levels) +
registered spec, same forward bar as the pm-high gate; the two cohorts
overlap so the adjudicator attributes which does the work. In-sample
counts for nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 21:06:37 -05:00
momentum devandClaude Fable 5 9390f3c2a6 perf: gate-receipts and loop-session projections stop shipping fat rows
/api/snapshots ran 33-60s even after retention pruned the events table —
profiling each projection found query_gate_receipts (21s) and
query_loop_session (10s) fetching thousands of full payload rows to
Python to keep 100 (or ONE). Both now select newest-first with SQL
LIMIT / single-column reads: 0.12s and 0.07s against prod. Remaining
projections (watch_states 6s, daily_performance 5s, approval_queue 3.5s)
are the same class — later pass if the snapshot still feels slow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 22:33:01 -05:00
momentum devandClaude Fable 5 f0fa104511 feat: cap entry notional at broker buying power (cash-account GFV guard)
On the Tradier live cash account buying_power maps to cash_available —
SETTLED cash. Capping every entry's notional at it means the bot never
spends today's sale proceeds today, which is the entire good-faith
-violation rule. That replaces the blunt 1-trade/day cap: the live
account now trades like paper (cap back to 5/day), throughput
self-limits at ~settled/position-size sequential trades and refreshes
each morning at T+1. On margin accounts buying power is ~4x equity so
the cap never binds — one universal rule, no broker branches. Fail-open
when the field is absent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 09:21:55 -05:00
momentum devandClaude Fable 5 c7249b22e2 feat: orb_pmhigh_gate_v1 — premarket-high reclaim gate, forward OOS test
H3 (enter the pm-high break at 9:30 instead of the ORB) FAILED its
pre-registered bar on the year replay: paired delta -0.069R (n=689) —
entering earlier on the same names does not pay; the 5-minute wait stays.

The decomposition found the real signal: ORB fires where RTH price also
reclaimed the premarket high ran +0.316R (n=689) vs -0.298R (n=477) where
it never did — 100th percentile of a 2000-draw random-gate null at the
same keep-rate (null p99 +0.127), sign-consistent 14/14 months. First
lever of 15 to survive both checks in-sample.

Because the split was found in this dataset, it counts for nothing until
fresh fires confirm: new 'pmhigh_gate' intervention kind (fail-open on a
missing premarket_structure row, RTH-only reclaim check at/before the
entry bar) + registered spec with expR_delta >= 0.05 at n >= 200 forward
bar. Cohort self-registers on the next nightly experiments run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:20:25 -05:00
momentum devandClaude Fable 5 3fe0cb0891 feat: H3 replay — premarket-high break at 9:30 vs ORB, paired comparison
The pre-registered premarket hypothesis H3 (the source trader's actual
entry) made executable: both legs run per symbol-day over the year of
labeled gapper sessions, sharing the deployed exit engine (config parity)
and +1-tick adverse fills so the paired delta isolates ENTRY choice.

Comparison metric locked in the module docstring before the first run:
paired mean R delta (H3-ORB) where both fire, pass at >= +0.05R with
n >= 300 pairs, plus a stop-unit guard (median stop-distance % and
captured-move % printed for both legs — R parity from a tighter stop is
a fail, the vwap-reclaim lesson).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:13:57 -05:00
momentum devandClaude Fable 5 37757d3d47 fix: armed trigger names now get watcher scoring (blank reasoning panels)
Four of ten armed triggers had NO criteria events ever — the trigger book
arms from the live gapper scan while the watcher scores only the
discovery watchlist, so scanner-only names (TCX/ZTG class) showed an
empty reasoning panel with nothing behind it.

- arm pass injects every complete armed candidate into the watchlist
  provider's extra_symbols (its standing manual-add hook)
- provider returns injected symbols FIRST: the watcher truncates at
  max_symbols, so extras appended after a full research list were
  silently dropped — 'always include manually injected symbols' was
  untrue exactly when the list was full

60 watcher/watchlist/trigger tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:41:11 -05:00
momentum devandClaude Fable 5 72a9ecbd0b fix: reasoning panel flicker + stuck loading on live ticks
Every SSE snapshot re-render rebuilt the expanded panel's HTML back to
'loading…' and re-fetched — panels flickered per tick and never looked
settled. Rendered panels now cache per symbol: re-renders repaint from
cache instantly, background refresh at most once/min, single-flight
guard against fetch races. Watch panel and trigger panel share the
cache; toggleCriteria's duplicate direct fetch removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:36:31 -05:00
momentum devandClaude Fable 5 a6be643fbb ci: build image on dashboard static changes too
api/static/** ships inside the image but wasn't in the docker workflow's
paths filter — dashboard-only commits (a6da02f trigger-row reasoning,
81bf154, da17be8) silently produced no image and only reached production
by riding along with later .py commits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:20:46 -05:00
momentum devandClaude Fable 5 a6da02f809 feat: reasoning timeline on armed-trigger rows (click to expand)
The day's relevant names live in ARMED TRIGGERS — the reasoning panel now
opens right there: click a trigger row for the score/rvol timelines,
criteria lanes, and worded reasons, same renderer as the watch panel.
Caret + hover affordance; open panels survive snapshot re-renders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:11:02 -05:00
momentum devandClaude Fable 5 2be23673be feat: reasoning-over-time drill-down — score/rvol timelines + criteria lanes + worded reasons
Clicking a watch-state row now shows HOW the verdict evolved, not just the
latest flags:
- setup score across the session vs the 60% gate (SVG line)
- relative volume vs its 2.5x floor
- per-criterion pass/fail lanes over the whole session (the variance of
  each logical step at a glance)
- each criterion's latest reason in words (rvol 4.9 (min 2.5), gap 80.9%,
  price vs breakout ...) instead of bare pass/fail marks

Backend: query_symbol_criteria gains history[] (server-side strided to
~240 samples) + detail[]. The generic LIKE scan took 12s/click over weeks
of criteria rows; now a partial expression index on the extracted symbol
(idx_events_criteria_symbol, built at boot) + 24h window + SQL-side
stride = <1s. Latest-verdict fallback keeps stale symbols and historical
views working (covered by existing projection tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 19:37:35 -05:00
momentum devandClaude Fable 5 0c28cf921f feat: one-click source links on catalyst feed headlines
Every LLM catalyst read's headline now links to the raw article
(raw_news_items.raw_url joined by headline), styled as a distinct dotted
underline per NN/g citation guidance — any AI verdict traces to its
source in one click. Rows without a matching raw item degrade to plain
text. Join verified against prod: 0.29s for the full feed query.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:40:49 -05:00
momentum devandClaude Fable 5 81bf1540ef feat: explicit + prefix on positive P&L values (CVD accessibility)
Gain/loss no longer rides on green/red alone — red-green color vision
deficiency affects ~8% of males; the minus sign already printed, now the
plus does too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:36:41 -05:00
momentum devandClaude Fable 5 da17be8969 feat: dashboard readability pass — type hierarchy, P&L masthead, trigger-row cleanup
- Two-texture typography: labels move to the UI sans (11px, brighter muted
  #93a0bd), values stay tabular mono — label/value pairs parse instead of
  dissolving into uniform mono soup
- TOTAL P&L is the one dominant instrument: 34px + green/red state wash and
  accent bar on the cell (CSS :has on the existing pos/neg classes, no JS)
- Equity tile: available funds as a small sub-line (fixes the two-line wrap
  that broke the strip height)
- Armed triggers: single-line ellipsized meta/levels (was a 3-line crumb
  wrap), 15px symbols, ARMED/WEAK state chips, subtle zebra, thinner bars,
  wider levels cell so entry/stop/now never clip
- Verified with headless before/after renders against live data; zero JS
  logic changes, node --check clean

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:19:16 -05:00
momentum devandClaude Fable 5 47a64688d8 fix: replay universe = scanner-discovered + historical gappers; add year-scale targeted backfill
eod_replay's universe was today's top-100 by ABSOLUTE volume — small-cap
gappers never crack that list, so 2026-07-28 replay found 1 gapper on a
day the live loop signaled 8 names. Universe is now the union of (a) the
session's symbol_discovered events, (b) historical_gappers for that date,
(c) most-actives only on a same-day replay (past-date most-actives would
be look-ahead). Verified: 7/28 replay now screens 6 gappers / 4 trades,
matching the live day. Historical sessions also fetch that day's
04:00-20:00 ET SIP bars explicitly instead of a from-now lookback window
that pulled the wrong day.

backfill_gapper_days.py: for each of the ~260 recorded gapper sessions,
pull SIP minute bars (premarket incl.) + 40d of daily bars for that day's
gappers only, then build premarket_structure. Idempotent (skips sessions
already covered). Fattens n for premarket H1/H2, pyramid_1r_v1, and the
orderflow experiments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 13:20:38 -05:00
momentum devandClaude Fable 5 054405dbdf feat: pyramid_1r_v1 experiment — add-into-strength, pre-registered
New 'pyramid' intervention kind in the replay engine: at +add_at_r on
initial risk, add add_frac x size (+adverse fill), whole-position stop to
blended breakeven; scratch there if a later bar trades through, else both
legs exit at the baseline exit. variant_r is per unit DEPLOYED
(P&L / ((1+add_frac) x risk)) so leverage alone cannot fake a pass.

Spec pyramid_1r_v1: add 0.5x at +1R, bar expR_delta >= +0.05 at n >= 300,
futility kill at n >= 500 below -0.02. Hypothesis notes the known failure
mode (median winners scratched at blended BE on first pullback). Shadow
only — no live-path changes; cohort self-registers on the next nightly
experiments run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 11:33:59 -05:00
momentum devandClaude Fable 5 733a182f10 feat: premarket-structure labeler (Phase 1 of premarket axis, observe-only)
Distills each gapper's premarket tape (first-hit price/time, pm high/low,
run %, open-vs-pm-high, VWAP distance, jackknife count) into a
premarket_structure table for entry-conversion analysis. Motivated by
FCUV 2026-07-31: our DB captured the full $1.98->$10.49 premarket ramp
while the ORB entered at $11.89 into the reversal.

Pre-registered hypotheses H1-H3 locked in the module docstring before any
data was examined; report command prints descriptive buckets only, with
n>=30/bucket floor and Phase-2 shadow bar required for promotion. Gates
nothing, trades nothing.

Wired into the nightly scheduler after premarket_eval. One session scan
per build (per-symbol queries were ~100x slower); partial session index
created post-close only (mid-market CONCURRENTLY build deadlocked against
live ingest).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 11:10:16 -05:00
momentum devandClaude Fable 5 3d6bc36955 feat: Equity/Avail funds tile in dashboard P&L strip
Shows total_equity and buying_power (settled available cash) from the
latest account snapshot. On the Tradier live cash account these diverge
for days after a deposit or sale (T+1) — the bot sizes off equity but
buys need settled cash, so the gap explains rejected entries at a glance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 13:44:49 -05:00
momentum devandClaude Fable 5 3a28b0f6a7 docs: add regime-gate flip requirement to Tradier live runbook
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 10:17:56 -05:00
momentum devandClaude Fable 5 4f1ccde370 docs: Tradier real-money flip runbook (00 cash-account delta)
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>
2026-07-30 10:17:33 -05:00
momentum devandClaude Fable 5 12f7fefec0 feat: nightly event retention + external telemetry push
- runtime/retention.py: prune 8 high-volume debug event types older than
  RETENTION_DAYS (default 14) once/day at RETENTION_HOUR (default 03:00).
  Audit events (orders, fills, closes, risk rules) are never pruned. The
  events table hit 6 GB in 3 weeks at ~340k rows/day; this caps it.
- runtime/telemetry_push.py: POST a JSON snapshot (equity, day realized
  P&L, trades/wins, open positions, last-event age) to TELEMETRY_PUSH_URL
  every TELEMETRY_PUSH_INTERVAL_SECONDS (default 300). Unset URL = step
  disabled. Push failures are reported in the step result, never raised —
  an external outage can't touch the loop. Works as healthchecks.io
  dead-man ping + payload log in one.
- wired both as scheduler steps in run_live_paper (retention gated by
  RETENTION_ENABLED, telemetry by TELEMETRY_PUSH_URL presence)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 14:25:09 -05:00
momentum devandClaude Fable 5 c5b43364be fix: don't fabricate last_equity for Tradier — realized P&L read $0 forever
Dashboard realized = broker equity delta (equity - last_equity). Tradier
balances carry no prior-session-close field and translate_balances echoed
current equity, making the delta permanently zero (win rate populated from
closed events while dollars showed $0). Absent last_equity now routes the
projection to its closed-events fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 09:39:48 -05:00
momentum devandClaude Fable 5 80422d91cd fix: bracket fills stay on legs; by-tag lookup surfaces entry-leg fill
General translation must not echo leg fills onto the OTOCO parent —
flatten_fills adds parent AND legs, double-counting entries in journal
reconstruction. The cancel-vs-fill reconcile still sees executed shares:
get_order_by_client_order_id surfaces the entry leg's fill on its result.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 09:36:52 -05:00
momentum devandClaude Fable 5 a2de50479a fix: three Tradier sandbox incident fixes (2026-07-28 reject loop)
Incident: entry-timeout cancels raced lazy sandbox fills -> untracked
positions; naked-stop guard then force-sold but every sell rejected
('more shares than your current long position') for hours.

1. translate_order: Tradier OTOCO/OTO parents carry no top-level
   symbol/side/qty (legs only) — inherit from the entry leg so
   symbol-scoped lookups can see brackets; get_orders symbols filter
   also matches by leg symbols. Without this the stop-leg search read
   a fully-protected position as naked.
2. close_position: cancel the symbol's resting sell orders/legs first —
   Tradier reserves held shares against open sells, so selling without
   releasing them always rejects.
3. cancel_entry (broker-agnostic): after cancelling, re-check the
   broker's final word by client_order_id; journal a fill for any
   executed shares instead of orphaning a real position (2026-07-06
   class, same race exists on Alpaca).

49 tests pass incl. new coverage for all three.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 09:11:00 -05:00
momentum devandClaude Fable 5 bd7183c861 fix: mark Tradier positions off Alpaca real-time trades, not delayed quotes
Exit manager reads position current_price for breakeven/trail ratchets and
the stop-breach guard; Tradier sandbox quotes are 15-min delayed, so marks
lagged reality. Prefer the Alpaca data client's latest trades (real-time),
fall back to Tradier /markets/quotes per missing symbol.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 17:39:46 -05:00
momentum devandClaude Fable 5 8c62804808 feat: broker-aware factory for reconcile/journal/health tools
eod_reconcile, backfill_journal and the admin health block were hardwired
to AlpacaPaperClient — on a BROKER=tradier stack they would silently
reconcile/report against the wrong broker. make_trading_client() honors
BROKER env with no silent cross-broker fallback (missing Tradier keys =>
skip with reason, never fall back to Alpaca). Admin UI shows the active
broker label; JSON key stays 'alpaca' for compatibility.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 13:13:01 -05:00
momentum devandClaude Fable 5 13c4b61e77 docs: tradier stack fresh-DB gotchas (missing research tables, knob seeding)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 13:04:50 -05:00
momentum devandClaude Fable 5 52144241b4 fix: dashboard broker badge reflects BROKER env (PAPER · TRADIER etc.)
Badge text was hardcoded to ALPACA; _broker_block now reports the active
broker name and derives paper/live from the Tradier sandbox host when
BROKER=tradier, keeping the base-URL-truth rule from plan 007.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 12:38:37 -05:00
momentum devandClaude Fable 5 154e44e8d5 fix: tradier stack compose must pin ALPACA_DATA_FEED=sip
Fresh DB has no config_overrides row, so without the compose-level pin the
parallel stack falls back to the thin IEX feed and its rvol/volume decisions
diverge from the main stack (which carries feed=sip in the DB).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 12:12:14 -05:00
momentum devandClaude Fable 5 df85201c45 feat: Tradier parallel-stack compose template
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>
2026-07-27 08:07:32 -05:00
momentum devandClaude Fable 5 1cefb51c63 feat: Tradier broker mirror — config-switch redundancy for Alpaca execution
tradier_broker/ mirrors every trading call the live loop makes on
AlpacaPaperClient (account, clock, positions, orders, submit incl.
bracket->OTOCO, cancel, replace, close) with identical signatures and
Alpaca-shaped returns, so trading_execution / exit_manager / flatten /
execution / sync run unchanged. Market data always delegates to Alpaca.

- BROKER=tradier (+ TRADIER_* env) swaps execution client in run_live_paper;
  default path untouched
- client_order_id -> tag preserves the lost-submit reconcile
- TradierApiError subclasses AlpacaApiError so the executor's
  rejection-vs-transport fork works as-is
- sandbox-verified: balances/clock/positions/orders, OTOCO preview
  (result:true), place -> tag lookup -> cancel round trip
- 18 unit tests (translation + client paths); executor integration checked
- docs/TRADIER-FALLBACK.md switch runbook

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 11:01:50 -05:00
momentum devandClaude Fable 5 48d75816b6 feat: backtest page — live-taken vs not-taken split + per-trade live P&L column
/api/backtest now annotates each replay trade with the live loop's realized
P&L for the same (session, symbol) and returns a live_split summary (taken vs
not-taken n/sumR/avgR). Page grows two summary cells, a Live P&L column with
an amber ≠ marker when sim and live disagree on a trade's sign, and a footer
caveat: minute-bar granularity + adverse fills flip individual rows — trust
aggregates, not rows. Read-only annotation; cached replay result unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 13:58:24 -05:00
momentum devandClaude Fable 5 6c6d0cd799 feat: weekly execution tallies — midday-entry net + market_exit post-run
Read-only nightly-scheduler job (Friday-gated): buckets realized P&L by entry
time (open/late-morning/midday) and, for each market_exit close, computes the
post-exit run-up capturable under a breakeven stop (0 if price undercuts entry
first; intrabar ties count as stopped). Emits a weekly_tally event + Telegram
summary. Evidence accumulator for the dead no_new_entries_after cutoff and the
weakness-exit winner-clipping question — no live trading path touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 22:25:14 -05:00
momentum devandClaude Fable 5 30c7232137 fix: market_context must not import runtime (research-isolation guard)
The DB-config wiring was unnecessary there anyway — market_context reads
zero trading knobs; the guard test (plan 009) rightly rejected the import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 23:36:29 -05:00
momentum devandClaude Fable 5 82273d9d4e feat: image version identity + DB-config parity for nightly scripts
Version: CI now tags images YYYY.MM.DD-<shortsha> alongside :latest/:sha
and passes GIT_SHA/BUILD_TAG build-args; the running app reports them via
dashboard /api/health and admin /api/status (TrueNAS custom apps only show
'custom/1.0.0', so the app self-reports what it actually runs).

Config parity: every scheduler-run script (nightly_tune, eod_replay,
premarket_eval, regime, market_context, experiments cli) now loads
config_overrides at startup like the live services do — a bare env would
replay under DEFAULT exits (the documented 4.5x-baseline landmine).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 23:28:06 -05:00
momentum devandClaude Fable 5 537e0c7e12 docs: cutover config model — knobs in config_overrides table, seed-from-env migration
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:31:33 -05:00
momentum devandClaude Fable 5 495a3fa5d6 feat: strategy knobs in the database — config_overrides table, admin-managed
Owner preference: the bulk of TRADING_*/TRIGGER_* logic switches should live
in the DB, not the compose file (keys + connection info stay in compose —
the DB address cannot live inside the DB, and secrets don't belong in a
table the stack can SELECT). Precedence at boot: compose env < overrides.env
< config_overrides table; every entrypoint already routes through
env_overrides.load(), which now applies the table last. Admin /api/config
and profile-apply write the table (secrets whitelist unchanged); the config
page shows source=database. DB down = silent fall-through to file/compose,
boot never blocks on config. One-shot migration:
  python -m runtime.env_overrides seed-from-env
run in any stack container to copy its current effective knobs into the
table, after which the compose lines are dead weight strippable at the next
redeploy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:31:16 -05:00
momentum devandClaude Fable 5 108f61a9bc docs: config model — strategy knobs in data/overrides.env via admin panel, compose keeps infra+secrets only
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:21:45 -05:00
momentum devandClaude Fable 5 de1973fbff fix: telemetry heartbeat insert needs an explicit uuid id (NOT NULL, no default)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 19:36:19 -05:00
momentum devandClaude Fable 5 625abee66f fix: tuner heartbeat also recorded in DB — file-only heartbeat was unverifiable
The 07-22 'tuner NOT running' alert could not be diagnosed remotely: the
heartbeat is a file on the scheduler container's volume, invisible to
everything else, and indistinguishable from a stale scheduler image or a
DATA_DIR mismatch. _write_heartbeat now also inserts a telemetry_events
row (best-effort); the boot staleness check falls back to the DB row when
the file is absent, so 'tuner ran, no profitable config' stops looking
like 'tuner dead'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 19:35:50 -05:00
momentum devandClaude Fable 5 148e300af4 docs: live-money cutover runbook + one-command DB seed script
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>
2026-07-22 17:29:06 -05:00
momentum devandClaude Fable 5 5287c8ebfe feat: live-money prep — equity-floor kill switch + boot order reconciliation
TRADING_EQUITY_FLOOR (0=off): equity at/below an absolute dollar floor
permanently halts the session and flattens the book — the total-drawdown
backstop the per-day breaker can't provide (a slow bleed of -2.9% days
never trips it). Same flatten path, loud risk event, bounce does not
re-open.

TRADING_BOOT_RECONCILE (default on): at boot, diff the broker's open
orders against today's submitted events — an open BUY on an unheld symbol
this process never submitted is a crash-restart orphan (the 2026-07-06
duplicate-order incident) and is cancelled. Orders on held symbols are
never touched (cancelling a bracket parent strips its protective stop).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:13:26 -05:00
momentum devandClaude Fable 5 e9a1f577a9 feat: research.slippage — paper-vs-real fill variance from recorded NBBO
Prices each closed round-trip against the nearest quote_snapshots NBBO:
per-trade spread cost, book-walk flag (order size > displayed ask), and a
cost-adjusted P&L total. Measured this week: 0.3-0.5% median spreads on
gate-passing names, ~12% profit drag — far below the static 2-4% microcap
proxy. Coverage gaps (quote recorder only follows tracked names) reported
per-trade, not silently dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 10:04:47 -05:00
momentum devandClaude Fable 5 21b38109aa test: clamp test pins BACKTEST_MAX_SESSIONS env (default moved to 300)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 09:30:14 -05:00
momentum devandClaude Fable 5 9d0a9df60e backtest: raise session clamp 45 -> 300 (env BACKTEST_MAX_SESSIONS)
The stored minute-bar history is 273 sessions (~13 months); the 45-session
clamp blocked months-scale ranges now that the nightly labeler keeps
setup_events current. Requests stay serialized behind _backtest_lock; a
full-history run takes minutes by operator choice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 09:29:55 -05:00
momentum devandClaude Fable 5 ed176b3712 fix: backtest starvation — schedule the labeler nightly, make inserts idempotent
Dashboard backtest sources sessions from setup_events v1, which only the
labeler writes — and the labeler was never in the nightly scheduler, so
setups (and therefore backtests) froze at the last manual run (07-17).
Root blocker was _insert crashing on duplicate setup_ids on any re-run;
now ON CONFLICT DO NOTHING, so incremental nightly builds are safe.
Scheduler gains 'labeler build' + 'build-tod1' after eod_replay in both
compose files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 06:51:56 -05:00
momentum devandClaude Fable 5 5f33799a9a fix: actually wire loop_session into the snapshot payload
The prior commit imported query_loop_session but a botched idempotency
guard skipped inserting the snapshot field (the import string itself
matched the 'already present' check) — ruff unused-import failed CI and
the stale-session banner had no data. Field added; banner live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 05:22:49 -05:00