Files
personal-site/static/components/eq.html
T
2026-08-28 13:14:15 +02:00

162 lines
6.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@300;400;500&display=swap');
.eq-wrap { font-family: 'IBM Plex Sans', monospace; padding: 2rem 0 1rem; }
.eq-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 0.25rem; }
.eq-title { font-family: 'IBM Plex Mono', monospace; font-size: 11px; font-weight: 500; letter-spacing: 0.12em; color: var(--color-text-secondary); text-transform: uppercase; }
.eq-ytd { font-family: 'IBM Plex Mono', monospace; font-size: 22px; font-weight: 500; color: var(--color-text-primary); margin: 0.1rem 0 1.5rem; }
.eq-ytd span { font-size: 13px; font-weight: 400; color: #1D9E75; margin-left: 8px; }
.eq-divider { border: none; border-top: 0.5px solid var(--color-border-tertiary); margin-bottom: 1.5rem; }
.eq-legend { display: flex; gap: 20px; margin-bottom: 0.75rem; font-size: 12px; font-family: 'IBM Plex Mono', monospace; color: var(--color-text-secondary); align-items: center; }
.eq-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 1.5rem; }
.eq-stat { background: var(--color-background-secondary); border-radius: var(--border-radius-md); padding: 0.75rem 1rem; }
.eq-stat-label { font-size: 11px; font-family: 'IBM Plex Mono', monospace; color: var(--color-text-secondary); letter-spacing: 0.05em; margin-bottom: 4px; }
.eq-stat-val { font-size: 16px; font-weight: 500; font-family: 'IBM Plex Mono', monospace; }
.eq-stat-val.pos { color: #1D9E75; }
.eq-stat-val.neg { color: #E24B4A; }
.eq-note { font-size: 11px; color: var(--color-text-secondary); margin-top: 1rem; font-family: 'IBM Plex Mono', monospace; }
</style>
<div class="eq-wrap">
<p class="eq-title">Equity · Portfolio Performance</p>
<div class="eq-header">
<div class="eq-ytd">+18.61% <span>YTD</span></div>
<span style="font-size:11px; font-family:'IBM Plex Mono',monospace; color:var(--color-text-secondary);">2026</span>
</div>
<hr class="eq-divider">
<div class="eq-legend">
<span style="display:flex;align-items:center;gap:6px;">
<span style="width:16px;height:2px;background:#1D9E75;border-radius:1px;display:inline-block;"></span>cumulative
</span>
<span style="display:flex;align-items:center;gap:6px;">
<span style="width:16px;height:10px;background:rgba(29,158,117,0.25);border:1px solid #1D9E75;border-radius:2px;display:inline-block;"></span>monthly
</span>
</div>
<div style="position:relative; width:100%; height:220px;">
<canvas id="eqChart" role="img" aria-label="Line chart of cumulative and monthly equity returns. Cumulative YTD is plus 18.61%.">
Monthly: Jan +7.75%, Feb 11.71%, Mar +0.91%, Apr +6.67%, May +3.13%, Jun 0.04%, Jul +2.66%, Now +9.45%. Cumulative: Jan +7.75%, Feb 4.87%, Mar 4.00%, Apr +2.40%, May +5.60%, Jun +5.56%, Jul +8.37%, Now +18.61%.
</canvas>
</div>
<div class="eq-stats">
<div class="eq-stat">
<div class="eq-stat-label">best month</div>
<div class="eq-stat-val pos">+9.45%</div>
</div>
<div class="eq-stat">
<div class="eq-stat-label">worst month</div>
<div class="eq-stat-val neg">11.71%</div>
</div>
<div class="eq-stat">
<div class="eq-stat-label">cumulative YTD</div>
<div class="eq-stat-val pos">+18.61%</div>
</div>
</div>
<p class="eq-note">// data through 2026 YTD · monthly returns & cumulative</p>
</div>
<script>
(function() {
const monthly = [7.75, -11.71, 0.91, 6.67, 3.13, -0.04, 2.66, 9.45];
const cumul = [7.75, -4.87, -4.00, 2.40, 5.60, 5.56, 8.37, 18.61];
const labels = ['jan','feb','mar','apr','may','jun','jul','now'];
function initChart() {
const isDark = matchMedia('(prefers-color-scheme: dark)').matches;
const gridColor = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)';
const textColor = isDark ? 'rgba(255,255,255,0.45)' : 'rgba(0,0,0,0.45)';
const zeroColor = isDark ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.15)';
new Chart(document.getElementById('eqChart'), {
type: 'bar',
data: {
labels,
datasets: [
{
type: 'line',
label: 'cumulative',
data: cumul,
borderColor: '#1D9E75',
borderWidth: 2,
pointBackgroundColor: '#1D9E75',
pointRadius: 4,
pointHoverRadius: 6,
tension: 0.35,
fill: { target: 'origin', above: 'rgba(29,158,117,0.08)', below: 'rgba(226,75,74,0.08)' },
yAxisID: 'y',
order: 1
},
{
type: 'bar',
label: 'monthly',
data: monthly,
backgroundColor: monthly.map(v => v >= 0 ? 'rgba(29,158,117,0.25)' : 'rgba(226,75,74,0.25)'),
borderColor: monthly.map(v => v >= 0 ? '#1D9E75' : '#E24B4A'),
borderWidth: 1,
borderRadius: 3,
yAxisID: 'y',
order: 2
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: isDark ? '#2C2C2A' : '#fff',
borderColor: isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.1)',
borderWidth: 1,
titleColor: textColor,
bodyColor: isDark ? 'rgba(255,255,255,0.8)' : 'rgba(0,0,0,0.8)',
titleFont: { family: "'IBM Plex Mono', monospace", size: 11 },
bodyFont: { family: "'IBM Plex Mono', monospace", size: 12 },
padding: 10,
callbacks: {
label: ctx => {
const sign = ctx.parsed.y >= 0 ? '+' : '';
return ` ${ctx.dataset.label}: ${sign}${ctx.parsed.y.toFixed(2)}%`;
}
}
}
},
scales: {
x: {
grid: { display: false },
border: { display: false },
ticks: { color: textColor, font: { family: "'IBM Plex Mono', monospace", size: 11 }, autoSkip: false }
},
y: {
grid: {
color: ctx => ctx.tick.value === 0 ? zeroColor : gridColor,
lineWidth: ctx => ctx.tick.value === 0 ? 1 : 0.5
},
border: { display: false },
ticks: {
color: textColor,
font: { family: "'IBM Plex Mono', monospace", size: 11 },
callback: v => (v >= 0 ? '+' : '') + v.toFixed(1) + '%'
}
}
}
}
});
}
if (typeof Chart !== 'undefined') {
initChart();
} else {
var s = document.createElement('script');
s.src = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js';
s.onload = initChart;
document.head.appendChild(s);
}
})();
</script>