가상투자 종목카드박스 레이아웃 수정

This commit is contained in:
Macbook
2026-05-26 16:42:57 +09:00
parent 7cf43609dc
commit c0d21ac825
25 changed files with 1632 additions and 61 deletions
+63 -1
View File
@@ -7,6 +7,8 @@ import {
loadPaperTrades,
loadStrategies,
saveLiveStrategySettings,
pinCandleWatch,
loadLiveStrategySettings,
type PaperSummaryDto,
type PaperTradeDto,
type StrategyDto,
@@ -39,7 +41,9 @@ import {
type VirtualSessionConfig,
type VirtualTargetItem,
type VirtualCardViewMode,
resolveTargetCandleType,
} from '../utils/virtualTradingStorage';
import { normalizeStartCandleType } from '../utils/strategyStartNodes';
import { useAppSettings, resolveAppDefaults } from '../hooks/useAppSettings';
import { coerceFiniteNumber } from '../utils/safeFormat';
import {
@@ -231,10 +235,15 @@ const VirtualTradingPage: React.FC<Props> = ({
t.market === market ? { ...t, strategyId } : t,
));
if (!session.running || !strategyId) return;
const target = targets.find(t => t.market === market);
const candleType = normalizeStartCandleType(
resolveTargetCandleType(target ?? { candleType: undefined }, snapshots[market]?.timeframe),
);
try {
await saveLiveStrategySettings({
market,
strategyId,
candleType,
isLiveCheck: true,
executionType: session.executionType,
positionMode: session.positionMode,
@@ -243,7 +252,59 @@ const VirtualTradingPage: React.FC<Props> = ({
} catch {
window.alert('종목 전략 변경 저장에 실패했습니다.');
}
}, [session]);
}, [session, targets, snapshots]);
const handleTargetCandleTypeChange = useCallback(async (market: string, candleType: string) => {
const normalized = normalizeStartCandleType(candleType);
setTargets(prev => prev.map(t =>
t.market === market ? { ...t, candleType: normalized } : t,
));
if (!session.running) return;
const target = targets.find(t => t.market === market);
const strategyId = target?.strategyId ?? session.globalStrategyId;
if (!strategyId) return;
try {
await saveLiveStrategySettings({
market,
strategyId,
candleType: normalized,
isLiveCheck: true,
executionType: session.executionType,
positionMode: session.positionMode,
skipWatchlistSync: true,
});
await pinCandleWatch(market, normalized);
if (normalized !== '1m') {
await pinCandleWatch(market, '1m');
}
} catch {
window.alert('종목 평가 분봉 변경 저장에 실패했습니다.');
}
}, [session, targets]);
useEffect(() => {
let cancelled = false;
const missing = targets.filter(t => !t.candleType);
if (missing.length === 0) return;
void Promise.all(
missing.map(async t => {
try {
const s = await loadLiveStrategySettings(t.market);
return { market: t.market, candleType: s.candleType };
} catch {
return { market: t.market, candleType: undefined };
}
}),
).then(rows => {
if (cancelled) return;
setTargets(prev => prev.map(t => {
const row = rows.find(r => r.market === t.market);
if (t.candleType || !row?.candleType) return t;
return { ...t, candleType: normalizeStartCandleType(row.candleType) };
}));
});
return () => { cancelled = true; };
}, [targets.map(t => `${t.market}:${t.candleType ?? ''}`).join('|')]);
const resyncRunningSession = useCallback(async (nextSession: VirtualSessionConfig) => {
if (!session.running || targets.length === 0) return;
@@ -334,6 +395,7 @@ const VirtualTradingPage: React.FC<Props> = ({
snapshots={snapshots}
session={session}
onTargetStrategyChange={(market, strategyId) => void handleTargetStrategyChange(market, strategyId)}
onTargetCandleTypeChange={(market, ct) => void handleTargetCandleTypeChange(market, ct)}
liveStatusByMarket={mergedLiveStatus}
lastTickAtByMarket={lastTickAtByMarket}
selectedMarket={selectedMarket}