From 28f7c77103df82c1b6b6e76bde609a9c37653638 Mon Sep 17 00:00:00 2001 From: OpenSquared Date: Mon, 20 Jul 2026 20:30:24 +0200 Subject: [PATCH] feat: wavelets --- backend/services/wavelet_engine.py | 20 ++++++++++++++++++-- frontend/src/pages/InstrumentDashboard.tsx | 10 ++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/backend/services/wavelet_engine.py b/backend/services/wavelet_engine.py index 66b957c..1fd2a07 100644 --- a/backend/services/wavelet_engine.py +++ b/backend/services/wavelet_engine.py @@ -616,14 +616,29 @@ def wavelet_reliability( # showed up (earliest match) — compared against avg_cycle_days below to check # whether "one cycle" is actually a well-calibrated horizon, or systematically # too long/short relative to how quickly a real reversal actually confirms. - actual_delay = (min(matches) - d_pos) if confirmed else None - tested.append({"date": d_date, "confirmed": confirmed, "actual_delay_days": actual_delay}) + actual_delay = None + height_diff = None + if confirmed: + confirm_idx = min(matches) + actual_delay = confirm_idx - d_pos + # Direction agreeing doesn't mean the peak/trough had the same amplitude — + # the live (causal) reading can be a real reversal but distorted in height by + # edge effects. Compare the band's own value at the original causal turning + # point against its value at the confirming point in the hindsight + # reconstruction (same units as the band, e.g. price terms). + height_diff = abs(band["series"][t] - h_series[confirm_idx]) + tested.append({ + "date": d_date, "confirmed": confirmed, + "actual_delay_days": actual_delay, "height_diff": height_diff, + }) n_tested = len(tested) n_confirmed = sum(1 for t in tested if t["confirmed"]) delays = [t["actual_delay_days"] for t in tested if t["actual_delay_days"] is not None] avg_actual_delay = round(sum(delays) / len(delays), 1) if delays else None calibration_gap = round(sum(abs(avg_cycle_days - d) for d in delays) / len(delays), 1) if delays else None + heights = [t["height_diff"] for t in tested if t["height_diff"] is not None] + avg_height_diff = round(sum(heights) / len(heights), 6) if heights else None bands_out.append({ "label": band["label"], "n_tested": n_tested, @@ -633,6 +648,7 @@ def wavelet_reliability( "confirm_horizon": confirm_horizon, "avg_actual_delay_days": avg_actual_delay, "calibration_gap_days": calibration_gap, + "avg_height_diff": avg_height_diff, "turns": tested, }) diff --git a/frontend/src/pages/InstrumentDashboard.tsx b/frontend/src/pages/InstrumentDashboard.tsx index b30b6cb..bc2c4d3 100644 --- a/frontend/src/pages/InstrumentDashboard.tsx +++ b/frontend/src/pages/InstrumentDashboard.tsx @@ -2389,6 +2389,7 @@ export default function InstrumentDashboard({ instrumentIdProp, isVisible }: { i Horizon max Délai réel moyen Écart calibration + Écart hauteur Retournements testés Confirmés Indice de confiance @@ -2407,6 +2408,7 @@ export default function InstrumentDashboard({ instrumentIdProp, isVisible }: { i {b.confirm_horizon != null ? `+${b.confirm_horizon}j` : '—'} {b.avg_actual_delay_days != null ? `${b.avg_actual_delay_days}j` : '—'} {gap != null ? `${gap}j` : '—'} + {b.avg_height_diff != null ? b.avg_height_diff.toFixed(4) : '—'} {b.n_tested} {b.n_confirmed} @@ -2421,10 +2423,10 @@ export default function InstrumentDashboard({ instrumentIdProp, isVisible }: { i Pour chaque retournement détecté en mode causal (pente lissée sur {waveletReliability.smooth_days}j qui change de signe, sans regarder le futur), l'horizon de recalcul est propre à chaque bande — cycle moyen historique (pic-à-pic) × 1.10, pas un nombre de jours fixe identique pour toutes (10 jours ne veut rien dire pour une bande à 2j de période comme pour une à 20j). Le retournement confirme l'original s'il réapparaît n'importe - où entre la date d'origine et date+horizon. "Écart calibration" compare l'horizon supposé (cycle moyen) au délai réel observé — un écart élevé - signale que l'horizon utilisé n'est pas représentatif de la vitesse réelle de confirmation, indépendamment de l'indice de confiance lui-même. - Un indice bas signale des retournements souvent dus à un effet de bord de la décomposition (peu fiable pile au bord de la fenêtre disponible), - pas à un vrai signal. + où entre la date d'origine et date+horizon. "Écart calibration" compare l'horizon supposé (cycle moyen) au délai réel observé ; "Écart hauteur" + compare l'amplitude du pic/creux vue en direct à celle confirmée en hindsight — comparez-le au RMS de la bande (tableau du dessus) pour juger + s'il est significatif. Un indice bas signale des retournements souvent dus à un effet de bord de la décomposition (peu fiable pile au bord de + la fenêtre disponible), pas à un vrai signal.

)}