Files
momentum-system/docs/history/IGNITION_SETUP_SCOPE.md
T
momentum devandClaude Opus 4.8 5ac2cc7b89 Docs cleanup: current-state rewrite + three deployment modes + history/
- 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>
2026-07-10 10:56:53 -04:00

8.6 KiB
Raw Blame History

Momentum-ignition setup — shadow scope

Scope produced 2026-06-24 by a read-only 4-agent mapping workflow (wf_55582b6d). No code changed. A SECOND, shadow-only lane to catch PLSM-class vertical squeezes the pullback-ORB scorer is blind to.

Why

The live setup is impulse → pullback → breakout (strategy/evaluation/structure.py classify_setup). A true vertical squeeze gives no pullback and no formed opening range, so classify_setup returns NONE and the scorer blocks it — verified: PLSM was evaluated 1,895× on 2026-06-24 and blocked ~30% every time. This lane is the missing discipline: ride the vertical, not the base.

What it detects

An intraday vertical ignition with no consolidation: a fresh high-of-day above the prior all-time high (blue sky), accelerating price velocity (consecutive green bars + an X% move over K minutes), and an absolute volume burst. It deliberately catches the pattern that makes classify_setup return NONE.

Detection rules — all computable TODAY (binary, no fitting at this n)

signal rule source
Blue-sky fresh-HOD current.high > prior_ATH (max daily_bars.high before today) AND this bar is the running session-high HOD. Don't reuse detect_hod_break (needs consolidation a vertical never gives). daily_bars.high + minute_bars.high
Price velocity pct_move(close[t]/close[t-N]-1) ≥ 0.08 over N=5–10 RTH min, AND ≥4 of last 5 bars green, AND higher-highs. (PLSM 12:02–12:16: 10/14 green, +13.9% in 14 min.) minute_bars OHLC, pure arithmetic
Absolute volume burst last_bar_vol / mean(prior K=5 bars) ≥ 3.0 AND cumulative session volume ≥ 100k shares (or a $-volume floor). Absolute floors, NOT RVOL — scan_gappers RVOL zeros out on thin IPOs (adv=0), and rolling_avg_volume_20d is NULL. minute_bars.volume
catalyst (tag only) attach max-conviction bullish row (sentiment>0, is_dilutive=False); never a gate (Ollama gated off / can be down). PLSM "Soaring" → 0.9 conviction. news_catalyst_cache
VWAP (tag only) log above_vwap + dist_from_vwap as grading context (the one validated ~1.5× signal). compute_key_levels.vwap
float (placeholder) DO NOT GATE — symbols table is empty, float_shares NULL. Log None so a later float backfill can re-grade historical shadows. symbols.float_shares (empty)

Hook point

A new zero-arg closure step_shadow_ignition() in run_live_paper.py beside step_watch (~L528), registered via scheduler.add('shadow_ignition', …, enabled=_flag('SHADOW_IGNITION_ENABLED','0')) — ships dark. Pure-read against research_con (single psycopg2 conn — not thread-safe), emit-only, never references rt['execution']. Reuses the watcher provider's get_candidates/get_bars (same symbols/bars) but with its OWN wider caps (SHADOW_PRICE_MAX≈100, no gap ceiling) so it validates the very names live config excludes. Detection is a new pure detect_momentum_ignition(bars) in strategy/evaluation/ignition.py — NOT wired into classify_setup/evaluate_setup (the pullback criteria would reject it and STRATEGY_SETUPS=opening_range_break would filter it).

Shadow mechanism — structurally cannot trade

New EventType.SHADOW_SIGNAL + ShadowSignalEvent (symbol, trigger, would-be stop, signal_values dict, session_date). It is invisible to execution: query_ready_signals_snapshot filters strictly on event_type='signal_ready', the only thing TradingExecutionService.tick reads — a shadow_signal is never seen, and the lane never calls submit_breakout_now/request_approvals. (Precedent: the VWAP gate already shadow-logs via RiskRuleTriggeredEvent action_taken=shadow_logged.) Safety: wrap the step in try/except (a shadow failure can't kill the loop), once-per-session per-symbol debounce (so one squeeze logs once, not 1,895×), never emit signal_ready, never set a symbol "ready".

Measurement — reuse existing forward-outcome machinery (zero new metric code)

  1. Labeler: add build_ignition to research/labeler.py (setup_name=momentum_ignition, setup_version=ign_v1) reusing the identical label block (reached_1r/2r_before_minus_1r, max_upside_5/15/60m, time_to_max, failed_breakout_flag=the fizzle trap, held_vwap). report /lift filtered by version give runner-vs-trap separation per signal.
  2. eod_replay: add score_shadow_signals — after the close, read the day's actual shadow_signal events, walk minute_bars strictly after each, classify runner (+1R) / fizzle / halt-down-trap (the FCUV mode: a forward bar gapping far below the prior low, or R ≤ −1 within minutes). Run nightly.

Promotion bar — PRE-REGISTER before shadowing (so the bar can't move to fit noise)

  • reached_1r rate materially above the failed_breakout trap rate, AND
  • halt-down-trap rate below a ceiling, AND
  • distinct runner instances above the thin-n floor (≥30, or rate×count ≥8) — counted in distinct runners, NOT calendar days (the ~17–19 days are autocorrelated, one regime).
  • Decision hangs on the eod_replay as-fired numbers (what a live lever would actually capture).

Build phases

phase blast what
P0 none detect_momentum_ignition(bars) pure + unit-tested (synthetic bars + the PLSM 12:02–12:16 window). No wiring.
P1 low SHADOW_SIGNAL/ShadowSignalEvent + step_shadow_ignition (default OFF, own caps, debounce, try/except). Test asserts it NEVER emits signal_ready / never calls execution.
P2 low build_ignition (labeler) + score_shadow_signals (eod_replay), nightly. DB-unreachable tolerant; tests use :memory:/MOMENTUM_PG_SCHEMA.
P3 none Pre-register the promotion thresholds; shadow N weeks; accumulate. Zero capital, zero trading-path edits.
P4 high Only after P3 separation is proven — a tiny live lever behind require_ignition_confirmed (default OFF, mirrors require_above_vwap), minimally sized, bounded by ALL shipped catastrophe controls (enforced stops, anti-chase, BE@+5%, halt guard). The ORB book + anti-chase guards untouched — strictly additive.

Open questions for you

  • Stop definition for the R denominator: last-higher-low vs bar-low vs −8%? Changes every reached_1r/trap rate — pick ONE and pre-register. (Maps lean last-higher-low.)
  • Shadow gap ceiling: drop entirely or cap ~80%? Dropping surfaces the most extreme/trap-prone names (fine for shadow).
  • Catalyst tag needs Ollama running that day — force-enable it for the shadow grading pass so news-backed vs not can be measured?
  • Distinct-runner-n ≥30 may be unrealistic given runner sparsity — a Bayesian interval at low n instead of a point rate?

⚠️ P0 reality-check (2026-06-25) — READ BEFORE P1

The P0 detector is built + unit-tested (fires correctly on a clean synthetic vertical). But running it over the real PLSM 2026-06-24 bars fired 0 times — correctly — and the why reshapes this whole effort:

  • PLSM's session HIGH (12.85) was made at the 9:35 ET open (it gapped from ~$4 pre-market); RTH was then a day-long fade. The "+400% to $20" the trader cites happened pre-market, which the bot does not ingest (RTH-only minute_bars) or trade.
  • PLSM was halted for 228 of ~390 RTH minutes — its RTH move is halt-resumption GAPS (+3.6%, −12.5%, −17.3%, −14.4% …), NOT a smooth consecutive-bar vertical. A detector keyed on consecutive green bars + smooth velocity structurally cannot see a halt-gapped move.

Implication: the momentum-ignition opportunity is largely a pre-market + halt-resumption phenomenon, not an RTH smooth-vertical one. Capturing it would need (a) pre-market/extended-hours bar ingestion, (b) halt-aware, gap-up detection (resumption gaps, not consecutive bars), and (c) the bot to trade pre-market / halt resumptions — which runs straight into the halt-down blowup risk (FCUV) the anti-chase guards exist to prevent. That is a far bigger lift than an RTH detector, and an operator decision before P1: invest in pre-market+halt data/logic, or shelve the ignition lane and bank the ORB/float work instead. The P0 detector + tests remain a correct foundation either way.

Hard constraints (non-negotiable)

Shadow-only until forward data proves it · never touches the ORB book or anti-chase guards · ships dark (SHADOW_IGNITION_ENABLED=0) · binary rules, no fitting · do NOT gate on RVOL or float (both broken for exactly these names) · tests use :memory:/MOMENTUM_PG_SCHEMA (never the live DB).