deckhearth/lib/pricing/price-history.js

79 lines
2.2 KiB
JavaScript
Raw Normal View History

import { sql } from '../sql.js';
/**
* @typedef {Object} PriceSnapshotRow
* @property {number} cardId
* @property {string} source
* @property {string} [priceType]
* @property {string} [currency]
* @property {number} amount
* @property {string | Date} [recordedAt]
*/
/**
* Upsert daily price snapshots (idempotent per card/source/type/currency/day).
*
* @param {PriceSnapshotRow[]} rows
* @returns {Promise<number>} rows written
*/
export async function recordPriceSnapshots(rows) {
if (!rows?.length) return 0;
let written = 0;
for (const row of rows) {
if (row.cardId == null || row.amount == null || Number.isNaN(Number(row.amount))) {
continue;
}
const priceType = row.priceType ?? 'market';
const currency = row.currency ?? 'USD';
const recordedAt =
row.recordedAt instanceof Date
? row.recordedAt.toISOString().slice(0, 10)
: row.recordedAt ?? new Date().toISOString().slice(0, 10);
await sql`
INSERT INTO card_price_history (card_id, source, price_type, currency, amount, recorded_at)
VALUES (
${row.cardId},
${row.source},
${priceType},
${currency},
${Number(row.amount)},
${recordedAt}
)
ON CONFLICT (card_id, source, price_type, currency, recorded_at)
DO UPDATE SET amount = EXCLUDED.amount
`;
written += 1;
}
return written;
}
/**
* @param {number} cardId
* @param {{ days?: number }} [options]
* @returns {Promise<Array<{ date: string, amount: number, source: string, priceType: string, currency: string }>>}
*/
export async function getPriceHistoryPoints(cardId, { days = 90 } = {}) {
const cappedDays = Math.min(Math.max(Number(days) || 90, 1), 365);
const result = await sql`
SELECT recorded_at, amount, source, price_type, currency
FROM card_price_history
WHERE card_id = ${cardId}
AND recorded_at >= CURRENT_DATE - ${cappedDays}::integer
ORDER BY recorded_at ASC
`;
return result.rows.map((row) => ({
date: row.recorded_at instanceof Date
? row.recorded_at.toISOString().slice(0, 10)
: String(row.recorded_at).slice(0, 10),
amount: parseFloat(row.amount),
source: row.source,
priceType: row.price_type,
currency: row.currency,
}));
}