전략알림 수정
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package com.goldenchart.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 전략 DSL에 TIMEFRAME 래핑이 없으면 평가 분봉을 추론해 래핑한다.
|
||||
* (프론트 encodeConditionForSave 와 동일 목적)
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class StrategyDslTimeframeNormalizer {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public JsonNode normalize(JsonNode root) {
|
||||
if (root == null || root.isNull()) return root;
|
||||
if (hasTimeframeScope(root)) return root;
|
||||
|
||||
String primary = inferPrimaryCandleType(root);
|
||||
ObjectNode wrapped = objectMapper.createObjectNode();
|
||||
wrapped.put("id", UUID.randomUUID().toString());
|
||||
wrapped.put("type", "TIMEFRAME");
|
||||
wrapped.put("candleType", primary);
|
||||
ArrayNode children = objectMapper.createArrayNode();
|
||||
children.add(root);
|
||||
wrapped.set("children", children);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
public String normalizeJson(String json) {
|
||||
if (json == null || json.isBlank()) return json;
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(json);
|
||||
JsonNode normalized = normalize(root);
|
||||
return objectMapper.writeValueAsString(normalized);
|
||||
} catch (Exception e) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
/** 루트 TIMEFRAME 또는 AND|OR + 전부 TIMEFRAME 자식 */
|
||||
public static boolean hasTimeframeScope(JsonNode node) {
|
||||
if (node == null || node.isNull()) return false;
|
||||
String type = node.path("type").asText("");
|
||||
if ("TIMEFRAME".equals(type)) return true;
|
||||
if ("AND".equals(type) || "OR".equals(type)) {
|
||||
JsonNode children = node.path("children");
|
||||
if (children.isArray() && !children.isEmpty()) {
|
||||
for (JsonNode c : children) {
|
||||
if (!"TIMEFRAME".equals(c.path("type").asText(""))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return findNestedTimeframe(node) != null;
|
||||
}
|
||||
|
||||
private static JsonNode findNestedTimeframe(JsonNode node) {
|
||||
if (node == null || node.isNull()) return null;
|
||||
if ("TIMEFRAME".equals(node.path("type").asText(""))) return node;
|
||||
JsonNode children = node.path("children");
|
||||
if (children.isArray()) {
|
||||
for (JsonNode c : children) {
|
||||
JsonNode found = findNestedTimeframe(c);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
JsonNode child = node.path("child");
|
||||
if (!child.isMissingNode() && !child.isNull()) return findNestedTimeframe(child);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 트리에서 명시적 분봉 수집 — CONDITION 의 left/rightCandleType, TIMEFRAME 노드.
|
||||
*/
|
||||
public static Set<String> collectExplicitCandleTypes(JsonNode node) {
|
||||
Set<String> out = new LinkedHashSet<>();
|
||||
collectExplicitCandleTypes(node, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void collectExplicitCandleTypes(JsonNode node, Set<String> out) {
|
||||
if (node == null || node.isNull()) return;
|
||||
|
||||
String type = node.path("type").asText("");
|
||||
if ("TIMEFRAME".equals(type)) {
|
||||
out.add(LiveStrategyTimeframeService.normalize(node.path("candleType").asText("1m")));
|
||||
}
|
||||
|
||||
if ("CONDITION".equals(type)) {
|
||||
JsonNode cond = node.path("condition");
|
||||
if (!cond.isMissingNode() && !cond.isNull()) {
|
||||
if (cond.has("leftCandleType") && !cond.path("leftCandleType").asText("").isBlank()) {
|
||||
out.add(LiveStrategyTimeframeService.normalize(cond.path("leftCandleType").asText()));
|
||||
}
|
||||
if (cond.has("rightCandleType") && !cond.path("rightCandleType").asText("").isBlank()) {
|
||||
out.add(LiveStrategyTimeframeService.normalize(cond.path("rightCandleType").asText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode children = node.path("children");
|
||||
if (children.isArray()) {
|
||||
for (JsonNode c : children) collectExplicitCandleTypes(c, out);
|
||||
}
|
||||
JsonNode child = node.path("child");
|
||||
if (!child.isMissingNode() && !child.isNull()) collectExplicitCandleTypes(child, out);
|
||||
}
|
||||
|
||||
public static String inferPrimaryCandleType(JsonNode node) {
|
||||
JsonNode nested = findNestedTimeframe(node);
|
||||
if (nested != null) {
|
||||
return LiveStrategyTimeframeService.normalize(nested.path("candleType").asText("1m"));
|
||||
}
|
||||
|
||||
Set<String> explicit = collectExplicitCandleTypes(node);
|
||||
if (explicit.isEmpty()) return "1m";
|
||||
|
||||
Set<String> non1m = new LinkedHashSet<>();
|
||||
for (String ct : explicit) {
|
||||
if (!"1m".equals(ct)) non1m.add(ct);
|
||||
}
|
||||
if (non1m.size() == 1) return non1m.iterator().next();
|
||||
if (non1m.size() > 1) return non1m.iterator().next();
|
||||
if (explicit.size() == 1) return explicit.iterator().next();
|
||||
return explicit.iterator().next();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user