mirror of
https://github.com/caty21/forex-dashboard.git
synced 2026-07-27 20:37:45 +00:00
0657b50c07
Central bank governance (nouvel onglet Banques centrales) : - lib/centralBankGovernance.ts + app/api/central-bank-sources : scraping live du vote de la derniere reunion, dot plot Fed (SEP), et desormais les previsions macro (PIB + inflation) publiees par chaque BC elle-meme (Fed SEP, Eurosystem staff projections, BoJ Outlook Report PDF, SNB conditional forecast, BoC MPR, RBA SMP). GBP/NZD laisses honnetement vides quand aucune source chiffree fiable n'est accessible (RBNZ bloque par Cloudflare). - components/CentralBankSourcesTab.tsx : nouvel onglet avec cards par banque. Taux directeurs + Money Supply M3 : - data/rate_decisions.json corrige (JPY, EUR, NZD etc. etaient perimes d'1-2 decisions) et desormais auto-maintenu : .github/workflows/update-rate-decisions.yml (horaire) detecte les changements de taux via Trading Economics et fait glisser current -> prev sans perte de donnee. - data/money-supply-m3.json (nouveau) + .github/workflows/fetch-money-supply.yml (hebdo) : M3 par devise (proxy M2 pour l'USD, la Fed ne publiant plus M3 depuis 2006), affiche dans CurrencyCard. Calendrier economique elargi : - lib/calendar-countries.ts, lib/calendar-taxonomy.ts, lib/fxstreetCalendar.ts : couverture pays elargie + classification des evenements + source FXStreet. Fix InvestingLive : - lib/investinglive.ts : l'ancienne API WordPress (wp-json) renvoyait 404 depuis leur migration Nuxt.js -> reecrit vers api.investinglive.com/api/homepage/articles, + fix crash silencieux (Tldr pas toujours un tableau). Divers : - .vercel/ ajoute au .gitignore (ne doit jamais etre commite, cf. son propre README). - scripts/ (lancement PWA, push env Vercel), captures d'ecran, cache InvestingLive. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
100 lines
4.9 KiB
JavaScript
100 lines
4.9 KiB
JavaScript
// Scrape Trading Economics pour la masse monétaire M3 (niveau, devise locale)
|
|
// de chaque zone monétaire suivie par le dashboard.
|
|
//
|
|
// Source : meta description de https://tradingeconomics.com/{slug}/money-supply-m3
|
|
// Format observé : "Money Supply M3 in/In the {Country} increased/decreased to
|
|
// {value} {CCY} {Million|Billion} in {Month} from {value} {CCY} {Million|Billion}
|
|
// in {Month} of {Year}."
|
|
//
|
|
// USD : la Fed a arrêté de publier M3 en 2006 → proxy M2
|
|
// (https://tradingeconomics.com/united-states/money-supply-m2, même format de meta desc).
|
|
//
|
|
// Cadence : ces données sont mensuelles avec ~4-6 semaines de retard (le mois le
|
|
// plus récent apparaît des semaines après sa clôture) → un scraping hebdomadaire
|
|
// suffit largement, pas besoin d'une fréquence horaire comme pour les taux/OIS.
|
|
|
|
import { writeFileSync, readFileSync, mkdirSync } from "fs";
|
|
|
|
const CHROME_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",
|
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
"Cache-Control": "no-cache",
|
|
"Pragma": "no-cache",
|
|
};
|
|
|
|
const MONTHS = { January:"01",February:"02",March:"03",April:"04",May:"05",June:"06",
|
|
July:"07",August:"08",September:"09",October:"10",November:"11",December:"12" };
|
|
|
|
// slug TE + éventuel slug d'indicateur différent (USD → money-supply-m2, proxy)
|
|
const SERIES = {
|
|
USD: { slug: "united-states", indicator: "money-supply-m2", isProxy: true, proxyLabel: "M2 (M3 non publié par la Fed depuis 2006)" },
|
|
EUR: { slug: "euro-area", indicator: "money-supply-m3", isProxy: false },
|
|
GBP: { slug: "united-kingdom", indicator: "money-supply-m3", isProxy: false },
|
|
JPY: { slug: "japan", indicator: "money-supply-m3", isProxy: false },
|
|
CHF: { slug: "switzerland", indicator: "money-supply-m3", isProxy: false },
|
|
CAD: { slug: "canada", indicator: "money-supply-m3", isProxy: false },
|
|
AUD: { slug: "australia", indicator: "money-supply-m3", isProxy: false },
|
|
NZD: { slug: "new-zealand", indicator: "money-supply-m3", isProxy: false },
|
|
};
|
|
|
|
const DESC_RE = /Money Supply M[23][^.]*?\bto\s+([\d.]+)\s+([A-Z]{3})\s+(Million|Billion)\s+in\s+(\w+)\s+from\s+[\d.]+\s+[A-Z]{3}\s+(?:Million|Billion)\s+in\s+\w+\s+of\s+(\d{4})/i;
|
|
|
|
async function fetchMoneySupply(ccy, { slug, indicator }) {
|
|
try {
|
|
const res = await fetch(`https://tradingeconomics.com/${slug}/${indicator}`, { headers: CHROME_HEADERS });
|
|
if (!res.ok) return null;
|
|
const html = await res.text();
|
|
const meta = html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i)
|
|
?? html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*name=["']description["']/i);
|
|
if (!meta) return null;
|
|
|
|
const m = meta[1].match(DESC_RE);
|
|
if (!m) return null;
|
|
const [, value, ccyCode, unitWord, month, year] = m;
|
|
const monthNum = MONTHS[month];
|
|
if (!monthNum) return null;
|
|
|
|
return {
|
|
value: parseFloat(value),
|
|
unit: `${ccyCode} ${unitWord === "Billion" ? "Bn" : "Mn"}`,
|
|
period: `${year}-${monthNum}`,
|
|
source: `https://tradingeconomics.com/${slug}/${indicator}`,
|
|
};
|
|
} catch { return null; }
|
|
}
|
|
|
|
// ── Fusionne avec le fichier existant : garde l'ancienne valeur si le scrape échoue ──
|
|
|
|
let existing = {};
|
|
try {
|
|
existing = JSON.parse(readFileSync("data/money-supply-m3.json", "utf8"))[0]?.series ?? {};
|
|
} catch {}
|
|
|
|
const series = {};
|
|
for (const [ccy, cfg] of Object.entries(SERIES)) {
|
|
const fetched = await fetchMoneySupply(ccy, cfg);
|
|
if (fetched) {
|
|
series[ccy] = { ...fetched, isProxy: cfg.isProxy, ...(cfg.proxyLabel ? { proxyLabel: cfg.proxyLabel } : {}) };
|
|
console.log(`[TE] ${ccy} ✓ ${fetched.value} ${fetched.unit} (${fetched.period})`);
|
|
} else if (existing[ccy]) {
|
|
series[ccy] = existing[ccy];
|
|
console.log(`[TE] ${ccy} ✗ scrape échoué → conservé (${existing[ccy].period})`);
|
|
} else {
|
|
console.log(`[TE] ${ccy} ✗ scrape échoué, pas de fallback disponible`);
|
|
}
|
|
await new Promise(r => setTimeout(r, 800)); // évite le rate-limiting TE
|
|
}
|
|
|
|
mkdirSync("data", { recursive: true });
|
|
writeFileSync("data/money-supply-m3.json", JSON.stringify([{
|
|
updated_at: new Date().toISOString().slice(0, 10),
|
|
note: "Masse monétaire M3 (niveau, devise locale) par zone — scrapé automatiquement depuis tradingeconomics.com (meta description des pages /money-supply-m3). USD : proxy M2 (Fed n'a plus publié M3 depuis 2006). Mis à jour par .github/workflows/fetch-money-supply.yml (hebdomadaire).",
|
|
series,
|
|
}], null, 2) + "\n");
|
|
|
|
const ok = Object.keys(SERIES).filter(c => series[c]);
|
|
const failed = Object.keys(SERIES).filter(c => !series[c]);
|
|
console.log(`\n✓ Saved : ${ok.join(", ")}`);
|
|
if (failed.length) console.log(`✗ Failed : ${failed.join(", ")}`);
|