fix(stock-price): ignore missing data points in price history (#4429)

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
This commit is contained in:
AartSchinkel
2025-11-13 22:07:23 +01:00
committed by GitHub
parent 89aed57645
commit c30c98b153
2 changed files with 21 additions and 15 deletions

View File

@@ -20,11 +20,19 @@ export const fetchStockPriceHandler = createCachedWidgetRequestHandler({
if (data.chart.result.length !== 1) { if (data.chart.result.length !== 1) {
throw new Error("Received multiple results"); throw new Error("Received multiple results");
} }
if (!data.chart.result[0]) { const firstResult = data.chart.result[0];
if (!firstResult) {
throw new Error("Received invalid data"); throw new Error("Received invalid data");
} }
return {
return data.chart.result[0]; priceHistory:
firstResult.indicators.quote[0]?.close.filter(
// Filter out null values from price arrays (Yahoo Finance returns null for missing data points)
(value) => value !== null && value !== undefined,
) ?? [],
symbol: firstResult.meta.symbol,
shortName: firstResult.meta.shortName,
};
}, },
cacheDuration: dayjs.duration(5, "minutes"), cacheDuration: dayjs.duration(5, "minutes"),
}); });
@@ -43,7 +51,7 @@ const dataSchema = z
indicators: z.object({ indicators: z.object({
quote: z.array( quote: z.array(
z.object({ z.object({
close: z.array(z.number()), close: z.array(z.number().nullish()),
}), }),
), ),
}), }),

View File

@@ -26,15 +26,13 @@ export default function StockPriceWidget({ options, width, height }: WidgetCompo
const theme = useMantineTheme(); const theme = useMantineTheme();
const [{ data }] = clientApi.widget.stockPrice.getPriceHistory.useSuspenseQuery(options); const [{ data }] = clientApi.widget.stockPrice.getPriceHistory.useSuspenseQuery(options);
const stockValues = data.indicators.quote[0]?.close ?? []; const stockValuesChange = round(calculateChange(data.priceHistory.at(-1) ?? 0, data.priceHistory[0] ?? 0));
const stockValuesChange = round(calculateChange(stockValues[stockValues.length - 1] ?? 0, stockValues[0] ?? 0));
const stockValuesChangePercentage = round( const stockValuesChangePercentage = round(
calculateChangePercentage(stockValues[stockValues.length - 1] ?? 0, stockValues[0] ?? 0), calculateChangePercentage(data.priceHistory.at(-1) ?? 0, data.priceHistory[0] ?? 0),
); );
const stockValuesMin = Math.min(...stockValues); const stockValuesMin = Math.min(...data.priceHistory);
const stockGraphValues = stockValues.map((value) => value - stockValuesMin + 50); const stockGraphValues = data.priceHistory.map((value) => value - stockValuesMin + 50);
return ( return (
<Flex h="100%" w="100%"> <Flex h="100%" w="100%">
@@ -57,17 +55,17 @@ export default function StockPriceWidget({ options, width, height }: WidgetCompo
) : ( ) : (
<IconTrendingDown size="1.5rem" color={theme.colors.red[7]} /> <IconTrendingDown size="1.5rem" color={theme.colors.red[7]} />
)} )}
{data.meta.symbol} {data.symbol}
</Text> </Text>
{width > 280 && height > 280 && ( {width > 280 && height > 280 && (
<Text size="md" lh="1"> <Text size="md" lh="1">
{data.meta.shortName} {data.shortName}
</Text> </Text>
)} )}
</Stack> </Stack>
<Title pos="absolute" bottom={10} right={10} order={width > 280 ? 1 : 2} fw={700}> <Title pos="absolute" bottom={10} right={10} order={width > 280 ? 1 : 2} fw={700}>
{new Intl.NumberFormat().format(round(stockValues[stockValues.length - 1] ?? 0))} {new Intl.NumberFormat().format(round(data.priceHistory.at(-1) ?? 0))}
</Title> </Title>
{width > 280 && ( {width > 280 && (
@@ -90,11 +88,11 @@ export default function StockPriceWidget({ options, width, height }: WidgetCompo
) : ( ) : (
<IconTrendingDown size="1.5rem" color={theme.colors.red[7]} /> <IconTrendingDown size="1.5rem" color={theme.colors.red[7]} />
)} )}
{data.meta.symbol} {data.symbol}
</Text> </Text>
{width > 280 && height > 280 && ( {width > 280 && height > 280 && (
<Text size="md" lh="1"> <Text size="md" lh="1">
{data.meta.shortName} {data.shortName}
</Text> </Text>
)} )}
</Stack> </Stack>