No-chase entry shadow-validation: VWAP-pullback halves the ORB loss but FAILS the locked bar

The mechanics study found the no-chase entry (buy the VWAP reclaim, not the breakout high)
was the dominant lever. This codifies it as a permanent, pre-registered shadow-validation —
the disciplined close-out of the 4-lever investigation arc.

- strategy/evaluation/vwap_pullback_entry.py: pure find_vwap_pullback_entry — first forward
  bar that dips to the CUMULATIVE session VWAP (calculate_vwap, NOT the per-bar minute_bars.vwap
  midpoint, which would make 'reclaim' trivially true) and closes back above it; entry = its
  close, stop = its low (the tight no-chase structure stop). 4 tests.
- research/labeler.py validate_no_chase_entry + `validate-entry`: simulates it vs the ORB-high
  breakout via the live simulate_exit on real forward bars, judged in PERCENT net of a REALISTIC
  per-name cost proxy (4% sub-$3 / 2% else — no NBBO in bars). PRE-REGISTERED bar locked: mean
  net% must be STRICTLY POSITIVE, 'less negative than ORB' is NOT a pass.

IN-SAMPLE RESULT (v1 gappers, 19 autocorrelated days): the no-chase entry HALVES the loss
(ORB -7.94% -> no-chase -3.61% net, median -4.23%, offered on 75%), robustly (day-jackknife
[-3.83%,-3.48%]) -- confirming not-chasing the spike-top is the strongest mechanic -- but it
still does NOT cross zero under realistic cost: PRE-REGISTERED BAR = FAIL. Consistent with the
whole arc: no mechanizable entry/exit/selection lever beats cost on the full gapper book; the
path to positive is FEWER/cleaner setups (cut the ~70% fader population) COMBINED with this
no-chase entry. validate-entry re-runs forward; promote only if it clears the bar out-of-sample.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
momentum devandClaude Opus 4.8 committed 2026-06-29 18:08:56 -04:00
1 parent 97e3bf91a6
commit f170305a41
3 files changed
+176

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
"""No-chase VWAP-pullback entry primitive: finds the reclaim bar, tight structure stop."""
import pandas as pd
from strategy.evaluation.vwap_pullback_entry import find_vwap_pullback_entry
COLS = ["high", "low", "close", "vwap"]
def _df(rows):
return pd.DataFrame(rows, columns=COLS)
def test_finds_first_vwap_reclaim():
# bar0 trades above VWAP (no touch), bar1 dips to VWAP and closes back over it -> entry there
b = _df([
(10.5, 10.2, 10.4, 10.0), # above vwap all bar
(10.3, 9.9, 10.2, 10.0), # low 9.9 <= vwap 10.0 <= close 10.2 -> RECLAIM
(10.6, 10.3, 10.5, 10.1),
])
r = find_vwap_pullback_entry(b)
assert r.found and r.entry_idx == 1
assert r.entry_price == 10.2 and r.stop_price == 9.9 # close / low of the reclaim bar
assert r.stop_price < r.entry_price # a tight structure stop
def test_no_reclaim_when_price_stays_below_vwap():
# a one-way fade entirely under VWAP -> never reclaims -> no entry (anti falling-knife)
b = _df([(9.9, 9.5, 9.6, 10.0), (9.6, 9.2, 9.3, 10.0), (9.3, 9.0, 9.1, 10.0)])
r = find_vwap_pullback_entry(b)
assert not r.found and "no vwap reclaim" in r.reason
def test_no_reclaim_when_price_stays_above_vwap():
# price never pulls back to VWAP (no touch) -> no no-chase entry offered
b = _df([(10.5, 10.2, 10.4, 10.0), (10.8, 10.5, 10.7, 10.1), (11.0, 10.7, 10.9, 10.2)])
r = find_vwap_pullback_entry(b)
assert not r.found
def test_empty_bars():
assert not find_vwap_pullback_entry(_df([])).found