package com.goldenchart.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.ta4j.core.Rule; import org.ta4j.core.Strategy; import org.ta4j.core.TradingRecord; /** * 포지션 종속성 선택에 따른 매매 시그널 판정 코어 컴포넌트. * *

모드 설명

* */ @Component @Slf4j public class StrategySignalDeterminer { /** * positionMode 에 따라 포지션 제약 유무를 분기하여 시그널을 결정한다. * * @param strategy Ta4j Strategy (entryRule / exitRule 포함) * @param tradingRecord 현재 TradingRecord 상태 (LONG_ONLY 모드에서 참조) * @param index BarSeries 인덱스 * @param positionMode "LONG_ONLY" | "SIGNAL_ONLY" (null → LONG_ONLY 기본 적용) * @return "BUY" | "SELL" | "NONE" */ public String determineSignal(Strategy strategy, TradingRecord tradingRecord, int index, String positionMode) { try { if ("SIGNAL_ONLY".equals(positionMode)) { return determineSignalOnly(strategy, index); } // LONG_ONLY (기본) return determineLongOnly(strategy, tradingRecord, index); } catch (Exception e) { log.warn("[SignalDeterminer] 시그널 판정 오류 idx={} mode={}: {}", index, positionMode, e.getMessage()); return "NONE"; } } // ── 보조 오버로드: Rule[] 직접 전달 (LiveStrategyEvaluator 호환) ───────────── /** * Rule 배열을 직접 받아 positionMode 에 따라 시그널 판정. * entryRule = rules[0], exitRule = rules[1] */ public String determineSignalFromRules(Rule entryRule, Rule exitRule, TradingRecord tradingRecord, int index, String positionMode) { try { if ("SIGNAL_ONLY".equals(positionMode)) { boolean enterOk = entryRule != null && entryRule.isSatisfied(index); boolean exitOk = exitRule != null && exitRule.isSatisfied(index); if (enterOk) return "BUY"; if (exitOk) return "SELL"; return "NONE"; } // LONG_ONLY — tradingRecord 를 이용한 엄격 모드 boolean enterOk = entryRule != null && entryRule.isSatisfied(index, tradingRecord); boolean exitOk = exitRule != null && exitRule.isSatisfied(index, tradingRecord); if (enterOk) return "BUY"; if (exitOk) return "SELL"; return "NONE"; } catch (Exception e) { log.warn("[SignalDeterminer] Rule 판정 오류 idx={} mode={}: {}", index, positionMode, e.getMessage()); return "NONE"; } } // ── Private ─────────────────────────────────────────────────────────────── private String determineLongOnly(Strategy strategy, TradingRecord record, int index) { if (strategy.shouldEnter(index, record)) return "BUY"; if (strategy.shouldExit(index, record)) return "SELL"; return "NONE"; } private String determineSignalOnly(Strategy strategy, int index) { if (strategy.getEntryRule().isSatisfied(index)) return "BUY"; if (strategy.getExitRule().isSatisfied(index)) return "SELL"; return "NONE"; } }