goldenChat base source add
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
1. 시스템 개요 & 목적 (System Overview)
|
||||
본 시스템은 업비트(Upbit) 거래소의 시세 데이터를 백엔드(Spring Boot)가 중앙 집중식으로 관리·가공하고, 프론트엔드(다중 차트 UI) 및 FCM 푸시 알림 서버로 정제된 데이터를 분배하는 고성능 금융 트레이딩 데이터 디스패처 인프라 구축을 목적으로 한다.
|
||||
|
||||
핵심 설계 원칙:
|
||||
|
||||
프론트엔드는 데이터 가공 연산을 하지 않으며 오직 렌더링에만 집중한다.
|
||||
|
||||
백엔드 인메모리는 실시간 전략 감시에 필요한 최신 데이터(300개 제한)만 유지하여 RAM을 방어한다.
|
||||
|
||||
인메모리 범위를 벗어나는 과거 데이터는 온디맨드(On-Demand) REST API를 통해 페이징 중계한다.
|
||||
|
||||
2. 데이터 흐름 아키텍처 (Data Flow Pipeline)
|
||||
[업비트 서버 (REST / WebSocket)]
|
||||
│
|
||||
│ (1. 고정 관심종목 20개 + 2. 동적 차트 오픈 종목 최대 8개)
|
||||
▼
|
||||
[Spring Boot 백엔드 서버]
|
||||
├── ① UpbitWebSocketClient (수신 및 가상 스레드 분배)
|
||||
├── ② Ta4jStorage (인메모리 Map<String, BarSeries> 관리, 300개 스케일 제한)
|
||||
├── ③ HistoricalDataService (과거 캔들 페이징 중계기)
|
||||
├── ④ LiveStrategyScheduler (3초 주기 고정 관심종목 조건 감시 엔진)
|
||||
└── ⑤ TradingWebSocketBroker (STOMP 기반 실시간 지표 분배기)
|
||||
│
|
||||
├─ (STOMP Pub/Sub 실시간 갱신) ───────> [Frontend (Lightweight Charts)]
|
||||
└─ (FCM Open API / 비동기 스레드) ──> [FCM Server] ──> [안드로이드/웹 푸시]
|
||||
3. 백엔드 상세 설계 (Backend Components)
|
||||
3.1 인메모리 저장소 (Ta4jStorage)
|
||||
구조: 멀티스레드 환경에서 안전한 ConcurrentHashMap 기반의 2중 맵 구조.
|
||||
|
||||
Map<String, BarSeries Map<String,>> storage ➔ Key 1: 종목코드(예: KRW-BTC), Key 2: 봉타입(예: 1m, 5m, 15m, 1h, 1d)
|
||||
|
||||
메모리 슬라이딩 윈도우 제한:
|
||||
|
||||
모든 BarSeries 생성 시 series.setMaximumBarCount(300);를 반드시 설정한다.
|
||||
|
||||
301번째 실시간 캔들이 들어오면 가장 오래된 1번째 캔들은 메모리에서 자동 소멸해야 한다.
|
||||
|
||||
3.2 캔들 조립기 (BarBuilder) & 멀티 시간봉 처리
|
||||
업비트 웹소켓으로 수신한 초 단위 실시간 틱 데이터(Trade 또는 Ticker)를 바탕으로 분봉을 조립한다.
|
||||
|
||||
1분봉 조립 규칙:
|
||||
|
||||
수신한 틱의 타임스탬프가 현재 메모리 상 마지막 Bar의 시간 범위 내에 있다면: series.addPrice(price, volume) 호출하여 현재가(종가), 고가, 저가, 거래량을 실시간 갱신한다.
|
||||
|
||||
분이 변경되어 다음 시간 범위로 넘어가면: series.addBar(newBar)를 호출하여 새로운 캔들을 생성한다.
|
||||
|
||||
상위 시간봉(5m, 15m, 1h) 전파 규칙:
|
||||
|
||||
1분봉이 최종 마감(다음 분 00초 진입)되는 시점에, 해당 1분봉의 완결된 OHLCV 데이터를 상위 시간봉 BarSeries에 순차적으로 누적 산입(addPrice 또는 addBar)한다.
|
||||
|
||||
3.3 동적 구독 관리자 (DynamicSubscriptionManager)
|
||||
고정 관심종목(20개) 외에 프론트엔드 유저가 멀티차트 모드 등으로 신규 종목을 조회할 때 작동하는 동적 확장 아키텍처를 구현한다.
|
||||
|
||||
구독 인터셉터 가동: 프론트엔드가 특정 채널(예: /sub/charts/KRW-SAND/1m)을 구독하는 이벤트를 감지한다.
|
||||
|
||||
초기화 (Warm-up): 메모리에 해당 종목의 BarSeries가 없다면 즉시 업비트 REST API를 호출하여 최근 200개의 캔들을 받아와 Ta4j 메모리를 생성한다.
|
||||
|
||||
웹소켓 채널 확장: 백엔드의 UpbitWebSocketClient 소켓 커넥션을 유지한 채, 구독 코드 목록(codes)에 KRW-SAND를 동적으로 추가하여 재요청 패킷을 보낸다.
|
||||
|
||||
자원 수거 (GC): 특정 동적 종목의 STOMP 구독자 수가 0이 되면, 업비트 웹소켓 구독 목록에서 제거하고 Ta4jStorage에서 삭제하여 메모리를 방어한다.
|
||||
|
||||
3.4 과거 데이터 페이징 중계기 (HistoricalDataService)
|
||||
프론트엔드 차트 무한 과거 스크롤을 지원하기 위한 온디맨드 API 서브시스템이다.
|
||||
|
||||
동작 매커니즘:
|
||||
|
||||
프론트엔드가 요청한 기준 시간(to)이 현재 백엔드 Ta4j 인메모리(최근 300개) 범위 내에 있다면 ➔ 메모리 데이터를 슬라이싱하여 즉시 반환한다.
|
||||
|
||||
인메모리 범위를 벗어난 먼 과거의 데이터라면 ➔ 업비트 REST API(/v1/candles/minutes/{기반})를 호출한다. 이때 파라미터로 받은 to와 count=200을 그대로 업비트에 포워딩한다.
|
||||
|
||||
수신된 데이터를 가공하여 차트에 전달할 때, Ta4j 인메모리에 임시 임포트하여 해당 구간의 보조지표(RSI 등)를 계산한 뒤 값에 포함하여 반환한다.
|
||||
|
||||
3.5 전략 감시 및 비동기 푸시 엔진 (LiveStrategyScheduler)
|
||||
라운드 로빈 스케줄러: 3초 주기로 고정 관심종목 20개의 최신 인메모리 지표를 스캔한다. (틱 수신 스레드와 철저히 분리)
|
||||
|
||||
FCM 발송 격리: 조건 만족 시 메인 트레이딩 루프가 멈추지 않도록 @Async("fcmExecutor") 처리된 전용 스레드 풀에서 구글 FCM 오픈 API를 비동기 호출한다.
|
||||
|
||||
4. 프론트엔드 상세 설계 (Frontend Specs)
|
||||
4.1 다중 차트 독립 파이프라인 (최대 8개)
|
||||
화면 전반에 최대 8개의 Lightweight Charts 인스턴스를 동적으로 생성 및 관리할 수 있는 구조를 가진다.
|
||||
|
||||
각 차트 인스턴스는 고유한 Context 또는 상태 공간을 가지며, 백엔드로부터 온 메시지를 분배받는 독립적인 STOMP 구독 채널을 유지한다.
|
||||
|
||||
4.2 과거 차트 무한 스크롤 및 레이지 로딩 (Lazy Loading)
|
||||
이벤트 트리거: chart.timeScale().subscribeVisibleLogicalRangeChange() 핸들러를 바인딩한다.
|
||||
|
||||
임계점 판단: 유저가 차트를 왼쪽으로 스크롤하여 현재 화면에 보이는 최좌측 캔들의 인덱스가 남은 데이터 전체의 끝점 부근(logicalRange.from < 5)에 도달하면 역사 데이터 요청을 트리거한다.
|
||||
|
||||
API 호출: 현재 차트의 가장 오래된 캔들의 타임스탬프(to)를 파라미터로 얹어 백엔드 역사 데이터 API(GET /api/candles/history)를 호출한다.
|
||||
|
||||
데이터 접합 (Prepend): 서버로부터 받아온 과거 캔들 200개 배열을 차트 기존 데이터의 맨 앞(머리)에 이어 붙여 차트가 끊김 없이 왼쪽으로 확장되도록 구현한다.
|
||||
|
||||
중복 방지 락(Lock): API 통신이 진행 중인 동안에는 isLoading = true 상태를 유지하여 스크롤 오버랩에 의한 동일 타임스탬프 중복 요청을 원천 차단한다.
|
||||
|
||||
5. 데이터 인터페이스 포맷 (Data Interface Formats)
|
||||
5.1 [REST API] 과거 데이터 조회 및 차트 초기화 용 데이터
|
||||
Endpoint: GET /api/candles/history
|
||||
|
||||
Query Params: market=KRW-BTC, type=1m, to=2026-05-20T10:17:00Z, count=200
|
||||
|
||||
Response DTO (JSON):
|
||||
|
||||
JSON
|
||||
[
|
||||
{
|
||||
"time": 1779229200,
|
||||
"open": 92100000.0,
|
||||
"high": 92150000.0,
|
||||
"low": 92050000.0,
|
||||
"close": 92080000.0,
|
||||
"volume": 4.215,
|
||||
"rsi": 42.15
|
||||
}
|
||||
]
|
||||
5.2 [STOMP WebSocket] 실시간 경량 분배 데이터
|
||||
Topic: /sub/charts/{market}/{type} (예: /sub/charts/KRW-BTC/1m)
|
||||
|
||||
Payload DTO (JSON):
|
||||
|
||||
JSON
|
||||
{
|
||||
"t": 1779229260,
|
||||
"o": 92080000.0,
|
||||
"h": 92120000.0,
|
||||
"l": 92010000.0,
|
||||
"c": 92050000.0,
|
||||
"v": 1.845,
|
||||
"rsi": 38.40
|
||||
}
|
||||
Reference in New Issue
Block a user