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 [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}
/>

View File

@@ -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}

View File

@@ -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({
<Button size="sm" variant={showIndicators ? 'primary' : 'ghost'} onClick={onToggleIndicators} type="button">
Indicators
</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">
Auto Scale
</Button>

View File

@@ -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;