가상매매 차트 오류 수정
This commit is contained in:
@@ -16,6 +16,7 @@ import { useHistoryLoader } from '../../hooks/useHistoryLoader';
|
||||
import { useIndicatorSettings } from '../../hooks/useIndicatorSettings';
|
||||
import type { ChartManager } from '../../utils/ChartManager';
|
||||
import {
|
||||
buildVirtualTradingCardChartIndicators,
|
||||
buildVirtualTradingChartIndicators,
|
||||
resolveStrategyPrimaryTimeframe,
|
||||
resolveStrategyTimeframes,
|
||||
@@ -29,7 +30,6 @@ interface Props {
|
||||
theme?: Theme;
|
||||
running?: boolean;
|
||||
chartRealtimeSource?: ChartRealtimeSource;
|
||||
/** 차트 설정 — 지표 가격축 라벨·설명 */
|
||||
chartSeriesPriceLabels?: boolean;
|
||||
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
|
||||
/** 전체보기 분할 pane — 캔버스 높이 100% */
|
||||
@@ -65,10 +65,11 @@ const VirtualTargetCardChart: React.FC<Props> = ({
|
||||
setTimeframe(resolveStrategyPrimaryTimeframe(strategy));
|
||||
}, [strategy, market, chartTimeframe]);
|
||||
|
||||
const indicators = useMemo(
|
||||
() => buildVirtualTradingChartIndicators(strategy, getParams, getVisualConfig),
|
||||
[strategy, getParams, getVisualConfig],
|
||||
);
|
||||
const indicators = useMemo(() => (
|
||||
fillHeight
|
||||
? buildVirtualTradingChartIndicators(strategy, getParams, getVisualConfig)
|
||||
: buildVirtualTradingCardChartIndicators(strategy, getParams, getVisualConfig)
|
||||
), [strategy, getParams, getVisualConfig, fillHeight]);
|
||||
|
||||
const effectiveIndicators = useMemo(
|
||||
() => indicators.filter(ind => ind.timeframeVisibility?.[timeframe] !== false),
|
||||
@@ -92,6 +93,10 @@ const VirtualTargetCardChart: React.FC<Props> = ({
|
||||
if (!pending || !mgr || barsMarketRef.current !== marketRef.current) return;
|
||||
pendingBarRef.current = null;
|
||||
mgr.updateBar(pending);
|
||||
const wrap = canvasWrapRef.current;
|
||||
if (wrap && wrap.clientHeight > 0) {
|
||||
mgr.resetPaneHeights(wrap.clientHeight);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => {
|
||||
@@ -134,6 +139,20 @@ const VirtualTargetCardChart: React.FC<Props> = ({
|
||||
|
||||
barsMarketRef.current = barsMarket;
|
||||
|
||||
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.resetPaneHeights(wrap.clientHeight);
|
||||
}, []);
|
||||
|
||||
const requestChartReload = useCallback(() => {
|
||||
chartReloadTriggeredRef.current = true;
|
||||
setChartReloadTick(t => t + 1);
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
chartLiveReadyRef.current = false;
|
||||
pendingBarRef.current = null;
|
||||
@@ -141,23 +160,41 @@ const VirtualTargetCardChart: React.FC<Props> = ({
|
||||
}, [market, timeframe]);
|
||||
|
||||
useEffect(() => {
|
||||
const timers = [100, 200, 500, 1000, 2000].map(delay =>
|
||||
const wrap = canvasWrapRef.current;
|
||||
if (!wrap) return;
|
||||
|
||||
const timers = [100, 300, 800, 1500, 2500].map(delay =>
|
||||
setTimeout(() => {
|
||||
const wrap = canvasWrapRef.current;
|
||||
const mgr = managerRef.current;
|
||||
if (!wrap) return;
|
||||
const h = wrap.clientHeight;
|
||||
if (!canvasWrapRef.current) return;
|
||||
const h = canvasWrapRef.current.clientHeight;
|
||||
if (h <= 0) return;
|
||||
if (mgr?.hasMainSeries()) {
|
||||
mgr.resetPaneHeights(h);
|
||||
} else if (!chartReloadTriggeredRef.current && delay >= 1000 && bars.length > 0) {
|
||||
chartReloadTriggeredRef.current = true;
|
||||
setChartReloadTick(t => t + 1);
|
||||
if (managerRef.current?.hasMainSeries()) {
|
||||
managerRef.current.resetPaneHeights(h);
|
||||
} else if (!chartReloadTriggeredRef.current && delay >= 800 && barsReady) {
|
||||
requestChartReload();
|
||||
}
|
||||
}, delay),
|
||||
);
|
||||
return () => timers.forEach(clearTimeout);
|
||||
}, [market, timeframe, bars.length]);
|
||||
|
||||
const ro = new ResizeObserver(() => applyPaneHeights());
|
||||
ro.observe(wrap);
|
||||
|
||||
return () => {
|
||||
timers.forEach(clearTimeout);
|
||||
ro.disconnect();
|
||||
};
|
||||
}, [market, timeframe, barsReady, applyPaneHeights, requestChartReload]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!barsReady) return;
|
||||
if (managerRef.current?.hasMainSeries()) {
|
||||
applyPaneHeights();
|
||||
return;
|
||||
}
|
||||
if (!chartReloadTriggeredRef.current) {
|
||||
requestChartReload();
|
||||
}
|
||||
}, [barsReady, applyPaneHeights, requestChartReload]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!useUpbit) return;
|
||||
@@ -195,8 +232,12 @@ const VirtualTargetCardChart: React.FC<Props> = ({
|
||||
<span>과거 데이터 로딩…</span>
|
||||
</div>
|
||||
)}
|
||||
{!isLoading && !barsReady && (
|
||||
<div className="vtd-card-chart-loading">캔들 데이터 수집 중…</div>
|
||||
)}
|
||||
<TradingChart
|
||||
key={`${market}-${timeframe}-${chartReloadTick}`}
|
||||
chartVisible
|
||||
bars={bars}
|
||||
barsMarket={barsMarket}
|
||||
market={market}
|
||||
@@ -210,7 +251,6 @@ const VirtualTargetCardChart: React.FC<Props> = ({
|
||||
logScale={false}
|
||||
drawingsLocked
|
||||
drawingsVisible={false}
|
||||
displayTimezone={DEFAULT_DISPLAY_TIMEZONE}
|
||||
onCrosshair={noop as (d: LegendData | null) => void}
|
||||
onManagerReady={mgr => { managerRef.current = mgr; }}
|
||||
onAddDrawing={noop as (d: Drawing) => void}
|
||||
|
||||
@@ -1313,8 +1313,19 @@
|
||||
color: var(--accent, #3f7ef5);
|
||||
}
|
||||
|
||||
.vtd-card--chart-mode {
|
||||
.vtd-card.vtd-card--chart-mode {
|
||||
min-height: 420px;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.vtd-card.vtd-card--chart-mode > .vtd-card-chart-panel {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.vtd-grid:has(.vtd-card--chart-mode) {
|
||||
grid-auto-rows: minmax(440px, auto);
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.vtd-card-chart-panel {
|
||||
|
||||
@@ -266,3 +266,12 @@ export function buildVirtualTradingChartIndicators(
|
||||
if (!defaultIchimoku) return strategyIndicators;
|
||||
return [defaultIchimoku, ...strategyIndicators];
|
||||
}
|
||||
|
||||
/** 가상매매 카드 인라인 차트 — 캔들 영역 확보(일목·다중 지표 pane 제외, 전략 지표 최대 1개) */
|
||||
export function buildVirtualTradingCardChartIndicators(
|
||||
strategy: StrategyDto | undefined,
|
||||
getParams: GetParams,
|
||||
getVisual: GetVisual,
|
||||
): IndicatorConfig[] {
|
||||
return buildStrategyChartIndicators(strategy, getParams, getVisual).slice(0, 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user