TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1/**2 * Auction intelligence (§33–§35): a lot's current bid / estimate is compared with the RareIndex3 * Valuation only on a buyer-pays (all-in) basis — hammer + the house's buyer premium — and only4 * through the same gates as a marketplace ask (same variant, transaction-based RIV, ≥ 5 sales,5 * confidence ≥ 0.5, plausibility band, review threshold). Taxes, duties and shipping are out of6 * scope and are stated as such wherever an all-in figure is shown.7 */8import { assessAsk, type AskVerdict } from './scores.js';9import { allInPrice, type FeeBasis } from './fees.js';10import { round } from '@rareindex/shared';1112export interface LotAssessmentInput {13 /** auction house name / connector id for the fee schedule */14 house: string | null | undefined;15 /** lot amounts already converted to USD (null when absent) */16 currentBidUsd: number | null;17 bidCount: number | null;18 estimateLowUsd: number | null;19 estimateHighUsd: number | null;20 hammerPriceUsd?: number | null;21 /** connector flag for the lot's quoted amounts; lots are hammer-basis unless the connector says otherwise */22 buyerPremiumIncluded?: boolean | null;23 /** rate converting USD into the schedule currency (for tier boundaries); 1 when unknown */24 fxUsdToScheduleCurrency?: number;25 /** valuation of the lot's own variant (or the asset when raw and representative) */26 riv: { rivUsd: number | null; confidence: number | null; sampleSize: number | null; basis?: 'transactions' | 'comps' | 'guide' | 'none' | null } | null;27 /** false when the lot's grade does not match the valuation's variant */28 sameVariant?: boolean;29}3031export interface LotAssessment {32 buyerPremiumRate: number | null;33 feeBasis: FeeBasis;34 allInBidUsd: number | null;35 allInEstimateLowUsd: number | null;36 allInEstimateHighUsd: number | null;37 allInHammerUsd: number | null;38 rivUsd: number | null;39 /** (all-in bid − RIV) / RIV — null without a bid or when ungated/anomalous/review */40 bidVsRiv: number | null;41 /** (all-in low estimate − RIV) / RIV — null when ungated/anomalous/review */42 estimateVsRiv: number | null;43 /** verdict of the most informative comparison: the bid when there is one, else the low estimate */44 verdict: AskVerdict;45 reasons: string[];46 /** what the verdict is based on */47 basedOn: 'bid' | 'estimate' | 'none';48}4950const allIn = (usd: number | null, i: LotAssessmentInput) => {51 if (usd === null || !(usd > 0)) return null;52 return allInPrice({ price: usd, buyerPremiumIncluded: i.buyerPremiumIncluded ?? false, house: i.house, saleType: 'auction', sourceType: 'auction_house', fxToScheduleCurrency: i.fxUsdToScheduleCurrency });53};5455export function assessLot(i: LotAssessmentInput): LotAssessment {56 const hasBid = (i.bidCount ?? 0) > 0 && i.currentBidUsd !== null && i.currentBidUsd > 0;57 const bid = hasBid ? allIn(i.currentBidUsd, i) : null;58 const lo = allIn(i.estimateLowUsd, i);59 const hi = allIn(i.estimateHighUsd, i);60 const hammer = allIn(i.hammerPriceUsd ?? null, i);61 const ref = bid ?? lo ?? hi ?? hammer;62 const riv = i.riv?.rivUsd ?? null;63 const gate = (askUsd: number | null) =>64 assessAsk({ askUsd, rivUsd: riv, confidence: i.riv?.confidence ?? null, sampleSize: i.riv?.sampleSize ?? null, basis: i.riv?.basis ?? undefined, sameVariant: i.sameVariant });65 const bidA = bid ? gate(bid.allIn) : null;66 const loA = lo ? gate(lo.allIn) : null;67 const primary = bidA ?? loA;68 const usable = (a: ReturnType<typeof assessAsk> | null) => (a && (a.verdict === 'deal' || a.verdict === 'fair' || a.verdict === 'premium') ? a.discount : null);69 return {70 buyerPremiumRate: ref ? round(ref.rate, 4) : null,71 feeBasis: ref?.basis ?? 'unknown',72 allInBidUsd: bid?.allIn ?? null,73 allInEstimateLowUsd: lo?.allIn ?? null,74 allInEstimateHighUsd: hi?.allIn ?? null,75 allInHammerUsd: hammer?.allIn ?? null,76 rivUsd: riv,77 bidVsRiv: usable(bidA),78 estimateVsRiv: usable(loA),79 verdict: primary?.verdict ?? 'ungated',80 reasons: primary?.reasons ?? (riv === null ? ['no_riv'] : ['no_bid_or_estimate']),81 basedOn: bidA ? 'bid' : loA ? 'estimate' : 'none',82 };83}84