<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>巨无霸指数 (Big Mac Index) 经济学看板</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root { --primary: #d32f2f; --bg: #f8f9fa; --card: #ffffff; --text: #333; --border: #e9ecef; }
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; padding: 20px; margin: 0; }
.container { max-width: 1000px; margin: 0 auto; }
/* 头部与状态 */
header { text-align: center; margin-bottom: 30px; padding: 20px 0; }
h1 { color: var(--primary); margin-bottom: 5px; font-size: 2em; }
.status-bar { font-size: 12px; color: #666; background: #eee; display: inline-block; padding: 4px 12px; border-radius: 12px; margin-top: 8px;}
/* 知识卡片 */
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; }
.card { background: var(--card); padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.04); border-left: 4px solid var(--primary); }
.card h3 { margin-top: 0; color: var(--primary); font-size: 1.1em; }
.card p, .card ul { font-size: 14px; color: #555; margin-bottom: 8px; }
.formula-box { background: #fff5f5; padding: 10px; border-radius: 4px; font-family: monospace; font-size: 14px; margin: 8px 0; color: #b71c1c; border: 1px dashed #ffcdd2;}
/* 图表与表格 */
.chart-container { position: relative; height: 350px; width: 100%; margin-bottom: 30px; background: var(--card); padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.04);}
table { width: 100%; border-collapse: collapse; background: var(--card); border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.04); }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid var(--border); font-size: 14px; }
th { background-color: #f1f3f5; font-weight: bold; color: #495057; }
.overvalued { color: #d32f2f; font-weight: bold; }
.undervalued { color: #388e3c; font-weight: bold; }
.loading { text-align: center; padding: 40px; color: #888; }
/* 响应式 */
@media (max-width: 768px) { .info-grid { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<div class="container">
<header>
<h1>🍔 巨无霸指数 (Big Mac Index)</h1>
<div class="status-bar" id="status">⏳ 正在初始化...</div>
</header>
<!-- 1. 背景知识与计算说明 -->
<div class="info-grid">
<div class="card">
<h3>📖 什么是巨无霸指数?</h3>
<p>巨无霸指数由英国《经济学人》杂志于 <strong>1986年</strong> 推出,是一个非正式的经济指数。</p>
<p>它的核心理论基础是 <strong>购买力平价(PPP)</strong>:在理想状态下,同一种商品在世界各地的价格应该是一样的。</p>
<p>麦当劳的巨无霸汉堡因为在全球拥有高度标准化的配方和制作工艺,成为了衡量各国货币实际购买力的完美“标尺”。</p>
</div>
<div class="card">
<h3>🧮 核心计算逻辑</h3>
<p><strong>1. 隐含汇率 (Implied Rate)</strong></p>
<div class="formula-box">隐含汇率 = 当地巨无霸价格 ÷ 美国巨无霸价格</div>
<p><strong>2. 估值偏差 (Deviation)</strong></p>
<div class="formula-box">偏差% = (隐含汇率 - 实际汇率) ÷ 实际汇率 × 100%</div>
<p style="font-size:12px; color:#777; margin-bottom:0;">* 偏差为正数表示货币被<strong>高估</strong>,为负数表示被<strong>低估</strong>。</p>
</div>
</div>
<!-- 2. 可视化图表 -->
<div class="chart-container">
<canvas id="bigMacChart"></canvas>
</div>
<!-- 3. 详细数据列表 -->
<table>
<thead>
<tr>
<th>国家/货币</th>
<th>当地售价</th>
<th>实际汇率 (1 USD = ?)</th>
<th>隐含汇率 (1 USD = ?)</th>
<th>估值状态</th>
</tr>
</thead>
<tbody id="tableBody">
<tr><td colspan="5" class="loading">等待数据加载...</td></tr>
</tbody>
</table>
</div>
<script>
// 1. 巨无霸价格数据 (基于 The Economist 公开数据整理,单位:当地货币)
const BIG_MAC_PRICES = {
'USD': 5.69, 'CNY': 24.40, 'JPY': 450, 'EUR': 4.90, 'GBP': 4.20, 'KRW': 5500
};
// 2. 免费汇率 API
const EXCHANGE_API_URL = 'https://api.exchangerate-api.com/v4/latest/USD';
let chartInstance = null; // 用于存储图表实例,防止重复渲染
// 获取汇率数据
async function fetchExchangeRates() {
try {
const response = await fetch(EXCHANGE_API_URL);
if (!response.ok) throw new Error('API 请求失败');
const data = await response.json();
return data.rates;
} catch (error) {
console.error('获取汇率失败:', error);
document.getElementById('status').innerText = '⚠️ 汇率获取失败,请检查网络';
return null;
}
}
// 核心计算与渲染逻辑
function calculateAndRender(rates) {
const chartLabels = [];
const chartValues = [];
const tbody = document.getElementById('tableBody');
tbody.innerHTML = '';
const usdPrice = BIG_MAC_PRICES['USD'];
for (const [currency, localPrice] of Object.entries(BIG_MAC_PRICES)) {
const actualRate = rates[currency];
if (!actualRate) continue;
// 【核心计算】隐含汇率:当地价格 / 美国价格
const impliedRate = localPrice / usdPrice;
// 【核心计算】估值偏差百分比
const deviation = currency === 'USD' ? 0 : ((impliedRate - actualRate) / actualRate) * 100;
const deviationFixed = parseFloat(deviation.toFixed(2));
chartLabels.push(currency);
chartValues.push(deviationFixed);
// 渲染表格行
const row = `<tr>
<td><strong>${currency}</strong></td>
<td>${localPrice} ${currency}</td>
<td>${actualRate.toFixed(4)}</td>
<td>${impliedRate.toFixed(4)}</td>
<td class="${deviationFixed > 0 ? 'overvalued' : (deviationFixed < 0 ? 'undervalued' : '')}">
${deviationFixed > 0 ? '📈 高估' : (deviationFixed < 0 ? '📉 低估' : '基准')}
${Math.abs(deviationFixed)}%
</td>
</tr>`;
tbody.innerHTML += row;
}
// 渲染/更新图表
const ctx = document.getElementById('bigMacChart').getContext('2d');
if (chartInstance) chartInstance.destroy(); // 销毁旧图表
chartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: chartLabels,
datasets: [{
label: '货币估值偏差 (%)',
data: chartValues,
backgroundColor: chartValues.map(v => v >= 0 ? 'rgba(211, 47, 47, 0.7)' : 'rgba(56, 142, 60, 0.7)'),
borderColor: chartValues.map(v => v >= 0 ? 'rgba(211, 47, 47, 1)' : 'rgba(56, 142, 60, 1)'),
borderWidth: 1
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: {
legend: { display: false },
title: { display: true, text: '各国货币估值偏差 (基于巨无霸指数)', font: { size: 16 } }
},
scales: {
y: {
beginAtZero: true,
title: { display: true, text: '高估 (+) / 低估 (-) 百分比' },
grid: { color: '#f1f1f1' }
},
x: { grid: { display: false } }
}
}
});
const now = new Date().toLocaleTimeString();
document.getElementById('status').innerText = `✅ 数据已更新 (${now}) | 下次自动刷新: 1小时后 | 巨无霸价格周期: 2024`;
}
// 初始化与定时刷新 (每 1 小时 = 3600000 毫秒)
window.onload = async () => {
const rates = await fetchExchangeRates();
if (rates) calculateAndRender(rates);
setInterval(async () => {
console.log('⏰ 触发定时汇率刷新...');
const newRates = await fetchExchangeRates();
if (newRates) calculateAndRender(newRates);
}, 3600000);
};
</script>
</body>
</html>