From 1c6ddcd2169df959c3a64c5e1e70deb6b6287d23 Mon Sep 17 00:00:00 2001 From: Filip Hallqvist Date: Wed, 15 Oct 2025 17:06:58 +0200 Subject: [PATCH 1/2] feat: Use benchmarks API for historical data --- .../src/components/PriceFeed/Chart/chart.tsx | 74 +++++++++++++------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/apps/insights/src/components/PriceFeed/Chart/chart.tsx b/apps/insights/src/components/PriceFeed/Chart/chart.tsx index 7a4690d369..080153bfaa 100644 --- a/apps/insights/src/components/PriceFeed/Chart/chart.tsx +++ b/apps/insights/src/components/PriceFeed/Chart/chart.tsx @@ -160,12 +160,17 @@ const useChartElem = (symbol: string, feedId: string) => { return; } isBackfilling.current = true; - const url = new URL("/historical-prices", globalThis.location.origin); + + const url = new URL( + "https://benchmarks.pyth.network/v1/shims/tradingview/history", + ); url.searchParams.set("symbol", symbol); url.searchParams.set("from", from.toString()); url.searchParams.set("to", to.toString()); - url.searchParams.set("resolution", resolution); - url.searchParams.set("cluster", "pythnet"); + url.searchParams.set( + "resolution", + mapResolutionToBenchmarksApi(resolution), + ); abortControllerRef.current = new AbortController(); abortControllerRef.current.signal.addEventListener("abort", () => { @@ -179,7 +184,15 @@ const useChartElem = (symbol: string, feedId: string) => { return; } - const data = historicalDataSchema.parse(jsonData); + const benchmarkData = benchmarksApiResponseSchema.parse(jsonData); + + // Transform OHLC data to our format using close prices + const data = benchmarkData.t.map((timestamp, i) => ({ + time: timestamp as UTCTimestamp, + price: benchmarkData.c[i], // Use close price + confidence: 0, // No confidence data from benchmarks API + status: PriceStatus.Trading, + })); // Get the current historical price data // Note that .data() returns (WhitespaceData | LineData)[], hence the type cast. @@ -196,16 +209,17 @@ const useChartElem = (symbol: string, feedId: string) => { value: d.price, }), })); + // we have no confidence data, set confidence bands to match the price const newHistoricalConfidenceHighData = data.map((d) => ({ time: d.time, ...(d.status === PriceStatus.Trading && { - value: d.price + d.confidence, + value: d.price, }), })); const newHistoricalConfidenceLowData = data.map((d) => ({ time: d.time, ...(d.status === PriceStatus.Trading && { - value: d.price - d.confidence, + value: d.price, }), })); @@ -394,21 +408,15 @@ type ChartRefContents = { price: ISeriesApi<"Line">; }; -const historicalDataSchema = z.array( - z - .strictObject({ - timestamp: z.number(), - price: z.number(), - confidence: z.number(), - status: z.nativeEnum(PriceStatus), - }) - .transform((d) => ({ - time: Number(d.timestamp) as UTCTimestamp, - price: d.price, - confidence: d.confidence, - status: d.status, - })), -); +const benchmarksApiResponseSchema = z.object({ + s: z.string(), // status + t: z.array(z.number()), // timestamp + o: z.array(z.number()), // open + h: z.array(z.number()), // high + l: z.array(z.number()), // low + c: z.array(z.number()), // close + v: z.array(z.number()), // volume +}); const priceFormat = { type: "price", precision: 5, @@ -421,6 +429,30 @@ const confidenceConfig = { lineWidth: 1, } as const; +/** + * Map our internal resolution format to the benchmarks API resolution format + */ +function mapResolutionToBenchmarksApi(resolution: string): string { + switch (resolution) { + case "1s": + case "1m": { + return "1"; + } + case "5m": { + return "5"; + } + case "1H": { + return "60"; + } + case "1D": { + return "1D"; + } + default: { + throw new Error(`Unknown resolution: ${resolution}`); + } + } +} + const useChartResize = ( chartContainerRef: RefObject, chartRef: RefObject, From 2158485654dbc38d0ac36ac47064fa441ec1ee53 Mon Sep 17 00:00:00 2001 From: Filip Hallqvist Date: Fri, 14 Nov 2025 14:27:16 +0100 Subject: [PATCH 2/2] PR feedback --- .../src/components/PriceFeed/Chart/chart.tsx | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/insights/src/components/PriceFeed/Chart/chart.tsx b/apps/insights/src/components/PriceFeed/Chart/chart.tsx index 080153bfaa..a6e4806c1d 100644 --- a/apps/insights/src/components/PriceFeed/Chart/chart.tsx +++ b/apps/insights/src/components/PriceFeed/Chart/chart.tsx @@ -184,15 +184,7 @@ const useChartElem = (symbol: string, feedId: string) => { return; } - const benchmarkData = benchmarksApiResponseSchema.parse(jsonData); - - // Transform OHLC data to our format using close prices - const data = benchmarkData.t.map((timestamp, i) => ({ - time: timestamp as UTCTimestamp, - price: benchmarkData.c[i], // Use close price - confidence: 0, // No confidence data from benchmarks API - status: PriceStatus.Trading, - })); + const data = benchmarksApiResponseSchema.parse(jsonData); // Get the current historical price data // Note that .data() returns (WhitespaceData | LineData)[], hence the type cast. @@ -408,15 +400,24 @@ type ChartRefContents = { price: ISeriesApi<"Line">; }; -const benchmarksApiResponseSchema = z.object({ - s: z.string(), // status - t: z.array(z.number()), // timestamp - o: z.array(z.number()), // open - h: z.array(z.number()), // high - l: z.array(z.number()), // low - c: z.array(z.number()), // close - v: z.array(z.number()), // volume -}); +const benchmarksApiResponseSchema = z + .object({ + s: z.string(), + t: z.array(z.number()), + o: z.array(z.number()), + h: z.array(z.number()), + l: z.array(z.number()), + c: z.array(z.number()), + v: z.array(z.number()), + }) + .transform((data) => + data.t.map((timestamp, i) => ({ + time: timestamp as UTCTimestamp, + price: data.c[i], // Use close price for now + confidence: 0, // No confidence data from benchmarks API + status: PriceStatus.Trading, + })), + ); const priceFormat = { type: "price", precision: 5,