> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sequency.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Screener regime intraday

# Screener, Regime & Intraday API

Discovery + context + level inputs for strategy R\&D: the **screener** family
(candidate discovery by technical/options/fundamental/compression filters), the
market **regime** surface (2×3-grid context plus GEX / volatility / Greeks), the
**intraday levels & profile** that intraday strategies key off (VWAP, Initial
Balance, Opening Range, pivots, volume-profile HVN/LVN, compression), and trade
**outcome** analytics. Base path `/api/graph/v1`; internal Go→Python plumbing
under `/api/internal/intraday`. Auth as in the [README](./README.md).

> Most intraday `session`/`position`/`signal` endpoints are the live trading
> surface, not backtesting — R\&D mainly cares about the **levels/profile**,
> **volume-nodes**, **compression**, **screener**, **regime**, and **outcomes**
> reads. They're all listed for completeness.

## Screener & Regime

Screener endpoints filter stocks by technical, fundamental, options, and market-context criteria to surface high-conviction opportunities. Regime endpoints provide market microstructure context—gamma exposure (GEX), volatility regimes, and options Greeks—using a canonical 2×3 grid (BULL/NEUTRAL/BEAR × LOW/HIGH\_VOL) that gates strategy selection.

### POST /api/graph/v1/screener

Screen stocks using graph-native filters with optional relationship expansion. Returns paginated results with confluence scoring, technical indicators, and optional price levels, volume profile, and news.

**Request:** `ScreenerRequest` (filters, include options, sort, pagination)
**Response:** `ScreenerResponse` (stocks: \[StockResult], metadata: ScreenerMetadata)
**Caching:** 15s during market hours, 60s off-hours (Redis)
**Query target:** \<500ms with includes, \<200ms without

#### Filter contract: fail closed, never silently dropped (data#1606)

`ScreenerRequest.filters` (`Filters`, `api/app/models/screener.py`) and its 13
sub-filter models (`PriceFilter`, `VolumeFilter`, `OptionsFilter`,
`TechnicalFilter`, `FundamentalFilter`, `ConfluenceFilter`, `PulseFilter`,
`EarningsFilter`, `PriceEventFilter`, `DarkPoolFilter`, `ConvictionFilter`,
`ThemeFilter`, plus `Filters` itself) all set `model_config = {"extra":
"forbid"}` — an unknown/misspelled filter key is a `422` at parse time, not a
silently-ignored field. `ScreenerRequest`'s own envelope carries the same
`extra="forbid"`.

Beyond typo-rejection, every filter **leaf the caller explicitly sets** must
either compile into a graph predicate or the whole request is rejected — a
filter that is accepted by the schema but never wired into the query builder
used to return the *unfiltered* universe while claiming it was narrowed
(the original data#1606 defect). Enforcement is at the shared boundary
(`GraphService.screen_stocks` via `assert_all_filters_compilable`,
`api/app/services/screener/screener_filters.py`), so it applies uniformly to
the HTTP route, the natural-language screener, and internal callers like
`ThesisScreener` — not just this endpoint.

A request naming an unwired leaf gets `422`:

```json theme={null}
{"error": "unsupported_filter", "message": "...", "unsupported_filters": ["patterns"], "applied_filters": []}
```

As of this writing, six leaves are accepted by the schema but not yet wired
into the compiler, and are therefore always rejected if explicitly set:
`patterns`, `technical.compression`, `technical.atr_compression_max`,
`price_events.event_types`, `price_events.min_severity`,
`price_events.days`. Notably, filtering by pattern (the top-level `patterns`
list, or `technical.compression`) has no working path yet even though the
pattern registry itself already uses the current `nr7id` identifier (NR7 was
renamed from `nr7`) — the vocabulary rename is not the blocker, the missing
compiler wiring is. Pin-tested exhaustively in
`api/tests/unit/test_screener_filter_fail_closed.py`
(`EXPECTED_COMPILED`/`EXPECTED_UNSUPPORTED`), which fails closed itself: a
newly-added `Filters` field that isn't classified in either set breaks the
test rather than rotting silently.

### POST /api/graph/v1/options/screener

Screen stocks with options-centric defaults (IV percentile, chain OI, put/call ratios, pricing edges, Greeks alignment). Convenience wrapper over the base screener.

**Request:** `ScreenerRequest` · **Response:** `ScreenerResponse`

### POST /api/graph/v1/research/screener

Screen stocks by fundamental metrics (market cap, PE, PB, PS, EV/EBITDA, margins, ROE/ROA, debt ratios, dividend yield, FCF yield).

**Request:** `ResearchScreenerRequest` (filters: ResearchScreenerFilters, sort, pagination)
**Response:** `ResearchScreenerResponse` (results: \[ResearchScreenerResult], total\_matched, returned, offset, query\_ms)

### POST /api/graph/v1/compression/screener

Screen for compression patterns (NR7, inside days, VCP contractions, Bollinger Band squeezes) with ATR-based contraction scoring.

**Request:** `CompressionScreenerRequest` (days, min\_nr7\_count, is\_nr7\_today, min\_inside\_day\_count, compression\_level, max\_atr\_compression\_ratio, min\_vcp\_contractions, limit, offset)
**Response:** `CompressionScreenerResponse` (results: \[CompressionScreenerResult], total\_count, lookback\_days)

### POST /api/graph/v1/screener/top-setups

Batch fetch top bullish and bearish setups in one call. Filters by ADV30 ≥\$50M and Pulse bias; results cached in-memory.

**Request:** `TopSetupsRequest` (limit: 1–20, default 5)
**Response:** (inferred from handler) `{"bullish": ScreenerResponse, "bearish": ScreenerResponse, "errors": [str] | null}`

### GET /api/graph/v1/screener/sector-summary

Sector conviction aggregates for heatmap display. Groups stocks by sector and returns median conviction, median net confluence, and top movers by conviction delta.

**Response:** `SectorSummaryResponse` (sectors: \[SectorSummary], total\_stocks, query\_ms) · **Caching:** 60s (Redis)

### POST /api/graph/v1/screener/nl

Translate natural language queries into structured screener filters via LLM (e.g. "oversold tech with high volume").

**Request:** `NLScreenerRequest` (query: str, regime\_context: dict | null)
**Response:** `NLScreenerResponse` (filters: ScreenerRequest, explanation: str)

### GET /api/graph/v1/regime/current

Current market regime + signal-strength context. Returns the dominant regime classification and which signal types perform well (win\_rate ≥50%, profit\_factor ≥1.0) in the current regime.

**Response:** (inferred from handler) `{"current_regime": str, "timestamp": str | null, "strong_signals": [dict], "weak_signals": [dict], "performance": [dict]}`

### GET /api/graph/v1/regime/summary

All symbols — compact GEX + vol regime snapshot. Index ETFs (SPY, QQQ, IWM) with latest GEX regime, gamma flip strike, pin strike, vol regime, ATM IV, IV rank, term structure.

**Response:** `RegimeSummaryResponse` (symbols: \[RegimeSummaryItem], scheduler\_running: bool, last\_computation: str | null)

### GET /api/graph/v1/regime/{symbol}/gex

Latest GEX snapshot. Optional `expiration` (YYYY-MM-DD) to filter to one expiry; omit for aggregate.

**Params:** `symbol` (path), `expiration` (query, optional) · **Response:** `GEXSnapshotResponse`

### GET /api/graph/v1/regime/{symbol}/gex/history

GEX snapshot history (up to 90 days). **Params:** `symbol` (path), `days` (query 1–90, default 30). **Response:** `[GEXSnapshotResponse]`

### GET /api/graph/v1/regime/{symbol}/vol

Latest volatility regime classification (e.g. "low-vol", "high-vol", "vol-expanding"). **Params:** `symbol` (path). **Response:** `VolRegimeSnapshotResponse`

### GET /api/graph/v1/regime/{symbol}/vol/history

Vol regime history (up to 90 days). **Params:** `symbol` (path), `days` (query 1–90, default 30). **Response:** `[VolRegimeSnapshotResponse]`

### GET /api/graph/v1/regime/{symbol}/vanna-charm

Vanna-charm Greeks curve across strikes. **Params:** `symbol` (path), `expiration` (query, optional). **Response:** `VannaCharmResponse`

### GET /api/graph/v1/regime/{symbol}/auction-history

Downsampled GEX auction history + summary (regime transitions, average GEX, dominant regime, flip-strike range). **Params:** `symbol` (path), `hours` (query 1–168, default 72), `resolution` (query enum 1min/5min/15min/1h, default 5min). **Response:** `AuctionHistoryResponse`

### GET /api/graph/v1/regime/transitions/performance

Regime transition performance matrix — win rates, average returns, recommended actions per transition. **Params:** `lookback` (query enum 90d/1y/5y, default 1y), `signal_type` (query, optional). **Response:** (inferred) `{"transition_matrix": dict, "raw": [dict], "lookback": str}`

## Intraday levels, volume nodes, compression & outcomes

These power the intraday research toolkit — institutional-grade levels (VWAP,
Initial Balance, Opening Range, pivots), volume-profile nodes (HVN/LVN), volatility
compression (NR7, inside day, VCP), and trade outcome metrics. The `session` /
`position` / `signal` endpoints are the live-trading surface (listed for
completeness); the **levels/profile**, **volume-nodes**, **compression**, and
**outcomes** reads are what R\&D consumes.

### Session management (live trading surface)

* `POST /api/graph/v1/intraday/{symbol}/session` — start session. Body `StartSessionRequest` (mode: monitor|supervised|autonomous, trading\_mode: paper|live). → `IntradaySessionResponse`
* `GET /api/graph/v1/intraday/{symbol}/session` — get current session (`trading_mode` query, default paper). → `IntradaySessionResponse`
* `POST .../session/stop` · `.../session/halt` · `.../session/resume` — stop/pause/resume. → `IntradaySessionResponse`
* `POST .../session/mode` — change mode mid-session. Body `ModeChangeRequest`. → `IntradaySessionResponse`
* `GET /api/graph/v1/intraday/sessions` — all active sessions. → `AllSessionsResponse`
* `POST /api/graph/v1/intraday/sessions/stop-all` — kill-switch stop all. → `AllSessionsResponse`
* `POST /api/graph/v1/intraday/sessions/bulk-start` — bulk start. Body `BulkStartRequest` (items:\[{symbol,mode}], trading\_mode). → (inferred) `{created, already_active, errors, results}`

### Position management (live trading surface)

* `POST .../{symbol}/position/{position_id}/tighten-stop` — Body `TightenStopBody` (stop\_price>0).
* `POST .../position/{position_id}/close` — close at market.
* `POST .../position/{position_id}/close-partial` — Body `PartialCloseBody` (quantity>0).
* `POST .../position/{position_id}/breakeven` — move stop to entry.
* `POST .../position/{position_id}/mark-closed` — Body `MarkClosedBody` (exit\_price>0); monitor-mode manual close.

### Signal & exit actions, close phase (live trading surface)

* `POST .../{symbol}/signal/{signal_id}/approve|skip|dismiss` — supervised/monitor signal actions.
* `POST .../{symbol}/exit/approve|skip` — exit-recommendation actions (`position_id` query).
* `POST .../{symbol}/close-now` — close phase (15:30–15:45 ET) immediate close (`position_id`, `trading_mode` query).
* `POST .../{symbol}/mark-closed` — close-phase monitor-mode mark (`position_id`, `trading_mode` query; Body `MarkClosedTimeStopBody`).

### Real-time stream

* `GET /api/graph/v1/intraday/{symbol}/stream` — SSE event stream (signal, position\_opened/closed, phase\_change, level\_update); 30s heartbeat; `Last-Event-ID` replay. Content-Type `text/event-stream`.

### Intraday user profile (wizard persistence)

* `GET /api/graph/v1/user/intraday-profile/` → `IntradayProfileResponse`
* `PUT /api/graph/v1/user/intraday-profile/` — upsert (`UpsertIntradayProfileRequest`) → `IntradayProfileResponse`
* `PATCH /api/graph/v1/user/intraday-profile/` — partial update (`PatchIntradayProfileRequest`) → `IntradayProfileResponse`

### Signal statistics

* `GET /api/graph/v1/intraday/signal-stats` — per-signal-type backtest metrics + live outcome counts (30d). → `{signal_types: [{signal_type, backtest, live}]}`
* `GET /api/graph/v1/intraday/signal-stats/{signal_type}/detail` — equity curve (cumulative R), P\&L distribution, exit-reason breakdown, recent signals. → (inferred) `{signal_type, backtest_metrics, equity_curve, distribution, exit_reasons, recent_signals}`

### Disclaimer & privilege controls

* `GET .../intraday/disclaimer/status` → `DisclaimerStatusResponse`
* `POST .../intraday/disclaimer/acknowledge` — Body `DisclaimerAckRequest` (immutable, audit-logged).
* `GET .../intraday/disclaimer/content` → `DisclaimerContentResponse` (public).
* `POST .../intraday/kill-switch` — cancel orders / close positions / stop sessions (5/min). Body `KillSwitchRequest`.
* `POST .../intraday/disable` — kill-switch + revoke intraday access. Body `DisableIntradayRequest`.
* `GET .../intraday/privilege-events` — paginated privilege-change audit log.

### Internal (localhost only, Go → Python — no auth)

* `POST /api/internal/intraday/{symbol}/signal` — signal intake. Body `IncomingSignal`.
* `POST /api/internal/intraday/{symbol}/levels` — level-snapshot persistence. Body `IncomingLevelSnapshot`.
* `POST /api/internal/intraday/exit-notification` — partial/stop/full exit notifications.
* `POST /api/internal/intraday/close-phase/{session_id}` — trigger close phase (15:30 ET).

### Volume nodes (HVN/LVN)

* `GET /api/graph/v1/volume-nodes/{symbol}` — detect HVN/LVN. **Query:** `trade_date`, `session_type` (prior|developing, default prior), `hvn_threshold` (0–3σ, default 1.0), `lvn_threshold` (-3–0σ, default -1.0), `min_buckets` (1–10, default 2). → `VolumeNodesResponse`. (HVN = support/resistance; LVN = breakout zones. \<200ms; 503 if prior-session data stale.)
* `GET /api/graph/v1/volume-nodes/{symbol}/context` — nodes + full profile context (POC/VAH/VAL, strongest HVN/LVN). Same query params. → `VolumeNodeContext`

### Compression patterns

* `GET /api/graph/v1/stocks/{symbol}/compression` — daily compression history (ATR ratio, BB-width pct, NR7/inside-day, VCP contractions, score). `days` (1–365, default 30). → `CompressionHistoryResponse`
* `GET /api/graph/v1/stocks/{symbol}/compression/stats` — aggregated stats. → `CompressionStats`
* `GET /api/graph/v1/stocks/{symbol}/compression/trend` — recent-vs-earlier trend (`days` 5–90, default 20). → `CompressionTrendResponse`
* `GET /api/graph/v1/stocks/{symbol}/compression/nr7-count` — convenience NR7 count. → `int`
* `POST /api/graph/v1/compression/screener` — compression screen (see Screener above). → `CompressionScreenerResponse`

### Trade outcomes & analytics

* `GET /api/graph/v1/outcomes/signals/performance` — hit rates / avg return / Sharpe proxy grouped by `group_by` (conviction\_tier|signal\_type|regime|regime\_dominant|market\_phase|time\_bucket). Query: `min_date`, `max_date`, `variant`. → `{groups, effective_date_range, variant_dimension, mixed_variants}`
* `GET /api/graph/v1/outcomes/signals/slippage` — avg decision/market/total slippage (bps) by group.
* `GET /api/graph/v1/outcomes/signals/regime` — hit rate / avg return / directional accuracy per regime (`min_conviction`).
* `GET /api/graph/v1/outcomes/signals/funnel` — status distribution + top blocked/gated reasons.
* `GET /api/graph/v1/outcomes/signals/contracts` — hit rate/slippage by delta bucket × DTE bucket.
* `GET /api/graph/v1/outcomes/debates/performance` — debate-recommendation hit rate by verdict/strategy.
* `GET /api/graph/v1/outcomes/alpha` — cross-layer alpha attribution (`horizon` eod/1d/3d/5d). → `{sources:[{source, hit_rate, avg_return, sharpe_proxy, n}], ...}`

## Key schemas

### Levels (intraday institutional levels)

The level set intraday strategies key off (VWAP, IB, OR, pivots, volume profile):

```
{
  pivot_pp?, pivot_r1?..r3?, pivot_s1?..s3?: number   // floor pivots
  cpr_tc?, cpr_bc?: number                            // Camarilla pivot range
  ib_high?, ib_low?, ib_range?: number                // Initial Balance (09:30-10:00 ET)
  ib_1_5x_high?, ib_2x_high?, ib_1_5x_low?, ib_2x_low?: number   // IB extensions
  orb_5m_high?/low?, orb_15m_*, orb_30m_*: number     // Opening Range Breakout
  orb_5m_status?, orb_structure?, orb_directional_bias?: string
  vah?, val?, poc?: number                            // volume profile
  vwap?, vwap_plus_1sd?/2sd?, vwap_minus_1sd?/2sd?, price_vs_vwap_sd?: number
  pdh?, pdl?, pdc?: number                            // prior day H/L/C
  pwh?, pwl?: number                                  // prior week
  onh?, onl?: number                                  // overnight
  nearest_level?: string, dist_to_nearest_atr?: number
}
```

### VolumeNode

```
{
  symbol, trade_date, session_type, node_type: enum("HVN","LVN")
  price_low, price_high, price_mid: decimal-string
  node_volume: int, node_volume_pct?, bucket_count: int, avg_bucket_volume?: int
  node_rank?: int, z_score: decimal-string
  overlaps_value_area?: bool, contains_poc?: bool, computed_at?: ISO
}
```

### CompressionScreenerResult

```
{
  symbol, latest_date
  atr_compression_ratio?, range_vs_atr_14?, bb_width_percentile?, compression_score?: number
  compression_level?: enum("extreme","high","moderate","low","none")
  is_nr7?, is_inside_day?: bool, vcp_contraction_count?, nr7_count?, inside_day_count?: int
  close_price?, volume?, relative_volume?: number
}
```

### GEXSnapshotResponse

```
{
  symbol, time, spot_price: number
  total_gex: number, gex_regime: "positive"|"negative"|"neutral"
  gamma_flip_strike?, max_pos_gamma_strike?, max_neg_gamma_strike?, pin_strike?: number
  acceleration_zones: [AccelerationZoneResponse], gex_curve: [GEXStrikePointResponse]
  contracts_analyzed: int, expirations_used: int, data_quality: number(0-1), is_aggregate: bool
}
```

### VolRegimeSnapshotResponse

```
{
  symbol, time, regime: string, regime_confidence: int(0-100)
  term_structure?, near_term_iv?, far_term_iv?, atm_iv?: number
  iv_rank_252d?, iv_percentile_252d?, realized_vol_20d?, iv_rv_premium?: number
  skew_label?, skew_ratio?, vix_level?, vix_regime?: number/string
}
```

### RegimeSummaryItem

```
{
  symbol, gex_regime?, total_gex?, gamma_flip_strike?, pin_strike?
  vol_regime?, vol_regime_confidence?, atm_iv?, iv_rank_252d?
  term_structure?, skew_label?, vix_level?, last_updated?
}
```

### IntradaySessionResponse

```
{
  id, user_id, symbol, session_date, trading_mode: "paper"|"live"
  mode: enum("monitor","supervised","autonomous")
  status: enum("active","halted","stopped","error")
  phase: enum("pre_open","opening_range","ib_forming","ib_complete","afternoon","power_hour","close","done","closed")
  signals_received?, signals_acted?, trades_opened?, trades_closed?: int
  session_pnl?: number, created_at, updated_at, stopped_at?
}
```

### IncomingLevelSnapshot / IncomingSignal (Go → Python plumbing)

```
IncomingLevelSnapshot: { symbol, vwap?, ib_high?/low?, or_high?/low?, prior_high?/low?/close?, overnight_high?/low?, price?, cum_volume?, phase? }
IncomingSignal: { signal_id, symbol, signal_type, direction: "bullish"|"bearish", trigger_price, atr?, confluence_count?, phase?, timestamp?, levels?, quality_tier? }
```

> Full per-schema field listings for everything referenced here are in
> [`openapi.json`](./openapi.json) under `components.schemas`.
