SPB Git

spb/rareindex

Public

Ask vs RIV: auction bids are not asks — excluded from discounts, min ask and market depth (Yahoo! Auctions ¥1 opening bids surfaced as −90 % deals)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Sep 11, 2026) parent 4a63bb9 Browse files

2 changed files +6 −3

modified apps/web/src/lib/queries/depth.ts +1 −1
@@ -28,7 +28,7 @@ export const getAssetDepth = cache(async (assetId: string, variantId: string | n
28 const scope: AssetDepth['scope'] = target && !single ? 'variant' : 'asset'; 28 const scope: AssetDepth['scope'] = target && !single ? 'variant' : 'asset';
29 const asks = await rows<{ price_usd: unknown }>(sql` 29 const asks = await rows<{ price_usd: unknown }>(sql`
30 SELECT l.price_usd FROM listings l 30 SELECT l.price_usd FROM listings l
31− WHERE l.asset_id = ${assetId} AND l.availability = 'available' AND l.price_usd > 0 31+ WHERE l.asset_id = ${assetId} AND l.availability = 'available' AND l.price_usd > 0 AND l.listing_type <> 'auction'
32 ${scope === 'variant' ? sql`AND l.variant_id = ${target}` : sql``} 32 ${scope === 'variant' ? sql`AND l.variant_id = ${target}` : sql``}
33 AND NOT (l.grader IS NOT NULL AND l.grade IS NULL)`); 33 AND NOT (l.grader IS NOT NULL AND l.grade IS NULL)`);
34 const depth = marketDepth(asks.map((a) => num(a.price_usd)), riv); 34 const depth = marketDepth(asks.map((a) => num(a.price_usd)), riv);
modified workers/valuation/run.ts +5 −2
@@ -151,7 +151,9 @@ export async function valueAsset(assetId: string, opts: { now?: Date; rebuildHis
151 if (out.riv !== null && out.basis === 'transactions' && out.confidence >= ASK_MIN_CONFIDENCE && out.sampleSize >= ASK_MIN_SAMPLE) { 151 if (out.riv !== null && out.basis === 'transactions' && out.confidence >= ASK_MIN_CONFIDENCE && out.sampleSize >= ASK_MIN_SAMPLE) {
152 const lo = out.riv * ASK_ANOMALY_LOW_RATIO; 152 const lo = out.riv * ASK_ANOMALY_LOW_RATIO;
153 const hi = out.riv * ASK_ANOMALY_HIGH_RATIO; 153 const hi = out.riv * ASK_ANOMALY_HIGH_RATIO;
154− const scope = and(eq(listings.variantId, v.id), eq(listings.availability, 'available'), sql`${listings.priceUsd} > 0`); 154+ // Auction lots are excluded: a current/opening bid (often ¥1 on Yahoo! Auctions) is not an asking price.
155+ // Bid-vs-RIV belongs to auction intelligence with fees (§33–§35), not to the deal rails.
156+ const scope = and(eq(listings.variantId, v.id), eq(listings.availability, 'available'), sql`${listings.priceUsd} > 0`, sql`${listings.listingType} <> 'auction'`);
155 await db() 157 await db()
156 .update(listings) 158 .update(listings)
157 .set({ discountToRiv: sql`round((${listings.priceUsd} - ${out.riv}) / ${out.riv}, 4)`, flags: sql`array_remove(${listings.flags}, 'riv_anomaly')` }) 159 .set({ discountToRiv: sql`round((${listings.priceUsd} - ${out.riv}) / ${out.riv}, 4)`, flags: sql`array_remove(${listings.flags}, 'riv_anomaly')` })
@@ -268,7 +270,8 @@ async function snapshotValue(assetId: string, variantId: string, at: Date): Prom
268 270
269 async function activeListingStats(assetId: string, variantId: string | null): Promise<{ count: number; minAsk: number | null }> { 271 async function activeListingStats(assetId: string, variantId: string | null): Promise<{ count: number; minAsk: number | null }> {
270 const [row] = await db() 272 const [row] = await db()
271− .select({ n: sql<number>`count(*)::int`, min: sql<number | null>`min(${listings.priceUsd})` }) 273+ // count = every live listing; min ask = fixed-price / best-offer / ask only (an auction bid is not an ask)
274+ .select({ n: sql<number>`count(*)::int`, min: sql<number | null>`min(${listings.priceUsd}) filter (where ${listings.listingType} <> 'auction' and ${listings.priceUsd} > 0)` })
272 .from(listings) 275 .from(listings)
273 .where(and(eq(listings.assetId, assetId), eq(listings.availability, 'available'), variantId ? eq(listings.variantId, variantId) : undefined)); 276 .where(and(eq(listings.assetId, assetId), eq(listings.availability, 'available'), variantId ? eq(listings.variantId, variantId) : undefined));
274 return { count: row?.n ?? 0, minAsk: row?.min === null || row?.min === undefined ? null : Number(row.min) }; 277 return { count: row?.n ?? 0, minAsk: row?.min === null || row?.min === undefined ? null : Number(row.min) };
275 278
Menu

Theme