백테스팅 오류 수정

This commit is contained in:
Macbook
2026-05-23 21:34:27 +09:00
parent 4df8279a31
commit bbe82e510b
2 changed files with 31 additions and 11 deletions
+30 -11
View File
@@ -650,6 +650,9 @@ function App() {
signals: BacktestSignal[]; signals: BacktestSignal[];
showPrice: boolean; showPrice: boolean;
} | null>(null); } | null>(null);
/** applyBacktestMarkersWhenReady rAF 재시도 시 최신 차트 종목·타임프레임 검증용 */
const chartContextRef = useRef({ symbol, timeframe });
chartContextRef.current = { symbol, timeframe };
const syncBacktestMarkersRef = useRef<() => void>(() => {}); const syncBacktestMarkersRef = useRef<() => void>(() => {});
@@ -658,17 +661,23 @@ function App() {
payload: NonNullable<typeof btMarkerPayloadRef.current>, payload: NonNullable<typeof btMarkerPayloadRef.current>,
attempt = 0, attempt = 0,
) => { ) => {
btMarkerPayloadRef.current = payload;
const { symbol: curSym, timeframe: curTf } = chartContextRef.current;
if (payload.symbol !== curSym || payload.timeframe !== curTf) return;
const mgr = managerRef.current; const mgr = managerRef.current;
if (!mgr || !mgr.hasMainSeries()) { if (!mgr || !mgr.hasMainSeries()) {
if (attempt < 30) { if (attempt < 120) {
requestAnimationFrame(() => applyBacktestMarkersWhenReady(payload, attempt + 1)); requestAnimationFrame(() => applyBacktestMarkersWhenReady(payload, attempt + 1));
} }
return; return;
} }
btMarkerPayloadRef.current = payload; if (payload.symbol !== chartContextRef.current.symbol
|| payload.timeframe !== chartContextRef.current.timeframe) return;
mgr.setBacktestMarkers(payload.signals, payload.showPrice); mgr.setBacktestMarkers(payload.signals, payload.showPrice);
}, []); }, []);
/** reloadAll 직후 등 — payload 가 있을 때만 재적용 (없을 때 clear 하지 않음, 경쟁 방지) */
const syncBacktestMarkers = useCallback(() => { const syncBacktestMarkers = useCallback(() => {
const payload = btMarkerPayloadRef.current; const payload = btMarkerPayloadRef.current;
if ( if (
@@ -678,9 +687,7 @@ function App() {
payload.signals.length > 0 payload.signals.length > 0
) { ) {
applyBacktestMarkersWhenReady(payload); applyBacktestMarkersWhenReady(payload);
return;
} }
managerRef.current?.clearBacktestMarkers();
}, [symbol, timeframe, applyBacktestMarkersWhenReady]); }, [symbol, timeframe, applyBacktestMarkersWhenReady]);
syncBacktestMarkersRef.current = syncBacktestMarkers; syncBacktestMarkersRef.current = syncBacktestMarkers;
@@ -897,16 +904,18 @@ function App() {
// 백테스팅 시그널 → ref 갱신 후 ChartManager 마커 동기화 // 백테스팅 시그널 → ref 갱신 후 ChartManager 마커 동기화
useEffect(() => { useEffect(() => {
if (btMatchesChart && btSignals.length > 0) { if (btMatchesChart && btSignals.length > 0) {
btMarkerPayloadRef.current = { const payload = {
symbol, symbol,
timeframe, timeframe,
signals: btSignals, signals: btSignals,
showPrice: appDefaults.btShowPrice, showPrice: appDefaults.btShowPrice,
}; };
btMarkerPayloadRef.current = payload;
applyBacktestMarkersWhenReady(payload);
} else { } else {
btMarkerPayloadRef.current = null; btMarkerPayloadRef.current = null;
managerRef.current?.clearBacktestMarkers();
} }
syncBacktestMarkers();
let innerRaf = 0; let innerRaf = 0;
const outerRaf = requestAnimationFrame(() => { const outerRaf = requestAnimationFrame(() => {
innerRaf = requestAnimationFrame(syncBacktestMarkers); innerRaf = requestAnimationFrame(syncBacktestMarkers);
@@ -915,7 +924,7 @@ function App() {
cancelAnimationFrame(outerRaf); cancelAnimationFrame(outerRaf);
if (innerRaf) cancelAnimationFrame(innerRaf); if (innerRaf) cancelAnimationFrame(innerRaf);
}; };
}, [btMatchesChart, btSignals, btRunSeq, symbol, timeframe, appDefaults.btShowPrice, syncBacktestMarkers]); }, [btMatchesChart, btSignals, btRunSeq, symbol, timeframe, appDefaults.btShowPrice, applyBacktestMarkersWhenReady, syncBacktestMarkers]);
// ── 시뮬레이션 데이터 (Upbit 아닐 때) ────────────────────────────────── // ── 시뮬레이션 데이터 (Upbit 아닐 때) ──────────────────────────────────
const [simBars, setSimBars] = useState<OHLCVBar[]>(() => const [simBars, setSimBars] = useState<OHLCVBar[]>(() =>
@@ -1716,12 +1725,14 @@ function App() {
r.signals.length > 0 r.signals.length > 0
) { ) {
const showPrice = appDefaults.btShowPrice; const showPrice = appDefaults.btShowPrice;
applyBacktestMarkersWhenReady({ const payload = {
symbol: runSymbol, symbol: runSymbol,
timeframe: runTf, timeframe: runTf,
signals: r.signals, signals: r.signals,
showPrice, showPrice,
}); };
btMarkerPayloadRef.current = payload;
applyBacktestMarkersWhenReady(payload);
} }
if (appDefaults.btAutoPopup) setShowBtResult(true); if (appDefaults.btAutoPopup) setShowBtResult(true);
} }
@@ -1729,7 +1740,11 @@ function App() {
onOpenBtSettings={() => setShowBtSettings(true)} onOpenBtSettings={() => setShowBtSettings(true)}
onOpenBtResults={() => setShowBtResult(true)} onOpenBtResults={() => setShowBtResult(true)}
hasBtResult={btMatchesChart} hasBtResult={btMatchesChart}
onClearBtMarkers={() => { btClear(); managerRef.current?.clearBacktestMarkers(); }} onClearBtMarkers={() => {
btMarkerPayloadRef.current = null;
btClear();
managerRef.current?.clearBacktestMarkers();
}}
onToggleLiveStrategy={() => setShowLivePanel(v => !v)} onToggleLiveStrategy={() => setShowLivePanel(v => !v)}
liveStrategyActive={showLivePanel || monitoredMarkets.length > 0} liveStrategyActive={showLivePanel || monitoredMarkets.length > 0}
/> />
@@ -2003,7 +2018,11 @@ function App() {
<BacktestPanel <BacktestPanel
stats={btStats} stats={btStats}
signalCount={btSignals.length} signalCount={btSignals.length}
onClear={() => { btClear(); managerRef.current?.clearBacktestMarkers(); }} onClear={() => {
btMarkerPayloadRef.current = null;
btClear();
managerRef.current?.clearBacktestMarkers();
}}
/> />
)} )}
+1
View File
@@ -306,6 +306,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
m.resetPaneHeights(w.clientHeight); m.resetPaneHeights(w.clientHeight);
const fb = m.hasIchimoku() ? 28 : 0; const fb = m.hasIchimoku() ? 28 : 0;
m.setInitialVisibleRange(DISPLAY_COUNT, fb); m.setInitialVisibleRange(DISPLAY_COUNT, fb);
if (delay === 1400) onDataLoaded?.();
}, delay) }, delay)
); );
}, [applyPaneLayout, onDataLoaded, displayTimezone, timeframe, market]); }, [applyPaneLayout, onDataLoaded, displayTimezone, timeframe, market]);