Quand RV ment-elle, et de combien ?
RV mesure la variance vraiment réalisée tick-by-tick — pas celle qu'on a supposée le matin.
La realized variance est l'estimateur empirique le plus simple qu'on puisse
écrire sur des log-returns intraday : on les met au carré et on les somme sur
une fenêtre temporelle. Mais simple n'est pas exact. Cette page nomme de
combien RV ment quand on la lit à τ = 60 s, et où elle pourrait
mentir silencieusement si on l'implémentait sans discipline.
Définition
Soit m_i = (bid_i + ask_i) / 2 le mid-quote au tick i, et r_i = log(m_i) − log(m_{i-1}) le log-return entre deux ticks consécutifs. Pour une fenêtre
temporelle τ (en nanosecondes) :
RV(t, τ) = Σ_{i : t_i ∈ (t-τ, t]} r_i²
La fenêtre est temporelle, pas en éléments fixés. Raison : la densité de
ticks varie d'un facteur cinq entre 09:35 et 11:00 New York time
([bouchaud2018trades, Chap. 2 §2.4 — U-shape intraday] ;
docs/adr/0003-stats-and-classifier.md §1 L67-69). Compter en éléments
mélangerait deux régimes microstructurels distincts ; compter en temps les
sépare.
Convention en bordure : Err(EmptyWindow) si la fenêtre ne contient aucun
tick. Pas de NaN silencieux — invariant INV-RV-1 à INV-RV-5 de
l'ADR-0003 (docs/adr/0003-stats-and-classifier.md §1).
Algorithme — deque temporelle online
L'implémentation tient dans une VecDeque<(i64, f64)> qui mémorise les
couples (ts_ns, r²), plus un cumul sum_sq: f64. Voir
research/crates/salim-core/src/stats.rs
§RvAccumulator lignes 30-111 pour le code complet.
Étape de push(ts_ns, log_return) :
- Eviction temporelle :
while buf.front().0 < ts_ns - window_ns { sum_sq -= front.1; pop_front(); }. - Append :
sum_sq += r²; buf.push_back((ts_ns, r²)). - Snapshot :
sum_sqdirectement — pas de re-calcul.
Coût amorti O(1) par push, pire cas O(k) où k est le nombre d'éléments
expulsés au tick courant (rare en pratique, borné par INV-RV-2 ci-dessous).
Une discipline debug_assert!(sum_sq ≥ 0) post-eviction protège contre la
dérive négative quand la fenêtre se vide
(research/crates/salim-core/src/stats.rs:76-81).
Borne d'erreur — Andersen–Bollerslev–Diebold–Labys 2003
C'est le nombre qui doit être lu en même temps que la valeur. Sous le
modèle BS-Heston, pour un nombre N = τ/Δ de ticks à pas régulier :
Var(RV) ≈ 2 · IV² · (Δ / τ)
σ(RV) ≈ IV · √(2 / N)
[andersen2003modeling, Eq. 3 p. ~587] ; [@andersen2003modeling, Thm. 2 p.
~587] pour la consistance RV →_p IV as Δ → 0.
Numériquement, à τ = 60 s et Δ = 1 s (donc N = 60) :
σ(RV_short) / IV ≈ √(2 / 60) ≈ 18 %
— le 18 % est dérivé directement de l'Eq. 3 de [@andersen2003modeling, p.
~587]. C'est du bruit estimateur, pas de la vraie volatilité. Salim qui
lit RV_short(60s) = 2 · baseline lit en réalité RV_short / baseline ∈ [1.6, 2.4] à 1-σ ([andersen2003modeling, Eq. 3 p. ~587], propagation
multiplicative). Le seuil binaire « calme/agité » devient une zone floue.
Stabilité numérique — f64 naïf suffit (table compressée)
Le Σ r² peut être implémenté de cinq façons connues. Pour RV non-centered
sur n ticks bornés |n| ≤ 6·10⁶ (peak ARM open dans
docs/adr/0003-stats-and-classifier.md §1 L72) :
| Méthode | Borne erreur relative | Verdict |
|---|---|---|
f64 naïf sum_sq += r² | (n−1)·u ≈ 7·10⁻¹⁰ à n = 6·10⁶ ([higham2002accuracy, Thm. 4.1 p. ~81]) | OK — adopté |
| Pairwise summation | O(log n)·u ≈ 2·10⁻¹⁵ ([higham2002accuracy, Algo. 4.1 p. ~89]) | Overkill |
| Kahan compensated | ≈ 2u ≈ 2·10⁻¹⁶ ([higham2002accuracy, Thm. 4.7 p. ~95]) | Overkill |
| Welford one-pass | O(u) indep de n (Welford 1962) | Interdit pour RV — voir docs/adr/0003-stats-and-classifier.md §1 L72-86 |
Pourquoi Welford est interdit, en une phrase : RV n'a pas de moyenne soustraite
(c'est Σ r², pas Σ (r − r̄)²), donc la cancellation que Welford corrige
n'existe pas, et la version Welford de RV exigerait un état supplémentaire
sans gain d'exactitude
(docs/adr/0003-stats-and-classifier.md §1 L79-86).
L'erreur du f64 naïf à 7·10⁻¹⁰ ([higham2002accuracy, Thm. 4.1 p. ~81])
est dix-huit ordres de grandeur plus petite que le bruit estimateur de
18 % ([andersen2003modeling, Eq. 3 p. ~587]) calculé ci-dessus. Optimiser
la première sans bouger la seconde est du cargo cult.
Pour ton cas
Pour toi, Salim, en clair :
- Si tu surveilles
RV_short(60s)àΔ = 1 s(doncN = 60), ta valeur t'arrive avec \(\pm18\) % de bruit relatif ([@andersen2003modeling, Eq. 3 p. ~587]) — c'est intrinsèque à l'estimateur, pas un bug. - Un seuil mental « rough quand
RV > 2 × baseline» est en réalité « rough quandRV / baseline ∈ [1.6, 2.4]» à 1-σ (propagation directe des±18 %ci-dessus). Le seuil flotte. - C'est pour ça que le classifier de régime utilise hystérèse + dwell
(regime-classifier ;
docs/adr/0003-stats-and-classifier.md§4) plutôt qu'un seuil dur : l'hystérèse couvre la zone floue d'incertitude estimateur, le dwell évite qu'un tick chanceux ne bascule l'état.
Si tu veux serrer le ±18 %, la seule façon honnête est d'augmenter N
(donc τ, donc latence de décision) : à τ = 600 s, Δ = 1 s, N = 600, le
bruit relatif tombe à √(2/600) ≈ 5.8 % ([@andersen2003modeling, Eq. 3 p.
~587]). C'est la raison du double horizon RV_short(60s) / RV_long(600s) de
l'ADR-0003 §1.
Limites du résultat
- Le
r_i = log(m_i) − log(m_{i-1})suppose le mid-price stable d'un tick à l'autre. Quand le spread saute (> 1 tick),m_ibouge mécaniquement et injecte du bruit microstructure dansRV— biais documenté dans [bouchaud2018trades, Chap. 2] (« bid-ask bounce »). - La formule
Var(RV) ≈ 2 IV²(Δ/τ)([andersen2003modeling, Eq. 3 p. ~587]) suppose des returns i.i.d. quasi-normaux sans saut. Avec sauts purs, le biais est dans le numérateur (RVcapte aussiΣ J²) — c'est exactement ce queJ = 1 − BV/RVmesure, voir bipower-and-jumps. - La discipline
f64naïf bornée à7·10⁻¹⁰([@higham2002accuracy, Thm. 4.1 p. ~81]) suppose quen ≤ 6·10⁶; au-delà, repasser par pairwise ou Kahan sans changer le contrat. Re-vérifierINV-RV-4de l'ADR-0003 §1.
Adversaire endogène
estimator-instability — le risque que Salim sur-interprète une valeur
ponctuelle de RV ou un franchissement de seuil isolé. Antidote nommé dans
cette page : la borne ±18 % à N = 60 ([@andersen2003modeling, Eq. 3 p.
~587]) est lue en même temps que la valeur, et reliée explicitement à la
nécessité d'hystérèse côté classifier
(docs/adr/0003-stats-and-classifier.md §4).
Références
- ADR mère :
docs/adr/0003-stats-and-classifier.md§1 (L30-L110) - Code Rust :
research/crates/salim-core/src/stats.rs§RvAccumulator(L30-L111) - Délibération wiki-architecture :
delib-20260514-bccc/synthesis.md§T1 + §T3 - Pages adjacentes : bipower-and-jumps (cite RV pour
J = 1 − BV/RV), regime-classifier (consommeR(t) = RV_short / RV_long) -
Sources externes :
- [andersen2003modeling] Andersen, T. G., Bollerslev, T., Diebold, F. X., & Labys, P. (2003). Modeling and Forecasting Realized Volatility. Econometrica 71(2), 579–625. Thm. 2 p. ~587 (consistance) ; Eq. 3 p. ~587 (variance d'estimateur).
- [higham2002accuracy] Higham, N. J. (2002). Accuracy and Stability of Numerical Algorithms, 2nd ed., SIAM. Thm. 4.1 p. ~81 (somme récursive) ; Algo. 4.1 p. ~89 (pairwise) ; Thm. 4.7 p. ~95 (Kahan).
- [bouchaud2018trades] Bouchaud, J.-P., Bonart, J., Donier, J., Gould, M. (2018). Trades, Quotes and Prices. Cambridge University Press. Chap. 2 §2.4 (U-shape intraday) ; Chap. 2 (bid-ask bounce).