서버 가상매매 목록 지표없음 문제 개선

This commit is contained in:
Macbook
2026-06-07 19:14:13 +09:00
parent b294c2be5d
commit 2f3133d413
10 changed files with 299 additions and 62 deletions
+48 -15
View File
@@ -52,6 +52,11 @@ import {
reloadAppSettingsCache,
} from './useAppSettings';
import { hydrateVirtualTargetsIfEmpty } from '../utils/virtualTargetsHydrate';
import {
mergeTargetsWithLiveStrategySettings,
mergeVirtualSessionWithAppLive,
resolveVirtualTargetStrategyIdWithApp,
} from '../utils/virtualTargetHydrate';
export interface UseVirtualTradingCoreOptions {
defaultMarket?: string;
@@ -81,6 +86,7 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
const paperAutoTradeEnabled = paperAutoTradeEnabledProp ?? appChartDefaults.paperAutoTradeEnabled;
const paperAutoTradeBudgetPct = paperAutoTradeBudgetPctProp ?? appChartDefaults.paperAutoTradeBudgetPct;
const virtualTargetMaxCount = appChartDefaults.virtualTargetMaxCount;
const appLiveStrategyId = appChartDefaults.liveStrategyId ?? null;
const [targets, setTargets] = useState<VirtualTargetItem[]>(() => loadVirtualTargets());
const [session, setSession] = useState<VirtualSessionConfig>(() => loadVirtualSession());
@@ -95,8 +101,8 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
const reloadFromUiPreferences = useCallback(() => {
setTargets(loadVirtualTargets());
setSession(loadVirtualSession());
}, []);
setSession(mergeVirtualSessionWithAppLive(loadVirtualSession(), appLiveStrategyId));
}, [appLiveStrategyId]);
useEffect(() => {
if (!settingsLoaded) return;
@@ -113,6 +119,20 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
saveVirtualTargets(nextTargets);
}
}
const sessionFromDb = mergeVirtualSessionWithAppLive(loadVirtualSession(), appLiveStrategyId);
if (!cancelled) {
setSession(sessionFromDb);
}
if (nextTargets.length > 0) {
const withLive = await mergeTargetsWithLiveStrategySettings(
nextTargets,
sessionFromDb,
appLiveStrategyId,
);
if (!cancelled && withLive !== nextTargets) {
setTargets(withLive);
}
}
if (!cancelled) {
preferencesHydratedRef.current = true;
}
@@ -123,11 +143,12 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
const validIds = new Set(
s.map(x => x.id).filter((id): id is number => id != null && id > 0),
);
setSession(prev =>
prev.globalStrategyId != null && !validIds.has(prev.globalStrategyId)
setSession(prev => {
const next = prev.globalStrategyId != null && !validIds.has(prev.globalStrategyId)
? { ...prev, globalStrategyId: null }
: prev,
);
: prev;
return mergeVirtualSessionWithAppLive(next, appLiveStrategyId);
});
setTargets(prev =>
prev.map(t =>
t.strategyId != null && !validIds.has(t.strategyId)
@@ -142,7 +163,7 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
if (!cancelled) setLoading(false);
})();
return () => { cancelled = true; };
}, [settingsSessionKey, settingsLoaded, reloadFromUiPreferences]);
}, [settingsSessionKey, settingsLoaded, appLiveStrategyId, reloadFromUiPreferences]);
/** DB ui_preferences 반영(웹·모바일 동일 계정) */
useEffect(() => {
@@ -152,29 +173,40 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
});
}, [settingsLoaded, reloadFromUiPreferences]);
/** DB is_pinned → uiPreferences.virtual.targets 동기화 */
/** DB is_pinned·strategyId → uiPreferences.virtual.targets 동기화 */
useEffect(() => {
if (!settingsLoaded) return;
let cancelled = false;
const initial = loadVirtualTargets();
if (initial.length === 0) return;
const sessionFromDb = mergeVirtualSessionWithAppLive(loadVirtualSession(), appLiveStrategyId);
void (async () => {
const merged = await Promise.all(
initial.map(async t => {
try {
const s = await loadLiveStrategySettings(t.market);
const pinned = !!s.isPinned;
if (!!t.pinned === pinned) return t;
return { ...t, pinned };
let next = t;
if (!!t.pinned !== !!s.isPinned) {
next = { ...next, pinned: !!s.isPinned };
}
if (next.strategyId == null && s.strategyId != null && s.strategyId > 0 && s.isLiveCheck) {
next = { ...next, strategyId: s.strategyId };
}
return next;
} catch {
return t;
}
}),
);
if (!cancelled) setTargets(merged);
const withLive = await mergeTargetsWithLiveStrategySettings(
merged,
sessionFromDb,
appLiveStrategyId,
);
if (!cancelled) setTargets(withLive);
})();
return () => { cancelled = true; };
}, [settingsLoaded]);
}, [settingsLoaded, appLiveStrategyId]);
useEffect(() => {
if (!preferencesHydratedRef.current) return;
@@ -203,9 +235,9 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
const targetRefs = useMemo(
() => targets.map(t => ({
market: t.market,
strategyId: resolveVirtualTargetStrategyId(t, session.globalStrategyId),
strategyId: resolveVirtualTargetStrategyIdWithApp(t, session, appLiveStrategyId),
})),
[targets, session.globalStrategyId],
[targets, session, appLiveStrategyId],
);
const { snapshots, loadingByMarket: snapshotLoadingByMarket } = useVirtualIndicatorSnapshots(
@@ -400,6 +432,7 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
snapshotLoadingByMarket,
liveStatusByMarket: mergedLiveStatus,
lastTickAtByMarket,
appLiveStrategyId,
strategyNames,
virtualTargetMaxCount,
handleStart,