KITT on 2026-09-25 (+557%, 9.3M shares, the day's biggest mover) was benched as "gap 557% squeeze unverified (rvol 1.54)". Two separate faults: 1. The rvol baseline was the 20-day MEAN, and two 11-14M-share days on 09-01/09-02 put it at 1.66M against a 333k median. Squeeze verification (gap >= 150% needs rvol >= 5) now takes the stronger of the mean-baseline reading and the median-baseline one. Both are cumulative volume over a full day's baseline, not prorated, so they share the units the rvol >= 5 boundary was calibrated in. A reverse-split fake still reads ~0.01 on both. This applies in the watcher pre-filter, the trigger book's squeeze exception and the fast-path population gate. The ranking and the tod-rvol arm signal are unchanged. 2. A name's first ingest reached back only to ~09:15 (09:30 + 15 min, or the 20-minute refresh floor). KITT was found at 09:35, so its stored day began at 09:13 and missed the 09:00 squeeze and ~6M premarket shares. The first pass now covers the session from 04:00 ET. With both fixed, KITT's volume at 09:35 is ~20x its median day. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
3.1 KiB
Python
100 lines
3.1 KiB
Python
"""Read-side queries over the research market database."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
|
|
import pandas as pd
|
|
|
|
|
|
def query_session_symbols(
|
|
con,
|
|
session_date: date,
|
|
price_min: float = 1.0,
|
|
price_max: float = 20.0,
|
|
limit: int = 50,
|
|
) -> list[dict]:
|
|
"""Symbols with minute bars today whose latest close is inside the band.
|
|
|
|
Pass price_min=0 / price_max=inf to disable the band.
|
|
"""
|
|
rows = con.execute(
|
|
"""
|
|
WITH latest AS (
|
|
SELECT symbol, close, volume,
|
|
ROW_NUMBER() OVER (
|
|
PARTITION BY symbol ORDER BY timestamp DESC
|
|
) AS rn,
|
|
COUNT(*) OVER (PARTITION BY symbol) AS bar_count
|
|
FROM minute_bars
|
|
WHERE session_date = ?
|
|
)
|
|
SELECT symbol, close AS last_price, bar_count
|
|
FROM latest
|
|
WHERE rn = 1 AND close BETWEEN ? AND ?
|
|
ORDER BY bar_count DESC
|
|
LIMIT ?
|
|
""",
|
|
[session_date, price_min, price_max, limit],
|
|
).fetchall()
|
|
return [
|
|
{"symbol": r[0], "last_price": float(r[1]), "bar_count": int(r[2])}
|
|
for r in rows
|
|
]
|
|
|
|
|
|
def query_minute_bars(con, symbol: str, session_date: date) -> pd.DataFrame:
|
|
"""All of today's minute bars for a symbol, chronological."""
|
|
return con.execute(
|
|
"""
|
|
SELECT symbol, timestamp, session_date, is_premarket, is_regular_hours,
|
|
open, high, low, close, volume, vwap, quality_score
|
|
FROM minute_bars
|
|
WHERE symbol = ? AND session_date = ?
|
|
ORDER BY timestamp ASC
|
|
""",
|
|
[symbol, session_date],
|
|
).df()
|
|
|
|
|
|
def query_previous_close(con, symbol: str, session_date: date) -> float | None:
|
|
row = con.execute(
|
|
"""
|
|
SELECT close FROM daily_bars
|
|
WHERE symbol = ? AND trade_date < ?
|
|
ORDER BY trade_date DESC LIMIT 1
|
|
""",
|
|
[symbol, session_date],
|
|
).fetchone()
|
|
return float(row[0]) if row else None
|
|
|
|
|
|
def query_median_daily_volume(con, symbol: str, session_date: date, days: int = 20) -> float | None:
|
|
"""Median of the last ``days`` daily volumes — the spike-robust baseline for
|
|
squeeze verification (one or two prior squeeze days can multiply the mean)."""
|
|
row = con.execute(
|
|
"""
|
|
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY volume) FROM (
|
|
SELECT volume FROM daily_bars
|
|
WHERE symbol = ? AND trade_date < ?
|
|
ORDER BY trade_date DESC LIMIT ?
|
|
) recent
|
|
""",
|
|
[symbol, session_date, days],
|
|
).fetchone()
|
|
return float(row[0]) if row and row[0] is not None else None
|
|
|
|
|
|
def query_avg_daily_volume(con, symbol: str, session_date: date, days: int = 20) -> float | None:
|
|
row = con.execute(
|
|
"""
|
|
SELECT AVG(volume) FROM (
|
|
SELECT volume FROM daily_bars
|
|
WHERE symbol = ? AND trade_date < ?
|
|
ORDER BY trade_date DESC LIMIT ?
|
|
)
|
|
""",
|
|
[symbol, session_date, days],
|
|
).fetchone()
|
|
return float(row[0]) if row and row[0] is not None else None
|