Skip to content

Instantly share code, notes, and snippets.

@SnowyPainter
Created December 2, 2024 23:45
Show Gist options
  • Save SnowyPainter/22c70cfb00632ea10bbeaa2b31f8f96a to your computer and use it in GitHub Desktop.
Save SnowyPainter/22c70cfb00632ea10bbeaa2b31f8f96a to your computer and use it in GitHub Desktop.
// 전역 데이터 저장소
let chartData = [];
let svg, x, y, width, height, margin;
function initializeChart() {
margin = { top: 20, right: 20, bottom: 30, left: 50 };
width = 800 - margin.left - margin.right;
height = 400 - margin.top - margin.bottom;
// SVG 설정
svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// 스케일 초기 설정
x = d3.scaleBand()
.range([0, width])
.padding(0.2);
y = d3.scaleLinear()
.range([height, 0]);
// 축 생성
svg.append("g")
.attr("class", "x-axis")
.attr("transform", `translate(0,${height})`);
svg.append("g")
.attr("class", "y-axis");
}
function updateChart() {
const parseTime = d3.timeParse("%Y-%m-%d %H:%M:%S");
chartData.forEach(d => {
if (typeof d.timestamp === 'string') {
d.timestamp = parseTime(d.timestamp);
}
});
// 스케일 도메인 업데이트
x.domain(chartData.map(d => d.timestamp));
y.domain([
d3.min(chartData, d => d.low) * 0.999,
d3.max(chartData, d => d.high) * 1.001
]).nice();
// 축 업데이트
svg.select(".x-axis")
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%H:%M")));
svg.select(".y-axis")
.call(d3.axisLeft(y));
// 캔들스틱 업데이트
const candles = svg.selectAll(".candle")
.data(chartData, d => d.timestamp);
// 새로운 캔들스틱 추가
candles.enter()
.append("rect")
.attr("class", d => `candle ${d.close > d.open ? 'up' : 'down'}`)
.attr("x", d => x(d.timestamp))
.attr("y", d => y(Math.max(d.open, d.close)))
.attr("width", x.bandwidth())
.attr("height", d => Math.abs(y(d.open) - y(d.close)));
// 기존 캔들스틱 업데이트
candles
.attr("class", d => `candle ${d.close > d.open ? 'up' : 'down'}`)
.attr("x", d => x(d.timestamp))
.attr("y", d => y(Math.max(d.open, d.close)))
.attr("width", x.bandwidth())
.attr("height", d => Math.abs(y(d.open) - y(d.close)));
// 오래된 캔들스틱 제거
candles.exit().remove();
// 고가/저가 선 업데이트
const wicks = svg.selectAll(".wick")
.data(chartData, d => d.timestamp);
// 새로운 선 추가
wicks.enter()
.append("line")
.attr("class", "wick")
.attr("x1", d => x(d.timestamp) + x.bandwidth() / 2)
.attr("x2", d => x(d.timestamp) + x.bandwidth() / 2)
.attr("y1", d => y(d.high))
.attr("y2", d => y(d.low))
.attr("stroke", "black");
// 기존 선 업데이트
wicks
.attr("x1", d => x(d.timestamp) + x.bandwidth() / 2)
.attr("x2", d => x(d.timestamp) + x.bandwidth() / 2)
.attr("y1", d => y(d.high))
.attr("y2", d => y(d.low));
// 오래된 선 제거
wicks.exit().remove();
// 예측 포인트 업데이트
const points = svg.selectAll(".prediction-point")
.data(chartData.filter(d => d.prediction > 0), d => d.timestamp);
// 새로운 포인트 추가
points.enter()
.append("circle")
.attr("class", d => `prediction-point prediction-${d.prediction}`)
.attr("cx", d => x(d.timestamp) + x.bandwidth() / 2)
.attr("cy", d => y(d.close))
.attr("r", 4);
// 기존 포인트 업데이트
points
.attr("class", d => `prediction-point prediction-${d.prediction}`)
.attr("cx", d => x(d.timestamp) + x.bandwidth() / 2)
.attr("cy", d => y(d.close));
// 오래된 포인트 제거
points.exit().remove();
}
function connectWebSocket() {
const ws = new WebSocket('ws://localhost:8000/ws');
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
if (message.type === "initial") {
chartData = message.data;
updateChart();
} else if (message.type === "update") {
// 새 데이터 추가 및 오래된 데이터 제거
chartData.push(message.data);
if (chartData.length > 30) {
chartData.shift();
}
updateChart();
}
};
ws.onclose = function() {
// 연결이 끊어지면 3초 후 재연결 시도
setTimeout(connectWebSocket, 3000);
};
}
// 차트 초기화 및 웹소켓 연결 시작
document.addEventListener('DOMContentLoaded', () => {
initializeChart();
connectWebSocket();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment