// Generated, do not edit. sha256:4249f91eb8232d410f76bd1c74b9d6d6491317d71d4cb8ae4ce4ed0b5bdfa51b import { CONFIG } from './config.js'; const DAY_MS = 24 * 60 * 60 * 1000; export const PROVENANCE_KINDS = Object.freeze(['close', 'manual', 'live', 'spot', 'carried', 'none']); const dayOf = iso => String(iso).slice(0, 10); const msOf = day => Date.parse(`${day}T00:00:00Z`); const addDays = (day, n) => new Date(msOf(day) + n * DAY_MS).toISOString().slice(0, 10); export function dailyProvenance(rows, opts = {}) { const { assetKind = 'crypto', manualProviderId = null } = opts; const today = opts.today || new Date().toISOString().slice(0, 10); const perDay = new Map(); for (const r of rows || []) { if (r == null || r.fiatPrice == null || !(r.fiatPrice > 0)) continue; const day = dayOf(r.observedAt); const cur = perDay.get(day); perDay.set(day, cur && beats(cur, r, day === today) ? cur : r); } if (perDay.size === 0) return []; const days = [...perDay.keys()].sort(); const from = opts.from || days[0]; const until = opts.until || today; if (from > until) return []; const kindOf = (r, day) => kindFor(r, day, manualProviderId, today); const limit = CONFIG.PRICE_HISTORY.CARRY_FORWARD_DAYS[assetKind]; const out = []; let last = null; for (const d of days) if (d < from) last = { day: d, row: perDay.get(d) }; for (let day = from; day <= until; day = addDays(day, 1)) { const row = perDay.get(day); if (row) { last = { day, row }; out.push({ day, fiatPrice:row.fiatPrice, kind: kindOf(row, day), row, ageDays: 0, deviation: null }); continue; } const age = last ? Math.round((msOf(day) - msOf(last.day)) / DAY_MS) : null; if (last && (limit == null || age <= limit)) { out.push({ day, fiatPrice:last.row.fiatPrice, kind: 'carried', row: last.row, ageDays: age, deviation: null }); } else { out.push({ day, fiatPrice:null, kind: 'none', row: null, ageDays: age, deviation: null }); } } addDeviations(out); return out; } function kindFor(r, day, manualProviderId, today) { if (manualProviderId != null && r.providerId === manualProviderId) return 'manual'; if (day === today) return 'live'; return r.resolution !== 'timestamp' && !isProvisional(r) ? 'close' : 'spot'; } export function provenanceOfRow(row, opts = {}) { if (!row || !(row.fiatPrice > 0)) return null; const today = opts.today || new Date().toISOString().slice(0, 10); const voor = opts.day || today; const dag = dayOf(row.observedAt); const ageDays = Math.max(0, Math.round((msOf(voor) - msOf(dag)) / DAY_MS)); const kind = ageDays > 0 ? 'carried' : (row.derivedFrom === 'spot' && dag !== today ? 'spot' : kindFor(row, dag, opts.manualProviderId ?? null, today)); return { day: voor, fiatPrice:row.fiatPrice, kind, row, ageDays, deviation: null }; } export function provenanceOn(rows, day, opts = {}) { const today = opts.today || new Date().toISOString().slice(0, 10); const tot = addDays(day, 31) < today ? addDays(day, 31) : today; const d = dailyProvenance(rows, { ...opts, today, from: addDays(day, -31), until: tot < day ? day : tot }) .find(x => x.day === day) || null; const used = opts.usedPrice; if (d && d.fiatPrice == null && used != null) { const eerder = (rows || []).filter(r => r && r.fiatPrice > 0 && dayOf(r.observedAt) <= day && gelijk(r.fiatPrice, used)) .sort((a, b) => (a.observedAt < b.observedAt ? 1 : -1))[0]; if (!eerder) return d; return { day, fiatPrice:eerder.fiatPrice, kind: 'carried', row: eerder, deviation: null, ageDays: Math.round((msOf(day) - msOf(dayOf(eerder.observedAt))) / DAY_MS) }; } if (!d || used == null || d.fiatPrice == null || gelijk(d.fiatPrice, used)) return d; const bronDag = dayOf(d.row.observedAt); const alt = (rows || []).find(r => r && r.fiatPrice > 0 && dayOf(r.observedAt) === bronDag && gelijk(r.fiatPrice, used)); if (!alt) return d; const kind = d.kind === 'carried' ? 'carried' : kindFor(alt, day, opts.manualProviderId ?? null, today); return { ...d, fiatPrice:alt.fiatPrice, row: alt, kind }; } const gelijk = (a, b) => Math.abs(a - b) <= 1e-9 * Math.max(1, Math.abs(a), Math.abs(b)); function isProvisional(r) { const written = r.updatedAt || r.createdAt; return !!written && dayOf(written) <= dayOf(r.observedAt); } const seenAt = r => (r.resolution === 'timestamp' ? r.observedAt : (r.updatedAt || r.createdAt || '')); function beats(cur, cand, isToday = false) { if (isToday) return seenAt(cur) >= seenAt(cand); const curDay = cur.resolution !== 'timestamp', candDay = cand.resolution !== 'timestamp'; if (curDay !== candDay) return curDay; if (curDay) return (cur.id ?? 0) >= (cand.id ?? 0); return cur.observedAt >= cand.observedAt; } function addDeviations(out) { const real = k => k === 'close' || k === 'manual'; let before = null; const pct = (v, ref) => (ref ? Math.round(((v - ref) / ref) * 10000) / 100 : null); for (const d of out) { if (real(d.kind)) before = d.fiatPrice; else if (d.kind === 'spot') d.deviation = { before: pct(d.fiatPrice, before), after: null }; } let after = null; for (let i = out.length - 1; i >= 0; i--) { const d = out[i]; if (real(d.kind)) after = d.fiatPrice; else if (d.kind === 'spot') d.deviation.after = pct(d.fiatPrice, after); } } export function provenanceRuns(days) { const runs = []; for (const d of days || []) { const last = runs[runs.length - 1]; if (last && last.kind === d.kind) { last.until = d.day; last.days += 1; } else runs.push({ kind: d.kind, from: d.day, until: d.day, days: 1 }); } return runs; } export function originFrom(d, names = new Map()) { if (!d || d.fiatPrice == null || !d.row) return null; const r = d.row; return { day: d.day, kind: d.kind, fiatPrice:d.fiatPrice, ageDays: d.ageDays, deviation: d.deviation, source: names.get(r.providerId) || null, sourceDay: dayOf(r.observedAt), moment: d.kind === 'spot' || d.kind === 'live' ? (r.resolution === 'timestamp' ? r.observedAt : (r.updatedAt || r.createdAt || null)) : null, }; } export function liveOrigin(fiatPrice, source = null, now = new Date()) { if (fiatPrice == null) return null; const nu = now.toISOString(); return { day: nu.slice(0, 10), kind: 'live', fiatPrice, ageDays: 0, deviation: null, source, sourceDay: null, moment: nu }; } export function provenanceCounts(days) { const counts = Object.fromEntries(PROVENANCE_KINDS.map(k => [k, 0])); for (const d of days || []) counts[d.kind] += 1; return counts; }