OI + CVD + Volume Confluence Research

A single-symbol case study (BMTUSDT.P, one day) applied the user's own "Aggressive Participant Detector" — the confluence of significant open-interest change, significant CVD delta, CVD-confirms-price, and high relative volume — and found it flagged several of that day's most significant moves, including a genuine liquidation cascade. That's exactly the kind of anecdote this project has learned not to trust on its own: every other idea tested here also looked good in a small sample and evaporated under systematic, out-of-sample testing. This page runs that same test: does OI+CVD+Volume confluence, computed exactly per the user's own indicator logic, generalize across the full Bybit perps universe? Sections 1–4 below are the original systematic test. Sections 5–9 are a deeper follow-up investigation: reading the signal as a continuous market-microstructure narrative, a Wyckoff-style "is this level defended" context filter, a causal developing-volume-profile magnet test, a completely separate structural-short-the-basket strategy the drift confound itself surfaced, and finally the confluence signal run as an actual risk-managed trade (entry/stop/target) rather than a fixed-horizon average return.

1 · Data and method

OI retention check (done first, per plan): Bybit's /v5/market/open-interest was checked for a top-cap symbol (BTCUSDT) and two thin symbols (BMTUSDT, 1000PEPEUSDT) before committing to a date range. Retention goes back to at least Sep 2025 for all three — no truncation forced within the Jan–Aug 2026 window, so the full price-data range was usable.

Universe: 250-symbol combined universe (top-100 by turnover ∪ 166-symbol screened list), 243 with usable 1-min price + OI history, 235 after excluding the 8 tokenized/non-crypto instruments (XAUUSDT, XAGUSDT, XAUTUSDT, NVDAUSDT, MSTRUSDT, TSLAUSDT, PAXGUSDT, QQQUSDT — see the variant study for why these distort R-normalized and thin-sample results).

Classification: the user's own indicator logic (Aggressive Participation.txt), exact parameters — cvdSignificant: bar CVD delta > 1.5× its 20-bar rolling average magnitude; oiSignificant: bar OI delta > 2.0× its 50-bar rolling average magnitude; rvolHigh: volume ≥ 1.5× its 20-bar average; quadrant (New Longs / Short Covering / New Shorts / Long Liquidation) from price direction × OI direction, only classified when OI-significant; high_conviction requires all four conditions plus CVD-confirms-price; is_squeeze/is_cascade are high-conviction Short-Covering/Long-Liquidation bars respectively — computed on 5-minute bars.

OI delta, corrected: the case study's first pass used openInterest.diff(), which attributes the change up to a bar's open — i.e. the previous bar's change — onto the current bar. This flipped real classifications (a bar the buggy version called "no OI confirmation" was a genuine high-conviction New Longs signal once fixed). The corrected, leading version used everywhere on this page: OI(bar_open + 5min) − OI(bar_open), matched to each bar's own [open, close) window via merge_asof, with stale (>15min old) or missing OI snapshots treated as missing data rather than silently reused.

CVD, a documented limitation: Bybit's free history API has no tick-level buy/sell data. CVD is approximated the same way ta.requestVolumeDelta falls back when tick data is unavailable: each 1-minute bar's entire volume is classified buy (close≥open) or sell (close<open), summed to the 5-minute bar. This is a coarse proxy for order flow, not true trade-level delta — worth weighing against how much confidence to place in the CVD-dependent conditions below.

Discipline: train = Jan 1 – Apr 30 2026, test = May 1 – Aug 27 2026 (unseen); any horizon/direction choice made on train only, then evaluated unchanged on test; cost basis 0.055% taker + 0.02% slippage per side, round trip, applied to every result; no lookahead anywhere (rolling averages are trailing/causal, OI delta only ever uses information available by the bar's own close).

2 · Standalone signal — does confluence predict a move on its own?

For each signal category, every (horizon, direction) combination from 15 minutes to 24 hours was scored by net profit factor on TRAIN only; the best-scoring combo was then evaluated, unchanged, on TEST.

First pass — looked like a clean validation, wasn't

CategorySelected on trainTest tradesTest win% (net)Test avg ret (net)Test PF (net)
New Longs (high conviction)short, 24h143,97952.5%+0.334%1.155
New Shorts (high conviction)short, 24h102,67750.2%+0.437%1.207
is_squeezeshort, 24h99,94151.5%+0.362%1.172
is_cascadeshort, 24h137,29251.0%+0.421%1.199
any_high_convictionshort, 24h483,88951.4%+0.386%1.182

Every 95% CI on average return sat entirely above zero, on hundreds of thousands of trades — the kind of result this project has been designed to surface as real. It would have been very easy to stop here. One detail should have been the giveaway before running any control at all: five independent signal categories — New Longs, New Shorts, a squeeze, a cascade, "any" confluence — all independently selected the exact same answer, short/24-hour, on TRAIN. Genuinely different signals converging on an identical (direction, horizon) pick is a red flag, not reassuring corroboration.

The confound: pooled small-cap decay, not confluence

A control run with no confluence filter at all — shorting literally every 5-minute bar in the universe, 24-hour hold, same costs — reproduces almost the identical number:

Control: short every bar, 24h holdTrain PF (net)Test PF (net)
Full universe (235 symbols)1.2391.209
BTC / ETH / SOL / XRP only1.1280.830

The unconditional baseline alone accounts for essentially all of the "edge" above. Splitting out just the four most liquid majors makes it unambiguous: their unconditional short/24h profit factor is positive on train (1.13) and flips to a clear loss on test (0.83) — the exact opposite of what a real, stable effect should do. Pooling millions of heavily overlapping 5-minute bars across 235 symbols of wildly different baseline drift, then reporting one profit factor for the pooled set, conflates "does confluence work" with "which symbols happen to get flagged more often" and "is the broad small-cap/low-liquidity altcoin universe structurally bleeding over time" (post-listing hype fade, well documented independently of this framework). The first-pass result was almost entirely the latter.

Corrected test — excess return over each symbol's own baseline

Fix: for each symbol, compute the confluence-flagged subset's mean return minus that same symbol's own unconditional mean return, at the same horizon/direction/period — netting out each symbol's idiosyncratic drift. The unit of statistical inference becomes the symbol (n≈211–213 on train, up to 235 on test), not the individual overlapping 5-minute bar, which massively overstated precision in the first pass.

CategorySelected on trainTest symbolsTest excess (net)95% CIExcludes 0?
New Longs (high conviction)short, 4h235+0.016%[−0.021%, +0.052%]no
New Shorts (high conviction)long, 24h235+0.064%[−0.021%, +0.149%]no
is_squeezelong, 24h235+0.074%[−0.007%, +0.156%]no (barely)
is_cascadelong, 1h235+0.016%[+0.001%, +0.030%]yes
any_high_convictionlong, 24h235+0.058%[+0.023%, +0.092%]yes

Two categories now have a 95% CI that excludes zero — is_cascade and any_high_conviction — which is a more defensible result than the first pass, but the magnitude is the tell: 1.6 and 5.8 basis points of excess return per trade, after costs, after netting out drift. The round-trip cost already subtracted is 0.15%; an excess of 0.016–0.058% is a fifth to two-fifths of that cost itself sitting as the entire "edge" — well within range of a slightly different (still realistic) fee/slippage assumption, latency, or non-simultaneous fills across 100k+ trades erasing it entirely. It's also worth noting the framework's own thesis often didn't win: is_cascade ("long liquidation, forced selling") selected long (bet on a bounce), not short (bet on continued cascade); New Shorts and is_squeeze also selected long at 24h — a pick that looks more like "high-conviction bars tend to occur in coins with slightly better forward drift than that coin's own average," a generic effect, rather than confirmation of the squeeze/cascade mechanism specifically.

3 · As an entry filter on the existing fade/momentum engine

Reused the two core configs from the ATR-VWAP variant study (fade_vwap: 2.0×ATR trigger, fade to VWAP, 1×ATR stop, 60min; mom_2atr_fixedR: 2.0×ATR trigger, momentum, fixed 2R target, 60min — both already net-unprofitable on their own). Gated entries on "confluence agrees with trade direction" — long trades required a high-conviction New Longs or Squeeze bar, short trades required New Shorts or Cascade — at the last fully-closed 5-min confluence bar before entry (no lookahead).

ConfigSplitUngated PFGated (confluence agrees) PF
fade_vwapTrain0.6700.683
fade_vwapTest0.6770.690
mom_2atr_fixedRTrain0.5880.620
mom_2atr_fixedRTest0.6020.624

Gating nudges net PF up by ~0.01–0.03, consistently on both train and test — a faint, directionally-correct filtering effect, and one of the more internally-consistent results on this page since it held up out of sample. But it's rescuing a strategy still losing roughly a third of every unit risked (PF 0.60–0.69). Confluence does not turn either losing strategy profitable, and the size of the improvement is too small to matter practically.

fade_vwap, ungated test
0.677
fade_vwap, confluence-gated test
0.690
mom_2atr_fixedR, ungated test
0.602
mom_2atr_fixedR, confluence-gated test
0.624

Grey tick = breakeven (PF 1.0). Confluence moves the needle a little; nothing here gets remotely close to breakeven.

4 · What this means

Consistent with every other result in this project: a signal that looked promising in a single-symbol, single-day case study does not generalize once tested systematically, out-of-sample, across the full universe, with costs applied and confounds controlled for. The specific new lesson worth carrying forward from this round: always control for basket-wide/per-symbol baseline drift before trusting a pooled-universe backtest result. An apparently clean OOS validation — five unrelated signal categories independently selecting the identical (direction, horizon) answer — was in fact one shared confound, not five confirmations of a real effect. That kind of convergence is a red flag worth checking, not a reassuring sign the search is honing in on something real. If there's a real version of this idea, it likely needs genuine trade-level order-flow data (this project's CVD is a same-bar-volume proxy, not true tick delta) or actual liquidation prints (not inferred from price+OI direction) rather than a further tune of the same significance thresholds.

5 · Reading the signal as a continuous narrative — and does that generalize?

A separate chart-by-chart exercise cross-checked the same BMTUSDT day against the user's own manually-annotated swing points (9 chart-verified turning points, Sydney time). The independently-rebuilt OI/CVD pipeline reproduced the manual read to candle-level precision — a 06:25 "rollover" (OI still falling, CVD flips hard bullish, price reverses), a 08:05 short-covering liquidation, and a 08:45 "absorption" candle (real OI+CVD building, price can't close higher — a resting order soaking up the move) all matched. Going candle-by-candle across the full day (00:00–17:30 UTC) also surfaced real counterexamples: a 13:30 breakout that reversed violently one bar later despite strong CVD confirmation in both directions, and several genuine moves (04:50, the VWAP-return around 09:30) that the significance thresholds never flagged at all.

That led to a single continuous effort-vs-result efficiency metric instead of a discrete quadrant flag: efficiency = percentile_rank(|body, in ATR|) − percentile_rank(effort), where effort = |OI delta ratio| + |CVD delta ratio| + max(rVOL−1, 0), both ranks computed over a trailing 500-bar causal window (never looks forward). Near 0 = effort and result were commensurate (whether both were huge or both were tiny); very negative = absorption (heavy participation, little progress); very positive = vacuum (real progress on below-average participation — nobody defending the level). On the same BMTUSDT day, this recovers every landmark without being told where they were: the 02:30/13:30 breakouts and the day's most extreme bar (17:00, a violent cascade) all score ≈0 ("commensurate" — the biggest effort of the day produced the biggest result); 08:45, 15:45, and 15:55 (the hand-identified absorption candles) all score strongly negative; 04:50 (the move the discrete framework missed entirely) scores positive.

HypothesisThresholdSelected horizonTest excess (net)95% CIExcludes 0?
Absorption → fade0.34h−0.006%[−0.015%, +0.002%]no
Absorption → fade0.515m−0.007%[−0.010%, −0.005%]yes (wrong sign)
Absorption → fade0.715m−0.005%[−0.012%, +0.002%]no
Vacuum → momentum0.315m−0.002%[−0.003%, −0.0001%]yes (wrong sign)
Vacuum → momentum0.51h−0.003%[−0.009%, +0.004%]no
Vacuum → momentum0.71h+0.013%[−0.027%, +0.053%]no

Every effect size is 1–3 hundredths of a basis point — where a CI does exclude zero, it's in the wrong direction (negative, the opposite of what the hypothesis predicts), not confirmation. The metric explains a completed move well after the fact; it doesn't predict the next one.

6 · A Wyckoff-style context filter — does it need to happen at a defended level?

Absorption/vacuum only means something in Wyckoff's own framework in context — a spring or upthrust matters because it happens at a level the market already recognizes, not anywhere. Three "established boundary" definitions, all causal: the daily VWAP ± 2×ATR band edge, a rolling 4-hour swing high/low (Donchian-style, prior bars only), and the prior day's volume profile (POC / value area high-low / high-volume nodes, held fixed and applied to the whole next day — the standard "yesterday's value area" convention). A bar counts as "at a boundary" if price closes within 0.3×ATR of any of these.

(A real bug was caught and fixed while building this: the value-area expansion algorithm always favored expanding toward one side on a tie, including 0-vs-0 ties on sparse-volume days — that caused a runaway expansion through empty bins in one direction, producing nonsense VAH/VAL levels far outside the day's actual range. Fixed to the standard convention — expand both sides on a true tie — and the affected tests rerun; the fix barely moved the numbers and didn't change any conclusion.)

HypothesisBoundaryThresholdTest excess (net)95% CI
Absorption → fadeVWAP band0.5+0.051%[−0.071%, +0.173%]
Absorption → fadeRecent swing0.5+0.078%[−0.044%, +0.200%]
Absorption → fadeVolume profile0.3−0.031%[−0.046%, −0.015%]
Vacuum → momentumRecent swing0.5−0.099%[−0.150%, −0.048%]
Vacuum → momentumVolume profile0.5−0.024%[−0.045%, −0.003%]

Most combinations show no significant effect; several are significantly negative (momentum-following a vacuum near a swing point actively loses money). The one point estimate that looked encouraging — absorption-fade near a recent swing, +0.078% — has a CI crossing zero, and checking it against the four most liquid majors individually shows why: negative excess on 3 of 4 in train and all 4 in test. The pooled positive average comes entirely from thinner altcoins, the same pattern that broke the very first test on this page. No boundary type turns absorption/vacuum into a validated signal.

7 · Does price get pulled toward the developing volume-area as it forms?

A different framing of the same volume-profile idea: instead of yesterday's fixed levels, does price get pulled toward the currently-forming value area/POC as the session develops? Getting this right without lookahead needs care — a naive version would bin prices using the day's own eventual high-low range, which isn't known yet while the day is still forming. Fix: bins are fixed at session open (width = 0.15×the ATR at that same open, anchored on the opening price), and volume accrues into that fixed grid bar by bar — POC/value area are recomputed from only the volume seen so far at each point, never touching a future bar.

HypothesisSelected horizonTest excess (net)95% CI
Distance-to-POC magnet, ≥1 ATR away15m+0.0003%[−0.0003%, +0.0009%]
Distance-to-POC magnet, ≥2 ATR away15m+0.0002%[−0.0007%, +0.0011%]
Distance-to-POC magnet, ≥3 ATR away24h+0.0003%[−0.0466%, +0.0473%]
Outside developing value area → reversion24h+0.0265%[−0.0380%, +0.0910%]

All four cross zero. The best-looking one (value-area rejection) checked out clean against the four majors individually — mixed signs, small magnitudes, no confound this time, genuinely no signal. (A second bug was caught building this test: the first version of the value-area-rejection baseline was circular — it compared the flagged subset's return to a "baseline" that, by construction, was defined on the exact same rows, producing a fake exact-zero excess. Fixed by using a baseline direction rule defined on every bar, not just the flagged ones; the corrected result is the one shown here.)

8 · A different idea, surfaced by the drift confound itself: short the basket, structurally

The confound in Section 2 cuts both ways — if shorting everything in this universe for 24h produced a similar profit factor to the signal, maybe "durably short the structurally-declining small-cap basket" is a real strategy in its own right, worth testing honestly on its own terms rather than dismissed as a nuisance. Built as an actual static position — one short per symbol, entered at the start of a period and held (or stopped) to the end, not a repeated overlapping-bar backtest — with real Bybit funding-rate history included for the first time in this project (a short receives funding when longs pay shorts, pays when funding is negative; funding retention checked back to Jan 2026, same as the OI check).

(A real formula bug was also caught and fixed here: this project's shared engine code computes a short's return as entry/exit − 1, which is unbounded on the winning side and capped at −100% on the losing side — backwards from how a real short actually behaves. That's a negligible distortion for the small, ATR-stop-bounded trades those engines were built for, but a huge one for a multi-month static short where 80%+ moves are common. Corrected to the proper unlevered form, 1 − exit/entry, for this test.)

PeriodEqual-weighted mean95% CIMedian% symbols profitable
Train (Jan–Apr)+1.75%[−11.2%, +14.7%]+25.2%74.9%
Test (May–Aug)−15.81%[−40.6%, +9.0%]+6.8%58.3%
Full period−5.00%[−26.4%, +16.4%]+24.2%71.9%

The median symbol was a solid winner every period (+7% to +25%), and 58–75% of symbols were individually profitable to short — supporting the intuition that most of this basket does structurally bleed against USDT over multi-month windows. But the mean — what matters for an actual equal-weighted book — is dragged to statistical insignificance (and negative on test) by a handful of catastrophic blowups: AKEUSDT −2650% in the test period alone, uncapped since a short's loss has no ceiling if a thin-book coin spikes 10–25×. Funding was also a consistent headwind, not a tailwind (−4% to −9% average drag) — bearish sentiment tends to push perp price below spot, which is exactly when funding goes negative for shorts.

Stop-losses do not fix this, at any calibration tested. A flat percentage stop (15/25/40/60%) made every period's mean significantly more negative, with 43–88% of positions stopped out. Scaling the stop to each symbol's own daily ATR (3×/5×/8×/12×) didn't fix it either — every multiple still produced a significantly negative test-period mean, with 27–79% of positions stopped out even at generous multiples:

Stop basisTest mean95% CI% stopped out
No stop−15.81%[−40.60%, +8.97%]0%
3× daily ATR−12.80%[−17.81%, −7.79%]75.3%
5× daily ATR−16.35%[−23.41%, −9.29%]57.4%
8× daily ATR−19.19%[−28.39%, −9.98%]39.6%
12× daily ATR−17.87%[−29.12%, −6.62%]24.7%

The stop does shrink the worst-case tail (from −1925%/−2649% uncapped down to a few hundred percent), but achieves that by cutting a large share of positions that would have gone on to be winners — these coins have path volatility large enough, as ordinary noise and not just genuine squeezes, that a mechanical price-level stop can't distinguish "being genuinely squeezed to death" from "an ordinary wild week." That's a real, now twice-confirmed characteristic of this instrument class, not a bad choice of threshold. The tail risk is real and needs managing, but a hard stop isn't the tool — volatility-targeted position sizing or a time-based rotation (both untested here) would be the honest next step.

9 · The confluence signal, run as an actual risk-managed trade

Everything in Sections 2, 5, 6, and 7 measured a fixed-horizon average return with no stop and no target — a reasonable first screen for information content, but not the same claim as "this doesn't work as a trading strategy." One further hypothesis was worth testing properly: even if the signal doesn't beat a random entry in the same direction, a short entered specifically on bearish confluence (New Shorts / Long Liquidation, high-conviction) might still show positive expectancy simply because being short this basket has positive average expectancy (Section 8). Built as a real trade this time: entry on the 5-min confluence signal, executed at 1-min precision, a daily-ATR-based stop and R-multiple target (sized for multi-day/week holds, not the tight 5-min ATR used elsewhere in this project), max hold up to 30 days, exit at whichever comes first — checked bar-by-bar, not at a single fixed horizon. Grid-searched on train, evaluated unchanged on test, short and long signals scored completely separately.

SignalSelected on trainTest PF (net)Test avg return (net)95% CI
Short (New Shorts / Long Liquidation)2.5×ATR stop, 1R target, ≤30d0.828−1.87%[−2.78%, −0.96%]
Long (New Longs / Short Covering)4.0×ATR stop, 1.5R target, ≤30d1.345+4.14%[+2.13%, +6.14%]

The short side is significantly negative — and consistently so: all four majors negative (BTC/ETH/SOL/XRP), median per-symbol return −1.1%, only 40% of symbols profitable. That's a real, broad-based result, and it runs directly against the hypothesis. The long side looks significant but doesn't survive scrutiny: majors are mixed and near-zero, the top 5 of 235 symbols (all obscure micro-caps — AKEUSDT, BTWUSDT, LABUSDT, VELVETUSDT, EVAAUSDT) contribute 37% of the total P&L, median per-symbol return is only +1.1%, and several of those same micro-caps are the biggest losers on the short side — meaning these coins just had one large directional move each, and whichever signal happened to sit on the right side of it inherited an outsized "win" unrelated to the signal being informative. The train→test sign flip on both sides (short: train positive, test negative; long: train negative, test positive) from a 48-combo grid search on a comparatively thin trade count is itself the signature of overfitting a search, not of finding an edge.

10 · Overall conclusion

Five different framings of the same underlying idea, all tested with the same out-of-sample discipline: raw confluence quadrants, confluence as an entry filter on an existing engine, a continuous effort-vs-result metric, that metric conditioned on being at an established level (Wyckoff-style), and a developing-volume-profile magnet — none produce a validated forward-looking edge. A sixth, more careful attempt — the signal run as an actual risk-managed trade with genuine holding periods — also fails, and specifically refutes the idea that the short side benefits "for free" from the basket's structural drift. What the investigation did establish, and what's worth keeping: the underlying OI/CVD read is mechanistically sound and reproduces a skilled manual chart-read to candle-level precision — it's a good tool for explaining a completed move. It has not, across six different framings now, produced anything that reliably predicts the next one. Separately, a genuinely different and real phenomenon surfaced along the way — this basket does structurally decline on average, and a static short captures that at the median-symbol level — but its tail risk isn't manageable with a simple price stop at any calibration tried, which is itself the honest, useful finding from that thread: the strategy needs volatility-aware position sizing, not a stop-loss, and that remains untested.

Universe: Bybit top-100 (by 24h turnover) ∪ 166-symbol screened list, 243/250 symbols with usable 1-min price + OI history, 235 after excluding 8 tokenized/non-crypto instruments. Data: 1-min OHLCV (Bybit /v5/market/kline) resampled to 5-min bars; 5-min open interest (Bybit /v5/market/open-interest) and funding-rate history (Bybit /v5/market/funding/history, sections 8–9 only), Jan 1 – Aug 27 2026. Classification: user's own "Aggressive Participant Detector" (Aggressive Participation.txt), cvdLen=20/×1.5, oiLen=50/×2.0, rvolLen=20/×1.5, OI delta as the corrected leading difference. Costs: 0.055% taker + 0.02% slippage per side, round trip, applied throughout; funding accrual included where noted (sections 8–9). Train/test split: Jan 1 – Apr 30 2026 vs. May 1 – Aug 27 2026, any parameter/horizon/direction choice made on train only. CVD is a same-bar buy/sell-volume proxy (no tick data available from Bybit's free history API); liquidation quadrants are inferred from price+OI direction, not actual liquidation prints; order-book depth (which would let effort-vs-result be read directly rather than inferred) is not available from Bybit's free history API either.