fix: Date.UTC spread operator and EMA for rolling sats chart

This commit is contained in:
Dione
2026-06-10 19:17:32 +00:00
parent 0ad8f03990
commit b659d26ad1

View File

@ -1571,24 +1571,24 @@ function setupCumulCard(cutoff) {
function setupSatsCard(cutoff) {
const DAY_MS = 86400000;
/* Derive daily balances (latest snapshot per day) */
/* Derive daily balances (latest snapshot per day) using UTC date strings */
const dailyBalances = {};
const selectedAddr = getSelectedAddresses() || [];
selectedAddr.forEach(addr => {
const snaps = addressSnapshots[addr] || [];
snaps.forEach(snap => {
const dateStr = snap.block_timestamp.slice(0, 10);
const utcTs = new Date(snap.block_timestamp).getTime();
const dateStr = new Date(utcTs).toISOString().slice(0, 10);
const btcVal = getTokenAmount(snap?.wallet?.cbBTC, 'cbBTC') + getTokenAmount(snap?.collateral?.cbBTC, 'cbBTC');
const ts = new Date(snap.block_timestamp).getTime();
if (!dailyBalances[dateStr] || ts > dailyBalances[dateStr].ts) {
dailyBalances[dateStr] = { ts, dateStr, btcVal };
if (!dailyBalances[dateStr] || utcTs > dailyBalances[dateStr].ts) {
dailyBalances[dateStr] = { ts: utcTs, dateStr, btcVal };
} else {
dailyBalances[dateStr].btcVal += btcVal;
}
});
});
/* Sort by date, compute daily delta (acquisition), then fill continuous timeline */
/* Sort by date, compute daily delta (acquisition) */
const sortedDays = Object.values(dailyBalances).sort((a, b) => a.dateStr.localeCompare(b.dateStr));
const dailyDeltas = {};
sortedDays.forEach((d, i) => {
@ -1596,29 +1596,31 @@ function setupSatsCard(cutoff) {
dailyDeltas[d.dateStr] = d.btcVal - prevBalance;
});
/* Fill continuous daily timeline so no days are skipped */
const firstDate = new Date(sortedDays[0].dateStr).getTime();
const lastDate = new Date(sortedDays[sortedDays.length - 1].dateStr).getTime();
/* 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 = firstDate; t <= lastDate; t += DAY_MS) {
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 });
}
/* 30-day rolling average of daily deltas */
/* Exponentially weighted moving average — older days decay so average drops when there are no acquisitions */
const filtered = continuousDays.filter(d => d.ts >= cutoff);
const rollingAvg = [];
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++) {
const window = filtered.slice(Math.max(0, i - 29), i + 1);
const avgSats = Math.round(window.reduce((s, d) => s + d.delta, 0) / window.length * 1e8);
rollingAvg.push([filtered[i].ts, avgSats]);
ewmaVal = filtered[i].delta + decay * ewmaVal;
emaSats.push([filtered[i].ts, Math.round(ewmaVal * 1e8)]);
}
const latestAvg = rollingAvg.length > 0 ? rollingAvg[rollingAvg.length - 1][1] : 0;
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: rollingAvg }],
series: [{ name: 'Avg Sats/Day', data: emaSats }],
dataLabels: { enabled: false },
colors: [orangeBrandColor],
stroke: { curve: 'smooth', width: 2 },