벡테스팅 수정

This commit is contained in:
Macbook
2026-06-03 02:04:30 +09:00
parent 88fe92d8c3
commit 8de3b77479
14 changed files with 156 additions and 86 deletions
+1
View File
@@ -812,6 +812,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
const mgr = new ChartManager(containerRef.current, theme, {
compactPaneLayout: paneLayoutClamp,
autoSize: !paneLayoutClamp,
volumePaneEnabled: volumeVisibleRef.current,
});
managerRef.current = mgr;
setChartMgr(mgr);
+5 -13
View File
@@ -58,7 +58,7 @@ import {
import { pinStrategyEvaluationTimeframes } from '../utils/strategyTimeframePin';
import { persistVirtualTargetPinned } from '../utils/virtualTargetMutations';
import {
coalesceTargetsToDefaultStrategy,
freezeTargetsBeforeGlobalStrategyChange,
resolveVirtualTargetStrategyId,
} from '../utils/virtualTargetStrategy';
import { virtualTargetLimitMessage } from '../utils/virtualTargetLimits';
@@ -165,12 +165,6 @@ const VirtualTradingPage: React.FC<Props> = ({
useEffect(() => { saveVirtualTargets(targets); }, [targets]);
useEffect(() => { saveVirtualSession(session); }, [session]);
/** 예전 데이터: 종목 strategyId에 전역 ID가 복사된 경우 → 기본 전략 따름(null) */
useEffect(() => {
if (session.globalStrategyId == null) return;
setTargets(prev => coalesceTargetsToDefaultStrategy(prev, session.globalStrategyId));
}, [session.globalStrategyId]);
useEffect(() => { saveVirtualCardViewMode(viewMode); }, [viewMode]);
useEffect(() => { saveVirtualGridPreset(gridPreset); }, [gridPreset]);
@@ -299,13 +293,11 @@ const VirtualTradingPage: React.FC<Props> = ({
});
}, []);
/** 상단 기본전략 — 신규 추가 종목 템플릿만 변경, 기존 종목 전략은 유지 */
const handleGlobalStrategyChange = useCallback((strategyId: number | null) => {
const next = { ...session, globalStrategyId: strategyId };
setSession(next);
if (session.running && strategyId) {
void syncVirtualTargetsToBackend(targets, next, true);
}
}, [session, targets]);
setTargets(prev => freezeTargetsBeforeGlobalStrategyChange(prev, session.globalStrategyId));
setSession(prev => ({ ...prev, globalStrategyId: strategyId }));
}, [session.globalStrategyId]);
const handleStart = useCallback(async () => {
if (targets.length === 0) {
@@ -77,7 +77,7 @@ const VirtualLeftTargetPanel: React.FC<Props> = ({
...targets,
{
market,
strategyId: null,
strategyId: globalStrategyId,
koreanName: names.koreanName,
englishName: names.englishName,
},
@@ -33,14 +33,13 @@ const VirtualStrategyChartPopup: React.FC<Props> = ({
const chartSeriesPriceLabels = chartDefaults.chartSeriesPriceLabels;
const ko = getKoreanName(market);
const sym = market.replace(/^KRW-/, '');
const stratName = strategy?.name ?? '전략 미지정';
return (
<AppPopup
onClose={onClose}
title={sym}
titleKo={ko}
title={ko}
titleKo={stratName}
badge="CHART"
width={920}
maxWidth="96vw"
@@ -1,6 +1,9 @@
import React, { useMemo } from 'react';
import { getKoreanName } from '../../utils/marketNameCache';
import type { StrategyDto } from '../../utils/backendApi';
import {
resolveVirtualTargetNames,
resolveVirtualTargetStrategyDisplayName,
} from '../../utils/virtualTargetNames';
import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSnapshots';
import { useLiveReceiveFlash } from '../../hooks/useLiveReceiveFlash';
import type { ChartRealtimeSource } from '../../hooks/useChartRealtimeData';
@@ -47,6 +50,9 @@ interface Props {
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
chartLiveReceiveHighlight?: boolean;
ticker?: TickerData;
/** 투자대상 저장 한글명·영문 심볼 (없으면 티커·마켓 캐시) */
koreanName?: string;
englishName?: string;
/** 알림 목록 등 — 푸터 전략 셀렉트 읽기 전용 라벨 */
readOnlyStrategyLabel?: string;
/** 헤더 우측 레이아웃 (알림 목록 신호 슬롯: inline-quote) */
@@ -78,12 +84,22 @@ const VirtualTargetCard: React.FC<Props> = ({
chartPaneSeparator,
chartLiveReceiveHighlight = true,
ticker,
koreanName,
englishName,
readOnlyStrategyLabel,
headVariant = 'default',
}) => {
const inlineHeadQuote = headVariant === 'inline-quote';
const ko = getKoreanName(market);
const sym = market.replace(/^KRW-/, '');
const { koreanName: displayKo, englishName: displayEn } = resolveVirtualTargetNames(
market,
koreanName ?? ticker?.koreanName,
englishName,
);
const strategyDisplayName = resolveVirtualTargetStrategyDisplayName(
strategy,
strategies,
readOnlyStrategyLabel,
);
const rows = snapshot?.rows ?? [];
const metrics = useMemo(() => buildConditionMetrics(rows), [rows]);
@@ -129,9 +145,18 @@ const VirtualTargetCard: React.FC<Props> = ({
>
<div className="vtd-card-head">
<div className="vtd-card-head-main">
<div className="vtd-card-title">
<span className="vtd-card-ko">{ko}</span>
<span className="vtd-card-sym">{sym}</span>
<div className={`vtd-card-title${isChart ? ' vtd-card-title--chart' : ''}`}>
{isChart ? (
<>
<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-sym">{displayEn}</span>
</>
)}
</div>
</div>
{inlineHeadQuote ? (
@@ -259,6 +259,7 @@ const VirtualTargetCardChart: React.FC<Props> = ({
onCandlesReady={handleCandlesReady}
magnifierEnabled={false}
volumeVisible={false}
paneLayoutClamp
showHoverToolbar={false}
showPaneLegend={false}
showCandlePaneControls={false}
@@ -1,6 +1,9 @@
import React, { useMemo } from 'react';
import { getKoreanName } from '../../utils/marketNameCache';
import type { StrategyDto } from '../../utils/backendApi';
import {
resolveVirtualTargetNames,
resolveVirtualTargetStrategyDisplayName,
} from '../../utils/virtualTargetNames';
import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSnapshots';
import { useLiveReceiveFlash } from '../../hooks/useLiveReceiveFlash';
import type { ChartRealtimeSource } from '../../hooks/useChartRealtimeData';
@@ -37,6 +40,9 @@ interface Props {
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
chartLiveReceiveHighlight?: boolean;
ticker?: TickerData;
koreanName?: string;
englishName?: string;
readOnlyStrategyLabel?: string;
}
/** 전체보기 — 좌 차트 · 우 신호 */
@@ -61,9 +67,20 @@ const VirtualTargetFocusView: React.FC<Props> = ({
chartPaneSeparator,
chartLiveReceiveHighlight = true,
ticker,
koreanName,
englishName,
readOnlyStrategyLabel,
}) => {
const ko = getKoreanName(market);
const sym = market.replace(/^KRW-/, '');
const { koreanName: displayKo } = resolveVirtualTargetNames(
market,
koreanName ?? ticker?.koreanName,
englishName,
);
const strategyDisplayName = resolveVirtualTargetStrategyDisplayName(
strategy,
strategies,
readOnlyStrategyLabel,
);
const receiveSignal = useMemo(() => {
if (!running) return null;
@@ -80,9 +97,9 @@ const VirtualTargetFocusView: React.FC<Props> = ({
<div className={`vtd-focus-wrap${highlightReceiving ? ' vtd-focus-wrap--receiving' : ''}`}>
<div className="vtd-focus-head">
<div className="vtd-focus-head-main">
<div className="vtd-focus-title">
<span className="vtd-card-ko">{ko}</span>
<span className="vtd-card-sym">{sym}</span>
<div className="vtd-focus-title vtd-card-title--chart">
<span className="vtd-card-chart-strategy">{strategyDisplayName}</span>
<span className="vtd-card-ko">{displayKo}</span>
</div>
{running && <VirtualLiveBadge status={liveStatus} receiving={receiving} />}
</div>
@@ -140,6 +140,8 @@ const VirtualTargetGrid: React.FC<Props> = ({
chartPaneSeparator={chartPaneSeparator}
chartLiveReceiveHighlight={chartLiveReceiveHighlight}
ticker={tickers?.get(focusTarget.market)}
koreanName={focusTarget.koreanName}
englishName={focusTarget.englishName}
/>
) : (
<div
@@ -180,6 +182,8 @@ const VirtualTargetGrid: React.FC<Props> = ({
chartPaneSeparator={chartPaneSeparator}
chartLiveReceiveHighlight={chartLiveReceiveHighlight}
ticker={tickers?.get(t.market)}
koreanName={t.koreanName}
englishName={t.englishName}
/>
);
})}