실시간 차트 오류 수정
This commit is contained in:
@@ -8,17 +8,18 @@ import { DISPLAY_COUNT } from '../hooks/useUpbitData';
|
||||
/** Upbit 실시간 — 초기 히스토리가 이 개수 미만이면 setData 하지 않음 (종목 전환 직후 sparse 봉 → 캔들 폭 비정상) */
|
||||
const MIN_BARS_FOR_FULL_RELOAD = Math.min(50, Math.floor(DISPLAY_COUNT / 4));
|
||||
|
||||
/** 종목 전환 직후 stale/partial 데이터 차단 — fetch 완료(barsMarket 확정) + 충분한 히스토리만 렌더 */
|
||||
/** 종목 전환 직후 stale/partial 데이터 차단 — barsMarket 확정 후 렌더 허용 */
|
||||
function canApplyChartBars(
|
||||
barData: OHLCVBar[],
|
||||
barsMarket: string | null | undefined,
|
||||
market: string,
|
||||
): boolean {
|
||||
if (barData.length === 0) return false;
|
||||
if (barData.length < 2) return false;
|
||||
if (barsMarket === undefined) return true;
|
||||
if (barsMarket == null || barsMarket !== market) return false;
|
||||
if (!market.startsWith('KRW-')) return barData.length >= 2;
|
||||
return barData.length >= MIN_BARS_FOR_FULL_RELOAD;
|
||||
if (!market.startsWith('KRW-')) return true;
|
||||
if (barData.length >= MIN_BARS_FOR_FULL_RELOAD) return true;
|
||||
return true;
|
||||
}
|
||||
import DrawingCanvas, { hitTestDrawing } from './DrawingCanvas';
|
||||
import PaneLegend, { type PaneLegendProps } from './PaneLegend';
|
||||
@@ -172,6 +173,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
const prevSortedPKRef = useRef<string>(''); // 순서 무관 paramKey (reorder 감지용)
|
||||
const prevMarket = useRef<string>(market);
|
||||
const prevMarketTf = useRef<Timeframe>(timeframe);
|
||||
const lastAppliedMarketRef = useRef<string>('');
|
||||
const prevChartType = useRef<ChartType>(chartType);
|
||||
const prevTheme = useRef<Theme>(theme);
|
||||
const prevLogScale = useRef<boolean>(logScale);
|
||||
@@ -381,19 +383,19 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
barsRef.current = newBars;
|
||||
reloadInFlightRef.current = true;
|
||||
|
||||
try {
|
||||
do {
|
||||
reloadCoalesceRef.current = false;
|
||||
const gen = reloadGenRef.current;
|
||||
if (newBars.length === 0) break;
|
||||
const barData = barsRef.current.length >= 2 ? barsRef.current : newBars;
|
||||
if (barData.length < 2) break;
|
||||
|
||||
reloadSafetyTimers.current.forEach(clearTimeout);
|
||||
reloadSafetyTimers.current = [];
|
||||
|
||||
barsRef.current = newBars;
|
||||
const barData = newBars;
|
||||
barsRef.current = barData;
|
||||
|
||||
mgr.setData(barData, ct, th);
|
||||
if (gen !== reloadGenRef.current) {
|
||||
@@ -405,6 +407,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
mgr.setDisplayTimezone(displayTimezone, timeframe);
|
||||
commitCandleLayout(mgr);
|
||||
onCandlesReady?.();
|
||||
lastAppliedMarketRef.current = market;
|
||||
|
||||
let stale = false;
|
||||
for (const ind of inds) {
|
||||
@@ -449,6 +452,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
prevBarsKey.current = barsKey(barData, market);
|
||||
prevIndKey.current = indKey(inds);
|
||||
prevSortedPKRef.current = sortedParamKey(inds);
|
||||
lastAppliedMarketRef.current = market;
|
||||
|
||||
await new Promise<void>(resolve => requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
applyPaneLayout(mgr);
|
||||
@@ -818,6 +822,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
prevMarketTf.current = timeframe;
|
||||
prevBarsKey.current = '';
|
||||
prevIndKey.current = '';
|
||||
lastAppliedMarketRef.current = '';
|
||||
barsRef.current = [];
|
||||
reloadRetryRef.current = 0;
|
||||
reloadGenRef.current += 1;
|
||||
@@ -832,11 +837,11 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
// ── 데이터/인디케이터 동기화 ─────────────────────────────────────────────
|
||||
useEffect(() => {
|
||||
const mgr = managerRef.current;
|
||||
if (!mgr || !chartMgr || bars.length === 0) return;
|
||||
if (!mgr || !chartMgr || bars.length < 2) return;
|
||||
if (!canApplyChartBars(bars, barsMarket, market)) return;
|
||||
|
||||
// manager 준비 전 bars 가 먼저 도착한 경우(로그인 직후 등) 메인 시리즈 누락 방지
|
||||
if (!mgr.hasMainSeries()) {
|
||||
// manager 준비 전·종목 미적용 시 reloadAll 강제
|
||||
if (!mgr.hasMainSeries() || lastAppliedMarketRef.current !== market) {
|
||||
void reloadAll(mgr, bars, chartType, theme, logScale, indicators);
|
||||
return;
|
||||
}
|
||||
@@ -942,7 +947,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
// 데이터는 준비됐으나 메인 시리즈가 없는 빈 차트 자동 복구 (종목 전환·레이아웃 전환 직후)
|
||||
useEffect(() => {
|
||||
const mgr = managerRef.current;
|
||||
if (!mgr || !chartMgr || bars.length === 0) return;
|
||||
if (!mgr || !chartMgr || bars.length < 2) return;
|
||||
if (!canApplyChartBars(bars, barsMarket, market)) return;
|
||||
if (mgr.hasMainSeries()) return;
|
||||
|
||||
@@ -969,7 +974,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
// 종목 전환 직후 reloadAll 이 중단되어 캔들만 남은 경우 자동 복구
|
||||
useEffect(() => {
|
||||
const mgr = managerRef.current;
|
||||
if (!mgr || !chartMgr || bars.length === 0) return;
|
||||
if (!mgr || !chartMgr || bars.length < 2) return;
|
||||
if (!canApplyChartBars(bars, barsMarket, market)) return;
|
||||
|
||||
const expected = countExpectedVisibleIndicators(indicators);
|
||||
|
||||
Reference in New Issue
Block a user