Commit Graph
299 Commits
Author SHA1 Message Date
momentum devandClaude Fable 5 9bfd458956 feat: tod1 shadow track — zero-gap intraday volume-explosion detector
The 07-21 misses (CPHI +417%, DFNS +94%, GREE/WGRX +40%) were all static-gap
<10% names the validated gapper population excludes by design, but they share
a signature no refuted detector (liq1/run1) measured: rvol_tod (cum volume vs
median-20d prorated by elapsed session) at 50-1000x on a flat open. New
labeler setup tod1 fires on the rising edge of rvol_tod>=50 AND price>=
prev_close*1.20, restricted to static gap <10%, stop = 5-bar swing low,
scored via the shared _forward_labels. Shadow-only; pre-registered promotion
bar locked before forward data (median fwdmax60 >=+10%, median dd60 >-6%,
+1R>50%, top stratum rvol_tod>=200 clears alone).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 11:59:51 -05:00
momentum devandClaude Fable 5 3fdf268227 fix: trigger fires require a fresh cross — stale breaks after a blind window are chases
SNXX 2026-07-21: 18s after a mid-morning restart the fast path bought a
15-minute-old break +1.6% above the trigger — fires() only checked
price >= trigger, so any blind window (boot, halt, feed outage) fired
instantly on already-extended moves. A trigger now fires only if the live
price was seen BELOW the trigger since arming (a real cross), with a
small at-the-break tolerance (TRIGGER_FRESH_CROSS_TOL, default 1.5%)
so a fast break between ticks still fires; beyond it the name must pull
back below the level and re-cross. Gap-through runners stay covered by
the slow marketable path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 08:50:41 -05:00
momentum devandClaude Fable 5 256bdc31dc fix: clean-exit at date rollover — post-EOD boot ran overnight, day-2 took zero entries
A process booted AFTER the day's EOD flatten never gets exit-after-flatten,
survives midnight, and every memory-only daily flag (_session_closed, entry
caps, benches) stays stale — the documented day-2 limitation, hit live at the
2026-07-21 open (booted 17:11 the day before; signals READY + book armed but
zero gate evaluations, both entry paths silently blocked by _session_closed).
Main loop now exits cleanly when the session date rolls over; compose restarts
a fresh process that rebuilds the new day's state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 08:42:04 -05:00
momentum devandClaude Fable 5 5597250ff1 fix: marketable entries price from live last + time-of-day rvol arms fast path
Day-22 forensic (BIYA session): 6 entry submissions, 0 fills — IREX missed
+1.7R, IRE +1.65R by one cent. Two root causes fixed:

1. Slow-path limit rested AT the trigger while price ran 2% past it, and
   retries reused the same stale price. Marketable model now prices from
   max(trigger, live last) via price_provider; the anti-chase extension
   gate gets the live ref too (was an entry_ref==trigger no-op on this
   path). TRADING_ENTRY_FILL_MODEL flipped to marketable.

2. Fast trigger path armed nothing all day: legacy rvol (cum-so-far /
   full-day MEAN) is ~0.1-0.3 minutes after the open for a normal heavy
   name and its mean is poisoned by reverse-split/prior-squeeze days
   (BIYA legacy 1.27 vs real activity 481x median). New rvol_tod =
   cum / (MEDIAN(20d) * elapsed-session fraction) drives arm eligibility
   and the scanner keep-filter; legacy rvol untouched for validated
   boundaries (gap-squeeze rvol>=5).

Also: stagnation time-stop (TRADING_EXIT_STAGNATION_MIN/_R, default OFF)
in simulate_exit + manage_live with sweep_exits.py variants for
validation before any live enable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:40:15 -05:00
momentum devandClaude Fable 5 f10a8033a1 chart: add volume histogram + Bollinger Bands + EXIT markers
Owner asks on the candlesticks:
- VOLUME: histogram series (bottom 20% overlay), coloured by candle direction.
- BOLLINGER BANDS: 20-period 2-sigma, computed client-side from closes; price
  riding the upper band = strong, break back inside = fading (momentum context).
- EXIT markers were NEVER wired — the fills feed carries only entries
  (order_submitted), and the loop fills brackets via position-diffing so no
  order_filled fires. Exits live in position_closed; now surfaced as square
  markers with reason + P&L (green win / red loss). Entry 'expected' (unfilled
  submit) markers labelled distinctly. Fixed a self-inflicted bug: queried the
  event store AFTER closing it — moved the position_closed fetch inside the
  open-store block. 11 chart tests green incl. new exit-marker test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 14:28:15 -05:00
momentum devandClaude Fable 5 e7bd8fa279 fix: repair DashboardState.__init__ (my prior commit orphaned _backtest_lock)
The self-heal commit (a00f967) inserted the _research method INSIDE __init__ at
method-body indent, truncating it so _change_signal/_backtest_lock/_backtest_
cache never got set -> AttributeError on backtest. Moved _research to a sibling
method; __init__ now sets all attrs. Two chart/sparkline tests updated: a None
research_con now REOPENS lazily, so 'no research db attached' is reached only
when the reopen also fails (they monkeypatch open to fail). 22 api tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 12:06:19 -05:00
momentum devandClaude Fable 5 a00f967bdf fix: dashboard research-DB connection self-heals — the 'TradingView failing' root cause
The charts weren't broken — the data pipe was empty. dashboard_api opened the
research connection ONCE at boot; if the DB was unreachable then (container
started before Postgres, or a blip) it stayed None forever, so /api/chart and
/api/bars returned 'no research db attached' and every candlestick + sparkline
rendered blank. Live-verified: GET /api/chart returned bars:[] reason:'no
research db attached' on the deployed dashboard. Now DashboardState._research()
opens the connection LAZILY and reopens if None — same self-heal as the
event-store emit fix. 2 tests; chart/backtest endpoint tests still green.
No library change: Lightweight Charts v5.2 is fine, it just had nothing to draw.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 12:04:43 -05:00
momentum devandClaude Fable 5 5f04d5584a ui: surface hidden values + UI-REVIEW fixes 1 & 2
Owner: hover-only values were invisible. Now VISIBLE:
- Trades panel: Entry + Exit$ columns (projection now carries entry_price/
  exit_price; were hidden entirely).
- Armed triggers: entry/stop/now levels shown inline (were tooltip-only).
UI-REVIEW fix 1 (color): collapsed 3 greens->1 (#34d17e) and dup reds->
canonical #ff5a60 across CSS AND canvas markers (one --green/--red now).
UI-REVIEW fix 2 (a11y): :focus-visible outlines on all controls + aria-labels
on the money-path buttons (approve/reject/exit/flatten) — keyboard operators
were flying blind.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:54:21 -05:00
momentum devandClaude Fable 5 dd13c41fc8 test(019): de-flake watchlist concurrency test — assert single-snapshot consistency
The test compared len(w) and list(w) as two SEPARATE locked reads, so the
churn thread could mutate between them (30 vs 31) — a bug in the test, not the
Watchlist (each read is individually consistent; there is no torn-read
guarantee ACROSS two calls). Now snapshots once and asserts that snapshot never
tears + concurrent contains/slice never raise. 5/5 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 10:46:43 -05:00
momentum devandClaude Fable 5 b3a47d775d feat(019): discovery on its own thread + tuner heartbeat (honest stale alert)
Telemetry: discovery took 6-30s ON the single scheduler, freezing ingest/arm
so the trigger read stale bars — the real 'late edge detection'. Now discovery
runs on its own daemon thread with a dedicated DB connection; the watchlist it
mutates is a lock-guarded Watchlist (list-like drop-in, no torn reads); the
scheduler discover step auto-disables (single-writer). Loop is now 3 fast
threads (entries/exits/discovery) + slow scheduler for tolerant work.

Also: nightly_tune writes a heartbeat every run, so the boot alert distinguishes
'tuner not running' (real) from 'ran, no profitable ORB config, kept params'
(expected — the strategy has no edge). Kills the misleading Telegram alarm.
543 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:44:10 -05:00
momentum devandClaude Fable 5 a619a99f05 plans(019): move discovery off the scheduler — telemetry-proven 6-30s loop blocker
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:33:49 -05:00
momentum devandClaude Fable 5 5896d291d8 feat(018): sub-second exits on a dedicated thread + fast decision pipeline
Exits move off the single-threaded scheduler onto their own daemon thread at
1s (mirrors the fast trigger thread). Dedicated research connection (shared
facade isn't thread-safe); scheduler exits step auto-disabled when the thread
runs (single-caller invariant — no racing instance state, no double-submit);
manage() already idempotent (stop ratchets >1c up only). 3 driver tests + 539
suite green. On fast small-caps this is the 'get out fast' the owner asked for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:32:46 -05:00
momentum devandClaude Fable 5 375973fe21 plans(018): sub-second exit thread — the last real execution-latency lever
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:05:33 -05:00
momentum devandClaude Fable 5 680ddc2174 loop: faster decision pipeline — ingest 20->5s, arm 20->8s, watch 15->8s, trigger 4->2s
Owner: 'make all decisions as fast as possible.' The real entry-detection
ceiling was ingest — bars refreshed every 20s, so the 4s trigger thread read
data up to 20s stale and detected breakouts late. Ingest is incremental+cheap,
safe at 5s. Fresher bars + faster arm (trigger levels) + 2s trigger check +
the 4s exits shipped earlier = the whole ingest->detect->decide->exit loop
tightened. One batched data call per cycle so Alpaca rate limits are fine.
Sub-second exit management on a dedicated thread is the next (code) step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:04:29 -05:00
momentum devandClaude Fable 5 c3828eff15 exits: manage every 4s not 12s — kill the give-back on fast small-cap round-trips
Owner's real complaint: late exits gave back gains. Exits ran on the slow
scheduler at 12s while entries run on the 4s fast thread — exits were 3x
slower than entries. A small-cap can spike and round-trip inside 12s. Drop
TRADING_EXIT_MANAGE_INTERVAL_SECONDS to 4s (config, reversible, both compose
files). Deeper fix (exit mgmt on the sub-second trigger thread) planned
separately. Entry chase-tail (24% fill >5% above break) is the next
investigation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 21:53:57 -05:00
momentum devandClaude Fable 5 2fd0a860ad plans(017): lower-gap redesign REFUTED — no positive cell in gap x overhang grid
Sliced 2191 depth fires gap x overhang under real exit: lower-gap is WORST
(0-3% -0.831R), inverting the low-gap-edge hypothesis. Overhang helps a little
everywhere, nothing crosses zero. Exhaustive: 13 levers + regime + depth +
this grid = zero positive. No positive subpopulation in gap-ORB $2-20.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 21:13:26 -05:00
momentum devandClaude Fable 5 bdeb2ec4b4 plans(017): VERDICT — L2 depth is real but too weak; FAIL under real exit + random-gate null
Definitive test, n=2191 real deployed exit: ask-overhang separates +0.083R
(high -0.676 vs low -0.759, direction held sniff->scale = genuinely real) but
fails the random-gate null — it shrinks losses, never creates profit. Live
gap>=10% band n=13/74 = no signal. Same shape as regime gate on a negative-EV
base. 13 conversion levers + regime + depth tested, zero make it positive.
Real-money rec: NO. Depth edge argues for a broader-population redesign as
future research, not a live fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:00:07 -05:00
momentum devandClaude Fable 5 5875bbd5f1 fix(events): emit self-heals on a dropped DB connection (the Friday blind-loop bug)
The 2026-07-17 20:23 postgres blip left the live loop writing zero events for
hours: emit() executed on a RAW cursor, bypassing PgConnection._run's
reconnect. cursor() doesn't round-trip so the dead conn went unnoticed until
the raw execute failed with no retry, and self._conn was never rebuilt.
Routing emit through the facade execute() wraps the round-trip in _run, so a
DB blip reconnects on the next write instead of running blind. 2 reconnect
tests + 34 storage tests green. (Deeper 'halt trading when persistently
unable to write' safety guard queued separately.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 14:37:26 -05:00
momentum devandClaude Fable 5 0da3ac0ff7 plans(017): honest status — signal real but modest (AUC 0.586), clean verdict backfilling
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 11:48:08 -05:00
momentum devandClaude Fable 5 46d3460d5c experiments: depth_overhang_v1 — L2 order-book overhang gate (first signal to clear the bar+null)
Plan 017 Phase 0->3. Databento depth backfilled for 602 fires across 21
sessions -> depth_features table. Reweighted to true 23% population win rate,
gating on ask-overhang-above-break (keep>=0.034) lifts win rate 23->28% and
clears the random-gate null (+0.057 vs +0.05 on the conservative +1/-1
bracket). iv_depth_overhang_gate = keep-or-zero, skip-on-no-coverage (the
dilution-v1 lesson). This commit registers the REAL-exit harness run
(simulate_exit asymmetric trail) for the true live delta. Counter-intuitive
mechanism: more resting supply above the break = MORE likely to win
(absorption/fuel), invisible in bar data — the owner's 'size stacked above'
instinct, proven.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 11:32:06 -05:00
momentum devandClaude Fable 5 b9828e5e06 plans(017) Phase 0: GO — ask-overhang-above-break separates winners AUC 0.80
First genuine selection signal in the project. 40 real fires, Databento
XNAS.ITCH book at each trigger: naive imbalance = noise (AUC 0.45/0.50), but
ask-size resting within 1% ABOVE the break separates winners at AUC 0.80
(winner median 0.276 vs loser 0.005; median split 27% vs 67% win). Inverted
from supply-trap intuition = absorption/fuel mechanism, invisible in OHLCV.
n=23 single-day sniff -> GO to Phase 1 recorder, not to trading. Cost: pennies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 09:26:50 -05:00
momentum devandClaude Fable 5 17870872ca research: regime gate relevance — honest answer to 'if it fails everything what's the point'
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>
2026-07-18 09:21:34 -05:00
momentum devandClaude Fable 5 012bcd7b3b plans(017)+data: Databento L2 key verified live — Phase 0 is now a ~$0 same-day experiment
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>
2026-07-18 09:06:33 -05:00
momentum devandClaude Fable 5 4f720fca4f plans(017): L2 order-book depth recorder — free-first, spend-gated, pre-registered
The one untested selection axis (order-book depth = the owner's 'size stacked
above the break' instinct). Staged so $0 is spent until a free sniff test +
recorder prove the signal exists and covers the universe; paid feed is an
explicit owner gate; the signal faces the same pre-registered bar + random-gate
null as every refuted lever before it touches the live entry. Honest ceiling
stated: a PASS nudges odds, is not an autonomous oracle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 08:57:06 -05:00
momentum devandClaude Fable 5 053c927cf7 backtest: explain the regime-gated empty window + toggle to view raw-criteria trades
Owner saw a blank /backtest — it was CORRECT (the regime gate sat out every
recent session) but unexplained. Now: ?regime=0|1 override on /api/backtest,
an 'apply regime gate (as deployed)' checkbox, and an empty-state message
saying WHY there are no trades and what to click. Gated view = what the bot
would do; ungated = the raw simple criteria.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 22:40:03 -05:00
momentum devandClaude Fable 5 3ce8594605 research: supply-side shelf distillation — filed for reference (parked per owner direction)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 22:02:29 -05:00
momentum devandClaude Fable 5 d4d1383e2c plans: mark queued defects fixed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:57:56 -05:00
momentum devandClaude Fable 5 7a82a9f256 fix(admin_api): stop leaking the real .env on import
admin_api.py called load_dotenv() unconditionally at module level, so
merely importing it (e.g. pytest collecting
tests/unit/test_admin_effective_config.py) ran python-dotenv's upward
find_dotenv() search, loaded the real repo .env (secrets included),
and polluted os.environ for the rest of the test session.

Gate the load_dotenv() call behind `if __name__ == "__main__":` at the
same source location, so it still runs before the module-level
os.environ.get(...) constants when run as the entrypoint (`python
admin_api.py` is unchanged) but never fires on a plain import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:53:33 -05:00
momentum devandClaude Fable 5 6e6bca1518 fix(attribution): stop KeyError-ing when DATABASE_URL is unset
research/attribution.py's _connect() read os.environ["DATABASE_URL"]
directly, bypassing the storage facade fallback chain (DATABASE_URL,
PG* vars, LAN host retries) every other module goes through — and
KeyError-ing (uncaught by name, though still 500'd by the endpoint's
broad except) whenever DATABASE_URL was unset.

Delegate to storage.db.get_connection().raw instead, so DB resolution
matches EventStore/research.multi_schema, and a genuinely unreachable
DB fails with a catchable psycopg2.OperationalError rather than a
KeyError.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:49:55 -05:00
momentum devandClaude Fable 5 9772aca6bc fix(events): add optional side field to OrderFilledEvent
OrderFilledEvent had no side field, forcing api/main.py's chart() to
backfill it from the sibling order_submitted row. Add an additive-only
`side: str | None = None` field (existing payloads without it still
parse) and populate it at the sole production emit site
(alpaca_paper/execution.py _finalize) from the ExecutionRequest already
in scope. The api/main.py backfill workaround is left in place as a
harmless fallback for pre-existing events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:47:17 -05:00
momentum devandClaude Fable 5 849bd476f9 adjudicator: random-gate null for keep-or-zero PASSes + register orderflow H6 (quote-size book imbalance)
Null (the H2 lesson): zeroing share f of a negative baseline mechanically
yields expR_delta = f*|mean baseline|; a PASS on expR_delta_min now also
requires expR_delta_vs_null >= the same margin whenever zeroed_share >= 5%.
Exposure-shrinking can no longer masquerade as selection; non-gate
experiments unaffected. Honest disclosure: regime_meta_v1's FIRE-weighted
pass would not clear this bar either — its standing rests on the session-
level walk-forward OOS evidence, which no random gate reproduces.

H6: CKS order-flow-imbalance from quote_snapshots bid/ask SIZES —
size-weighted book imbalance over the 10-min pre-trigger window, zero
ask-tilted books; skip-on-no-coverage; judged under the null from day one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 19:50:05 -05:00
momentum devandClaude Fable 5 5bf089cdcc research: orderflow H1/H2/H4 verdicts — all refuted in substance; H2's formal PASS is a bar artifact
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>
2026-07-17 19:24:47 -05:00
momentum devandClaude Fable 5 07ddbd30b9 experiments: register orderflow H3 (spread-expansion gate) + H4 (volume-spike gate)
H4 (3-book): keep-or-zero on trigger-bar volume concentration vs prior 5-bar
sum (>1.0 = completed one-shot burst). H3 (5-book, direction disputed):
fragility-camp primary — zero fires whose fill-time relative spread exceeds
2x the name's own 10-min median from quote_snapshots (which carry bid/ask
SIZES since 07-02 — the note's data-upgrade #1 was already satisfied).
Coverage design learns the dilution_veto_v1 lesson: no-quote fires are
SKIPPED (variant None, excluded from n), so thin coverage cannot false-FAIL.
H5 deliberately deferred until H1's verdict (its incremental value is defined
net of H1's catches). 6 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 18:32:10 -05:00
momentum devandClaude Fable 5 46f80eccf2 experiments: register orderflow H1 (trigger-erasure exit) + H2 (flow-persistence gate)
From docs/research/orderflow_notes.md — the order-flow shelf distillation.
H1 (5-book convergence): exit on first post-fill 1-min close back below the
trigger within 5 bars — the transitory-impact/recross signature — else the
standing exit stack. H2 (4-book): keep-or-zero on pre-trigger volume-weighted
bar-sign persistence (<0.30 over 15 bars = choppy one-shot burst, zeroed);
strictly-before-entry window, fail-open on thin history. Both pre-registered
at the house bar (+0.05R, n>=300, standard fidelity); nightly adjudication
decides. 7 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 18:24:22 -05:00
momentum devandClaude Fable 5 cdb138c87d research: order-flow shelf distillation — 5 pre-registered-ready entry-conversion hypotheses
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>
2026-07-17 18:07:21 -05:00
momentum devandClaude Fable 5 a9aa37c885 backtest: replay the LIVE track only (v1) — shadow research tracks poisoned /backtest
replay_window reconstructed fires from ALL setup versions, so liq1/vr1/run1/pm1
(refuted shadow tracks, never traded live, 8:1 volume vs v1, tight-stop
artifacts) dominated /backtest: 17k trades / 45 sessions at -1.8R/trade —
garbage scale vs the harness-validated -0.08R/fire. reconstruct_fires_from_setups
gains an optional setup_version filter (default None: experiments unchanged);
replay_window pins it to v1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:22:03 -05:00
momentum devandClaude Fable 5 d50115dea8 backtest: 1d window button (owner request)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:09:06 -05:00
momentum devandClaude Fable 5 8fd0fc7921 plans(016): record first adjudication — FAIL on pre-registered bar (3% coverage; flagged fires outperformed)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:22:19 -05:00
momentum devandClaude Fable 5 d2114a7716 plans(016): DONE — merged f6999c7; backfill seeded
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:56:26 -05:00
momentum dev 3d0f404a29 Merge branch 'worktree-agent-a15ca95a014d606fe' 2026-07-17 09:56:24 -05:00
momentum devandClaude Fable 5 f6999c7f6a dilution(016): unbreak nightly EDGAR refresh + pre-register filing-veto experiment
premarket_eval's shadow enrichment call site queried session_date = current_date
(wrong on UTC-server Postgres at night, so it never matched any symbols) and
shared one try-block for float_cache + dilution filings (a float_cache failure
silently skipped the dilution refresh too). Fix: compute the ET session date
in Python, split into two independent try-blocks, lift into a testable
_refresh_shadow_enrichment(con, session_date). float_cache now applies its own
DDL unconditionally on use instead of only when an API key is present.

Adds iv_dilution_gate (additive, mirrors iv_regime_gate's keep-or-zero shape
keyed on symbol instead of session_date, fail-open on unfetched symbols) plus
its once-per-run preload, and registers the pre-registered dilution_veto_v1
experiment via the existing experiments/*.json self-registering cohort. Live
veto stays OFF; this only wires the shadow measurement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:54:49 -05:00
momentum devandClaude Fable 5 d8d37700aa plans(016): EDGAR dilution enrichment — unbreak nightly refresh + pre-register filing-veto experiment
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:29:40 -05:00
momentum devandClaude Fable 5 92488b72bc youtube: YT_PLAYLIST_URLS — comma-separated playlists via env, synced at boot
One playlist per add-playlist CLI call made adding a second playlist a chore.
Now YT_PLAYLIST_URLS takes a comma-separated list of playlist URLs (list=
param extracted) or bare ids; the transcripts service seeds the playlists
table from it at boot (insert-if-missing — hand-set labels are never
clobbered), and `python -m youtube_claims.run sync-playlists` does the same
on demand. Documented in both compose files; 3 tests.

Also: queue the two executor-surfaced defects (OrderFilledEvent side,
/api/attribution env hardcode) + the flaky nightly regime build-features
step in the plans index; regime_features 07-16 backfilled supervised.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:14:22 -05:00
momentum dev a4e7a6d534 Merge branch 'worktree-agent-ae4d3f36e18c6e7a5' 2026-07-17 09:05:47 -05:00
momentum devandClaude Fable 5 8feac6da81 plans(015): DONE — executed + review-approved first pass, awaiting merge
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 08:45:39 -05:00
momentum devandClaude Fable 5 8ac222a4ca research-tooling: /backtest replay dashboard — theoretical trades under current settings
Adds a read-only /backtest page showing how the bot WOULD have traded past
sessions under the CURRENTLY configured live settings: theoretical ORB fires
from setup_events, replayed through the shared exit engine (simulate_exit +
ExitConfig.from_env(), same adverse-fill accounting the exit-experiment
harness uses), screened through the shared population gate and (optionally)
the live regime gate. No new simulation math — replay.replay_window() is pure
assembly of existing pieces (reconstruct_fires_from_setups, _fire_in_population,
_load_regime_favorable, simulate_exit); api/main.py adds the /backtest and
/api/backtest routes plus a dedicated-lock 10-min cache so a slow replay never
blocks the live dashboard's SSE stream. Also extends /api/chart with an
optional date= anchor so the trade drill-in chart modal can reach sessions
older than the existing 72h-from-now window, and adds a one-line BACKTEST nav
link to the live dashboard header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 08:42:43 -05:00
momentum dev 0ed0abe0e2 Merge branch 'worktree-agent-acb00535650deac1a' 2026-07-17 08:16:59 -05:00
momentum devandClaude Fable 5 822584e072 plans(014): DONE — executed + review-approved first pass, awaiting merge
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 07:38:01 -05:00
momentum devandClaude Fable 5 dd81dbbda1 operator-plane: live decision trace — gate receipts feed + chart reject markers (plan 014)
Surfaces the entry_gate_evaluated decision receipts (plan 005) that were
write-only until now: a Decision Trace panel (per-gate value->threshold
chips, symbol filter, lazy-load + SSE refresh) and collapsed reject markers
merged into the plan-013 symbol chart's existing markers primitive.

- storage/projections.py: query_gate_receipts (case-insensitive symbol
  filter, newest-first, same _hist_window idiom as query_fills_feed)
- api/main.py: GET /api/decisions (clamped limit<=500, date-scoped);
  DashboardState.decisions() adds a server-computed epoch per row; factored
  chart()'s inline to_epoch into a shared _to_epoch_et helper
- api/static/dashboard.html: Decision Trace panel + refreshChart now also
  fetches /api/decisions and merges collapsed reject markers (same-reason
  bursts within 5min -> one marker) into the single setMarkers call

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 07:36:31 -05:00
momentum dev 968bcadc94 Merge branch 'worktree-agent-ae2ee0000d5e3a7eb' 2026-07-17 07:19:37 -05:00