feat(visualizer): add build overlay toggle

Default is off so candles match production; enable via the new Build button when needed.
This commit is contained in:
u1
2026-01-09 02:05:37 +01:00
parent 9420c89f52
commit a9ccc0b00e
4 changed files with 51 additions and 1 deletions

View File

@@ -107,6 +107,7 @@ function TradeApp({ user, onLogout }: { user: string; onLogout: () => void }) {
const [pollMs, setPollMs] = useLocalStorageState('trade.pollMs', envNumber('VITE_POLL_MS', 1000)); const [pollMs, setPollMs] = useLocalStorageState('trade.pollMs', envNumber('VITE_POLL_MS', 1000));
const [limit, setLimit] = useLocalStorageState('trade.limit', envNumber('VITE_LIMIT', 300)); const [limit, setLimit] = useLocalStorageState('trade.limit', envNumber('VITE_LIMIT', 300));
const [showIndicators, setShowIndicators] = useLocalStorageState('trade.showIndicators', true); const [showIndicators, setShowIndicators] = useLocalStorageState('trade.showIndicators', true);
const [showBuild, setShowBuild] = useLocalStorageState('trade.showBuild', false);
const [tab, setTab] = useLocalStorageState<'orderbook' | 'trades'>('trade.sidebarTab', 'orderbook'); const [tab, setTab] = useLocalStorageState<'orderbook' | 'trades'>('trade.sidebarTab', 'orderbook');
const [bottomTab, setBottomTab] = useLocalStorageState< const [bottomTab, setBottomTab] = useLocalStorageState<
'positions' | 'orders' | 'balances' | 'orderHistory' | 'positionHistory' 'positions' | 'orders' | 'balances' | 'orderHistory' | 'positionHistory'
@@ -288,6 +289,8 @@ function TradeApp({ user, onLogout }: { user: string; onLogout: () => void }) {
onTimeframeChange={setTf} onTimeframeChange={setTf}
showIndicators={showIndicators} showIndicators={showIndicators}
onToggleIndicators={() => setShowIndicators((v) => !v)} onToggleIndicators={() => setShowIndicators((v) => !v)}
showBuild={showBuild}
onToggleBuild={() => setShowBuild((v) => !v)}
seriesLabel={seriesLabel} seriesLabel={seriesLabel}
/> />

View File

@@ -18,6 +18,8 @@ type Props = {
onTimeframeChange: (tf: string) => void; onTimeframeChange: (tf: string) => void;
showIndicators: boolean; showIndicators: boolean;
onToggleIndicators: () => void; onToggleIndicators: () => void;
showBuild: boolean;
onToggleBuild: () => void;
seriesLabel: string; seriesLabel: string;
}; };
@@ -49,6 +51,8 @@ export default function ChartPanel({
onTimeframeChange, onTimeframeChange,
showIndicators, showIndicators,
onToggleIndicators, onToggleIndicators,
showBuild,
onToggleBuild,
seriesLabel, seriesLabel,
}: Props) { }: Props) {
const [isFullscreen, setIsFullscreen] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false);
@@ -272,6 +276,8 @@ export default function ChartPanel({
onTimeframeChange={onTimeframeChange} onTimeframeChange={onTimeframeChange}
showIndicators={showIndicators} showIndicators={showIndicators}
onToggleIndicators={onToggleIndicators} onToggleIndicators={onToggleIndicators}
showBuild={showBuild}
onToggleBuild={onToggleBuild}
priceAutoScale={priceAutoScale} priceAutoScale={priceAutoScale}
onTogglePriceAutoScale={() => setPriceAutoScale((v) => !v)} onTogglePriceAutoScale={() => setPriceAutoScale((v) => !v)}
seriesLabel={seriesLabel} seriesLabel={seriesLabel}
@@ -300,6 +306,7 @@ export default function ChartPanel({
ema20={indicators.ema20} ema20={indicators.ema20}
bb20={indicators.bb20} bb20={indicators.bb20}
showIndicators={showIndicators} showIndicators={showIndicators}
showBuild={showBuild}
bucketSeconds={bucketSeconds} bucketSeconds={bucketSeconds}
seriesKey={seriesKey} seriesKey={seriesKey}
fib={fibRenderable} fib={fibRenderable}

View File

@@ -5,6 +5,8 @@ type Props = {
onTimeframeChange: (tf: string) => void; onTimeframeChange: (tf: string) => void;
showIndicators: boolean; showIndicators: boolean;
onToggleIndicators: () => void; onToggleIndicators: () => void;
showBuild: boolean;
onToggleBuild: () => void;
priceAutoScale: boolean; priceAutoScale: boolean;
onTogglePriceAutoScale: () => void; onTogglePriceAutoScale: () => void;
seriesLabel: string; seriesLabel: string;
@@ -19,6 +21,8 @@ export default function ChartToolbar({
onTimeframeChange, onTimeframeChange,
showIndicators, showIndicators,
onToggleIndicators, onToggleIndicators,
showBuild,
onToggleBuild,
priceAutoScale, priceAutoScale,
onTogglePriceAutoScale, onTogglePriceAutoScale,
seriesLabel, seriesLabel,
@@ -45,6 +49,9 @@ export default function ChartToolbar({
<Button size="sm" variant={showIndicators ? 'primary' : 'ghost'} onClick={onToggleIndicators} type="button"> <Button size="sm" variant={showIndicators ? 'primary' : 'ghost'} onClick={onToggleIndicators} type="button">
Indicators Indicators
</Button> </Button>
<Button size="sm" variant={showBuild ? 'primary' : 'ghost'} onClick={onToggleBuild} type="button">
Build
</Button>
<Button size="sm" variant={priceAutoScale ? 'primary' : 'ghost'} onClick={onTogglePriceAutoScale} type="button"> <Button size="sm" variant={priceAutoScale ? 'primary' : 'ghost'} onClick={onTogglePriceAutoScale} type="button">
Auto Scale Auto Scale
</Button> </Button>

View File

@@ -25,6 +25,7 @@ type Props = {
ema20?: SeriesPoint[]; ema20?: SeriesPoint[];
bb20?: { upper: SeriesPoint[]; lower: SeriesPoint[]; mid: SeriesPoint[] }; bb20?: { upper: SeriesPoint[]; lower: SeriesPoint[]; mid: SeriesPoint[] };
showIndicators: boolean; showIndicators: boolean;
showBuild: boolean;
bucketSeconds: number; bucketSeconds: number;
seriesKey: string; seriesKey: string;
fib?: FibRetracement | null; fib?: FibRetracement | null;
@@ -116,6 +117,7 @@ export default function TradingChart({
ema20, ema20,
bb20, bb20,
showIndicators, showIndicators,
showBuild,
bucketSeconds, bucketSeconds,
seriesKey, seriesKey,
fib, fib,
@@ -596,12 +598,29 @@ export default function TradingChart({
s.bbLower?.setData(bbLower); s.bbLower?.setData(bbLower);
s.bbMid?.setData(bbMid); 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) { if (buildKeyRef.current !== seriesKey) {
buildSamplesRef.current.clear(); buildSamplesRef.current.clear();
buildKeyRef.current = seriesKey; buildKeyRef.current = seriesKey;
lastBuildCandleStartRef.current = null; 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 bs = resolveBucketSeconds(bucketSeconds, candles);
const eps = 1e-3; const eps = 1e-3;
const maxPointsPerCandle = 600; const maxPointsPerCandle = 600;
@@ -714,7 +733,21 @@ export default function TradingChart({
s.bbUpper?.applyOptions({ visible: showIndicators }); s.bbUpper?.applyOptions({ visible: showIndicators });
s.bbLower?.applyOptions({ visible: showIndicators }); s.bbLower?.applyOptions({ visible: showIndicators });
s.bbMid?.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(() => { useEffect(() => {
const s = seriesRef.current; const s = seriesRef.current;