From 50ae11c3c47434bf36c6c49eb456f60c27028cdc Mon Sep 17 00:00:00 2001 From: caty21 Date: Thu, 9 Jul 2026 11:54:32 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20remplace=20Atlanta=20Fed=20par=20des=20?= =?UTF-8?q?futures=20Euribor/SONIA=20r=C3=A9els=20pour=20EUR/GBP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Retire l'onglet Atlanta Fed MPT (USD) : jugé sans valeur ajoutée réelle. - EUR/GBP n'avaient qu'un unique point synthétique (agrégat year-end InvestingLive) gonflé en fausses réunions plates à 0% sur Courbe/ Probabilités/Réunions — d'où l'aspect cassé. Investing.com n'a de Rate Monitor pré-calculé (comme pour la Fed) ni pour la BCE ni pour la BoE (404 confirmé), donc pas d'équivalent direct au pipeline USD. - Remplace ce point unique par un vrai scrape des futures Euribor 3M (Eurex) et Three-Month SONIA (ICE), cotés sur investing.com (delayed, gratuit, pas de login) : conversion prix→taux (100−prix), mapping sur le calendrier réel BCE/BoE (échéance la plus proche ≥ date de réunion, fenêtre de grâce de 60j au-delà du dernier contrat coté, sinon on s'arrête plutôt que d'inventer), probabilité approximée avec la convention bps/25 déjà utilisée pour le fallback InvestingLive. - Résolution plus grossière que l'USD (mensuelle EUR, trimestrielle GBP) et clairement étiquetée comme telle dans un footnote dédié. Co-Authored-By: Claude Sonnet 5 --- .github/scripts/fetch-atlanta-mpt.mjs | 166 ------------ .github/scripts/fetch-rate-data.mjs | 150 +++++++++++ .github/workflows/fetch-atlanta-mpt.yml | 33 --- app/api/macro/route.ts | 2 - app/page.tsx | 2 +- components/CurrencyCard.tsx | 87 +------ data/atlanta-fed-mpt.json | 324 ------------------------ data/il-enrichment-cache.json | 2 +- data/rate-probabilities.json | 177 ++++++++----- lib/atlantaFedMpt.ts | 43 ---- tsconfig.tsbuildinfo | 2 +- 11 files changed, 285 insertions(+), 703 deletions(-) delete mode 100644 .github/scripts/fetch-atlanta-mpt.mjs delete mode 100644 .github/workflows/fetch-atlanta-mpt.yml delete mode 100644 data/atlanta-fed-mpt.json delete mode 100644 lib/atlantaFedMpt.ts diff --git a/.github/scripts/fetch-atlanta-mpt.mjs b/.github/scripts/fetch-atlanta-mpt.mjs deleted file mode 100644 index fb4b788..0000000 --- a/.github/scripts/fetch-atlanta-mpt.mjs +++ /dev/null @@ -1,166 +0,0 @@ -// Atlanta Fed Market Probability Tracker (MPT) — méthodologie alternative à -// Investing.com Fed Rate Monitor : au lieu des 30-day Fed Fund Futures, la -// Fed d'Atlanta déduit une distribution de probabilité du niveau du taux Fed -// à partir des OPTIONS sur futures SOFR 3 mois cotées au CME (le rationnel : -// la Fed conduit sa politique via des opérations repo, donc la distribution -// implicite du SOFR composé sur la fenêtre de référence du contrat permet de -// déduire les anticipations sur la fourchette cible du FOMC). -// Doc + téléchargements : https://www.atlantafed.org/cenfis/market-probability-tracker -// -// Format du fichier xl/worksheets/sheet3.xml (vérifié 2026-07-08) — format -// long, une ligne par (date, reference_start, target_range, field) : -// date : date d'observation (YYYY-MM-DD), publiée quotidiennement -// reference_start : date de début de la fenêtre de 3 mois référencée par le -// contrat SOFR (trimestrielle, style IMM) -// target_range : pour les champs agrégés (Rate:*, Prob: cut, Prob: hike) -// = fourchette cible ACTUELLE (contexte) ; pour les champs -// "Prob: XXXbps - YYYbps" = LA fourchette évaluée -// field / value : nom du champ + valeur (en bps, ex. "373.91" = 3.7391%) -// -// Pas de dépendance xlsx npm (le paquet du registre public a des CVE critiques -// non patchées depuis que SheetJS a arrêté d'y publier) — on lit le zip et le -// XML OOXML directement avec les modules Node natifs (zlib + regex). - -import { writeFileSync, mkdirSync } from "fs"; -import { inflateRawSync } from "zlib"; - -const XLSX_URL = "https://www.atlantafed.org/-/media/Project/Atlanta/FRBA/Documents/cenfis/market-probability-tracker/mpt_histdata.xlsx"; - -// ── Lecteur ZIP minimal (central directory + inflate raw deflate) ──────────── - -function readZip(buf) { - let eocdOff = -1; - for (let i = buf.length - 22; i >= Math.max(0, buf.length - 22 - 65536); i--) { - if (buf.readUInt32LE(i) === 0x06054b50) { eocdOff = i; break; } - } - if (eocdOff === -1) throw new Error("ZIP: End Of Central Directory introuvable"); - const cdEntries = buf.readUInt16LE(eocdOff + 10); - const cdOffset = buf.readUInt32LE(eocdOff + 16); - - const entries = {}; - let p = cdOffset; - for (let i = 0; i < cdEntries; i++) { - if (buf.readUInt32LE(p) !== 0x02014b50) throw new Error(`ZIP: central directory corrompue à l'offset ${p}`); - const compMethod = buf.readUInt16LE(p + 10); - const compSize = buf.readUInt32LE(p + 20); - const nameLen = buf.readUInt16LE(p + 28); - const extraLen = buf.readUInt16LE(p + 30); - const commentLen = buf.readUInt16LE(p + 32); - const lfhOffset = buf.readUInt32LE(p + 42); - const name = buf.toString("utf8", p + 46, p + 46 + nameLen); - entries[name] = { compMethod, compSize, lfhOffset }; - p += 46 + nameLen + extraLen + commentLen; - } - return { - read(name) { - const e = entries[name]; - if (!e) return null; - const nameLen = buf.readUInt16LE(e.lfhOffset + 26); - const extraLen = buf.readUInt16LE(e.lfhOffset + 28); - const dataStart = e.lfhOffset + 30 + nameLen + extraLen; - const raw = buf.subarray(dataStart, dataStart + e.compSize); - return e.compMethod === 0 ? raw : inflateRawSync(raw); - }, - }; -} - -function parseSharedStrings(xml) { - return Array.from(xml.matchAll(/(?:]*>([^<]*)<\/t>|[\s\S]*?<\/r>)*<\/si>/g)) - .map(m => m[0].match(/]*>([^<]*)<\/t>/g)?.map(t => t.replace(/<[^>]+>/g, "")).join("") ?? ""); -} - -function parseSheetRows(xml, strings) { - const rowRe = /]*>([\s\S]*?)<\/row>/g; - const cellRe = /]*>(?:([^<]*)<\/v>)?<\/c>/g; - const rows = []; - let rm, first = true; - while ((rm = rowRe.exec(xml)) !== null) { - if (first) { first = false; continue; } // ligne d'en-tête - const cells = {}; - let cm; cellRe.lastIndex = 0; - while ((cm = cellRe.exec(rm[1])) !== null) { - const [, ref, type, val] = cm; - cells[ref] = val === undefined ? null : (type === "s" ? strings[+val] : val); - } - rows.push(cells); - } - return rows; -} - -function excelSerialToIso(serial) { - const epoch = Date.UTC(1899, 11, 30); - return new Date(epoch + Number(serial) * 86400000).toISOString().slice(0, 10); -} - -// ── Fetch + parse ───────────────────────────────────────────────────────────── - -async function fetchAtlantaMpt() { - const res = await fetch(XLSX_URL, { - headers: { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" }, - }); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - const buf = Buffer.from(await res.arrayBuffer()); - - const zip = readZip(buf); - const strings = parseSharedStrings(zip.read("xl/sharedStrings.xml").toString("utf8")); - const rows = parseSheetRows(zip.read("xl/worksheets/sheet3.xml").toString("utf8"), strings); - - // { A: date, B: reference_start (serial), C: target_range, D: field, E: value } - const records = rows - .filter(r => r.A && r.B && r.D && r.E !== null) - .map(r => ({ date: r.A, ref: Number(r.B), range: r.C, field: r.D, value: parseFloat(String(r.E).trim()) })); - - if (!records.length) throw new Error("aucune ligne exploitable trouvée dans sheet3"); - - const maxDate = records.reduce((m, r) => (r.date > m ? r.date : m), ""); - const latest = records.filter(r => r.date === maxDate); - const refStarts = [...new Set(latest.map(r => r.ref))].sort((a, b) => a - b); - - const windows = refStarts.map(ref => { - const windowRows = latest.filter(r => r.ref === ref); - const get = (field) => windowRows.find(r => r.field === field)?.value ?? null; - const anchorRange = windowRows.find(r => r.field === "Prob: hike")?.range ?? null; - // La colonne "range" (C) vaut toujours la fourchette ACTUELLE/ancre pour - // toutes les lignes de la fenêtre (y compris les lignes de distribution) — - // la fourchette évaluée par chaque bucket est encodée dans le nom du champ - // lui-même ("Prob: 375bps - 400bps"), pas dans la colonne range. - const distribution = windowRows - .filter(r => r.field.startsWith("Prob: ") && r.field !== "Prob: cut" && r.field !== "Prob: hike") - .map(r => ({ rangeLabel: r.field.replace(/^Prob:\s*/, ""), probPct: r.value })) - .sort((a, b) => parseInt(a.rangeLabel) - parseInt(b.rangeLabel)); - return { - windowStartIso: excelSerialToIso(ref), - anchorRange, - probCutPct: get("Prob: cut"), - probHikePct: get("Prob: hike"), - rate25: get("Rate: 25th percentile"), - rateMean: get("Rate: mean"), - rateMode: get("Rate: mode"), - rate75: get("Rate: 75th percentile"), - distribution, - }; - }); - - return { asOf: maxDate, windows }; -} - -// ── Main ────────────────────────────────────────────────────────────────────── - -try { - console.log("Fetching Atlanta Fed MPT historical data…"); - const data = await fetchAtlantaMpt(); - console.log(`✓ asOf=${data.asOf}, ${data.windows.length} fenêtres trimestrielles`); - console.log(` front window: ${data.windows[0].windowStartIso} — hike=${data.windows[0].probHikePct}% cut=${data.windows[0].probCutPct}%`); - - mkdirSync("data", { recursive: true }); - writeFileSync("data/atlanta-fed-mpt.json", JSON.stringify({ - updated_at: new Date().toISOString().slice(0, 10), - source: "https://www.atlantafed.org/cenfis/market-probability-tracker", - note: "Probabilités dérivées des options sur futures SOFR 3 mois (CME) — méthodologie Atlanta Fed, mise à jour quotidienne. Fourchettes exprimées en bps (350bps-375bps = 3.50%-3.75%). Alternative aux 30-day Fed Fund Futures (Investing.com Fed Rate Monitor).", - ...data, - }, null, 2) + "\n"); - console.log("✓ Saved data/atlanta-fed-mpt.json"); -} catch (e) { - console.error(`✗ Atlanta Fed MPT fetch failed: ${e.message}`); - process.exit(0); // échec silencieux — ne bloque pas le workflow, données précédentes conservées -} diff --git a/.github/scripts/fetch-rate-data.mjs b/.github/scripts/fetch-rate-data.mjs index a77bc89..99b0998 100644 --- a/.github/scripts/fetch-rate-data.mjs +++ b/.github/scripts/fetch-rate-data.mjs @@ -303,6 +303,140 @@ function parseRateMonitorTable(ccy, html) { return { today: { midpoint: currentRate, rows } }; } +// ── 1bis. Investing.com futures strips (EUR/GBP) ────────────────────────────── +// Investing.com n'a de Rate Monitor (probabilité pré-calculée par réunion) que +// pour la Fed (voir note en tête de fichier — ecb/boe-rate-monitor → 404). Mais +// Investing.com sert bien les prix (delayed) des futures Euribor 3M (Eurex) et +// Three-Month SONIA (ICE) — la référence que les praticiens utilisent pour lire +// les anticipations BCE/BoE en l'absence d'un outil équivalent au Fed Rate +// Monitor. Contrairement à ce dernier, il n'y a PAS de distribution de +// probabilité publiée par contrat : on déduit nous-mêmes une probabilité +// approximative (même convention bps/25 déjà utilisée pour le fallback +// InvestingLive plus bas), en mappant chaque échéance de future sur le +// calendrier réel de réunions (réunion → échéance la plus proche ≥ sa date). +// Au-delà de la dernière échéance réellement cotée, on prolonge à plat sur une +// fenêtre de grâce (FUTURES_GRACE_DAYS) ; au-delà, on s'arrête — pas de réunion +// affichée plutôt qu'une valeur inventée. +// +// Résolution intrinsèquement plus grossière que l'USD : Euribor cote ~mensuel +// (contrats vus : Jul/Aug/Sep/Oct 26), SONIA cote trimestriel (Jun/Sep 26 +// seulement, 2 contrats dispo sur investing.com) — donc plusieurs réunions +// rapprochées peuvent partager la même valeur implicite. +// +// Calendriers dupliqués depuis lib/rateprobability.ts (ce script est un .mjs +// autonome sans accès aux imports TS de l'app) — à garder synchronisés, même +// cadence de maintenance qu'indiqué là-bas (~1x/an, à la publication de +// chaque banque centrale). +const ECB_MEETINGS = [ + "2026-07-23", "2026-09-10", "2026-10-29", "2026-12-17", + "2027-02-04", "2027-03-18", "2027-04-29", "2027-06-10", + "2027-07-22", "2027-09-09", "2027-10-28", "2027-12-16", +]; +const BOE_MEETINGS = [ + "2026-07-30", "2026-09-17", "2026-11-05", "2026-12-17", + "2027-02-04", "2027-03-18", "2027-04-29", "2027-06-17", + "2027-07-29", "2027-09-16", "2027-11-04", "2027-12-16", +]; + +const FUTURES_STRIP_CONFIG = { + EUR: { overviewUrl: "https://www.investing.com/rates-bonds/euribor-futures", prefix: "FEU3c", meetings: ECB_MEETINGS }, + GBP: { overviewUrl: "https://www.investing.com/rates-bonds/three-month-sonia-futures", prefix: "SON3c", meetings: BOE_MEETINGS }, +}; +const FUTURES_GRACE_DAYS = 60; + +// La page overview liste les contrats liés dans un tableau "relative-table" — +// chaque ligne est un . On (re)découvre +// cette liste à chaque run plutôt que de figer des cid en dur : les contrats +// expirent (mensuel/trimestriel) et le cid pertinent change avec le temps. +async function fetchInvestingComContractList(overviewUrl, prefix) { + try { + const res = await fetch(overviewUrl, { headers: CHROME_HEADERS }); + if (!res.ok) { console.error(`[futures] ${overviewUrl} → ${res.status}`); return []; } + const html = await res.text(); + if (/just a moment|cf-browser-verification|enable javascript/i.test(html)) { + console.error(`[futures] Cloudflare challenge on ${overviewUrl}`); + return []; + } + const re = //g; + const seen = new Map(); + let m; + while ((m = re.exec(html)) !== null) { + const [, href, label] = m; + if (!label.startsWith(prefix) || seen.has(label)) continue; + seen.set(label, href); + } + return [...seen.entries()] + .map(([label, url]) => ({ label, url, n: parseInt(label.slice(prefix.length), 10) })) + .filter(c => Number.isFinite(c.n)) + .sort((a, b) => a.n - b.n); + } catch (e) { + console.error(`[futures] ${overviewUrl} error: ${e.message}`); + return []; + } +} + +// Chaque page contrat (?cid=NNNN) rend son propre prix + échéance server-side +// (vérifié : pas besoin d'exécuter le JS client). Convention Eurex/ICE "100 − +// taux", identique à Eurodollar/Fed Funds. +async function fetchContractQuote(url) { + try { + const res = await fetch(url, { headers: CHROME_HEADERS }); + if (!res.ok) return null; + const html = await res.text(); + const settlement = html.match(/data-test="settlement_date"[^>]*>(\d{2})\/(\d{2})\/(\d{4})]*>([\d.]+) setTimeout(r, 400)); + } + if (points.length < 2) { console.warn(`[futures/${ccy}] only ${points.length} contract(s) priced`); return null; } + points.sort((a, b) => a.dateIso.localeCompare(b.dateIso)); + + const currentRate = FALLBACK_RATES[ccy] ?? 0; + const nowIso = new Date().toISOString().slice(0, 10); + const graceIso = new Date(new Date(points.at(-1).dateIso).getTime() + FUTURES_GRACE_DAYS * 86400000) + .toISOString().slice(0, 10); + + const rows = []; + for (const meetingIso of cfg.meetings) { + if (meetingIso < nowIso) continue; + if (meetingIso > graceIso) break; // hors horizon réel couvert par les futures : on s'arrête plutôt que d'inventer + const point = points.find(p => p.dateIso >= meetingIso) ?? points.at(-1); + const impliedRate = parseFloat((100 - point.price).toFixed(4)); + const cumulBps = parseFloat(((impliedRate - currentRate) * 100).toFixed(2)); + rows.push({ + meeting: new Date(meetingIso + "T12:00:00Z").toLocaleDateString("en-US", { month: "short", day: "numeric" }), + meeting_iso: meetingIso, + implied_rate_post_meeting: impliedRate, + prob_move_pct: Math.min(100, Math.round(Math.abs(cumulBps) / 25 * 100)), + prob_is_cut: cumulBps < 0, + change_bps: cumulBps, + num_moves: parseFloat((cumulBps / 25).toFixed(3)), + }); + } + if (!rows.length) return null; + console.log(`[futures/${ccy}] ✓ ${points.length} contrats réels (${points.map(p => p.dateIso).join(", ")}) → ${rows.length} réunions`); + return { today: { midpoint: currentRate, rows } }; +} + // ── 2. InvestingLive fallback (Giuseppe Dellamotta, hebdomadaire) ───────────── const IL_CB_MAP = { @@ -440,6 +574,22 @@ for (const ccy of IC_SUPPORTED) { await new Promise(r => setTimeout(r, 600)); } +// 1bis — Investing.com futures strips (Euribor 3M / SONIA 3M) → EUR/GBP +// (voir doc détaillée plus haut, section "1bis. Investing.com futures strips") +console.log("\n=== Investing.com Futures Strips (EUR/GBP) ==="); +for (const ccy of Object.keys(FUTURES_STRIP_CONFIG)) { + let data = null; + for (let attempt = 1; attempt <= 3 && !data; attempt++) { + if (attempt > 1) { + console.log(`[futures/${ccy}] retry ${attempt}/3…`); + await new Promise(r => setTimeout(r, 2000 * attempt)); + } + data = await fetchFuturesStrip(ccy); + } + if (data) results[ccy] = data; + await new Promise(r => setTimeout(r, 600)); +} + // 2 — InvestingLive → fallback for any missing CB const missing = CCYS.filter(c => !results[c]); if (missing.length) { diff --git a/.github/workflows/fetch-atlanta-mpt.yml b/.github/workflows/fetch-atlanta-mpt.yml deleted file mode 100644 index 1c29303..0000000 --- a/.github/workflows/fetch-atlanta-mpt.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Fetch Atlanta Fed Market Probability Tracker - -on: - schedule: - - cron: '30 11 * * *' # quotidien 11h30 UTC (donnée mise à jour ~1x/jour par l'Atlanta Fed) - workflow_dispatch: # déclenchement manuel - -permissions: - contents: write - -jobs: - fetch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - persist-credentials: true - - - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Fetch Atlanta Fed MPT (options SOFR) - run: node .github/scripts/fetch-atlanta-mpt.mjs - - - name: Commit updated data (if changed) - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add data/atlanta-fed-mpt.json - git diff --staged --quiet || git commit -m "chore: update Atlanta Fed MPT data [skip ci]" - git push diff --git a/app/api/macro/route.ts b/app/api/macro/route.ts index 7f12c6e..40ec134 100644 --- a/app/api/macro/route.ts +++ b/app/api/macro/route.ts @@ -6,7 +6,6 @@ export const dynamic = "force-dynamic"; import cpiOverridesRaw from "@/data/cpi_overrides.json"; import rateDecisionsRaw from "@/data/rate_decisions.json"; import moneySupplyM3Raw from "@/data/money-supply-m3.json"; -import { getAtlantaFedMpt } from "@/lib/atlantaFedMpt"; import { fetchFFThisWeek, fetchFFEvents } from "@/lib/forexfactory"; import type { FFEvent } from "@/lib/forexfactory"; import { fetchTECoreInflation, fetchTEMoMInflation, fetchTEInflationYoY, fetchTECoreCPIMoM, fetchTECoreConsumerPricesIndex, fetchTEPPIMoM, fetchTECoreInflationPages, fetchTEInflationYoYPages, fetchTEAUDCommodityYoY, fetchTEGDPGrowthRate, fetchTEUnemploymentRate, fetchTESTIRRate, fetchTEEmploymentChange } from "@/lib/tecpi"; @@ -1427,7 +1426,6 @@ export async function GET(req: NextRequest) { const data = { currency, indicators, moneySupplyM3: getMoneySupplyM3(currency), - atlantaFedMpt: currency === "USD" ? getAtlantaFedMpt() : null, forecasts: { // CPI — TE calendar forecast (priorité) puis ForexFactory cpi: parseTeF(teCpiForecast?.cpiYoY) ?? ffForecasts.cpi ?? null, diff --git a/app/page.tsx b/app/page.tsx index 7a67e64..5ab68fe 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -49,7 +49,7 @@ export default function Dashboard() { const [globalMacroSlide, setGlobalMacroSlide] = useState<"mon"|"infl"|"cro"|"empl">("mon"); const [globalCardTab, setGlobalCardTab] = useState<"overview"|"mispricing"|"focus">("overview"); const [globalSignauxSlide, setGlobalSignauxSlide] = useState<"ois"|"cot"|"sent">("ois"); - const [globalOisChartTab, setGlobalOisChartTab] = useState<"curve"|"probas"|"meetings"|"atlanta">("curve"); + const [globalOisChartTab, setGlobalOisChartTab] = useState<"curve"|"probas"|"meetings">("curve"); const [macroSyncEnabled, setMacroSyncEnabled] = useState(false); // ── Sentiment multi-paires Myfxbook → {CCY: {longPct, shortPct, pair}} ────── diff --git a/components/CurrencyCard.tsx b/components/CurrencyCard.tsx index baf69d3..5352fac 100644 --- a/components/CurrencyCard.tsx +++ b/components/CurrencyCard.tsx @@ -16,7 +16,6 @@ import { biasLabel, calcMacroScore } from "@/lib/scoring"; import { saveCache, loadCache, formatCacheDate } from "@/lib/localCache"; import type { Currency, BiasPhase, RateExpectation, MacroSection } from "@/lib/types"; import type { CBRatePath, ILWeeklyDelta } from "@/lib/rateprobability"; -import type { AtlantaFedMpt } from "@/lib/atlantaFedMpt"; import type { SentimentEntry, CotEntry } from "@/lib/types"; import type { CalendarEvent } from "@/app/api/calendar/route"; import NarrativeButton from "./NarrativeButton"; @@ -49,7 +48,6 @@ interface MacroData { indicators: Record; forecasts?: MacroForecasts | null; moneySupplyM3?: MoneySupplyM3 | null; - atlantaFedMpt?: AtlantaFedMpt | null; fetchedAt: string; } @@ -69,8 +67,8 @@ interface Props { onCardTabChange?: (id: "overview" | "mispricing" | "focus") => void; syncSignauxSlide?: "ois" | "cot" | "sent"; onSignauxSlideChange?: (id: "ois" | "cot" | "sent") => void; - syncOisChartTab?: "curve" | "probas" | "meetings" | "atlanta"; - onOisChartTabChange?: (id: "curve" | "probas" | "meetings" | "atlanta") => void; + syncOisChartTab?: "curve" | "probas" | "meetings"; + onOisChartTabChange?: (id: "curve" | "probas" | "meetings") => void; isLoading?: boolean; } @@ -506,15 +504,14 @@ const STIR_INSTRUMENT: Partial void; - atlantaMpt?: AtlantaFedMpt | null; + syncChartTab?: "curve" | "probas" | "meetings"; + onChartTabChange?: (id: "curve" | "probas" | "meetings") => void; }) { - const [localChartTab, setLocalChartTab] = useState<"curve" | "probas" | "meetings" | "atlanta">("curve"); + const [localChartTab, setLocalChartTab] = useState<"curve" | "probas" | "meetings">("curve"); const chartTab = syncChartTab ?? localChartTab; - const setChartTab = (id: "curve" | "probas" | "meetings" | "atlanta") => { + const setChartTab = (id: "curve" | "probas" | "meetings") => { setLocalChartTab(id); onChartTabChange?.(id); }; @@ -626,7 +623,6 @@ function OISEnhancedBlock({ ratePath, syncChartTab, onChartTabChange, atlantaMpt { id: "curve" as const, label: "Courbe" }, { id: "probas" as const, label: "Probabilités" }, { id: "meetings" as const, label: "Réunions" }, - ...(ratePath.currency === "USD" && atlantaMpt ? [{ id: "atlanta" as const, label: "Atlanta Fed" }] : []), ]).map(t => (