stats: /stats overview, economy, energy and Gouda pages; CBS series map; nav, homepage, /start and sitemap links
Deploy production / deploy (push) Successful in 1m10s

- CBS_SERIES: CPI, household tariffs (2007-now), energy balance, renewables, heat pumps,
  solar, cars, population (monthly, since 1942, components), house prices, housing stock,
  municipal levies for Gouda and its neighbours
- shared StatChart/Kpi/chart helpers with per-chart source attribution
- drop Eurostat nrg_pc_20x_h (404; CBS covers that history)
This commit is contained in:
stan committed 2026-10-25 20:50:47 +00:00
1 parent 6dcbcfee25
commit 76bfb3dcec
24 files changed
+3386 -9

No files matched your search

+406
View File
@@ -0,0 +1,406 @@
<script lang="ts">
import StatChart from '#lib/stats/StatChart.svelte';
import Kpi from '#lib/stats/Kpi.svelte';
import { barRankOption, latest, lineOption, spread, yoy } from '#lib/stats/chart.js';
import { num, period } from '#lib/stats/format.js';
import { COUNTRY } from '#lib/stats/regions.js';
let { data } = $props();
const s = $derived(data.series);
const a = $derived(data.attributions);
const pts = (id: string) => s[id]?.points ?? [];
const RATES = [
['US', 'Fed', '#3b82f6'],
['XM', 'ECB', '#eab308'],
['KR', 'Bank of Korea', '#e11d48'],
['GB', 'Bank of England', '#a855f7'],
['JP', 'Bank of Japan', '#10b981'],
['CN', 'PBoC', '#f97316']
] as const;
const usCpi = $derived(yoy(pts('fred:CPIAUCSL')).filter(([d]) => d >= '1997-01-01'));
const hicp = (g: string) => pts(`eurostat:prc_hicp_minr:RCH_A.TOTAL.${g}`);
const y = (g: string) => pts(`eurostat:irt_lt_mcby_m:MCBY.${g}`);
const frde = $derived(spread(y('FR'), y('DE')));
const itde = $derived(spread(y('IT'), y('DE')));
const nlde = $derived(spread(y('NL'), y('DE')));
const us10 = $derived(pts('fred:DGS10'));
const curve = $derived(spread(us10, pts('fred:DGS2')));
const EU_LIST = ['NL', 'DE', 'FR', 'BE', 'IT', 'ES', 'PL', 'SE', 'DK', 'AT', 'EU27_2020', 'EA20'];
const lastOf = (id: string) => latest(s[id]?.points);
const lastYear = (prefix: string) =>
EU_LIST.map((g) => ({ g, p: lastOf(`${prefix}.${g}`) }))
.filter((r) => r.p)
.map((r) => ({
label: COUNTRY[r.g] ?? r.g,
value: r.p![1],
highlight: r.g === 'NL',
year: r.p![0].slice(0, 4)
}));
const debt = $derived(lastYear('eurostat:gov_10dd_edpt1:PC_GDP.S13.GD'));
const deficit = $derived(lastYear('eurostat:gov_10dd_edpt1:PC_GDP.S13.B9'));
const migrationPerK = $derived(
EU_LIST.filter((g) => !g.startsWith('E'))
.map((g) => {
const m = lastOf(`eurostat:migr_imm1ctz:TOTAL.COMPLET.TOTAL.NR.T.${g}`);
const pop = pts(`eurostat:demo_pjan:NR.TOTAL.T.${g}`).find(([d]) => m && d === m[0]);
return m && pop
? { label: COUNTRY[g] ?? g, value: (m[1] / pop[1]) * 1000, highlight: g === 'NL' }
: null;
})
.filter((r) => r != null)
);
const k = (id: string) => lastOf(id);
const fed = $derived(k('bis:M:US'));
const ecb = $derived(k('bis:M:XM'));
const bok = $derived(k('bis:M:KR'));
const nlInfl = $derived(k('eurostat:prc_hicp_minr:RCH_A.TOTAL.NL'));
const eaInfl = $derived(k('eurostat:prc_hicp_minr:RCH_A.TOTAL.EA20'));
const usInfl = $derived(latest(usCpi));
const spreadNow = $derived(latest(frde));
const us10Now = $derived(latest(us10));
</script>
<svelte:head>
<title>economy · stats · Stan Runge</title>
<meta
name="description"
content="Interest rates, inflation, bond spreads, stock indices, GDP, public debt and migration: the Netherlands, Europe, the US and Korea."
/>
</svelte:head>
<section class="stats-intro">
<span class="stats-eyebrow">stats / economy</span>
<h1>Economy</h1>
<p>
Central bank rates, inflation, 10-year bond yields and spreads, stock markets, growth, public
finances and migration. The Netherlands next to Europe, the US and South Korea, refreshed daily
from open sources.
</p>
</section>
<div class="kpis">
<Kpi
label="Fed policy rate"
value={num(fed?.[1], 2)}
unit="%"
sub={period(fed?.[0])}
color="#3b82f6"
/>
<Kpi
label="ECB policy rate"
value={num(ecb?.[1], 2)}
unit="%"
sub={period(ecb?.[0])}
color="#eab308"
/>
<Kpi
label="Bank of Korea"
value={num(bok?.[1], 2)}
unit="%"
sub={period(bok?.[0])}
color="#e11d48"
/>
<Kpi
label="NL inflation (HICP)"
value={num(nlInfl?.[1])}
unit="%"
sub={period(nlInfl?.[0])}
color="#f97316"
/>
<Kpi
label="Euro area inflation"
value={num(eaInfl?.[1])}
unit="%"
sub={period(eaInfl?.[0])}
color="#10b981"
/>
<Kpi
label="US inflation (CPI)"
value={num(usInfl?.[1])}
unit="%"
sub={period(usInfl?.[0])}
color="#06b6d4"
/>
<Kpi
label="France–Germany 10y spread"
value={num(spreadNow ? spreadNow[1] * 100 : null, 0)}
unit="bp"
sub={period(spreadNow?.[0])}
color="#a855f7"
/>
<Kpi
label="US 10-year yield"
value={num(us10Now?.[1], 2)}
unit="%"
sub={period(us10Now?.[0])}
color="#64748b"
/>
</div>
<h2 class="stats-section-title">Rates and inflation</h2>
<div class="stats-grid">
<StatChart
title="Central bank policy rates"
subtitle="% · monthly"
sources={['bis']}
attributions={a}
option={(t) =>
lineOption(
t,
RATES.map(([c, name, color]) => ({ name, color, kind: 'step', points: pts(`bis:M:${c}`) })),
{ unit: '%', zoom: true }
)}
/>
<StatChart
title="Inflation"
subtitle="% year on year · HICP (Europe), CPI-U (US)"
sources={['eurostat', 'fred']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{ name: 'Netherlands', points: hicp('NL'), color: '#f97316' },
{ name: 'Euro area', points: hicp('EA20'), color: '#10b981' },
{ name: 'Germany', points: hicp('DE'), color: '#64748b' },
{ name: 'United States', points: usCpi, color: '#3b82f6' }
],
{ unit: '%', zoom: true }
)}
/>
</div>
<h2 class="stats-section-title">Bond markets</h2>
<div class="stats-grid">
<StatChart
title="10-year government bond yields"
subtitle="% · monthly (EMU convergence criterion yields; US Treasury, Korea via OECD)"
sources={['eurostat', 'fred']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{ name: 'Netherlands', points: y('NL'), color: '#f97316' },
{ name: 'Germany', points: y('DE'), color: '#64748b' },
{ name: 'France', points: y('FR'), color: '#3b82f6' },
{ name: 'Italy', points: y('IT'), color: '#10b981' },
{ name: 'Spain', points: y('ES'), color: '#eab308' },
{ name: 'United States', points: us10, color: '#a855f7' },
{ name: 'South Korea', points: pts('fred:IRLTLT01KRM156N'), color: '#e11d48' }
],
{ unit: '%', zoom: true }
)}
/>
<StatChart
title="Spreads over Germany"
subtitle="percentage points · 10-year yield minus the German Bund"
sources={['eurostat']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{ name: 'France − Germany', points: frde, color: '#3b82f6', kind: 'area' },
{ name: 'Italy − Germany', points: itde, color: '#10b981' },
{ name: 'Netherlands − Germany', points: nlde, color: '#f97316' }
],
{ unit: 'pp', zoom: true }
)}
/>
<StatChart
title="US yield curve: 10-year minus 2-year"
subtitle="percentage points · monthly average · below zero = inverted"
sources={['fred']}
attributions={a}
option={(t) =>
lineOption(t, [{ name: '10y − 2y', points: curve, color: '#a855f7', kind: 'area' }], {
unit: 'pp',
zoom: true,
legend: false
})}
/>
<StatChart
title="Share prices"
subtitle="index 2015 = 100 · OECD main economic indicators"
sources={['fred']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{ name: 'Netherlands', points: pts('fred:SPASTT01NLM661N'), color: '#f97316' },
{ name: 'United States', points: pts('fred:SPASTT01USM661N'), color: '#3b82f6' },
{ name: 'South Korea', points: pts('fred:SPASTT01KRM661N'), color: '#e11d48' },
{ name: 'China', points: pts('fred:SPASTT01CNM661N'), color: '#eab308' }
],
{ zoom: true }
)}
/>
<StatChart
title="S&P 500 and NASDAQ Composite"
subtitle="index points · weekly average"
sources={['fred']}
attributions={a}
wide
option={(t) =>
lineOption(
t,
[
{ name: 'S&P 500', points: pts('fred:SP500'), color: '#3b82f6' },
{ name: 'NASDAQ Composite', points: pts('fred:NASDAQCOM'), color: '#10b981', right: true }
],
{ zoom: true, unit: 'S&P', rightUnit: 'NASDAQ' }
)}
/>
</div>
<h2 class="stats-section-title">Growth and jobs</h2>
<div class="stats-grid">
<StatChart
title="Real GDP growth"
subtitle="% change on the previous year"
sources={['eurostat']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{
name: 'Netherlands',
points: pts('eurostat:nama_10_gdp:CLV_PCH_PRE.B1GQ.NL'),
color: '#f97316',
kind: 'bar'
},
{
name: 'EU 27',
points: pts('eurostat:nama_10_gdp:CLV_PCH_PRE.B1GQ.EU27_2020'),
color: '#3b82f6'
},
{
name: 'Germany',
points: pts('eurostat:nama_10_gdp:CLV_PCH_PRE.B1GQ.DE'),
color: '#64748b'
}
],
{ unit: '%' }
)}
/>
<StatChart
title="US real GDP and unemployment"
subtitle="billions of chained 2017 USD (SAAR) · unemployment %"
sources={['fred']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{ name: 'Real GDP', points: pts('fred:GDPC1'), color: '#3b82f6', kind: 'area' },
{ name: 'Unemployment rate', points: pts('fred:UNRATE'), color: '#e11d48', right: true }
],
{ unit: 'USD bn', rightUnit: '%', zoom: true }
)}
/>
</div>
<h2 class="stats-section-title">Public finances and migration</h2>
<div class="stats-grid">
<StatChart
title="Government debt"
subtitle={`% of GDP · ${debt[0]?.year ?? ''}`}
sources={['eurostat']}
attributions={a}
height="360px"
option={(t) => barRankOption(t, debt, { unit: '% of GDP', color: '#a855f7' })}
/>
<StatChart
title="Government balance"
subtitle={`surplus (+) or deficit (−), % of GDP · ${deficit[0]?.year ?? ''}`}
sources={['eurostat']}
attributions={a}
height="360px"
option={(t) => barRankOption(t, deficit, { unit: '% of GDP', color: '#10b981' })}
/>
<StatChart
title="Dutch government: spending vs revenue"
subtitle="% of GDP · general government"
sources={['eurostat']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{
name: 'Spending',
points: pts('eurostat:gov_10a_main:PC_GDP.S13.TE.NL'),
color: '#e11d48'
},
{
name: 'Revenue',
points: pts('eurostat:gov_10a_main:PC_GDP.S13.TR.NL'),
color: '#10b981'
},
{
name: 'EU 27 spending',
points: pts('eurostat:gov_10a_main:PC_GDP.S13.TE.EU27_2020'),
color: '#e11d48',
dashed: true
},
{
name: 'EU 27 revenue',
points: pts('eurostat:gov_10a_main:PC_GDP.S13.TR.EU27_2020'),
color: '#10b981',
dashed: true
}
],
{ unit: '% of GDP', min: 'dataMin' }
)}
/>
<StatChart
title="Immigration"
subtitle="people arriving per year"
sources={['eurostat']}
attributions={a}
option={(t) =>
lineOption(
t,
[
{
name: 'Netherlands',
points: pts('eurostat:migr_imm1ctz:TOTAL.COMPLET.TOTAL.NR.T.NL'),
color: '#f97316',
kind: 'area'
},
{
name: 'Belgium',
points: pts('eurostat:migr_imm1ctz:TOTAL.COMPLET.TOTAL.NR.T.BE'),
color: '#eab308'
},
{
name: 'Sweden',
points: pts('eurostat:migr_imm1ctz:TOTAL.COMPLET.TOTAL.NR.T.SE'),
color: '#3b82f6'
},
{
name: 'Austria',
points: pts('eurostat:migr_imm1ctz:TOTAL.COMPLET.TOTAL.NR.T.AT'),
color: '#e11d48'
}
],
{ decimals: 0 }
)}
/>
<StatChart
title="Immigration per 1,000 inhabitants"
subtitle="latest year"
sources={['eurostat']}
attributions={a}
height="340px"
option={(t) => barRankOption(t, migrationPerK, { unit: 'per 1,000', color: '#06b6d4' })}
/>
</div>
<p class="stats-note">
Policy rates: BIS central bank policy rates (month end). Yields for the euro countries are
Eurostat's EMU convergence-criterion series.
</p>