Pourquoi le ratio RV_short/RV_long n'est pas le bon signal ?
L'hypothèse initiale était fausse. Voici comment on l'a su, et avec quoi on l'a remplacée.
Cette page n'est pas une démonstration. C'est un carnet de bord. On va
suivre, dans l'ordre où elles sont arrivées, les quatre étapes d'une
correction : l'hypothèse posée, la mesure qui l'a contredite, la math qui
l'a expliquée, le signal qui l'a remplacée. C'est ADR-0005 raconté comme
le réacteur l'a vécu — « le réacteur apprend de ce qu'il brûle ».
T0 — l'hypothèse qu'on a parié
On a parié que le ratio
R(t) = RV_short(60s) / RV_long(600s)
mesurerait le passage du marché en régime rugueux. L'intuition était
naturelle : en jours calmes, court et long se ressemblent (R ≈ 1) ; en
jours violents, le court explose tandis que le long met du temps à intégrer
le choc (R → 1.5+). On a fixé deux seuils dans l'ADR-0003 §4 :
θ_high = 0.7 (entrée Rough), θ_low = 0.4 (sortie). La state machine
3-états Smooth / Transition / Rough, son hystérèse double-nommée
anti-chattering + Ulysses-contract, son dwell minimum de 60 s — tout était
en place. Il restait à observer.
On a écrit le pipeline. Il compilait. Les tests de propriété passaient. Il ne manquait plus qu'à le lâcher sur une journée connue pour être violente, et regarder le ribbon de régime s'allumer rouge.
T1 — le smoke run, 27 janvier 2021
On a choisi la journée pic du short squeeze GameStop / AMC, le moment le plus mémorable du retail trading de la décennie (le lendemain, Robinhood coupera l'achat — mais le 27 c'est le pic de volatilité naturelle, sans intervention courtier). Si un signal de rugosité de marché existe et veut dire quelque chose, c'est cette journée-là qu'il doit s'allumer le plus fort.
On a streamé les 11 millions de ticks XNAS sur AMC ce jour-là depuis
le .dbn.zst databento, on a accumulé RV_short(60s) et RV_long(600s)
sur 899 fenêtres de fin-de-minute, on a fait tourner la state machine — et
on a regardé la distribution.

897 fenêtres Smooth, 2 Transition, 0 Rough.
Sur le jour le plus rugueux de la décennie pour AMC, sur 11 millions de
ticks streamés, le classifier est resté Smooth 99.8 % du temps. Les deux
transitions vers Transition n'ont jamais débordé en Rough ; aucune n'a
tenu son dwell de 60 s. Le ribbon de régime, qu'on avait dessiné pour qu'il
s'allume rouge, était plat bleu.
Le bug n'était pas dans le code. Le code mesurait exactement ce qu'on lui avait demandé de mesurer. Le bug était dans l'hypothèse.
T2 — la math du plafonnement
On a regardé la figure ci-dessus. La distribution de R(t) sur la journée
est centrée autour de 0.094, le 99e percentile est à 0.231. Pas
une fenêtre n'atteint 0.4. Le seuil θ_low = 0.4 est inaccessible. Le
seuil θ_high = 0.7 est au-delà de toute observation possible — pas
sur cette journée, pas sur n'importe laquelle.
Pourquoi ? À l'équilibre statistique, RV_long collecte \(~10\times\) plus de
samples que RV_short (10 minutes vs 1 minute). En posant
RV_long(τ_long) ≈ E[r²] · N_long et RV_short(τ_short) ≈ E[r²] · N_short
(approximation Andersen–Bollerslev–Diebold–Labys 2003 Eq. 3) :
R ≈ N_short / N_long
≈ τ_short / τ_long (densité de ticks comparable entre les deux fenêtres)
= 60s / 600s
= 0.1
Le ratio est structurellement borné par sa propre définition, pas par
la rugosité du marché. C'est la moyenne empirique mesurée : R̄ = 0.094,
exactement le 0.1 attendu. La queue droite atteint 0.23 lors de fenêtres
ultra-actives où la densité de ticks short_60s dépasse temporairement la
densité moyenne du long_600s — mais jamais à des facteurs qui
permettraient à R d'atteindre 0.7.
Citation stricte : docs/adr/0005-classifier-signal-j-based.md §"Pourquoi
R plafonne" L21–24. La borne théorique était écrite explicitement dans
l'invariant INV-X-5 de ADR-0003 §1 (« R̄ → τ_short/τ_long ≈ 0.1 sur
returns iid stationnaires ») — on l'avait écrit, on ne l'avait pas lu
quand on a calibré θ_high = 0.7. Le code respectait sa propre spec ; la
spec, au moment de la calibration, contredisait l'autre spec.
Conclusion mathématique : nos seuils 0.7/0.4 supposaient un signal qui
peut atteindre 1.0. Le ratio R ne peut pas, par construction. Le
classifier n'était pas mal calibré sur le mauvais signal — il était bien
calibré sur un signal qui ne pouvait pas le déclencher.
T3 — le pivot vers J
Le pipeline calculait déjà autre chose. Pour chaque fenêtre, depuis le
commit 7746e00, WindowVerdict.j_bv_rv contenait
J(t) = 1 − BV(t) / RV(t)
où BV est la bipower variation (BV(t) = (π/2) · Σ |r_i| · |r_{i-1}|).
Sous absence de saut, BV →_p RV asymptotiquement
([barndorff2004power, Thm. 1, p. ~7]) — donc J → 0 quand la trajectoire
est continue. En présence de sauts, RV capte le saut au carré (r² ≫ |r|·|r_{i-1}|) tandis que BV ne le voit pas (le produit de retours
voisins étouffe les sauts isolés) — donc J → 1 quand la variance vient
des discontinuités.
J ∈ [−ε, 1] à fenêtre finie, avec ε ≈ 10/N (invariant INV-BV-3,
ADR-0003 §2). Les seuils 0.7 et 0.4 retrouvent leur sens — ils
correspondent maintenant à « la majorité de la variance vient de sauts »
et « la majorité vient du diffusif », exactement la sémantique régime.
Sémantique alignée. La délibération-mère tick/delib-20260513-c2b8
parlait de régime rugueux Mandelbrot — Mandelbrot = sauts = ratio des
variances biaisé vers la composante discontinue. C'est précisément ce que
J mesure. Le ratio R, lui, mesurait l'inertie d'intégration d'un
estimateur lent par rapport à un estimateur rapide — une grandeur réelle,
mais sans rapport avec la rugosité topologique.
Le wiring change. Une ligne dans salim-pipeline::worker::maybe_emit :
- classifier.step(r_short_over_long, ts_ns)
+ classifier.step(j_short, ts_ns)
Aucun nouveau calcul. Le pipeline calculait déjà J par discipline
d'observabilité (ADR-0003 §2). On bascule juste quel signal alimente la
state machine. La colonne WindowVerdict.r_short_over_long reste exportée
au Parquet pour audit — schéma V1 non breaking
(ADR-0005 §"Impact sur WindowVerdict").
Le re-run post-pivot sur la même journée AMC 2021-01-27 a donné
J median = 0.360, p99 = 0.504, max = 0.553. Strictement sous
θ_high = 0.7 — et c'est un vrai verdict. La journée du squeeze était
volatile mais directionnelle et continue, pas une succession de jumps
discrets. Le J-based classifier dit Smooth, à raison. C'est documenté
dans docs/lore/CHRONICLES.md §"2026-05-14 — Le squeeze n'est pas rough".
T4 — les leçons
Ce qui a été appris, en trois phrases qui survivront à la session.
Leçon 1 — borne naturelle d'un estimateur. Un ratio est borné par les
fenêtres dont il est issu, pas par ce qu'on aimerait qu'il mesure. Avant
de calibrer les seuils d'un signal, dérive sa borne théorique à
l'équilibre. Si la borne est b̄ et que tes seuils visent au-delà de
b̄, le signal ne s'allumera jamais — peu importe le ticker, peu importe
la journée, peu importe l'amplitude réelle du phénomène que tu cherches à
détecter. Cinq minutes d'algèbre R ≈ N_short/N_long auraient évité la
boucle complète. La discipline n'est pas « écris la spec ». La spec
était écrite (INV-X-5). La discipline est « relis la spec quand tu
calibres une autre partie de la spec ».
Leçon 2 — empirisme avant shipping. Si on n'avait pas streamé 11
millions de ticks sur AMC 2021-01-27 avant de partager une figure à
Salim, l'erreur aurait survécu. Toute analyse théorique aurait conclu «
le classifier est cohérent, les invariants tiennent » — ce qui était
vrai, et inutile, et trompeur. Le smoke run sur une journée connue est
une discipline non-négociable, parce qu'il révèle les erreurs de
spécification que la cohérence formelle ne révèle pas. Le réacteur apprend
de ce qu'il brûle, pas de ce qu'il pourrait brûler. Cf. ADR-0003 §5
(gate de déploiement property-tests) — étendu de facto à « gate
empirique sur fixture réelle ».
Leçon 3 — discipline ADR. ADR-0005 amende ADR-0003 §1 §4. Pas
n'importe quel changement — un changement typé avec source empirique
nommée (commit 7746e00), invariants conservés (INV-CLASSIFIER- MONOTONIC-DWELL, hystérèse), lien superseded-by explicite, schéma
Parquet non-breaking (r_short_over_long conservé en colonne d'audit). Le
pivot s'écrit dans le ledger, pas dans Git seul. La trace cognitive vit
là où on la cherchera dans six mois — dans docs/adr/, à côté de la
décision originelle qu'elle corrige.
Pour ton cas
Si tu construis un signal à partir d'un ratio — RV / IV, BV / RV,
spread / mid, volume_aggressor / volume_total, n'importe lequel —
demande-toi quelle est sa borne théorique à l'équilibre.
Écris-la en deux lignes au-dessus du code, avant de calibrer les
seuils. Si le seuil que tu vises est au-dessus de cette borne, le signal
ne s'allumera jamais ; si la borne est très inférieure à 1.0, la borne
est l'échelle à laquelle tu dois caler tes seuils. Cinq minutes de math
au tableau économisent une session de débogage sur 11 millions de ticks.
C'est moins glamour que d'optimiser un sweep d'hyperparamètres sur 200 instruments. C'est l'algèbre du primaire. Mais c'est le geste qui sépare « j'ai mesuré quelque chose » de « j'ai mesuré ce que je croyais mesurer ».
Conséquences code
WindowVerdict.r_short_over_longest conservé dans le schéma ParquetV1WindowVerdictspour debug et audit. Schéma non-breaking — un consommateur Parquet existant ne casse pas. VoirADR-0005 §"Impact sur WindowVerdict".- Patch (a) min-tick guard.
PipelineConfig.min_tick_count_short = 30etmin_tick_count_long = 300. Sans ça,J = 1artefactuel sur les premières fenêtres mono-tick (BVindéfini sur 1 retour seul \(\to\)bv = 0\(\to\)J = 1 − 0/RV = 1, trompeur). Voir bipower-and-jumps §"Min-tick guard". - Patch (b) BV long accumulator. Second
BvAccumulatorcâblé surwindow_long_nspour calculerJ_longet l'émettre dans la rowlong_600s(au lieu dubv = 0placeholder du MVP). Diagnostic seulement — le classifier reste surJ_short. Voir regime-classifier §"Signal alimentant la state machine".
Pour la formule J = 1 − BV/RV elle-même, ses invariants, et le détail de
la consistance asymptotique BV →_p IV — voir
bipower-and-jumps. Pour la state machine, son
hystérèse, son dwell — voir regime-classifier.
Pour RV et sa variance d'estimateur — voir
realized-variance.
Footer
Adversaire endogène : estimator-instability — le risque que tu
construises un signal sans dériver sa borne. La leçon 1 est l'antidote
explicite.
Pages soeurs realized-variance \(\cdot\) bipower-and-jumps \(\cdot\) regime-classifier \(\cdot\) cases-index
Sources primaires
docs/adr/0005-classifier-signal-j-based.md(toute la page)docs/adr/0003-stats-and-classifier.md§1 §2 §4- Smoke run AMC 2021-01-27, commit
7746e00 docs/lore/CHRONICLES.md§"2026-05-14 — Le squeeze n'est pas rough"- [barndorff2004power] Barndorff-Nielsen, O. E. & Shephard, N. (2004), Power and Bipower Variation with Stochastic Volatility and Jumps, J. Financial Econometrics 2(1), 1–37, Thm. 1, p. ~7
- [delib-20260514-bccc] délibération-mère §T4 (résolution wheeler — « la meilleure pédagogie microstructure est l'histoire d'une erreur qu'on a corrigée »)