가상매매 챠트모드 수정

This commit is contained in:
Macbook
2026-06-08 21:20:54 +09:00
parent 80c78d75bc
commit 77562e5b8f
3 changed files with 42 additions and 34 deletions
@@ -2,7 +2,6 @@ import React, { memo, useMemo } from 'react';
import type { StrategyDto } from '../../utils/backendApi'; import type { StrategyDto } from '../../utils/backendApi';
import { import {
resolveVirtualTargetNames, resolveVirtualTargetNames,
resolveVirtualTargetStrategyDisplayName,
} from '../../utils/virtualTargetNames'; } from '../../utils/virtualTargetNames';
import { resolveVirtualTargetStrategyId } from '../../utils/virtualTargetStrategy'; import { resolveVirtualTargetStrategyId } from '../../utils/virtualTargetStrategy';
import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSnapshots'; import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSnapshots';
@@ -100,11 +99,6 @@ const VirtualTargetCard: React.FC<Props> = ({
koreanName ?? ticker?.koreanName, koreanName ?? ticker?.koreanName,
englishName, englishName,
); );
const strategyDisplayName = resolveVirtualTargetStrategyDisplayName(
strategy,
strategies,
readOnlyStrategyLabel,
);
const rows = snapshot?.rows ?? []; const rows = snapshot?.rows ?? [];
const effectiveStrategyId = resolveVirtualTargetStrategyId( const effectiveStrategyId = resolveVirtualTargetStrategyId(
{ strategyId }, { strategyId },
@@ -160,10 +154,7 @@ const VirtualTargetCard: React.FC<Props> = ({
<div className="vtd-card-head-main"> <div className="vtd-card-head-main">
<div className={`vtd-card-title${isChart ? ' vtd-card-title--chart' : ''}`}> <div className={`vtd-card-title${isChart ? ' vtd-card-title--chart' : ''}`}>
{isChart ? ( {isChart ? (
<> <span className="vtd-card-ko">{displayKo}</span>
<span className="vtd-card-chart-strategy">{strategyDisplayName}</span>
<span className="vtd-card-ko">{displayKo}</span>
</>
) : ( ) : (
<> <>
<span className="vtd-card-ko">{displayKo}</span> <span className="vtd-card-ko">{displayKo}</span>
@@ -16,7 +16,7 @@ import { useHistoryLoader } from '../../hooks/useHistoryLoader';
import { useIndicatorSettings } from '../../hooks/useIndicatorSettings'; import { useIndicatorSettings } from '../../hooks/useIndicatorSettings';
import type { ChartManager } from '../../utils/ChartManager'; import type { ChartManager } from '../../utils/ChartManager';
import { import {
buildVirtualTradingCardChartIndicators, buildStrategyChartIndicators,
buildVirtualTradingChartIndicators, buildVirtualTradingChartIndicators,
resolveStrategyPrimaryTimeframe, resolveStrategyPrimaryTimeframe,
resolveStrategyTimeframes, resolveStrategyTimeframes,
@@ -73,7 +73,7 @@ const VirtualTargetCardChart: React.FC<Props> = ({
const indicators = useMemo(() => ( const indicators = useMemo(() => (
fillHeight fillHeight
? buildVirtualTradingChartIndicators(strategy, getParams, getVisualConfig) ? buildVirtualTradingChartIndicators(strategy, getParams, getVisualConfig)
: buildVirtualTradingCardChartIndicators(strategy, getParams, getVisualConfig) : buildStrategyChartIndicators(strategy, getParams, getVisualConfig)
), [strategy, getParams, getVisualConfig, fillHeight]); ), [strategy, getParams, getVisualConfig, fillHeight]);
const effectiveIndicators = useMemo( const effectiveIndicators = useMemo(
@@ -91,6 +91,17 @@ const VirtualTargetCardChart: React.FC<Props> = ({
const marketRef = useRef(market); const marketRef = useRef(market);
marketRef.current = market; marketRef.current = market;
const syncChartLayout = useCallback(() => {
const wrap = canvasWrapRef.current;
const mgr = managerRef.current;
if (!wrap || !mgr?.hasMainSeries()) return;
const chartWrap = wrap.querySelector<HTMLElement>('.tv-chart-wrap');
const w = chartWrap?.clientWidth ?? wrap.clientWidth;
const h = chartWrap?.clientHeight ?? wrap.clientHeight;
if (w <= 0 || h <= 0) return;
mgr.syncLayout(w, h);
}, []);
const handleCandlesReady = useCallback(() => { const handleCandlesReady = useCallback(() => {
chartLiveReadyRef.current = true; chartLiveReadyRef.current = true;
const pending = pendingBarRef.current; const pending = pendingBarRef.current;
@@ -98,11 +109,8 @@ const VirtualTargetCardChart: React.FC<Props> = ({
if (!pending || !mgr || barsMarketRef.current !== marketRef.current) return; if (!pending || !mgr || barsMarketRef.current !== marketRef.current) return;
pendingBarRef.current = null; pendingBarRef.current = null;
mgr.updateBar(pending); mgr.updateBar(pending);
const wrap = canvasWrapRef.current; syncChartLayout();
if (wrap && wrap.clientHeight > 0) { }, [syncChartLayout]);
mgr.applyCandleOnlyLayout(true, wrap.clientHeight);
}
}, []);
const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => { const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => {
if (barsMarketRef.current !== marketRef.current) return; if (barsMarketRef.current !== marketRef.current) return;
@@ -147,12 +155,6 @@ const VirtualTargetCardChart: React.FC<Props> = ({
const barsReady = bars.length >= 2 && barsMarket === market; const barsReady = bars.length >= 2 && barsMarket === market;
const applyPaneHeights = useCallback(() => {
const wrap = canvasWrapRef.current;
const mgr = managerRef.current;
if (!wrap || wrap.clientHeight <= 0 || !mgr?.hasMainSeries()) return;
mgr.applyCandleOnlyLayout(true, wrap.clientHeight);
}, []);
const requestChartReload = useCallback(() => { const requestChartReload = useCallback(() => {
chartReloadTriggeredRef.current = true; chartReloadTriggeredRef.current = true;
@@ -169,38 +171,36 @@ const VirtualTargetCardChart: React.FC<Props> = ({
const wrap = canvasWrapRef.current; const wrap = canvasWrapRef.current;
if (!wrap) return; if (!wrap) return;
const timers = [100, 300, 800, 1500, 2500].map(delay => const timers = [200, 600, 1200].map(delay =>
setTimeout(() => { setTimeout(() => {
if (!canvasWrapRef.current) return; if (!canvasWrapRef.current) return;
const h = canvasWrapRef.current.clientHeight;
if (h <= 0) return;
if (managerRef.current?.hasMainSeries()) { if (managerRef.current?.hasMainSeries()) {
managerRef.current.applyCandleOnlyLayout(true, h); syncChartLayout();
} else if (!chartReloadTriggeredRef.current && delay >= 800 && barsReady) { } else if (!chartReloadTriggeredRef.current && delay >= 600 && barsReady) {
requestChartReload(); requestChartReload();
} }
}, delay), }, delay),
); );
const ro = new ResizeObserver(() => applyPaneHeights()); const ro = new ResizeObserver(() => syncChartLayout());
ro.observe(wrap); ro.observe(wrap);
return () => { return () => {
timers.forEach(clearTimeout); timers.forEach(clearTimeout);
ro.disconnect(); ro.disconnect();
}; };
}, [market, timeframe, barsReady, applyPaneHeights, requestChartReload]); }, [market, timeframe, barsReady, syncChartLayout, requestChartReload]);
useEffect(() => { useEffect(() => {
if (!barsReady) return; if (!barsReady) return;
if (managerRef.current?.hasMainSeries()) { if (managerRef.current?.hasMainSeries()) {
applyPaneHeights(); syncChartLayout();
return; return;
} }
if (!chartReloadTriggeredRef.current) { if (!chartReloadTriggeredRef.current) {
requestChartReload(); requestChartReload();
} }
}, [barsReady, applyPaneHeights, requestChartReload]); }, [barsReady, syncChartLayout, requestChartReload]);
useEffect(() => { useEffect(() => {
if (!streamActive) return; if (!streamActive) return;
@@ -265,9 +265,10 @@ const VirtualTargetCardChart: React.FC<Props> = ({
volumeVisible={false} volumeVisible={false}
paneLayoutClamp paneLayoutClamp
showHoverToolbar={false} showHoverToolbar={false}
showPaneLegend={false} showPaneLegend
showChartRightToolbar={false}
showCandlePaneControls={false} showCandlePaneControls={false}
defaultCandleOnly crosshairInfoVisible={false}
candleAreaPriceLabelsEnabled={chartCandleAreaPriceLabels} candleAreaPriceLabelsEnabled={chartCandleAreaPriceLabels}
indicatorAreaPriceLabelsEnabled={chartSeriesPriceLabels} indicatorAreaPriceLabelsEnabled={chartSeriesPriceLabels}
paneSeparatorOptions={chartPaneSeparator} paneSeparatorOptions={chartPaneSeparator}
@@ -1608,6 +1608,22 @@
display: none !important; display: none !important;
} }
/* 보조지표 pane 좌측 상단 지표명 */
.vtd-card-chart-canvas .pane-legend-item {
right: 46px;
width: auto;
max-width: none;
}
.vtd-card-chart-canvas .pane-legend-name {
font-size: 10px;
flex: 0 0 auto;
max-width: none;
overflow: visible;
text-overflow: clip;
white-space: nowrap;
}
.vtd-card-chart-canvas > .tv-chart-wrap { .vtd-card-chart-canvas > .tv-chart-wrap {
flex: 1; flex: 1;
min-height: 0; min-height: 0;