goldenChat base source add
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
package com.goldenchart.websocket;
|
||||
|
||||
import com.goldenchart.dto.CandleBarDto;
|
||||
import com.goldenchart.entity.GcLiveStrategySettings;
|
||||
import com.goldenchart.repository.GcLiveStrategySettingsRepository;
|
||||
import com.goldenchart.service.LiveStrategyEvaluator;
|
||||
import com.goldenchart.service.LiveStrategyTimeframeService;
|
||||
import com.goldenchart.service.TradeSignalService;
|
||||
import com.goldenchart.trading.pipeline.OrderExecutionQueue;
|
||||
import com.goldenchart.storage.Ta4jStorage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ta4j.core.BarSeries;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 3초 주기 라운드 로빈 스케줄러.
|
||||
*
|
||||
* <p>설정 방식이 REALTIME_TICK 이고 isLiveCheck = true 인 종목에 대해
|
||||
* 현재 진행 중인 캔들 인덱스(series.getEndIndex())로 전략을 평가하여
|
||||
* BUY/SELL 시그널이 발생하면 STOMP 토픽으로 발행한다.
|
||||
*
|
||||
* <p>동시성 안전:
|
||||
* <ul>
|
||||
* <li>BarBuilder 의 partialBars 와 Ta4jStorage 는 ConcurrentHashMap 기반</li>
|
||||
* <li>이 스케줄러는 단일 스레드로 돌기 때문에 중복 평가 없음</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class LiveStrategyScheduler {
|
||||
|
||||
private final GcLiveStrategySettingsRepository settingsRepo;
|
||||
private final LiveStrategyEvaluator evaluator;
|
||||
private final Ta4jStorage ta4jStorage;
|
||||
private final TradingWebSocketBroker broker;
|
||||
private final BarBuilder barBuilder;
|
||||
private final TradeSignalService tradeSignalService;
|
||||
private final OrderExecutionQueue orderExecutionQueue;
|
||||
private final LiveStrategyTimeframeService timeframeService;
|
||||
|
||||
/**
|
||||
* 3초마다 REALTIME_TICK 설정 종목을 순회하여 전략 판정 후 시그널 발행.
|
||||
* fixedDelay=3000: 이전 실행이 끝난 뒤 3초 후 재실행 (오버랩 방지).
|
||||
*/
|
||||
@Scheduled(fixedDelay = 3000)
|
||||
public void tick() {
|
||||
List<GcLiveStrategySettings> activeSettings = settingsRepo.findAllByIsLiveCheckTrue();
|
||||
if (activeSettings.isEmpty()) return;
|
||||
|
||||
for (GcLiveStrategySettings s : activeSettings) {
|
||||
if (!"REALTIME_TICK".equals(s.getExecutionType())) continue;
|
||||
if (s.getStrategyId() == null) continue;
|
||||
|
||||
String market = s.getMarket();
|
||||
String candleType = timeframeService.resolveCandleType(
|
||||
market, s.getUserId(), s.getDeviceId());
|
||||
|
||||
if (!ta4jStorage.exists(market, candleType)) continue;
|
||||
|
||||
BarSeries series = ta4jStorage.getOrCreate(market, candleType);
|
||||
if (series.isEmpty()) continue;
|
||||
|
||||
String signal = evaluator.evaluateRealtimeTick(market, candleType);
|
||||
if ("BUY".equals(signal) || "SELL".equals(signal)) {
|
||||
// 현재 진행 중인 캔들 정보 조회
|
||||
org.ta4j.core.Bar lastBar = series.getLastBar();
|
||||
// Ta4j 0.22: getEndTime() → Instant; 시작 시간 = endTime - duration
|
||||
long barStartEpoch = lastBar.getEndTime().getEpochSecond()
|
||||
- lastBar.getTimePeriod().getSeconds();
|
||||
|
||||
CandleBarDto dto = CandleBarDto.builder()
|
||||
.time(barStartEpoch)
|
||||
.open(lastBar.getOpenPrice().doubleValue())
|
||||
.high(lastBar.getHighPrice().doubleValue())
|
||||
.low(lastBar.getLowPrice().doubleValue())
|
||||
.close(lastBar.getClosePrice().doubleValue())
|
||||
.volume(lastBar.getVolume().doubleValue())
|
||||
.signal(signal)
|
||||
.build();
|
||||
|
||||
broker.publish(market, candleType, dto);
|
||||
log.info("[LiveStrategyScheduler] REALTIME_TICK signal={} market={}", signal, market);
|
||||
|
||||
// DB 저장
|
||||
try {
|
||||
tradeSignalService.save(
|
||||
s.getDeviceId(), s.getUserId(),
|
||||
market, s.getStrategyId(), null,
|
||||
signal,
|
||||
lastBar.getClosePrice().doubleValue(),
|
||||
barStartEpoch, candleType, "REALTIME_TICK"
|
||||
);
|
||||
if (s.getDeviceId() != null) {
|
||||
orderExecutionQueue.submitSignal(
|
||||
s.getDeviceId(), s.getUserId(), market,
|
||||
s.getStrategyId(), signal,
|
||||
lastBar.getClosePrice().doubleValue());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.warn("[LiveStrategyScheduler] 시그널 DB 저장 실패: {}", ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user