diff --git a/components/CurrencyCard.tsx b/components/CurrencyCard.tsx index 6ea69fb..cc7b793 100644 --- a/components/CurrencyCard.tsx +++ b/components/CurrencyCard.tsx @@ -816,7 +816,7 @@ function OISEnhancedBlock({ ratePath, syncChartTab, onChartTabChange }: { {/* Liste des réunions */}
- {scenariosData.map(d => { + {scenariosData.map((d, i) => { const isPeak = ratePath.peakMeeting?.dateIso === d.dateIso; const isDown = d.rate < currentRate - 0.001; const isUp = d.rate > currentRate + 0.001; @@ -824,6 +824,9 @@ function OISEnhancedBlock({ ratePath, syncChartTab, onChartTabChange }: { const barW = Math.max(4, Math.min(100, ((d.rate - minR2) / range) * 100)); const cumBps = d.cumulBps; const cumStr = cumBps === 0 ? null : `${cumBps > 0 ? "+" : ""}${cumBps}`; + // Pour la 1ère réunion : montrer STIR original si IL a surchargé la valeur + const stirProb = i === 0 ? ratePath.ilCurrent?.stirProbPct : undefined; + const ilFused = stirProb !== undefined && Math.abs(d.prob - stirProb) > 5; return (
)} - {d.prob > 0 && ( + {/* Probabilité : si fusion IL/STIR, afficher les deux sources */} + {ilFused ? ( + + + {d.prob.toFixed(0)}% + + + ({stirProb?.toFixed(0)}%) + + + ) : d.prob > 0 ? ( {d.prob.toFixed(0)}% - )} + ) : null}
); })} diff --git a/lib/rateprobability.ts b/lib/rateprobability.ts index ec09f53..95201bf 100644 --- a/lib/rateprobability.ts +++ b/lib/rateprobability.ts @@ -29,7 +29,8 @@ export interface ILWeeklyDelta { export interface ILCurrent { bpsYearEnd: number; // bps fin d'an selon l'article IL courant - probPct: number; // probabilité de move à la prochaine réunion (IL) + probPct: number; // probabilité de move à la prochaine réunion (IL analyste) + stirProbPct?: number; // probabilité originale STIR/IC (avant fusion IL) isNoChange: boolean; // l'analyste anticipe un statu quo isCut: boolean; // l'analyste anticipe une baisse articleDate: string; // date de publication de l'article (YYYY-MM-DD) @@ -351,7 +352,7 @@ export async function fetchAllCBPaths(): Promise { if (rbnzPath) data["NZD"] = rbnzPath; } - // Enrichissement IL : deltas hebdo pour toutes les devises + // Enrichissement IL : fusion proba première réunion + deltas hebdo for (const [ccyStr, ilEntry] of Object.entries(ilData)) { const ccy = ccyStr as keyof RateProbData; const path = data[ccy]; @@ -374,15 +375,50 @@ export async function fetchAllCBPaths(): Promise { }; } + const ilProb = ilEntry.nextMeetingProbPct; + const ilIsCut = !ilEntry.nextMeetingIsHike && !ilEntry.nextMeetingIsNoChange; + const m0 = path.meetings[0]; + const stirProb = m0?.probMovePct ?? 0; + const ilCurrent: ILCurrent = { - bpsYearEnd: ilEntry.bpsYearEnd, - probPct: ilEntry.nextMeetingProbPct, - isNoChange: ilEntry.nextMeetingIsNoChange, - isCut: !ilEntry.nextMeetingIsHike && !ilEntry.nextMeetingIsNoChange, - articleDate: ilEntry.publishedDate, + bpsYearEnd: ilEntry.bpsYearEnd, + probPct: ilProb, + stirProbPct: stirProb || undefined, + isNoChange: ilEntry.nextMeetingIsNoChange, + isCut: ilIsCut, + articleDate: ilEntry.publishedDate, }; - data[ccy] = { ...path, yearEndImplied, ilCurrent, ...(ilDelta ? { ilDelta } : {}) }; + // Fusion STIR + IL pour la première réunion : + // Si l'IL a une proba valide ET qu'elle diffère du STIR de plus de 8pp → on fusionne + // (le STIR IC peut avoir des artefacts de parsing ; l'analyste IL lit la même donnée proprement) + let updatedMeetings = path.meetings; + if (m0 && ilProb > 0 && !ilEntry.nextMeetingIsNoChange && Math.abs(ilProb - stirProb) > 8) { + const updatedM0: RateProbMeeting = { + ...m0, + probMovePct: ilProb, + probIsCut: ilIsCut, + changeBps: ilProb > 50 ? (ilIsCut ? -25 : 25) : 0, + impliedRate: ilProb > 50 + ? parseFloat((path.currentRate + (ilIsCut ? -0.25 : 0.25)).toFixed(4)) + : path.currentRate, + }; + updatedMeetings = [updatedM0, ...path.meetings.slice(1)]; + } + + // Recalcule peakMeeting après fusion + const peakMeeting = updatedMeetings.length + ? updatedMeetings.reduce((best, m) => m.probMovePct > best.probMovePct ? m : best, updatedMeetings[0]) + : null; + + data[ccy] = { + ...path, + meetings: updatedMeetings, + peakMeeting: peakMeeting && peakMeeting.probMovePct > 0 ? peakMeeting : path.peakMeeting, + yearEndImplied, + ilCurrent, + ...(ilDelta ? { ilDelta } : {}), + }; } return data;