From 311d79c4c4eb4ea503d42e09f96ea7b6c6f67091 Mon Sep 17 00:00:00 2001 From: Saravana Achu Mac Date: Mon, 4 May 2026 17:10:57 -0700 Subject: [PATCH] fix(B4): show latest bar timestamp in ticker header --- web/src/views/HomeView.dom.test.tsx | 15 +++++++++ web/src/views/HomeView.tsx | 51 +++++++++++++++++++++++------ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/web/src/views/HomeView.dom.test.tsx b/web/src/views/HomeView.dom.test.tsx index 0ff67ae..0f24f3b 100644 --- a/web/src/views/HomeView.dom.test.tsx +++ b/web/src/views/HomeView.dom.test.tsx @@ -110,4 +110,19 @@ describe('TickerHeader', () => { await waitFor(() => expect(screen.getAllByText('Apple Inc.').length).toBeGreaterThan(0)); expect(fetchResearchProfileMock).toHaveBeenCalledTimes(1); }); + + it('shows the latest chart bar timestamp instead of the render time', async () => { + fetchResearchProfileMock.mockResolvedValue({ companyName: 'Apple Inc.' }); + fetchChartBarsMock.mockResolvedValueOnce([ + { ts: Date.UTC(2024, 0, 3, 19, 30), open: 210, high: 213, low: 209, close: 212, volume: 1000 }, + { ts: Date.UTC(2024, 0, 3, 20, 30), open: 212, high: 214, low: 211, close: 213, volume: 1200 }, + ]); + + renderTickerHeader(); + + expect(screen.getByText(/Latest bar pending ET/)).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText(/Jan 3, 2024, 03:30 PM ET · NASDAQ/)).toBeInTheDocument(); + }); + }); }); diff --git a/web/src/views/HomeView.tsx b/web/src/views/HomeView.tsx index 78bcbe3..4616a7d 100644 --- a/web/src/views/HomeView.tsx +++ b/web/src/views/HomeView.tsx @@ -55,6 +55,19 @@ function formatPriceLabel(ts: number, period: Period) { return d.toLocaleDateString([], { month: 'short', day: 'numeric' }); } +function formatAsOfTimestamp(ts: number | null) { + if (ts == null) return 'Latest bar pending'; + + return new Date(ts).toLocaleString('en-US', { + timeZone: 'America/New_York', + month: 'short', + day: 'numeric', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + }); +} + function average(values: number[]) { return values.reduce((sum, value) => sum + value, 0) / values.length; } @@ -150,7 +163,15 @@ function normalizeResearchProfile(profile: any): ResearchProfile | null { } // ─── Ticker header ──────────────────────────────────────────────────────────── -export function TickerHeader({ symbol, profile }: { symbol: string; profile?: ResearchProfile | null }) { +export function TickerHeader({ + symbol, + profile, + latestBarTimestamp, +}: { + symbol: string; + profile?: ResearchProfile | null; + latestBarTimestamp?: number | null; +}) { const navigate = useNavigate(); const { botState } = useAppContext(); const data = botState.symbols?.[symbol]; @@ -211,17 +232,20 @@ export function TickerHeader({ symbol, profile }: { symbol: string; profile?: Re
- {new Date().toLocaleString('en-US', { - month: 'short', day: 'numeric', year: 'numeric', - hour: '2-digit', minute: '2-digit', - })} ET · NASDAQ + {formatAsOfTimestamp(latestBarTimestamp ?? null)} ET · NASDAQ
); } // ─── Stock chart ────────────────────────────────────────────────────────────── -function StockChart({ symbol }: { symbol: string }) { +function StockChart({ + symbol, + onLatestBarTimestamp, +}: { + symbol: string; + onLatestBarTimestamp?: (timestamp: number | null) => void; +}) { const [period, setPeriod] = useState('1Y'); const [bars, setBars] = useState([]); const [loading, setLoading] = useState(false); @@ -237,12 +261,18 @@ function StockChart({ symbol }: { symbol: string }) { setLoading(true); setError(null); setBars([]); + onLatestBarTimestamp?.(null); fetchChartBars(symbol, period) - .then(data => { if (!cancelled) setBars(data); }) + .then(data => { + if (!cancelled) { + setBars(data); + onLatestBarTimestamp?.(data.at(-1)?.ts ?? null); + } + }) .catch(err => { if (!cancelled) setError(err?.message ?? 'Failed to load chart'); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; - }, [symbol, period]); + }, [symbol, period, onLatestBarTimestamp]); const closes = bars.map(b => b.close); const rsi = calculateRsi(closes); @@ -742,6 +772,7 @@ export function HomeView() { const { activeSymbol, setActiveSymbol } = useAppContext(); const [profile, setProfile] = useState(null); const [profileLoading, setProfileLoading] = useState(false); + const [latestBarTimestamp, setLatestBarTimestamp] = useState(null); useEffect(() => { if (!activeSymbol) { @@ -770,8 +801,8 @@ export function HomeView() { return (
- - + +