population: learn_params + eod_replay consume the shared gate (kills look-ahead)
Both offline consumers now screen through strategy.population.population_reject instead of divergent, look-ahead admits: - learn_params.load_sessions: replaces the `gap>=0.03 OR day_rvol>=2.0` admit, whose day_rvol was a FULL-DAY volume sum (outcome-time look-ahead, the 2026-07-08 class). gap is already a FRACTION (open vs prev close) — passed as-is; the OPEN price is passed for the price band. Stored daily bars expose no open-knowable volume, so volume=None (fail-open on the day-volume floor) — no day-end volume is smuggled into admission. - eod_replay._daily_gappers: screens on the OPEN price + STATIC open gap (was t_close look-ahead) via the shared gate; the local gap is converted to a FRACTION before the call, so it inherits the >=150% squeeze exception. Deletes the stale $1-5 / 3-60% WATCHER_/TRIGGER_ constants (KEEPS RISK_PCT). The second-stage scan_gappers is made permissive (membership already decided by the shared gate), removing the `gap_pct <= 60%` cap that wrongly dropped squeezes. Prod thresholds passed explicitly (bare from_env would disable gap). Unit test replaces the live-DB smoke run: $15/+12% qualifies, $4/+45% rejected unless >=150%+rvol5. research/labeler.py deferred to plan 006 (no locatable price/gap admission screen); sweep_exits/sweep_filters likewise deferred. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66e81a48f3
commit
61ca8ae096
3 files changed
+99
-23
No files matched your search
@@ -0,0 +1,55 @@
|
||||
"""Unit test standing in for the `python eod_replay.py <date>` smoke verify
|
||||
(the live DB must never be touched). Pins that eod_replay._daily_gappers now
|
||||
screens through the shared population gate on the OPEN price + STATIC open gap,
|
||||
inheriting the >=150% squeeze exception and dropping the old $1-5 / 3-60% /
|
||||
t_close look-ahead screen.
|
||||
|
||||
Verify (plan step 3): a $15 name with a 12% open gap qualifies; a $4 name with a
|
||||
45% gap does not unless >=150% + rvol>=5.
|
||||
"""
|
||||
|
||||
from datetime import date
|
||||
|
||||
import eod_replay
|
||||
|
||||
SESS = date(2026, 7, 10)
|
||||
PRIOR = date(2026, 7, 9)
|
||||
|
||||
|
||||
def _rows(open_px, prev_close, day_vol=1000.0, prior_vol=1000.0):
|
||||
"""daily_bars rows newest-first: (trade_date, open, close, volume).
|
||||
row[0] = session day, row[1..] = prior days (for prev_close + 20-day adv)."""
|
||||
day = (SESS, open_px, open_px, day_vol)
|
||||
prior = [(PRIOR, prev_close, prev_close, prior_vol) for _ in range(20)]
|
||||
return [day] + prior
|
||||
|
||||
|
||||
class _Result:
|
||||
def __init__(self, rows):
|
||||
self._rows = rows
|
||||
|
||||
def fetchall(self):
|
||||
return self._rows
|
||||
|
||||
|
||||
class _FakeCon:
|
||||
def __init__(self, by_sym):
|
||||
self.by_sym = by_sym
|
||||
|
||||
def execute(self, sql, params): # params == [symbol, session_date]
|
||||
return _Result(self.by_sym.get(params[0], []))
|
||||
|
||||
|
||||
def test_daily_gappers_uses_shared_population_gate():
|
||||
by_sym = {
|
||||
# open price, prev_close chosen so open/prev_close - 1 == target gap
|
||||
"BIG15": _rows(15.0, 15.0 / 1.12), # $15, +12% -> IN
|
||||
"DEAD4": _rows(4.0, 4.0 / 1.45), # $4, +45% dead band -> OUT
|
||||
"SQZ6": _rows(4.0, 4.0 / 3.0, day_vol=6000, prior_vol=1000), # +200% rvol6 -> IN
|
||||
"SQZ2": _rows(4.0, 4.0 / 3.0, day_vol=2000, prior_vol=1000), # +200% rvol2 -> OUT
|
||||
"CHEAP": _rows(0.98, 0.98 / 1.20), # $0.98 below band -> OUT
|
||||
"LOWGAP": _rows(10.0, 10.0 / 1.05), # +5% below floor -> OUT
|
||||
}
|
||||
con = _FakeCon(by_sym)
|
||||
keep = eod_replay._daily_gappers(con, SESS, list(by_sym))
|
||||
assert set(keep) == {"BIG15", "SQZ6"}
|
||||
Reference in new issue
Block a user