실시간 차트 갱신오류 수정
This commit is contained in:
@@ -7,6 +7,20 @@ import { DISPLAY_COUNT } from '../hooks/useUpbitData';
|
|||||||
|
|
||||||
/** Upbit 실시간 — 초기 히스토리가 이 개수 미만이면 setData 하지 않음 (종목 전환 직후 1~2봉만 로드되는 깨짐 방지) */
|
/** Upbit 실시간 — 초기 히스토리가 이 개수 미만이면 setData 하지 않음 (종목 전환 직후 1~2봉만 로드되는 깨짐 방지) */
|
||||||
const MIN_BARS_FOR_FULL_RELOAD = Math.min(50, Math.floor(DISPLAY_COUNT / 4));
|
const MIN_BARS_FOR_FULL_RELOAD = Math.min(50, Math.floor(DISPLAY_COUNT / 4));
|
||||||
|
|
||||||
|
/** 종목 전환 직후 stale/partial 데이터 차단 — fetch 완료(barsMarket 확정) 후 sparse history도 렌더 허용 */
|
||||||
|
function canApplyChartBars(
|
||||||
|
barData: OHLCVBar[],
|
||||||
|
barsMarket: string | null | undefined,
|
||||||
|
market: string,
|
||||||
|
): boolean {
|
||||||
|
if (barData.length === 0) return false;
|
||||||
|
if (barsMarket === undefined) return true;
|
||||||
|
if (barsMarket == null || barsMarket !== market) return false;
|
||||||
|
if (!market.startsWith('KRW-')) return true;
|
||||||
|
if (barData.length >= MIN_BARS_FOR_FULL_RELOAD) return true;
|
||||||
|
return barData.length >= 2;
|
||||||
|
}
|
||||||
import DrawingCanvas, { hitTestDrawing } from './DrawingCanvas';
|
import DrawingCanvas, { hitTestDrawing } from './DrawingCanvas';
|
||||||
import PaneLegend, { type PaneLegendProps } from './PaneLegend';
|
import PaneLegend, { type PaneLegendProps } from './PaneLegend';
|
||||||
import ChartHoverToolbar from './ChartHoverToolbar';
|
import ChartHoverToolbar from './ChartHoverToolbar';
|
||||||
@@ -167,14 +181,9 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
const [candleOnlyMode, setCandleOnlyMode] = useState(false);
|
const [candleOnlyMode, setCandleOnlyMode] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ready = barsMarket == null
|
if (barsMarket === market) {
|
||||||
? false
|
|
||||||
: barsMarket === undefined
|
|
||||||
? true
|
|
||||||
: barsMarket === market;
|
|
||||||
if (ready) {
|
|
||||||
barsRef.current = bars;
|
barsRef.current = bars;
|
||||||
} else if (barsMarket != null && barsMarket !== market) {
|
} else {
|
||||||
barsRef.current = [];
|
barsRef.current = [];
|
||||||
}
|
}
|
||||||
}, [bars, barsMarket, market]);
|
}, [bars, barsMarket, market]);
|
||||||
@@ -296,8 +305,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
const barData = barsRef.current;
|
const barData = barsRef.current;
|
||||||
const inds = indicatorsRef.current;
|
const inds = indicatorsRef.current;
|
||||||
if (!m || barData.length === 0) return;
|
if (!m || barData.length === 0) return;
|
||||||
if (barsMarket !== undefined && barsMarket !== market) return;
|
if (!canApplyChartBars(barData, barsMarket, market)) return;
|
||||||
if (market.startsWith('KRW-') && barData.length < MIN_BARS_FOR_FULL_RELOAD) return;
|
|
||||||
prevBarsKey.current = '';
|
prevBarsKey.current = '';
|
||||||
reloadAllRef.current?.(
|
reloadAllRef.current?.(
|
||||||
m, barData,
|
m, barData,
|
||||||
@@ -315,7 +323,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
const barData = barsRef.current;
|
const barData = barsRef.current;
|
||||||
const inds = indicatorsRef.current;
|
const inds = indicatorsRef.current;
|
||||||
if (!m || barData.length === 0) return;
|
if (!m || barData.length === 0) return;
|
||||||
if (barsMarket !== undefined && barsMarket !== market) return;
|
if (!canApplyChartBars(barData, barsMarket, market)) return;
|
||||||
|
|
||||||
const expected = countExpectedVisibleIndicators(inds);
|
const expected = countExpectedVisibleIndicators(inds);
|
||||||
if (expected === 0) return;
|
if (expected === 0) return;
|
||||||
@@ -699,8 +707,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
// 데이터가 이미 barsRef에 있지만 차트에 아직 세팅되지 않은 경우 (예: 멀티레이아웃 초기 렌더) 재로드
|
// 데이터가 이미 barsRef에 있지만 차트에 아직 세팅되지 않은 경우 (예: 멀티레이아웃 초기 렌더) 재로드
|
||||||
if (barsRef.current.length > 0 && !m.hasMainSeries()) {
|
if (barsRef.current.length > 0 && !m.hasMainSeries()) {
|
||||||
const bm = barsMarketRef.current;
|
const bm = barsMarketRef.current;
|
||||||
if (bm != null && bm !== marketRef.current) return;
|
if (!canApplyChartBars(barsRef.current, bm, marketRef.current)) return;
|
||||||
if (marketRef.current.startsWith('KRW-') && barsRef.current.length < MIN_BARS_FOR_FULL_RELOAD) return;
|
|
||||||
void reloadAll(
|
void reloadAll(
|
||||||
m,
|
m,
|
||||||
barsRef.current,
|
barsRef.current,
|
||||||
@@ -723,6 +730,22 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
});
|
});
|
||||||
ro.observe(containerRef.current);
|
ro.observe(containerRef.current);
|
||||||
|
|
||||||
|
const bootstrapIfReady = () => {
|
||||||
|
const m = managerRef.current;
|
||||||
|
if (!m || m.hasMainSeries()) return;
|
||||||
|
const data = barsRef.current;
|
||||||
|
if (!canApplyChartBars(data, barsMarketRef.current, marketRef.current)) return;
|
||||||
|
void reloadAllRef.current(
|
||||||
|
m,
|
||||||
|
data,
|
||||||
|
prevChartType.current,
|
||||||
|
prevTheme.current,
|
||||||
|
prevLogScale.current,
|
||||||
|
indicatorsRef.current,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
requestAnimationFrame(() => requestAnimationFrame(bootstrapIfReady));
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsub();
|
unsub();
|
||||||
unsubClick();
|
unsubClick();
|
||||||
@@ -772,12 +795,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mgr = managerRef.current;
|
const mgr = managerRef.current;
|
||||||
if (!mgr || !chartMgr || bars.length === 0) return;
|
if (!mgr || !chartMgr || bars.length === 0) return;
|
||||||
|
if (!canApplyChartBars(bars, barsMarket, market)) return;
|
||||||
const barsReady = barsMarket === undefined
|
|
||||||
? true
|
|
||||||
: barsMarket === market;
|
|
||||||
if (!barsReady) return;
|
|
||||||
if (market.startsWith('KRW-') && bars.length < MIN_BARS_FOR_FULL_RELOAD) return;
|
|
||||||
|
|
||||||
// manager 준비 전 bars 가 먼저 도착한 경우(로그인 직후 등) 메인 시리즈 누락 방지
|
// manager 준비 전 bars 가 먼저 도착한 경우(로그인 직후 등) 메인 시리즈 누락 방지
|
||||||
if (!mgr.hasMainSeries()) {
|
if (!mgr.hasMainSeries()) {
|
||||||
@@ -887,7 +905,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mgr = managerRef.current;
|
const mgr = managerRef.current;
|
||||||
if (!mgr || !chartMgr || bars.length === 0) return;
|
if (!mgr || !chartMgr || bars.length === 0) return;
|
||||||
if (barsMarket !== undefined && barsMarket !== market) return;
|
if (!canApplyChartBars(bars, barsMarket, market)) return;
|
||||||
|
|
||||||
const expected = countExpectedVisibleIndicators(indicators);
|
const expected = countExpectedVisibleIndicators(indicators);
|
||||||
if (expected === 0) return;
|
if (expected === 0) return;
|
||||||
@@ -897,7 +915,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
const m = managerRef.current;
|
const m = managerRef.current;
|
||||||
if (!m || !m.hasMainSeries()) return;
|
if (!m || !m.hasMainSeries()) return;
|
||||||
if (barsMarket !== undefined && barsMarket !== market) return;
|
if (!canApplyChartBars(bars, barsMarket, market)) return;
|
||||||
const exp = countExpectedVisibleIndicators(indicatorsRef.current);
|
const exp = countExpectedVisibleIndicators(indicatorsRef.current);
|
||||||
if (exp === 0 || m.getLoadedIndicatorCount() >= exp) return;
|
if (exp === 0 || m.getLoadedIndicatorCount() >= exp) return;
|
||||||
prevBarsKey.current = '';
|
prevBarsKey.current = '';
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export function useStompChartData(
|
|||||||
.then(newBars => {
|
.then(newBars => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
barsRef.current = newBars;
|
barsRef.current = newBars;
|
||||||
historyReadyRef.current = newBars.length > 0;
|
historyReadyRef.current = true;
|
||||||
setBarsMarket(market);
|
setBarsMarket(market);
|
||||||
setBars(newBars);
|
setBars(newBars);
|
||||||
setLatestBar(newBars[newBars.length - 1] ?? null);
|
setLatestBar(newBars[newBars.length - 1] ?? null);
|
||||||
@@ -269,6 +269,9 @@ export function useStompChartData(
|
|||||||
last1mVolRef.current = 0;
|
last1mVolRef.current = 0;
|
||||||
last1mTimeRef.current = 0;
|
last1mTimeRef.current = 0;
|
||||||
void pinChartWatch(market, STOMP_TICK_CANDLE_TYPE);
|
void pinChartWatch(market, STOMP_TICK_CANDLE_TYPE);
|
||||||
|
if (candleType !== STOMP_TICK_CANDLE_TYPE) {
|
||||||
|
void pinChartWatch(market, candleType);
|
||||||
|
}
|
||||||
|
|
||||||
const topic = `/sub/charts/${market}/${STOMP_TICK_CANDLE_TYPE}`;
|
const topic = `/sub/charts/${market}/${STOMP_TICK_CANDLE_TYPE}`;
|
||||||
let alive = true;
|
let alive = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user