매수, 매도 색상 수정
This commit is contained in:
@@ -1,13 +1,7 @@
|
||||
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.StrategyConditionTimeframeService;
|
||||
import com.goldenchart.service.TradeSignalService;
|
||||
import com.goldenchart.trading.pipeline.OrderExecutionQueue;
|
||||
import com.goldenchart.service.BarCloseStrategyEvaluationService;
|
||||
import com.goldenchart.storage.Ta4jStorage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -23,7 +17,6 @@ import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
@@ -43,11 +36,7 @@ public class BarBuilder {
|
||||
|
||||
private final Ta4jStorage ta4jStorage;
|
||||
private final TradingWebSocketBroker broker;
|
||||
private final LiveStrategyEvaluator liveStrategyEvaluator;
|
||||
private final TradeSignalService tradeSignalService;
|
||||
private final OrderExecutionQueue orderExecutionQueue;
|
||||
private final GcLiveStrategySettingsRepository liveSettingsRepo;
|
||||
private final StrategyConditionTimeframeService conditionTimeframes;
|
||||
private final BarCloseStrategyEvaluationService barCloseEvaluation;
|
||||
|
||||
/** 상위 집계 타임프레임 (분 단위 집계 주기: 3, 5, 10, 15, 30, 60, 240, 1440) */
|
||||
private static final int[] UPPER_MINUTES = {3, 5, 10, 15, 30, 60, 240, 10080, 1440};
|
||||
@@ -116,9 +105,9 @@ public class BarBuilder {
|
||||
ta4jStorage.addBar(market, "1m", bar);
|
||||
log.debug("[BarBuilder] 1m Bar 확정: {} @ {}", market, endTime);
|
||||
|
||||
if (shouldEvaluateOnClose(market, "1m")) {
|
||||
if (barCloseEvaluation.shouldEvaluateOnClose(market, "1m")) {
|
||||
BarSeries series1m = ta4jStorage.getOrCreate(market, "1m");
|
||||
evaluateStrategyOnBarClose(market, "1m", series1m.getEndIndex(), bar);
|
||||
barCloseEvaluation.onMaturedBarClose(market, "1m", series1m.getEndIndex(), bar);
|
||||
}
|
||||
|
||||
// 상위 타임프레임 전파
|
||||
@@ -147,98 +136,20 @@ public class BarBuilder {
|
||||
Bar upperBar = buildTa4jBar(partial, Duration.ofMinutes(minutes), upperEnd);
|
||||
ta4jStorage.addBar(market, type, upperBar);
|
||||
BarSeries upperAfter = ta4jStorage.getOrCreate(market, type);
|
||||
if (shouldEvaluateOnClose(market, type)) {
|
||||
if (barCloseEvaluation.shouldEvaluateOnClose(market, type)) {
|
||||
int maturedUpper = Math.max(upperAfter.getBeginIndex(), upperAfter.getEndIndex() - 1);
|
||||
Bar maturedUpperBar = upperAfter.getBar(maturedUpper);
|
||||
evaluateStrategyOnBarClose(market, type, maturedUpper, maturedUpperBar);
|
||||
barCloseEvaluation.onMaturedBarClose(market, type, maturedUpper, maturedUpperBar);
|
||||
}
|
||||
} else {
|
||||
log.warn("[BarBuilder] 상위봉 endTime 역행 {} {} last={} expected={} — 갱신만 수행",
|
||||
market, type, lastUpperEndInstant, upperEndInstant);
|
||||
ta4jStorage.updateLastBar(market, type, partial.close, partial.volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 활성 실시간 전략 설정 중 전략 DSL에 해당 분봉이 포함된 항목이 있는지 */
|
||||
private boolean shouldEvaluateOnClose(String market, String candleType) {
|
||||
String ct = LiveStrategyTimeframeService.normalize(candleType);
|
||||
return liveSettingsRepo.findActiveByMarket(market).stream()
|
||||
.filter(s -> Boolean.TRUE.equals(s.getIsLiveCheck()) && s.getStrategyId() != null)
|
||||
.anyMatch(s -> conditionTimeframes.usesTimeframe(s.getStrategyId(), ct));
|
||||
}
|
||||
|
||||
/**
|
||||
* 봉 마감 시점 전략 평가 + 시그널 STOMP 발행(평가 분봉 채널) + DB/주문 큐 적재.
|
||||
*/
|
||||
private void evaluateStrategyOnBarClose(String market, String candleType, int maturedIndex,
|
||||
Bar signalBar) {
|
||||
List<GcLiveStrategySettings> activeSettings = liveSettingsRepo.findActiveByMarket(market).stream()
|
||||
.filter(s -> Boolean.TRUE.equals(s.getIsLiveCheck()) && s.getStrategyId() != null)
|
||||
.toList();
|
||||
if (activeSettings.isEmpty()) return;
|
||||
|
||||
long candleTimeEpoch = signalBar.getEndTime().getEpochSecond()
|
||||
- signalBar.getTimePeriod().getSeconds();
|
||||
|
||||
for (GcLiveStrategySettings s : activeSettings) {
|
||||
if (!conditionTimeframes.usesTimeframe(s.getStrategyId(), candleType)) continue;
|
||||
|
||||
String closeSignal = "NONE";
|
||||
String signalExecType = null;
|
||||
|
||||
if ("CANDLE_CLOSE".equals(s.getExecutionType())) {
|
||||
closeSignal = liveStrategyEvaluator.evaluateSettingOnCandleClose(
|
||||
s, market, candleType, maturedIndex);
|
||||
signalExecType = "CANDLE_CLOSE";
|
||||
} else if ("REALTIME_TICK".equals(s.getExecutionType())) {
|
||||
closeSignal = liveStrategyEvaluator.evaluateSettingRealtimeAtClose(
|
||||
s, market, candleType, maturedIndex);
|
||||
signalExecType = "REALTIME_TICK";
|
||||
}
|
||||
|
||||
if (!"BUY".equals(closeSignal) && !"SELL".equals(closeSignal)) continue;
|
||||
|
||||
publishStrategySignal(market, candleType, signalBar, closeSignal, s, signalExecType);
|
||||
try {
|
||||
tradeSignalService.save(
|
||||
s.getDeviceId(), s.getUserId(),
|
||||
market, s.getStrategyId(), null,
|
||||
closeSignal, signalBar.getClosePrice().doubleValue(),
|
||||
candleTimeEpoch, candleType, signalExecType);
|
||||
if (s.getDeviceId() != null) {
|
||||
orderExecutionQueue.submitSignal(
|
||||
s.getDeviceId(), s.getUserId(), market,
|
||||
s.getStrategyId(), closeSignal,
|
||||
signalBar.getClosePrice().doubleValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("[BarBuilder] 시그널 처리 실패 ({}/{} strategyId={}): {}",
|
||||
market, candleType, s.getStrategyId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 평가 분봉 STOMP 채널로 봉 마감 시그널 발행 (소유자·전략 메타 포함) */
|
||||
private void publishStrategySignal(String market, String candleType, Bar bar, String signal,
|
||||
GcLiveStrategySettings setting, String executionType) {
|
||||
long barStartEpoch = bar.getEndTime().getEpochSecond() - bar.getTimePeriod().getSeconds();
|
||||
CandleBarDto dto = CandleBarDto.builder()
|
||||
.time(barStartEpoch)
|
||||
.open(bar.getOpenPrice().doubleValue())
|
||||
.high(bar.getHighPrice().doubleValue())
|
||||
.low(bar.getLowPrice().doubleValue())
|
||||
.close(bar.getClosePrice().doubleValue())
|
||||
.volume(bar.getVolume().doubleValue())
|
||||
.signal(signal)
|
||||
.candleType(candleType)
|
||||
.strategyId(setting.getStrategyId())
|
||||
.userId(setting.getUserId())
|
||||
.deviceId(setting.getDeviceId())
|
||||
.executionType(executionType)
|
||||
.build();
|
||||
broker.publish(market, candleType, dto);
|
||||
log.info("[BarBuilder] bar-close signal={} market={} candleType={} strategyId={} userId={}",
|
||||
signal, market, candleType, setting.getStrategyId(), setting.getUserId());
|
||||
}
|
||||
|
||||
/** 현재 진행 중인 1분봉을 STOMP 로 발행 (실시간 틱 렌더링용, 시그널 없음). */
|
||||
private void publishCurrentBar(String market) {
|
||||
PartialBar p = partialBars.get(market);
|
||||
|
||||
Reference in New Issue
Block a user