From a9ccc0b00eac130bc18cfa7dc3c0979cac49f1c4 Mon Sep 17 00:00:00 2001 From: u1 Date: Fri, 9 Jan 2026 02:05:37 +0100 Subject: [PATCH] feat(visualizer): add build overlay toggle Default is off so candles match production; enable via the new Build button when needed. --- apps/visualizer/src/App.tsx | 3 ++ .../src/features/chart/ChartPanel.tsx | 7 ++++ .../src/features/chart/ChartToolbar.tsx | 7 ++++ .../src/features/chart/TradingChart.tsx | 35 ++++++++++++++++++- 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/apps/visualizer/src/App.tsx b/apps/visualizer/src/App.tsx index 10c1989..5e529b3 100644 --- a/apps/visualizer/src/App.tsx +++ b/apps/visualizer/src/App.tsx @@ -107,6 +107,7 @@ function TradeApp({ user, onLogout }: { user: string; onLogout: () => void }) { const [pollMs, setPollMs] = useLocalStorageState('trade.pollMs', envNumber('VITE_POLL_MS', 1000)); const [limit, setLimit] = useLocalStorageState('trade.limit', envNumber('VITE_LIMIT', 300)); const [showIndicators, setShowIndicators] = useLocalStorageState('trade.showIndicators', true); + const [showBuild, setShowBuild] = useLocalStorageState('trade.showBuild', false); const [tab, setTab] = useLocalStorageState<'orderbook' | 'trades'>('trade.sidebarTab', 'orderbook'); const [bottomTab, setBottomTab] = useLocalStorageState< 'positions' | 'orders' | 'balances' | 'orderHistory' | 'positionHistory' @@ -288,6 +289,8 @@ function TradeApp({ user, onLogout }: { user: string; onLogout: () => void }) { onTimeframeChange={setTf} showIndicators={showIndicators} onToggleIndicators={() => setShowIndicators((v) => !v)} + showBuild={showBuild} + onToggleBuild={() => setShowBuild((v) => !v)} seriesLabel={seriesLabel} /> diff --git a/apps/visualizer/src/features/chart/ChartPanel.tsx b/apps/visualizer/src/features/chart/ChartPanel.tsx index 8b0815e..02b5557 100644 --- a/apps/visualizer/src/features/chart/ChartPanel.tsx +++ b/apps/visualizer/src/features/chart/ChartPanel.tsx @@ -18,6 +18,8 @@ type Props = { onTimeframeChange: (tf: string) => void; showIndicators: boolean; onToggleIndicators: () => void; + showBuild: boolean; + onToggleBuild: () => void; seriesLabel: string; }; @@ -49,6 +51,8 @@ export default function ChartPanel({ onTimeframeChange, showIndicators, onToggleIndicators, + showBuild, + onToggleBuild, seriesLabel, }: Props) { const [isFullscreen, setIsFullscreen] = useState(false); @@ -272,6 +276,8 @@ export default function ChartPanel({ onTimeframeChange={onTimeframeChange} showIndicators={showIndicators} onToggleIndicators={onToggleIndicators} + showBuild={showBuild} + onToggleBuild={onToggleBuild} priceAutoScale={priceAutoScale} onTogglePriceAutoScale={() => setPriceAutoScale((v) => !v)} seriesLabel={seriesLabel} @@ -300,6 +306,7 @@ export default function ChartPanel({ ema20={indicators.ema20} bb20={indicators.bb20} showIndicators={showIndicators} + showBuild={showBuild} bucketSeconds={bucketSeconds} seriesKey={seriesKey} fib={fibRenderable} diff --git a/apps/visualizer/src/features/chart/ChartToolbar.tsx b/apps/visualizer/src/features/chart/ChartToolbar.tsx index e4dcb39..638dc76 100644 --- a/apps/visualizer/src/features/chart/ChartToolbar.tsx +++ b/apps/visualizer/src/features/chart/ChartToolbar.tsx @@ -5,6 +5,8 @@ type Props = { onTimeframeChange: (tf: string) => void; showIndicators: boolean; onToggleIndicators: () => void; + showBuild: boolean; + onToggleBuild: () => void; priceAutoScale: boolean; onTogglePriceAutoScale: () => void; seriesLabel: string; @@ -19,6 +21,8 @@ export default function ChartToolbar({ onTimeframeChange, showIndicators, onToggleIndicators, + showBuild, + onToggleBuild, priceAutoScale, onTogglePriceAutoScale, seriesLabel, @@ -45,6 +49,9 @@ export default function ChartToolbar({ + diff --git a/apps/visualizer/src/features/chart/TradingChart.tsx b/apps/visualizer/src/features/chart/TradingChart.tsx index 44044b2..cfecb61 100644 --- a/apps/visualizer/src/features/chart/TradingChart.tsx +++ b/apps/visualizer/src/features/chart/TradingChart.tsx @@ -25,6 +25,7 @@ type Props = { ema20?: SeriesPoint[]; bb20?: { upper: SeriesPoint[]; lower: SeriesPoint[]; mid: SeriesPoint[] }; showIndicators: boolean; + showBuild: boolean; bucketSeconds: number; seriesKey: string; fib?: FibRetracement | null; @@ -116,6 +117,7 @@ export default function TradingChart({ ema20, bb20, showIndicators, + showBuild, bucketSeconds, seriesKey, fib, @@ -596,12 +598,29 @@ export default function TradingChart({ s.bbLower?.setData(bbLower); s.bbMid?.setData(bbMid); + s.build.applyOptions({ visible: showBuild }); + if (!showBuild) { + buildSamplesRef.current.clear(); + buildKeyRef.current = seriesKey; + lastBuildCandleStartRef.current = null; + s.build.setData([]); + } + if (buildKeyRef.current !== seriesKey) { buildSamplesRef.current.clear(); buildKeyRef.current = seriesKey; lastBuildCandleStartRef.current = null; } + if (!showBuild) { + s.sma20?.applyOptions({ visible: showIndicators }); + s.ema20?.applyOptions({ visible: showIndicators }); + s.bbUpper?.applyOptions({ visible: showIndicators }); + s.bbLower?.applyOptions({ visible: showIndicators }); + s.bbMid?.applyOptions({ visible: showIndicators }); + return; + } + const bs = resolveBucketSeconds(bucketSeconds, candles); const eps = 1e-3; const maxPointsPerCandle = 600; @@ -714,7 +733,21 @@ export default function TradingChart({ s.bbUpper?.applyOptions({ visible: showIndicators }); s.bbLower?.applyOptions({ visible: showIndicators }); s.bbMid?.applyOptions({ visible: showIndicators }); - }, [candleData, volumeData, oracleData, smaData, emaData, bbUpper, bbLower, bbMid, showIndicators, candles, bucketSeconds, seriesKey]); + }, [ + candleData, + volumeData, + oracleData, + smaData, + emaData, + bbUpper, + bbLower, + bbMid, + showIndicators, + showBuild, + candles, + bucketSeconds, + seriesKey, + ]); useEffect(() => { const s = seriesRef.current;