diff --git a/.github/scripts/fetch-rate-data.mjs b/.github/scripts/fetch-rate-data.mjs index 7c38786..a77bc89 100644 --- a/.github/scripts/fetch-rate-data.mjs +++ b/.github/scripts/fetch-rate-data.mjs @@ -400,6 +400,18 @@ function buildILFallback(ccy, il) { const CCYS = ["USD","EUR","GBP","JPY","CAD","AUD","NZD","CHF"]; const results = {}; +// Charge les données déjà en place AVANT tout fetch — sert de filet de sécurité +// plus bas : investing.com renvoie parfois 403 spécifiquement aux IPs GitHub +// Actions (bloc IP anti-bot, confirmé en direct — pas un problème de parsing), +// et ce blocage est intermittent. Sans ce filet, un seul run bloqué écrasait +// la vraie courbe multi-réunions USD par le fallback InvestingLive (un unique +// point "year-end"), et cette dégradation restait visible jusqu'au prochain +// run réussi. On préfère désormais garder la dernière bonne donnée connue. +let existingData = {}; +try { + existingData = JSON.parse(readFileSync("data/rate-probabilities.json", "utf8")).data ?? {}; +} catch {} + // 0 — Sources officielles → écrasent les ancres si data/rate_decisions.json a dérivé console.log("\n=== Fed (official) ==="); const fedRate = await fetchFedRate(); @@ -411,10 +423,19 @@ if (snbRate !== null) FALLBACK_RATES.CHF = snbRate; // 1 — Investing.com Fed Rate Monitor → USD uniquement (seule CB avec cet outil ; // les autres slugs *-rate-monitor n'existent pas sur investing.com → 404 attendus) +// Retry (403/erreur réseau ponctuels) — n'aide pas contre un vrai bloc IP +// persistant sur tout le run, mais couvre les échecs vraiment transitoires. const IC_SUPPORTED = ["USD"]; console.log("\n=== Investing.com Rate Monitors ==="); for (const ccy of IC_SUPPORTED) { - const data = await fetchInvestingCom(ccy); + let data = null; + for (let attempt = 1; attempt <= 3 && !data; attempt++) { + if (attempt > 1) { + console.log(`[IC/${ccy}] retry ${attempt}/3…`); + await new Promise(r => setTimeout(r, 2000 * attempt)); + } + data = await fetchInvestingCom(ccy); + } if (data) results[ccy] = data; await new Promise(r => setTimeout(r, 600)); } @@ -432,6 +453,17 @@ if (missing.length) { } } +// 3 — Filet de sécurité : si le résultat du jour n'a qu'1 réunion (fallback IL +// dégradé) alors qu'on avait déjà une vraie courbe multi-réunions, on la garde. +for (const ccy of CCYS) { + const newRows = results[ccy]?.today?.rows?.length ?? 0; + const oldRows = existingData[ccy]?.today?.rows?.length ?? 0; + if (newRows <= 1 && oldRows > 1) { + console.log(`[preserve] ${ccy}: nouveau fetch dégradé (${newRows} réunion) → conserve l'ancienne courbe (${oldRows} réunions)`); + results[ccy] = existingData[ccy]; + } +} + // ── Multi-week snapshot rotation ────────────────────────────────────────────── // snapshots = tableau chronologique (plus récent en [0]) de jusqu'à 12 semaines let snapshots = []; diff --git a/data/il-enrichment-cache.json b/data/il-enrichment-cache.json index 82b3a7e..cf3052f 100644 --- a/data/il-enrichment-cache.json +++ b/data/il-enrichment-cache.json @@ -1 +1 @@ -{"ts":1783528251592,"data":{"current":{"NZD":{"currency":"NZD","nextMeetingProbPct":82,"nextMeetingIsHike":true,"nextMeetingIsNoChange":false,"bpsYearEnd":62,"publishedDate":"2026-07-03"},"USD":{"currency":"USD","nextMeetingProbPct":17,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":30,"publishedDate":"2026-07-03"},"EUR":{"currency":"EUR","nextMeetingProbPct":28,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":24,"publishedDate":"2026-07-03"},"JPY":{"currency":"JPY","nextMeetingProbPct":1,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":20,"publishedDate":"2026-07-03"},"GBP":{"currency":"GBP","nextMeetingProbPct":7,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":18,"publishedDate":"2026-07-03"},"AUD":{"currency":"AUD","nextMeetingProbPct":15,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":10,"publishedDate":"2026-07-03"},"CAD":{"currency":"CAD","nextMeetingProbPct":9,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":8,"publishedDate":"2026-07-03"},"CHF":{"currency":"CHF","nextMeetingProbPct":4,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":5,"publishedDate":"2026-07-03"}},"prev":{"NZD":{"currency":"NZD","nextMeetingProbPct":62,"nextMeetingIsHike":true,"nextMeetingIsNoChange":false,"bpsYearEnd":55,"publishedDate":"2026-06-26"},"USD":{"currency":"USD","nextMeetingProbPct":30,"nextMeetingIsHike":true,"nextMeetingIsNoChange":false,"bpsYearEnd":31,"publishedDate":"2026-06-26"},"EUR":{"currency":"EUR","nextMeetingProbPct":31,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":27,"publishedDate":"2026-06-26"},"GBP":{"currency":"GBP","nextMeetingProbPct":14,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":20,"publishedDate":"2026-06-26"},"JPY":{"currency":"JPY","nextMeetingProbPct":3,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":19,"publishedDate":"2026-06-26"},"CAD":{"currency":"CAD","nextMeetingProbPct":4,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":18,"publishedDate":"2026-06-26"},"AUD":{"currency":"AUD","nextMeetingProbPct":13,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":8,"publishedDate":"2026-06-26"},"CHF":{"currency":"CHF","nextMeetingProbPct":6,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":5,"publishedDate":"2026-06-26"}},"prevDate":"2026-06-26"}} \ No newline at end of file +{"ts":1783535815952,"data":{"current":{"NZD":{"currency":"NZD","nextMeetingProbPct":82,"nextMeetingIsHike":true,"nextMeetingIsNoChange":false,"bpsYearEnd":62,"publishedDate":"2026-07-03"},"USD":{"currency":"USD","nextMeetingProbPct":17,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":30,"publishedDate":"2026-07-03"},"EUR":{"currency":"EUR","nextMeetingProbPct":28,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":24,"publishedDate":"2026-07-03"},"JPY":{"currency":"JPY","nextMeetingProbPct":1,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":20,"publishedDate":"2026-07-03"},"GBP":{"currency":"GBP","nextMeetingProbPct":7,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":18,"publishedDate":"2026-07-03"},"AUD":{"currency":"AUD","nextMeetingProbPct":15,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":10,"publishedDate":"2026-07-03"},"CAD":{"currency":"CAD","nextMeetingProbPct":9,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":8,"publishedDate":"2026-07-03"},"CHF":{"currency":"CHF","nextMeetingProbPct":4,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":5,"publishedDate":"2026-07-03"}},"prev":{"NZD":{"currency":"NZD","nextMeetingProbPct":62,"nextMeetingIsHike":true,"nextMeetingIsNoChange":false,"bpsYearEnd":55,"publishedDate":"2026-06-26"},"USD":{"currency":"USD","nextMeetingProbPct":30,"nextMeetingIsHike":true,"nextMeetingIsNoChange":false,"bpsYearEnd":31,"publishedDate":"2026-06-26"},"EUR":{"currency":"EUR","nextMeetingProbPct":31,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":27,"publishedDate":"2026-06-26"},"GBP":{"currency":"GBP","nextMeetingProbPct":14,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":20,"publishedDate":"2026-06-26"},"JPY":{"currency":"JPY","nextMeetingProbPct":3,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":19,"publishedDate":"2026-06-26"},"CAD":{"currency":"CAD","nextMeetingProbPct":4,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":18,"publishedDate":"2026-06-26"},"AUD":{"currency":"AUD","nextMeetingProbPct":13,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":8,"publishedDate":"2026-06-26"},"CHF":{"currency":"CHF","nextMeetingProbPct":6,"nextMeetingIsHike":false,"nextMeetingIsNoChange":true,"bpsYearEnd":5,"publishedDate":"2026-06-26"}},"prevDate":"2026-06-26"}} \ No newline at end of file