전략편집기 시간봉 선택오류 수정
This commit is contained in:
@@ -573,6 +573,13 @@ export function useChartWorkspace({
|
||||
const [favoritesList, setFavoritesList] = useState<string[]>(getFavorites);
|
||||
// DB 로드 완료 여부 (false → DB 로드 전, localStorage 기반 동작)
|
||||
const [dbLoaded, setDbLoaded] = useState(false);
|
||||
/** DB·앱설정·지표 기본값 반영 완료 — 이후 STOMP 초기 로딩 시작 (이중 페인트 방지) */
|
||||
const [chartBootstrapComplete, setChartBootstrapComplete] = useState(false);
|
||||
/** 싱글 레이아웃 차트 paint 완료 */
|
||||
const [singleChartPaintReady, setSingleChartPaintReady] = useState(false);
|
||||
/** 멀티 슬롯별 paint 완료 */
|
||||
const slotPaintReadyRef = useRef<Record<number, boolean>>({});
|
||||
const [slotPaintEpoch, setSlotPaintEpoch] = useState(0);
|
||||
|
||||
const priorityMarkets = useMemo(
|
||||
() => [symbol, ...watchlist],
|
||||
@@ -1111,6 +1118,7 @@ export function useChartWorkspace({
|
||||
|
||||
const handleCandlesReady = useCallback(() => {
|
||||
chartLiveReadyRef.current = true;
|
||||
setSingleChartPaintReady(true);
|
||||
const pending = pendingRealtimeBarRef.current;
|
||||
const mgr = managerRef.current;
|
||||
if (!pending || !mgr || barsMarketRef.current !== chartContextRef.current.symbol) return;
|
||||
@@ -1118,16 +1126,26 @@ export function useChartWorkspace({
|
||||
mgr.updateBar(pending);
|
||||
}, []);
|
||||
|
||||
const handleSingleChartPaintReadyChange = useCallback((ready: boolean) => {
|
||||
setSingleChartPaintReady(ready);
|
||||
}, []);
|
||||
|
||||
const handleSlotPaintReadyChange = useCallback((slotIndex: number, ready: boolean) => {
|
||||
slotPaintReadyRef.current[slotIndex] = ready;
|
||||
setSlotPaintEpoch(v => v + 1);
|
||||
}, []);
|
||||
|
||||
const chartRealtimeSource = appDefaults.chartRealtimeSource ?? 'BACKEND_STOMP';
|
||||
const chartConfigReady = appSettingsLoaded && dbLoaded && chartBootstrapComplete;
|
||||
const { bars: upbitBars, barsMarket: upbitBarsMarket, latestBar, wsStatus, isLoading, error } = useChartRealtimeData(
|
||||
symbol,
|
||||
timeframe,
|
||||
useMemo(() => ({
|
||||
onTickUpdate: handleTickUpdate,
|
||||
onNewCandle: handleNewCandle,
|
||||
enabled: chartScreenActive && useUpbit,
|
||||
enabled: chartScreenActive && useUpbit && chartConfigReady,
|
||||
source: chartRealtimeSource as 'BACKEND_STOMP' | 'UPBIT_DIRECT',
|
||||
}), [handleTickUpdate, handleNewCandle, chartScreenActive, useUpbit, chartRealtimeSource]),
|
||||
}), [handleTickUpdate, handleNewCandle, chartScreenActive, useUpbit, chartConfigReady, chartRealtimeSource]),
|
||||
);
|
||||
|
||||
// 실제로 차트에 넘길 bars (초기 200개, 실시간 수신 후에도 변경 없음)
|
||||
@@ -1167,8 +1185,15 @@ export function useChartWorkspace({
|
||||
useLayoutEffect(() => {
|
||||
chartLiveReadyRef.current = false;
|
||||
pendingRealtimeBarRef.current = null;
|
||||
setSingleChartPaintReady(false);
|
||||
}, [symbol, timeframe]);
|
||||
|
||||
useEffect(() => {
|
||||
slotPaintReadyRef.current = {};
|
||||
setSlotPaintEpoch(v => v + 1);
|
||||
setSingleChartPaintReady(false);
|
||||
}, [layoutDef.count, sessionKey]);
|
||||
|
||||
// Upbit 초기 로딩 완료 시 stats 업데이트
|
||||
useEffect(() => {
|
||||
if (useUpbit && upbitBars.length > 0) {
|
||||
@@ -1379,6 +1404,10 @@ export function useChartWorkspace({
|
||||
});
|
||||
}, [focusedIndicatorId, indicators, layoutDef.count]);
|
||||
|
||||
useEffect(() => {
|
||||
setSingleChartPaintReady(false);
|
||||
}, [effectiveIndicators]);
|
||||
|
||||
const handleExpandIndicator = useCallback((id: string) => {
|
||||
setFocusedIndicatorId(id);
|
||||
}, []);
|
||||
@@ -1701,7 +1730,7 @@ export function useChartWorkspace({
|
||||
const bulkSettingsIndicators = layoutDef.count > 1 ? activeSlotIndicators : indicators;
|
||||
|
||||
/** 설정 화면에서 DB 기본값 저장 후 차트 복귀 시 — 전역 기본값을 차트 인스턴스에 일괄 반영 */
|
||||
const lastSyncedSettingsRevisionRef = useRef(indicatorSettingsRevision);
|
||||
const lastSyncedSettingsRevisionRef = useRef(-1);
|
||||
const syncChartsFromSavedDefaults = useCallback(() => {
|
||||
const merge = (inds: IndicatorConfig[]) =>
|
||||
mergeGlobalDefaultsOntoChartIndicators(inds, getParams, getVisualConfig);
|
||||
@@ -1729,15 +1758,25 @@ export function useChartWorkspace({
|
||||
}, [layoutDef.count, activeSlot, activeSlotIndicators, getParams, getVisualConfig]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!chartVisible) return;
|
||||
if (indicatorSettingsRevision <= lastSyncedSettingsRevisionRef.current) return;
|
||||
if (consumeSkipNextChartDefaultsSync()) {
|
||||
lastSyncedSettingsRevisionRef.current = indicatorSettingsRevision;
|
||||
if (!appSettingsLoaded || !dbLoaded) {
|
||||
setChartBootstrapComplete(false);
|
||||
return;
|
||||
}
|
||||
syncChartsFromSavedDefaults();
|
||||
lastSyncedSettingsRevisionRef.current = indicatorSettingsRevision;
|
||||
}, [chartVisible, indicatorSettingsRevision, syncChartsFromSavedDefaults]);
|
||||
if (chartVisible && indicatorSettingsRevision > lastSyncedSettingsRevisionRef.current) {
|
||||
if (consumeSkipNextChartDefaultsSync()) {
|
||||
lastSyncedSettingsRevisionRef.current = indicatorSettingsRevision;
|
||||
} else {
|
||||
syncChartsFromSavedDefaults();
|
||||
lastSyncedSettingsRevisionRef.current = indicatorSettingsRevision;
|
||||
}
|
||||
}
|
||||
setChartBootstrapComplete(true);
|
||||
}, [appSettingsLoaded, dbLoaded, chartVisible, indicatorSettingsRevision, syncChartsFromSavedDefaults]);
|
||||
|
||||
useEffect(() => {
|
||||
setChartBootstrapComplete(false);
|
||||
lastSyncedSettingsRevisionRef.current = -1;
|
||||
}, [sessionKey]);
|
||||
|
||||
/** 보조지표 일괄 설정 적용 — 전체 지표 DB + 차트 인스턴스 병합 */
|
||||
const handleBulkIndicatorApply = useCallback((result: {
|
||||
@@ -1911,6 +1950,23 @@ export function useChartWorkspace({
|
||||
settingsPageProps,
|
||||
]);
|
||||
|
||||
const allSlotsPaintReady = useMemo(() => {
|
||||
void slotPaintEpoch;
|
||||
for (let i = 0; i < layoutDef.count; i++) {
|
||||
if (!slotPaintReadyRef.current[i]) return false;
|
||||
}
|
||||
return layoutDef.count > 0;
|
||||
}, [layoutDef.count, slotPaintEpoch]);
|
||||
|
||||
const chartDataFetched = !useUpbit || (!isLoading && bars.length >= 2 && barsMarket === symbol);
|
||||
const chartPaintComplete = layoutDef.count > 1 ? allSlotsPaintReady : singleChartPaintReady;
|
||||
const chartDataLoading = useUpbit && chartVisible && (!chartConfigReady || !chartDataFetched);
|
||||
const chartAreaLoading = useUpbit && chartVisible && (
|
||||
!chartConfigReady
|
||||
|| !chartPaintComplete
|
||||
|| (layoutDef.count === 1 && !chartDataFetched)
|
||||
);
|
||||
|
||||
const viewProps: ChartWorkspaceViewProps = {
|
||||
onOpenNotificationsPage,
|
||||
onOpenAppDownload,
|
||||
@@ -1946,6 +2002,7 @@ export function useChartWorkspace({
|
||||
paperCashBalance, paperCoinQty, paperTradingEnabled, paperAutoTradeEnabled,
|
||||
appDefaults, stats, priceMap, alerts, hoverBar, quoteBar, liveQuote,
|
||||
isLoading, error: error ?? null, bulkSettingsIndicators, chartIndicatorsForSettings,
|
||||
chartConfigReady, chartAreaLoading, chartDataLoading,
|
||||
getParams, getVisualConfig, activeSlotIndicators,
|
||||
setSymbol, setLegend, setChartType, setMode, setLogScale, setShowGrid,
|
||||
setDrawingTool, setDrawings, setDrawingsLocked, setDrawingsVisible,
|
||||
@@ -1974,7 +2031,8 @@ export function useChartWorkspace({
|
||||
handleTogglePriceExtremeLabels,
|
||||
handleCustomOverlayActiveChange, handleCustom1OverlayActiveChange,
|
||||
handleSaveCustomOverlaySelection, syncCustomOverlaysToManager,
|
||||
handleCandlesReady, applyBacktestMarkersWhenReady, btRun, refreshPaperAccount,
|
||||
handleCandlesReady, handleSingleChartPaintReadyChange, handleSlotPaintReadyChange,
|
||||
applyBacktestMarkersWhenReady, btRun, refreshPaperAccount,
|
||||
handlePaperOrderFilled, handleLiveSettingsChange, clearLiveMarkers,
|
||||
goToMarketChart, handleTimezoneChange,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user