전략분봉 저장 에러 문제 수정

This commit is contained in:
Macbook
2026-05-23 21:08:46 +09:00
parent eaf067db1c
commit 70ac67afe7
18 changed files with 2445 additions and 498 deletions
@@ -0,0 +1,114 @@
import React, { useEffect, useRef, useState } from 'react';
import { createChart, CandlestickSeries, type IChartApi, type ISeriesApi, type Time, ColorType } from 'lightweight-charts';
import type { Timeframe } from '../../types';
import { fetchUpbitCandles } from '../../utils/upbitApi';
const TF_OPTIONS: Timeframe[] = ['1m', '5m', '15m', '1h', '4h', '1D'];
interface Props {
market: string;
wsLabel?: string;
}
const PaperMiniChart: React.FC<Props> = ({ market, wsLabel = 'WebSocket <100ms' }) => {
const containerRef = useRef<HTMLDivElement>(null);
const chartRef = useRef<IChartApi | null>(null);
const seriesRef = useRef<ISeriesApi<'Candlestick'> | null>(null);
const [timeframe, setTimeframe] = useState<Timeframe>('1h');
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!containerRef.current) return undefined;
const chart = createChart(containerRef.current, {
layout: {
background: { type: ColorType.Solid, color: '#121520' },
textColor: '#9aa5ce',
},
grid: {
vertLines: { color: 'rgba(122,162,247,0.06)' },
horzLines: { color: 'rgba(122,162,247,0.06)' },
},
rightPriceScale: { borderColor: 'rgba(122,162,247,0.12)' },
timeScale: { borderColor: 'rgba(122,162,247,0.12)' },
crosshair: { mode: 1 },
});
const series = chart.addSeries(CandlestickSeries, {
upColor: '#22c55e',
downColor: '#ef4444',
borderUpColor: '#22c55e',
borderDownColor: '#ef4444',
wickUpColor: '#22c55e',
wickDownColor: '#ef4444',
});
chartRef.current = chart;
seriesRef.current = series;
const ro = new ResizeObserver(() => {
if (containerRef.current) {
chart.applyOptions({
width: containerRef.current.clientWidth,
height: containerRef.current.clientHeight,
});
}
});
ro.observe(containerRef.current);
return () => {
ro.disconnect();
chart.remove();
chartRef.current = null;
seriesRef.current = null;
};
}, []);
useEffect(() => {
let cancelled = false;
setLoading(true);
(async () => {
try {
const bars = await fetchUpbitCandles(market, timeframe, 200);
if (cancelled || !seriesRef.current) return;
seriesRef.current.setData(
bars.map(b => ({
time: b.time as Time,
open: b.open,
high: b.high,
low: b.low,
close: b.close,
})),
);
chartRef.current?.timeScale().fitContent();
} catch {
/* ignore */
} finally {
if (!cancelled) setLoading(false);
}
})();
return () => { cancelled = true; };
}, [market, timeframe]);
return (
<div className="ptd-chart-wrap">
<div className="ptd-chart-head">
<span className="ptd-chart-title"> </span>
<span className="ptd-ws-badge">{wsLabel} </span>
</div>
<div ref={containerRef} className="ptd-chart-canvas" />
{loading && <div className="ptd-chart-loading"> </div>}
<div className="ptd-tf-row">
{TF_OPTIONS.map(tf => (
<button
key={tf}
type="button"
className={`ptd-tf-btn${timeframe === tf ? ' ptd-tf-btn--active' : ''}`}
onClick={() => setTimeframe(tf)}
>
{tf}
</button>
))}
</div>
</div>
);
};
export default PaperMiniChart;