feat: wavelets

This commit is contained in:
OpenSquared
2026-07-19 16:03:09 +02:00
parent b900d77aa0
commit 9079d4e9c5
3 changed files with 27 additions and 8 deletions

View File

@@ -1430,7 +1430,7 @@ export default function InstrumentDashboard({ instrumentIdProp, isVisible }: { i
const [waveletFamily, setWaveletFamily] = useState<'gmw' | 'morlet' | 'bump'>('gmw')
const [waveletMethod, setWaveletMethod] = useState<'cwt' | 'ssq'>('cwt')
const [waveletCausal, setWaveletCausal] = useState(false)
const [waveletLookback, setWaveletLookback] = useState(130)
const [waveletLookback, setWaveletLookback] = useState(260)
const [waveletData, setWaveletData] = useState<any | null>(null)
const [loadingWavelet, setLoadingWavelet] = useState(false)
const [waveletReliability, setWaveletReliability] = useState<any | null>(null)
@@ -2155,7 +2155,7 @@ export default function InstrumentDashboard({ instrumentIdProp, isVisible }: { i
<label className="flex items-center gap-1 text-slate-400">
Lookback
<input type="number" min={32} value={waveletLookback}
onChange={e => setWaveletLookback(Math.max(32, Number(e.target.value) || 130))}
onChange={e => setWaveletLookback(Math.max(32, Number(e.target.value) || 260))}
className="w-14 bg-dark-900 border border-slate-700/40 rounded px-1.5 py-0.5 text-white" />
</label>
)}
@@ -2387,6 +2387,8 @@ export default function InstrumentDashboard({ instrumentIdProp, isVisible }: { i
<th className="py-1 pr-3 font-normal">Bande</th>
<th className="py-1 pr-3 font-normal text-right" title="Écart moyen entre deux pics (ou deux creux) consécutifs, mesuré sur l'historique — pas la période théorique de la bande.">Cycle moyen</th>
<th className="py-1 pr-3 font-normal text-right" title="= cycle moyen × 1.10 — le retournement peut être confirmé n'importe où entre la date d'origine et date+horizon, pas juste pile à cette date">Horizon max</th>
<th className="py-1 pr-3 font-normal text-right" title="Délai réel moyen (parmi les retournements confirmés) entre la date d'origine et l'apparition du retournement confirmant, en hindsight">Délai réel moyen</th>
<th className="py-1 pr-3 font-normal text-right" title="Écart absolu moyen entre le cycle moyen (l'horizon supposé) et le délai réel observé — petit = l'horizon est bien calibré, grand = il est trop long ou trop court par rapport à la réalité">Écart calibration</th>
<th className="py-1 pr-3 font-normal text-right">Retournements testés</th>
<th className="py-1 pr-3 font-normal text-right">Confirmés</th>
<th className="py-1 pr-3 font-normal text-right">Indice de confiance</th>
@@ -2396,11 +2398,15 @@ export default function InstrumentDashboard({ instrumentIdProp, isVisible }: { i
{waveletReliability.bands.map((b: any, i: number) => {
const pct = b.confidence_pct
const color = pct == null ? 'text-slate-500' : pct >= 70 ? 'text-emerald-400' : pct >= 40 ? 'text-amber-400' : 'text-red-400'
const gap = b.calibration_gap_days
const gapColor = gap == null ? 'text-slate-600' : gap <= 2 ? 'text-emerald-400' : gap <= 8 ? 'text-amber-400' : 'text-red-400'
return (
<tr key={b.label} className="border-t border-slate-800/60">
<td className="py-1 pr-3" style={{ color: WAVELET_BAND_COLORS[i % WAVELET_BAND_COLORS.length] }}>{b.label}</td>
<td className="py-1 pr-3 text-right text-slate-400 font-mono">{b.avg_cycle_days != null ? `${b.avg_cycle_days}j` : '—'}</td>
<td className="py-1 pr-3 text-right text-slate-400 font-mono">{b.confirm_horizon != null ? `+${b.confirm_horizon}j` : '—'}</td>
<td className="py-1 pr-3 text-right text-slate-400 font-mono">{b.avg_actual_delay_days != null ? `${b.avg_actual_delay_days}j` : '—'}</td>
<td className={clsx('py-1 pr-3 text-right font-mono', gapColor)}>{gap != null ? `${gap}j` : '—'}</td>
<td className="py-1 pr-3 text-right text-slate-300 font-mono">{b.n_tested}</td>
<td className="py-1 pr-3 text-right text-slate-300 font-mono">{b.n_confirmed}</td>
<td className={clsx('py-1 pr-3 text-right font-mono font-semibold', color)}>
@@ -2415,8 +2421,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
entre la date d'origine et date+horizon. 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.
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.
</p>
</div>
)}