vcp-screener skill and tested look-ahead-free against the whole eligible universe — then re-tested on a window twice as long.NOT ADOPTED The VCP screen does not beat the NSE universe, on either window. Do not wire it into sizing.
Entering the breakout at the pivot — the textbook Minervini trade, modelled honestly as a BUY-STOP — has a −2.22% median and a 41.3% win rate, and it printed those identical figures on both windows. Entering at the as-of close is −1.68% per trade on the wider window against a base rate of +0.02%. Every dip variant is negative too.
The screen's apparent edge did not survive a longer window. On 50 as-of dates it beat the universe by +1.30pp on 70% of dates; on 86 as-of dates the same code produced −0.94pp on 50% of dates. That is the headline.
Buying the pattern does not pay, and ranking the market with the pattern does not pay either — once you give the test enough history. The first window's +1.30pp excess was a favourable period, not an edge.
| series (20-day forward) | 50 dates 2025-10 → 2026-09 | 86 dates 2025-01 → 2026-09 | verdict |
|---|---|---|---|
| VCP picks, entry at close | −0.10% | −1.68% | negative |
| VCP sleeve, per date | +1.35% | −0.92% | flips sign |
| base rate (universe) | +0.05% | +0.02% | ~flat |
| excess vs universe | +1.30pp | −0.94pp | flips sign |
| dates beating the universe | 70.0% | 50.0% | no better than coin |
| pivot buy-stop (textbook entry) | +0.16% | −0.17% | ~zero |
| pivot median / win rate | −2.22% / 41.3% | −2.22% / 41.3% | identical — robustly negative |
| dip −3% limit | +0.11% | −0.36% | flips sign |
| dip −5% limit | −0.49% | −0.69% | negative |
| dip −8% limit | −0.44% | −0.23% | negative |
Only two lines are stable across windows, and both are negatives: the pivot median (−2.22%) and the pivot win rate (41.3%). Everything that looked like an edge moved.
On the 50-date window I ran a split-half test. The best rule held in both halves (+4.78pp early / +4.64pp late) and beat a size-matched control. It looked robust.
It was not. Those two halves were adjacent five-month spans of the same favourable regime. Adding 36 earlier dates — spanning a different market phase — took the same rule to −5.55pp early / +2.41pp late and the overall excess to −1.52pp.
A split-half test inside a single window cannot detect a window effect. It measures stability within a regime. The only protection is a window long enough to contain more than one.
| window | sessions | as-of dates | trades | universe / date | VCP / date |
|---|---|---|---|---|---|
| window 1 | 520 | 50 | 641 | ~1,550 | ~13 |
| window 2 | 700 | 86 | 902 | ~1,537 | ~10 |
stride 5 sessions · horizon 20 sessions · cache spans 2024-01-25 → 2026-10-15 · 0 live requests. Window 2 exists because the cache was extended backwards specifically to test whether window 1 was a regime artefact — it was.
≤ end only; the forward return starts at end. No future bar touches the decision.max(252, LOOKBACK+1) sessions, so a 12-month RS and a 252-day high/low always exist.last = n_sess − horizon).| policy | order type | fills when | fill price |
|---|---|---|---|
| at the as-of close | market | immediately | close |
| at the pivot | BUY-STOP | high ≥ pivot | open if it gapped through, else pivot |
| dip −3/−5/−8% | BUY LIMIT | low ≤ bid | open if it gapped below, else the bid |
A stop can never fill better than its trigger; a limit below the market can. Confusing them is what produced the fake first result (tab 6).
discover.py and rule 18.| window | series | n | mean | median | win | worst | best |
|---|---|---|---|---|---|---|---|
| 50d | per trade | 641 | −0.10% | −1.13% | 45.4% | −85.96% | +50.25% |
| 50d | per date (sleeve) | 50 | +1.35% | +0.07% | 50.0% | −14.95% | +18.28% |
| 86d | per trade | 902 | −1.68% | −1.50% | 43.9% | −97.95% | +52.40% |
| 86d | per date (sleeve) | 86 | −0.92% | −0.84% | 40.7% | −20.77% | +15.91% |
Per-trade weights broad days heavily; per-date deploys equally every day. Both degrade on the wider window.
| window | n | mean | median | win |
|---|---|---|---|---|
| 50d | 50 | +0.05% | +0.37% | 52.0% |
| 86d | 86 | +0.02% | +0.62% | 55.8% |
The universe itself was roughly flat over both windows — so the VCP picks were not fighting a falling tape. They simply lost to it.
| window | dates | mean | median | beat universe | range |
|---|---|---|---|---|---|
| 50d | 50 | +1.30pp | +1.76pp | 70.0% | −10.83 … +14.55 |
| 86d | 86 | −0.94pp | +0.07pp | 50.0% | −22.40 … +14.55 |
This is the single most important row in the report. The 70%-of-dates claim does not reproduce; on the wider window the picks are a coin flip against simply owning the universe equally.
| window | fills | mean | median | win | fill rate | stopped |
|---|---|---|---|---|---|---|
| 50d | 562 | +0.16% | −2.22% | 41.3% | 87.7% | 30.4% |
| 86d | 766 | −0.17% | −2.22% | 41.3% | 84.9% | 31.1% |
The median and the win rate are identical to one decimal across a 36% larger sample. That is the most reproducible finding in the whole study, and it says the entry loses money.
| window | dip | n | mean | median | win | fills | stopped |
|---|---|---|---|---|---|---|---|
| 50d | −3% | 489 | +0.11% | −1.80% | 40.7% | 76.3% | 35.2% |
| 50d | −5% | 371 | −0.49% | −1.66% | 38.8% | 57.9% | 33.2% |
| 50d | −8% | 248 | −0.44% | −1.12% | 42.7% | 38.7% | 33.1% |
| 86d | −3% | 694 | −0.36% | −2.94% | 37.6% | 76.9% | 40.5% |
| 86d | −5% | 552 | −0.69% | −2.27% | 37.1% | 61.2% | 40.2% |
| 86d | −8% | 394 | −0.23% | −1.72% | 41.9% | 43.7% | 39.1% |
Unlike the day-1 breakout study (rule 18, where dip −5% was the best policy), deeper dips get no better here and the stop-out rate rises to ~40%. The fixed stop is 7.5% below the fill — see tab 6.
| score | 50d n | 50d mean | 86d n | 86d mean | 86d median |
|---|---|---|---|---|---|
| 70–80 | 489 | +0.34% | 680 | −0.03% | −1.90% |
| 80–90 | 72 | −1.00% | 85 | −1.27% | −5.54% |
| 90+ | 1 | −4.67% | 1 | −4.67% | −4.67% |
Higher composite score ⇒ worse outcome, consistently. A warning about the 25/25/20/15/15 weighting, not a tradable inverse (n is tiny above 80).
| RS percentile | 50d n | 50d mean | 50d median | 50d win | 86d n | 86d mean | 86d median | 86d win |
|---|---|---|---|---|---|---|---|---|
| <70 | 14 | +6.03% | −1.85% | 42.9% | 21 | +7.03% | +0.67% | 52.4% |
| 70–85 | 218 | −0.31% | −2.34% | 38.5% | 323 | −0.45% | −2.28% | 39.3% |
| 85–95 | 265 | −0.35% | −3.08% | 38.9% | 339 | −0.75% | −3.00% | 39.2% |
| 95+ (n=83 on 86d) | 65 | +2.58% | +2.62% | 60.0% | 83 | +1.44% | +0.72% | 54.2% |
On 50 dates, RS 95+ was the only bucket with a positive median and ≥60% winners — which is what launched the control in tab 5. On 86 dates it decays but does not invert: still the best of the upper buckets, but +2.58% → +1.44% and 60.0% → 54.2% winners, while the lowest bucket (RS <70, n=21) is also positive. The bucket's magnitude is not stable, so do not size on it. (Note the two big buckets barely move: 85–95 stays ~−0.35% → −0.75%.)
| bucket | n | mean | median | win |
|---|---|---|---|---|
| Weak VCP | 45 | +0.42% | +0.19% | 51.1% |
| Good VCP | 342 | +0.70% | −1.85% | 42.7% |
| Strong VCP | 62 | −0.63% | −4.67% | 35.5% |
| Developing VCP | 54 | +0.33% | −1.31% | 46.3% |
| No VCP | 58 | −2.47% | −4.61% | 27.6% |
| Damaged | 58 | −2.47% | −4.61% | 27.6% |
| Pre-breakout | 203 | +1.58% | −2.22% | 42.4% |
| Breakout | 33 | −2.42% | −3.13% | 30.3% |
| VCP-adjacent | 198 | +1.06% | −2.22% | 41.9% |
| Textbook VCP | 5 | +22.24% | +30.30% | 60.0% |
| 3 contractions (86d n=64, +1.19%) | 45 | +2.63% | −1.93% | 42.2% |
| gapped through pivot | 339 | +0.06% | −2.05% | 42.8% |
The one durable signal, and it holds on both windows: No VCP / Damaged at −2.47% / 27.6% win (50d) and −2.55% / 27.8% win (86d) — the screen is genuinely good at identifying what not to touch. Everything else in this tab moves between windows. "Textbook VCP" is n=1 in the rated bucket (n=5/7 in the pattern bucket): noise.
| picks that day | dates (86d) | pick mean | market mean | excess | excess median | excess win |
|---|---|---|---|---|---|---|
| <10 picks | 45 | −0.14% | +1.91% | −2.05pp | −1.22pp | 40.0% |
| 10–20 picks | 33 | −1.52% | −1.53% | +0.01pp | +0.39pp | 57.6% |
| ≥20 picks | 8 | −2.85% | −4.26% | +1.41pp | +0.91pp | 75.0% |
What is stable: the pick count correlates negatively with the forward market return in both windows (−0.585 on 50 dates → −0.444 on 86) — few tight bases form just before a rally. That is a market-timing signal, not a selection one, and it is worth knowing: a crowded screening day is a poor day to buy.
What is not: the same count's correlation with the market-relative excess is weakly positive in both windows (+0.049 → +0.185) yet the bucket table flips sign between them (50 dates: thin days best; 86 dates: thin days worst at −2.05pp, broad days best at +1.41pp). A +0.05…+0.19 correlation and a sign-flipping bucket table are both consistent with noise. Do not build a rule on either.
RS percentile is a 12-month momentum rank. So "the top VCP names by RS" is also "the strongest-momentum tight bases there are" — and ranking the whole universe by RS might pay just as well. If it does, the pattern is an extra step and we would be adopting momentum with more code.
Every sleeve is scored on the identical dates and forward window, and reported as its excess over the whole eligible universe on that date.
| sleeve | 50d excess | 50d early / late | 86d excess | 86d early / late |
|---|---|---|---|---|
| top-3 by RS — whole universe | +1.63pp | −10.63 / +13.90 | −12.73pp | −25.19 / −0.27 |
| top-3 by RS — Stage-2 passers | +8.57pp | +1.45 / +15.68 | −6.86pp | −13.97 / +0.25 |
| top-N by RS — Stage-2, N = VCP count | +2.72pp | −2.93 / +8.36 | −3.39pp | −10.46 / +3.68 |
| top-3 by RS — valid VCP + Stage-2 | +4.71pp | +4.78 / +4.64 | −1.52pp | −5.55 / +2.41 |
50d win rates: 52.0% / 72.0% / 66.0% / 71.4%. 86d win rates: 38.4% / 29.1% / 33.7% / 49.4%. Every sleeve loses to the universe on the wider window.
It was real in that window. The 50-date window sat entirely inside the stronger phase of the sample; its two halves were adjacent spans of the same regime, so a split-half test had nothing to compare against. The moment the window reached back into Jan–Sep 2025, the same rule went to −5.55pp early / +2.41pp late.
This is the third time in this project that a window has decided the answer. The lesson is recorded in memory as a standing rule.
The first run reported +2.83% mean, 50.7% win — an apparently excellent strategy. It was fiction, and both errors were order-type semantics.
| # | bug | symptom | fix |
|---|---|---|---|
| 1 | the pivot entry filled at the pivot even when the bar gapped through it | +2.83% mean — free gap premium on every gap-through | model it as a BUY-STOP: fill at the open when the bar gapped past the trigger ⇒ +0.16% |
| 2 | the dip policy reused the base's contraction-low stop, which on a deep base sits above the dip bid | +9.72% mean with a 79% "stopped out" rate and a +552% outlier — stopping out booked a profit | a dip entry gets a fixed stop 7.5% below the fill ⇒ −0.44% |
331 of 641 signals were affected by bug 2 alone. Both are regression-tested (test_vcp.py, 70 checks green).
| # | bug | fix |
|---|---|---|
| 1 | contraction depth divided by the low | ÷ the high — matches vcp_pattern_calculator.py |
| 2 | pivot risk divided by the stop, overstating it | ÷ the reference price — matches pivot_proximity_calculator.py; locked at 7.53% vs 1.13% |
| 3 | a dash DELIV_PER silently dropped whole sessions | coerce to numeric |
| 4 | _rank_key let a high-scoring non-VCP outrank a buyable pattern | rating tier first, score second |
| 5 | study() fell off the end without returning | return res |
--screen N, local cache only, no live requests, no orders. Its genuinely reliable output is the negative filter: No VCP / Damaged names are the worst cohort on both windows (−2.47% / 27.6% win on 50 dates, −2.55% / 27.8% on 86).The durable lessons are not about VCP. First: an entry-policy study must state which order type it models — a stop cannot fill better than its trigger and a limit below the market can; mixing them manufactured a 2.8% edge from nothing. Second: a split-half test inside one window proves nothing about the next window. Extend the window before believing the result.
Sources: NSE bhavcopy cache (data/bhav/, 700 sessions, local — zero live requests), db/chanakya/vcp.py (--study, --control, --screen), reports/vcp-study.json, reports/vcp-control.json. Adopt/test-more thresholds follow house strategy rules 11–19.
Disclaimer: This report is generated by an AI assistant for educational and informational purposes only. It does not constitute investment advice, a buy/sell recommendation, or SEBI-registered research. Past performance does not guarantee future returns. Trading carries significant risk of loss. Please consult a SEBI-registered financial advisor before making any investment decisions. Data sourced from NSE, BSE, Moneycontrol, and other public sources. Verify all data independently.