5.9 KiB
🎯 구현 목표 (Context & Goal) 현재 구축된 Backend (Spring Boot + Ta4j)와 Frontend (Lightweight Charts) 시스템에 "실시간 매매전략 실행 설정 기능"을 도입합니다. 유저가 화면에서 설정한 옵션(실행 타이밍, 모니터링 여부)에 따라 백엔드 연산 주기와 프론트엔드 차트 시그널 마커(setMarkers)가 유기적으로 연동되도록 아키텍처를 확장해 주세요.
🛠️ 1. 요구사항 명세 및 정책 (Requirements) ① 설정 화면 UI 구성 (Frontend) 실시간 전략 체크 토글: ON / OFF 스위치
매매전략 실행 방식 라디오 버튼: (실시간 전략 체크가 ON 일 때만 활성화)
A. 봉 마감 직후 체크 (At Candle Close)
B. 실시간 틱(장중) 체크 (Intraday Tick)
주의: 이 설정값들은 전역 상태(Redux/Zustand 등)로 관리되며, 변경 즉시 백엔드로 REST API를 통해 반영되어 런타임에 동적으로 감시 스레드가 제어되어야 합니다.
② 백엔드 실행 코어 제어 정책 (Backend Threading) 설정값 OFF 시: 해당 종목 및 유저에 대한 기술적 분석 지표 및 전략 체크 루프를 완전히 중단하여 서버 자원을 방어합니다.
설정값 ON + 방식 A (봉 마감 직후):
업비트 웹소켓에서 들어온 틱 데이터로 분봉 조립 중, '분이 바뀌어 새로운 Bar가 추가되는 시점(addBar)'에만 해당 종목의 Strategy.isSatisfied(lastMaturedBarIndex)를 1회 판정합니다.
설정값 ON + 방식 B (실시간 틱 체크):
백엔드의 3초 주기 라운드 로빈 스케줄러가 돌아갈 때, 아직 마감되지 않은 현재 진행 중인 캔들 인덱스(series.getEndIndex())를 기준으로 전략 만족 여부를 강제 판정합니다.
💾 2. 데이터 구조 및 인터페이스 설계 (Data Struct) ① [PUT] /api/strategy/settings (전략 설정 변경 API) JSON { "market": "KRW-BTC", "isLiveCheck": true, // 실시간 전략 체크 ON/OFF "executionType": "CANDLE_CLOSE" // "CANDLE_CLOSE" (방식A) 또는 "REALTIME_TICK" (방식B) } ② [STOMP WebSocket] 확장된 실시간 데이터 프로토콜 백엔드에서 프론트엔드로 실시간 캔들을 발행할 때, 설정 상태와 매칭된 시그널 유무를 명확히 DTO 포맷에 주입합니다.
Topic: /sub/charts/{market}/{type}
Payload (JSON):
JSON { "t": 1779229260, "o": 92080000.0, "h": 92120000.0, "l": 92010000.0, "c": 92050000.0, "v": 1.845, "rsi": 38.40, "signal": "BUY" // "BUY", "SELL", "NONE" 중 하나 (전략 체크가 OFF이거나 조건 미달 시 무조건 "NONE") } 💻 3. AI 구현 가이드라인 코드 (Reference Code for AI) [Backend] 가상 코드를 참고하여 동적 분기 서비스 레이어를 작성해 주세요. Java @Service @RequiredArgsConstructor public class LiveStrategyEvaluator {
private final Ta4jStorage ta4jStorage;
private final UserStrategySettingsRepository settingsRepository;
// 방식 A: 봉 마감 엔진에서 트리거됨
public String evaluateCandleClose(String market, String candleType, int maturedIndex) {
UserSetting setting = settingsRepository.findByMarket(market);
if (!setting.isLiveCheck() || !setting.getExecutionType().equals("CANDLE_CLOSE")) {
return "NONE";
}
Strategy strategy = ta4jStorage.getStrategy(market);
BarSeries series = ta4jStorage.getSeries(market, candleType);
if (strategy.getEntryRule().isSatisfied(maturedIndex)) return "BUY";
if (strategy.getExitRule().isSatisfied(maturedIndex)) return "SELL";
return "NONE";
}
// 방식 B: 3초 스케줄러 엔진에서 트리거됨
public String evaluateRealtimeTick(String market, String candleType) {
UserSetting setting = settingsRepository.findByMarket(market);
if (!setting.isLiveCheck() || !setting.getExecutionType().equals("REALTIME_TICK")) {
return "NONE";
}
BarSeries series = ta4jStorage.getSeries(market, candleType);
Strategy strategy = ta4jStorage.getStrategy(market);
int currentIndex = series.getEndIndex(); // 마감되지 않은 현재 진행형 캔들
if (strategy.getEntryRule().isSatisfied(currentIndex)) return "BUY";
if (strategy.getExitRule().isSatisfied(currentIndex)) return "SELL";
return "NONE";
}
} [Frontend] STOMP 데이터를 받아 Lightweight Charts에 마커를 누적 처리하는 훅(Hook) 또는 컴포넌트 로직을 작성해 주세요. JavaScript // 프론트엔드 캔들 및 마커 실시간 업데이트 제어부 let activeMarkers = []; // 차트에 표시된 마커 상태 캐시 배열
stompClient.subscribe(/sub/charts/${market}/${candleType}, (message) => {
const data = JSON.parse(message.body);
// 1. 기본 캔들 렌더링 무조건 실행
candleSeries.update({
time: data.t,
open: data.o,
high: data.h,
low: data.l,
close: data.c
});
// 2. 백엔드에서 주입된 signal 판별 (NONE이 아닐 때만 차트 오버레이 마커 추가)
if (data.signal !== 'NONE') {
// 중복 타임스탬프 마커 삽입 방지 (방식 B 장중 틱 동적 체크 대응)
const isDuplicate = activeMarkers.some(m => m.time === data.t && m.signalType === data.signal);
if (!isDuplicate) {
const newMarker = {
time: data.t,
position: data.signal === 'BUY' ? 'belowBar' : 'aboveBar',
color: data.signal === 'BUY' ? '#26a69a' : '#ef5350',
shape: data.signal === 'BUY' ? 'arrowUp' : 'arrowDown',
text: data.signal === 'BUY' ? '실시간 매수' : '실시간 매도',
signalType: data.signal // 커스텀 식별자
};
activeMarkers.push(newMarker);
candleSeries.setMarkers(activeMarkers);
}
}
});