From 52f7ad82c68bda2d7f9f531ae013d92a958d6ff4 Mon Sep 17 00:00:00 2001 From: dirtydishes Date: Tue, 20 Jan 2026 11:05:40 -0500 Subject: [PATCH] Add equities prints range endpoint Adds GET /prints/equities/range for fetching underlying-scoped equity prints over a requested time window, used by chart overlays. --- services/api/src/index.ts | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/services/api/src/index.ts b/services/api/src/index.ts index d282b86..54c7ae3 100644 --- a/services/api/src/index.ts +++ b/services/api/src/index.ts @@ -51,6 +51,7 @@ import { fetchEquityCandlesRange, fetchRecentOptionNBBO, fetchEquityPrintsAfter, + fetchEquityPrintsRange, fetchEquityPrintJoinsAfter, fetchEquityQuotesAfter, fetchInferredDarkAfter, @@ -143,6 +144,13 @@ const candleReplaySchema = replayParamsSchema.extend({ interval_ms: z.coerce.number().int().positive() }); +const equityPrintRangeSchema = z.object({ + underlying_id: z.string().min(1), + start_ts: z.coerce.number().int().nonnegative(), + end_ts: z.coerce.number().int().nonnegative(), + limit: limitSchema.optional() +}); + type Channel = | "options" | "options-nbbo" @@ -223,6 +231,24 @@ const parseBooleanParam = (value: string | null | undefined): boolean => { return ["1", "true", "yes", "on"].includes(normalized); }; +const parseEquityPrintRangeParams = ( + url: URL +): { underlyingId: string; startTs: number; endTs: number; limit: number } => { + const params = equityPrintRangeSchema.parse({ + underlying_id: url.searchParams.get("underlying_id") ?? undefined, + start_ts: url.searchParams.get("start_ts") ?? undefined, + end_ts: url.searchParams.get("end_ts") ?? undefined, + limit: url.searchParams.get("limit") ?? undefined + }); + + return { + underlyingId: params.underlying_id, + startTs: params.start_ts, + endTs: params.end_ts, + limit: params.limit ?? env.REST_DEFAULT_LIMIT + }; +}; + const parseCandleParams = ( url: URL ): { @@ -796,6 +822,22 @@ const run = async () => { return jsonResponse({ data }); } + if (req.method === "GET" && url.pathname === "/prints/equities/range") { + try { + const { underlyingId, startTs, endTs, limit } = parseEquityPrintRangeParams(url); + const data = await fetchEquityPrintsRange(clickhouse, underlyingId, startTs, endTs, limit); + return jsonResponse({ data }); + } catch (error) { + return jsonResponse( + { + error: "invalid equity range query", + detail: error instanceof Error ? error.message : String(error) + }, + 400 + ); + } + } + if (req.method === "GET" && url.pathname === "/quotes/equities") { const limit = parseLimit(url.searchParams.get("limit")); const data = await fetchRecentEquityQuotes(clickhouse, limit);