diff --git a/index.html b/index.html
index aa9f1a1..ec2f668 100644
--- a/index.html
+++ b/index.html
@@ -259,8 +259,8 @@ window.WalletManager = WalletManager;
-- sats
-
30D AVG
-
Daily Acquisition
+
RUNNING AVG
+
Sats per Day
@@ -1602,38 +1602,36 @@ function setupSatsCard(cutoff) {
dailyDeltas[d.dateStr] = d.btcVal - prevBalance;
});
- /* Fill continuous daily timeline — iterate every day from first to last */
- const firstUTC = Date.UTC(new Date(sortedDays[0].dateStr).getUTCFullYear(), new Date(sortedDays[0].dateStr).getUTCMonth(), new Date(sortedDays[0].dateStr).getUTCDate());
- const lastUTC = Date.UTC(new Date(sortedDays[sortedDays.length - 1].dateStr).getUTCFullYear(), new Date(sortedDays[sortedDays.length - 1].dateStr).getUTCMonth(), new Date(sortedDays[sortedDays.length - 1].dateStr).getUTCDate());
- const continuousDays = [];
- for (let t = firstUTC; t <= lastUTC; t += DAY_MS) {
- const ds = new Date(t).toISOString().slice(0, 10);
- continuousDays.push({ ts: t, dateStr: ds, delta: dailyDeltas[ds] || 0 });
- }
+ /* Compute total sats acquired and days elapsed since first snapshot */
+ const firstUTC = sortedDays.length > 0 ? Date.UTC(new Date(sortedDays[0].dateStr).getUTCFullYear(), new Date(sortedDays[0].dateStr).getUTCMonth(), new Date(sortedDays[0].dateStr).getUTCDate()) : Date.now();
+ const lastUTC = sortedDays.length > 0 ? Date.UTC(new Date(sortedDays[sortedDays.length - 1].dateStr).getUTCFullYear(), new Date(sortedDays[sortedDays.length - 1].dateStr).getUTCMonth(), new Date(sortedDays[sortedDays.length - 1].dateStr).getUTCDate()) : Date.now();
+ const daysElapsed = Math.max(1, Math.ceil((lastUTC - firstUTC) / DAY_MS));
+ const totalBtc = sortedDays[sortedDays.length - 1]?.btcVal || 0;
+ const totalSats = Math.round(totalBtc * 1e8);
+ const avgSatsPerDay = Math.round(totalSats / daysElapsed);
- /* Exponentially weighted moving average — older days decay so average drops when there are no acquisitions */
- const filtered = continuousDays.filter(d => d.ts >= cutoff);
- const halfLife = 7;
- const decay = Math.pow(0.5, 1 / halfLife);
- let ewmaVal = filtered[0]?.delta || 0;
- const emaSats = [];
- for (let i = 0; i < filtered.length; i++) {
- ewmaVal = filtered[i].delta + decay * ewmaVal;
- emaSats.push([filtered[i].ts, Math.round(ewmaVal * 1e8)]);
- }
-
- const latestAvg = emaSats.length > 0 ? emaSats[emaSats.length - 1][1] : 0;
- document.getElementById('avg-sats-val').innerText = new Intl.NumberFormat('en-US').format(Math.round(latestAvg));
- const options = {
- chart: { id: 'instance-sats', type: 'area', height: 200, background: 'transparent', toolbar: { show: false }, sparkline: { enabled: false }, zoom: { enabled: true, type: 'x', autoScaleYaxis: true }, animations: { enabled: false } },
- series: [{ name: 'Avg Sats/Day', data: emaSats }],
+ /* Build continuous daily series: one data point per day */
+ const filteredSats = [];
+ let runningTotalBtc = 0;
+ for (let t = firstUTC; t <= lastUTC; t += DAY_MS) {
+ const ds = new Date(t).toISOString().slice(0, 10);
+ runningTotalBtc += dailyDeltas[ds] || 0;
+ const daysSoFar = Math.max(1, Math.ceil((t - firstUTC) / DAY_MS) + 1);
+ const runningAvgSats = Math.round((runningTotalBtc * 1e8) / daysSoFar);
+ filteredSats.push([t, runningAvgSats]);
+ }
+ const latestAvg = filteredSats.length > 0 ? filteredSats[filteredSats.length - 1][1] : 0;
+ document.getElementById('avg-sats-val').innerText = new Intl.NumberFormat('en-US').format(latestAvg);
+ const options = {
+ chart: { id: 'instance-sats', type: 'area', height: 200, background: 'transparent', toolbar: { show: false }, sparkline: { enabled: false }, zoom: { enabled: true, type: 'x', autoScaleYaxis: true }, animations: { enabled: false } },
+ series: [{ name: 'Avg Sats/Day', data: filteredSats }],
dataLabels: { enabled: false },
colors: [orangeBrandColor],
stroke: { curve: 'smooth', width: 2 },
fill: { type: 'gradient', gradient: { type: 'vertical', shadeIntensity: 0, colorStops: [{ offset: 0, color: orangeBrandColor, opacity: 0.16 }, { offset: 100, color: orangeBrandColor, opacity: 0 }] } },
grid: { show: false, padding: { left: 0, right: 0, top: 0, bottom: 0 } },
markers: { size: 0, hover: { size: 5 } },
- xaxis: { type: 'datetime', labels: { show: true, style: { colors: '#4B5563', fontSize: '10px', fontWeight: 600 }, datetimeFormatter: { year: 'yyyy', month: 'MMM yy', day: 'dd MMM' } }, axisBorder: { show: false }, axisTicks: { show: false } },
+ xaxis: { type: 'datetime', labels: { show: true, style: { colors: '#4B5563', fontSize: '10px', fontWeight: 600 }, datetimeFormatter: { year: 'yyyy', month: 'MMM yy', day: 'dd MMM' } }, axisBorder: { show: false }, axisTicks: { show: false }, minTickInterval: '1day' },
yaxis: { opposite: true, labels: { offsetX: -10, style: { colors: '#4B5563', fontSize: '10px', fontWeight: 600 }, formatter: (val) => { if (val >= 1e6) return (val / 1e6).toFixed(0) + 'M'; if (val >= 1e3) return (val / 1e3).toFixed(0) + 'k'; return val; } }, axisBorder: { show: false }, axisTicks: { show: false } },
tooltip: { enabled: true, theme: 'dark', shared: false, intersect: false, custom: function({ series, seriesIndex, dataPointIndex, w }) { const rawData = w.config.series[seriesIndex].data[dataPointIndex]; if (!rawData) return ''; const date = new Date(rawData[0]); const dateString = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); const sats = rawData[1]; const btcVal = (sats / 1e8).toFixed(6); const priceStr = todayBtcPrice ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(todayBtcPrice) : '—'; const dollarDay = todayBtcPrice ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format((sats / 1e8) * todayBtcPrice) : '—'; return '' + dateString + '
₿ ' + btcVal + '/day
' + dollarDay + '
'; } },
crosshairs: { show: true, width: 1, position: 'back', stroke: { color: '#4B5563', width: 1, dashArray: 3 } }
@@ -1705,8 +1703,24 @@ function startTickUpdates(data) {
}
/* ===================================================================
- Global Event Listeners
- =================================================================== */
+ Double-click to reset zoom
+ =================================================================== */
+function resetChartZoom(chartInstance) {
+ if (chartInstance) {
+ chartInstance.updateOptions({
+ xaxis: { min: undefined, max: undefined }
+ }, false, false, true);
+ }
+}
+
+document.getElementById('chart-container-btc').addEventListener('dblclick', () => resetChartZoom(window.btcChart));
+document.getElementById('chart-container-cumul').addEventListener('dblclick', () => resetChartZoom(window.cumulChart));
+document.getElementById('chart-container-sats').addEventListener('dblclick', () => resetChartZoom(window.satsChart));
+document.getElementById('chart-container-breakdown').addEventListener('dblclick', () => resetChartZoom(window.breakdownChart));
+
+/* ===================================================================
+ Global Event Listeners
+ =================================================================== */
document.querySelectorAll('.filter-checkbox').forEach(cb => {
cb.addEventListener('change', () => updateDashboard());
});