auto: sync 2026-06-02 00:03

This commit is contained in:
Capucine Gest
2026-06-02 00:03:37 +02:00
parent ff2fcc9619
commit 52d531182d
4 changed files with 66 additions and 5 deletions
+21 -2
View File
@@ -6,6 +6,7 @@ import rateDecisionsRaw from "@/data/rate_decisions.json";
import { fetchFFThisWeek, fetchFFEvents } from "@/lib/forexfactory";
import type { FFEvent } from "@/lib/forexfactory";
import { fetchTECoreInflation, fetchTEMoMInflation } from "@/lib/tecpi";
import { fetchTEInflationForecasts } from "@/lib/tradingeconomics";
const FRED_BASE = "https://api.stlouisfed.org/fred/series/observations";
const REVALIDATE = 86400; // cache 24h
@@ -605,6 +606,13 @@ function toPmiIndicator(raw: { value: number | null; prev: number | null }): Ind
};
}
// ── Parser forecast string TE ("2.8%" ou "0.4") → number ─────────────────────
function parseTeF(s: string | null | undefined): number | null {
if (!s) return null;
const n = parseFloat(s.replace("%", "").trim());
return isNaN(n) ? null : n;
}
// ── Server-side cache ─────────────────────────────────────────────────────────
const _cache = new Map<string, { data: unknown; ts: number }>();
@@ -948,13 +956,20 @@ export async function GET(req: NextRequest) {
}
// ── PMI (Mfg + Services + Composite) + consensus FF ──────────────────────────
const [ffPMI, pmiMfgRaw, pmiSvcRaw, pmiCompositeRaw, ffForecasts] = await Promise.all([
const toDateObj = new Date();
toDateObj.setDate(toDateObj.getDate() + 21);
const toDate = toDateObj.toISOString().slice(0, 10);
const [ffPMI, pmiMfgRaw, pmiSvcRaw, pmiCompositeRaw, ffForecasts, teForecastMap] = await Promise.all([
fetchFFPMI(currency),
scrapePMI(currency, "manufacturing-pmi"),
scrapePMI(currency, "services-pmi"),
scrapePMI(currency, "composite-pmi"),
fetchFFForecasts(currency),
fetchTEInflationForecasts(today, toDate),
]);
const teCpiForecast = teForecastMap[currency];
// FF en priorité (forecast + actual) ; TE en fallback
indicators.pmiMfg = ffPMI.mfg ? toPmiIndicator(ffPMI.mfg) : toPmiIndicator(pmiMfgRaw);
indicators.pmiServices = ffPMI.svc ? toPmiIndicator(ffPMI.svc) : toPmiIndicator(pmiSvcRaw);
@@ -1047,7 +1062,11 @@ export async function GET(req: NextRequest) {
const data = {
currency, indicators,
forecasts: {
cpi: ffForecasts.cpi,
// CPI — TE calendar forecast (priorité) puis ForexFactory
// Les forecasts TE sont des strings "2.8%" → parseFloat les convertit en number
cpi: parseTeF(teCpiForecast?.cpiYoY) ?? ffForecasts.cpi ?? null,
cpiCore: parseTeF(teCpiForecast?.cpiCore) ?? null,
cpiMoM: parseTeF(teCpiForecast?.cpiMoM) ?? null,
cpiSurprise: ffForecasts.cpiSurprise,
unemployment: ffForecasts.unemployment,
unemploymentSurprise: ffForecasts.unemploymentSurprise,
+4 -2
View File
@@ -13,6 +13,8 @@ import NarrativeButton from "./NarrativeButton";
interface Ind { value: number | null; prev: number | null; surprise: number | null; trend: "up"|"down"|"flat"|null; lastUpdated: string | null }
interface MacroForecasts {
cpi: number | null; cpiSurprise: number | null;
cpiCore: number | null;
cpiMoM: number | null;
unemployment: number | null; unemploymentSurprise: number | null;
pmiMfg: number | null; pmiMfgSurprise: number | null;
pmiSvc: number | null; pmiSvcSurprise: number | null;
@@ -356,8 +358,8 @@ export default function CurrencyCard({ currency, expectations, yields, sentiment
{/* ── INFLATION ───────────────────────────────────────────────────── */}
<SectionHeader label="Inflation" />
<Row label="CPI Core YoY" ind={inds?.cpiCore ?? null} unit="%" consensus={fc?.cpi ?? null} surpriseVsCons={fc?.cpiSurprise ?? null} />
<Row label="CPI MoM" ind={inds?.cpiMoM ?? null} unit="%" />
<Row label="CPI Core YoY" ind={inds?.cpiCore ?? null} unit="%" consensus={fc?.cpiCore ?? fc?.cpi ?? null} surpriseVsCons={fc?.cpiSurprise ?? null} />
<Row label="CPI MoM" ind={inds?.cpiMoM ?? null} unit="%" consensus={fc?.cpiMoM ?? null} />
{/* ── CROISSANCE ──────────────────────────────────────────────────── */}
<SectionHeader label="Croissance" />
+40
View File
@@ -215,6 +215,46 @@ function parseCalendarHTML(html: string): TECalendarEvent[] {
return events;
}
// ── Forecasts CPI depuis le calendrier TE ────────────────────────────────────
// Lit les events inflation futurs du calendrier TE et retourne les forecasts
// par devise pour cpiCore YoY, cpiMoM et cpiHeadline YoY.
// Utilisé par la macro route pour remplir forecasts.cpiCore / forecasts.cpiMoM.
export interface TEInflationForecasts {
cpiYoY: string | null;
cpiCore: string | null;
cpiMoM: string | null;
}
export async function fetchTEInflationForecasts(
fromDate?: string,
toDate?: string,
): Promise<Partial<Record<Currency, TEInflationForecasts>>> {
const events = await fetchTECalendarHTML(fromDate, toDate);
const result: Partial<Record<Currency, TEInflationForecasts>> = {};
const now = fromDate ? new Date(fromDate + "T00:00:00Z") : new Date();
for (const ev of events) {
if (ev.category !== "inflation") continue;
if (new Date(ev.date) <= now) continue;
if (!ev.forecast) continue;
const ccy = ev.currency;
const title = ev.title.toLowerCase();
if (!result[ccy]) result[ccy] = { cpiYoY: null, cpiCore: null, cpiMoM: null };
const r = result[ccy]!;
if (!r.cpiCore && /core.*inflation.*yoy|core.*cpi.*yoy|core.*rate.*yoy/i.test(title)) {
r.cpiCore = ev.forecast;
} else if (!r.cpiMoM && /inflation.*mom|cpi.*mom|rate.*mom/i.test(title)) {
r.cpiMoM = ev.forecast;
} else if (!r.cpiYoY && /inflation.*yoy|cpi.*yoy|inflation\s+rate.*yoy/i.test(title)) {
r.cpiYoY = ev.forecast;
}
}
return result;
}
// ── Paid API (when TRADING_ECONOMICS_API_KEY is set) ─────────────────────────
export async function fetchTECalendar(
+1 -1
View File
File diff suppressed because one or more lines are too long