// Generated, do not edit. sha256:19640cdaa8812f06608f0f5d7ab246cafacd608773aca0ad6879af4404a7d7ed import { GENESIS_ISO, MS_PER_WEEK, MS_PER_DAY, WMA, MAYER, POWERLAW } from './constants.js'; import { t } from '../i18n/index.js'; const GENESIS_MS = new Date(GENESIS_ISO).getTime(); export function daysSinceGenesis(tsMs) { return (tsMs - GENESIS_MS) / (24 * 60 * 60 * 1000); } export function genesisMs() { return GENESIS_MS; } export function toDailyGrid(rows) { const byDay = new Map(); for (const r of rows) { const v = r.fiatPrice; if (v != null && v > 0) byDay.set(r.observedAt.slice(0, 10), v); } if (byDay.size === 0) throw new Error(t('signals.noHistory')); const days = [...byDay.keys()].sort(); const startMs = Date.parse(`${days[0]}T00:00:00Z`); const endMs = Date.parse(`${days[days.length - 1]}T00:00:00Z`); const out = []; let last = null; for (let ms = startMs; ms <= endMs; ms += MS_PER_DAY) { const iso = new Date(ms).toISOString().slice(0, 10); if (byDay.has(iso)) last = byDay.get(iso); out.push([ms, last]); } return out; } export function dailyGridSources(rows) { const byDay = new Map(); for (const r of rows) { const v = r.fiatPrice; if (v != null && v > 0) { byDay.set(Date.parse(`${r.observedAt.slice(0, 10)}T00:00:00Z`), { providerId: r.providerId, sourceName: r.sourceName ?? null }); } } return byDay; } export function powerLawModel() { return { A: POWERLAW.FIT.A, n: POWERLAW.FIT.N }; } export function powerLawValue(fit, tsMs) { return fit.A * Math.pow(daysSinceGenesis(tsMs), fit.n) * (fit.scaleAt ? fit.scaleAt(tsMs) : 1); } export function powerLawFor(currency, usdRates = null) { const own = POWERLAW.UPPER.DEFAULT_BY_CCY[String(currency).toUpperCase()]; if (own != null) return { fit: powerLawModel(), upper: own }; if (!usdRates || !usdRates.length) return null; return { fit: { ...powerLawModel(), scaleAt: ts => rateOn(usdRates, ts) }, upper: POWERLAW.UPPER.DEFAULT_BY_CCY.USD }; } function rateOn(grid, ts) { let lo = 0, hi = grid.length - 1, hit = 0; while (lo <= hi) { const mid = (lo + hi) >> 1; if (grid[mid][0] <= ts) { hit = mid; lo = mid + 1; } else hi = mid - 1; } return grid[hit][1]; } export function weeklyAggregate(daily) { const out = []; if (daily.length === 0) return out; let week = Math.floor(daily[0][0] / MS_PER_WEEK); let sum = 0, count = 0; for (const [ts, price] of daily) { const w = Math.floor(ts / MS_PER_WEEK); if (w !== week) { if (count > 0) out.push([week * MS_PER_WEEK, sum / count]); week = w; sum = price; count = 1; } else { sum += price; count++; } } if (count > 0) out.push([week * MS_PER_WEEK, sum / count]); return out; } export function movingAverage(series, window) { const out = []; let sum = 0; for (let i = 0; i < series.length; i++) { sum += series[i][1]; if (i >= window) sum -= series[i - window][1]; out.push({ x: series[i][0], y: i >= window - 1 ? sum / window : null }); } return out; } export function mayerSeries(daily, days) { const ma = movingAverage(daily, days); return daily.map(([ts, price], i) => ({ x: ts, y: ma[i].y == null ? null : price / ma[i].y, })); } export function powerLawSignal(daily, fit, lowerFactor, upperFactor) { const [ts, price] = daily[daily.length - 1]; const f = powerLawValue(fit, ts); if (price <= f * lowerFactor) return { color: 'green', key: 'plStrong' }; if (price <= f) return { color: 'green', key: 'plBuy' }; if (price <= f * upperFactor) return { color: 'yellow', key: 'plAbove' }; return { color: 'red', key: 'plFar' }; } export function wmaSignal(weekly) { const ma = movingAverage(weekly, WMA.WEEKS); const lastWma = [...ma].reverse().find(d => d.y != null); if (!lastWma || weekly.length === 0) return null; const ratio = weekly[weekly.length - 1][1] / lastWma.y; if (ratio < WMA.SIGNAL_MULT) return { color: 'green', key: 'wmaBuy', mult: ratio }; if (ratio < WMA.RED_MULT) return { color: 'yellow', key: 'wmaAbove', mult: ratio }; return { color: 'red', key: 'wmaFar', mult: ratio }; } export function mayerSignal(mayer) { const last = [...mayer].reverse().find(d => d.y != null); if (!last) return null; const v = last.y; if (v < MAYER.GREEN_BELOW) return { color: 'green', key: 'mayerLow', val: v }; if (v < MAYER.RED_ABOVE) return { color: 'yellow', key: 'mayerMid', val: v }; return { color: 'red', key: 'mayerHigh', val: v }; }