VCP Screener — port, study & verdict

NSE · local bhavcopy cache · zero live requests · 16 Oct 2026
Volatility Contraction Pattern (Minervini), ported from the vcp-screener skill and tested look-ahead-free against the whole eligible universe — then re-tested on a window twice as long.

Verdict

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.

close entry−1.68%
base rate+0.02%
pivot buy-stop−0.17%
pivot median−2.22%
excess, 86 dates−0.94pp

The one-line summary

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.

Two windows, same code — 50 dates vs 86 dates

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.94ppflips sign
dates beating the universe70.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.

The methodological lesson — worth more than the result

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.

Sample

windowsessionsas-of datestradesuniverse / dateVCP / date
window 152050641~1,550~13
window 270086902~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.

Look-ahead controls

  • Selection uses bars ≤ end only; the forward return starts at end. No future bar touches the decision.
  • The base rate uses the identical entry, exit, horizon, universe and dates — so every number quoted as an excess is comparable.
  • The study refuses to start before max(252, LOOKBACK+1) sessions, so a 12-month RS and a 252-day high/low always exist.
  • An as-of date counts only when its full forward window is in the cache (last = n_sess − horizon).

Order-type semantics — the part that decided the study

policyorder typefills whenfill price
at the as-of closemarketimmediatelyclose
at the pivotBUY-STOPhigh ≥ pivotopen if it gapped through, else pivot
dip −3/−5/−8%BUY LIMITlow ≤ bidopen 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).

Design choices

  • RS benchmark = cross-sectional percentile inside the scanned universe (~1,540 NSE names), not an index — an S&P-relative rating would put a third of NSE above 90.
  • Extension penalty retained (up to −35, hard cut above +40% vs the 20DMA) — corroborated independently by discover.py and rule 18.
  • The trend gate is a gate: a valid VCP below its 50/200 DMA is not a candidate. Tab 5 tests whether the pattern adds anything given that gate.

A · enter at the as-of close

windowseriesnmeanmedianwinworstbest
50dper trade641−0.10%−1.13%45.4%−85.96%+50.25%
50dper date (sleeve)50+1.35%+0.07%50.0%−14.95%+18.28%
86dper trade902−1.68%−1.50%43.9%−97.95%+52.40%
86dper 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.

B · base rate — every eligible name, identical rule

windownmeanmedianwin
50d50+0.05%+0.37%52.0%
86d86+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.

C · paired excess — picks minus the universe, per date

windowdatesmeanmedianbeat universerange
50d50+1.30pp+1.76pp70.0%−10.83 … +14.55
86d86−0.94pp+0.07pp50.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.

D · pivot policy — the textbook Minervini entry (BUY-STOP)

windowfillsmeanmedianwinfill ratestopped
50d562+0.16%−2.22%41.3%87.7%30.4%
86d766−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.

E · dip policy — resting BUY LIMIT below the as-of close (rule 17/18 structure)

windowdipnmeanmedianwinfillsstopped
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.

Composite score — inverted on both windows

score50d n50d mean86d n86d mean86d median
70–80489+0.34%680−0.03%−1.90%
80–9072−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 — the bucket that looked promising, then decayed

RS percentile50d n50d mean50d median50d win86d n86d mean86d median86d win
<7014+6.03%−1.85%42.9%21+7.03%+0.67%52.4%
70–85218−0.31%−2.34%38.5%323−0.45%−2.28%39.3%
85–95265−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%.)

Other buckets — window 1 (50 dates)

bucketnmeanmedianwin
Weak VCP45+0.42%+0.19%51.1%
Good VCP342+0.70%−1.85%42.7%
Strong VCP62−0.63%−4.67%35.5%
Developing VCP54+0.33%−1.31%46.3%
No VCP58−2.47%−4.61%27.6%
Damaged58−2.47%−4.61%27.6%
Pre-breakout203+1.58%−2.22%42.4%
Breakout33−2.42%−3.13%30.3%
VCP-adjacent198+1.06%−2.22%41.9%
Textbook VCP5+22.24%+30.30%60.0%
3 contractions (86d n=64, +1.19%)45+2.63%−1.93%42.2%
gapped through pivot339+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.

Breadth — a market timer in disguise, and it does not reproduce

picks that daydates (86d)pick meanmarket meanexcessexcess medianexcess win
<10 picks45−0.14%+1.91%−2.05pp−1.22pp40.0%
10–20 picks33−1.52%−1.53%+0.01pp+0.39pp57.6%
≥20 picks8−2.85%−4.26%+1.41pp+0.91pp75.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.

Why this test exists

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.

sleeve50d excess50d early / late86d excess86d 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.

How to read it

  • The ordering is stable; the sign is not. VCP ranks above the size-matched Stage-2 draw, which beats Stage-2, which beats raw RS — in both windows. So the trend gate plus the pattern genuinely sort better than momentum alone.
  • But the whole momentum family underperformed the equal-weight universe over 2025-01 → 2026-09. Every sleeve is negative; the VCP sleeve is merely the least negative.
  • "VCP adds +1.87pp over the size-matched control" is true and useless here — it is the difference between two losing sleeves (−1.52pp vs −3.39pp).
  • Confound that cannot be resolved with this data: the study cannot separate "the VCP pattern has no edge" from "momentum had no edge over this 1.8 years on NSE". Both describe the same numbers. Separating them needs a momentum-matched benchmark, not just an equal-weight universe.
  • The consistent ordering (VCP > Stage-2 > universe-ranked) is worth keeping as a hypothesis — it is the only thing here that reproduced across windows.

Where the earlier "+4.71pp, stable across halves" came from

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 two bugs that made the first study lie

The first run reported +2.83% mean, 50.7% win — an apparently excellent strategy. It was fiction, and both errors were order-type semantics.

#bugsymptomfix
1the pivot entry filled at the pivot even when the bar gapped through it+2.83% mean — free gap premium on every gap-throughmodel it as a BUY-STOP: fill at the open when the bar gapped past the trigger ⇒ +0.16%
2the 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 profita 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).

Earlier port errors, fixed and tested

#bugfix
1contraction depth divided by the low÷ the high — matches vcp_pattern_calculator.py
2pivot risk divided by the stop, overstating it÷ the reference price — matches pivot_proximity_calculator.py; locked at 7.53% vs 1.13%
3a dash DELIV_PER silently dropped whole sessionscoerce to numeric
4_rank_key let a high-scoring non-VCP outrank a buyable patternrating tier first, score second
5study() fell off the end without returningreturn res

Decision

  • NOT ADOPTED  VCP as an entry signal — any policy, any stop. Do not add it to the strategist's entry pool and do not size on it.
  • NOT ADOPTED  VCP + RS as a ranking sleeve. The 50-date result did not reproduce on 86 dates.
  • USE AS-IS  the screener as a monitoring surface — --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).
  • OPEN  the ordering hypothesis (VCP > Stage-2 > raw RS) reproduced across both windows and is the one thing worth a further test — against a momentum-matched benchmark, not an equal-weight universe.
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.