매매 시그널 알림 화면 수정

This commit is contained in:
Macbook
2026-05-29 00:46:20 +09:00
parent cbad62a5b0
commit e43b5cbd5a
45 changed files with 2186 additions and 415 deletions
@@ -9,6 +9,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import java.util.List;
import java.util.Optional;
@@ -18,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class StrategyConditionTimeframeServiceTest {
@Mock
@@ -109,7 +112,11 @@ class StrategyConditionTimeframeServiceTest {
"candleTypes": ["1m", "3m", "5m"],
"children": [{
"type": "CONDITION",
"condition": { "indicatorType": "RSI", "period": 14 }
"condition": {
"indicatorType": "RSI",
"period": 14,
"leftCandleType": "1m"
}
}]
}
""");
@@ -235,6 +242,40 @@ class StrategyConditionTimeframeServiceTest {
assertFalse(service.usesTimeframe(6L, "1m"));
}
@Test
void collectForStrategy_cciMultiTfDropsStale1mFromStartArray() {
GcStrategy strategy = new GcStrategy();
strategy.setId(7L);
strategy.setName("CCI | 3분 5분 10분 15분 1시간");
strategy.setBuyConditionJson("""
{
"type": "TIMEFRAME",
"candleType": "1m",
"candleTypes": ["1m", "3m", "5m", "10m", "15m", "1h"],
"children": [{
"type": "AND",
"children": [
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "3m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "5m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "10m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "15m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "1h" } }
]
}]
}
""");
when(strategyRepo.findById(7L)).thenReturn(Optional.of(strategy));
when(dslNormalizer.alignSellStartTimeframeToBuy(org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any()))
.thenAnswer(inv -> inv.getArgument(1));
when(dslNormalizer.normalizeJson(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any()))
.thenAnswer(inv -> inv.getArgument(0));
Set<String> tfs = service.collectForStrategy(7L);
assertEquals(Set.of("3m", "5m", "10m", "15m", "1h"), tfs);
assertFalse(service.usesTimeframe(7L, "1m"));
assertTrue(service.usesTimeframe(7L, "3m"));
}
@Test
void collectForStrategy_legacyTreeDefaultsTo1m() {
GcStrategy legacy = new GcStrategy();
@@ -76,6 +76,31 @@ class StrategyDslTimeframeNormalizerTest {
assertEquals("5m", root.path("candleType").asText());
}
@Test
void resolveStartCandleTypes_explicitStart5mBeatsConditionDefault1m() throws Exception {
String json = """
{
"type": "TIMEFRAME",
"candleType": "5m",
"candleTypes": ["5m"],
"children": [{
"type": "CONDITION",
"condition": {
"indicatorType": "CCI",
"leftCandleType": "1m",
"rightCandleType": "1m"
}
}]
}
""";
JsonNode root = objectMapper.readTree(json);
assertEquals(Set.of("5m"), StrategyDslTimeframeNormalizer.resolveStartCandleTypes(root));
String normalized = normalizer.normalizeJson(json, "cci_5분봉");
JsonNode out = objectMapper.readTree(normalized);
assertEquals("5m", out.path("candleType").asText());
assertEquals("5m", out.path("candleTypes").get(0).asText());
}
@Test
void resolveStartCandleTypes_meaningfulOverridesStaleStart1m() throws Exception {
String json = """
@@ -161,6 +186,31 @@ class StrategyDslTimeframeNormalizerTest {
assertEquals("15m", root.path("candleType").asText());
}
@Test
void resolveStartCandleTypes_dropsStale1mWhenConditionsUseMultipleUpperTfs() throws Exception {
String json = """
{
"type": "TIMEFRAME",
"candleType": "1m",
"candleTypes": ["1m", "3m", "5m", "10m", "15m", "1h"],
"children": [{
"type": "AND",
"children": [
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "3m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "5m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "10m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "15m" } },
{ "type": "CONDITION", "condition": { "indicatorType": "CCI", "leftCandleType": "1h" } }
]
}]
}
""";
JsonNode root = objectMapper.readTree(json);
assertEquals(
Set.of("3m", "5m", "10m", "15m", "1h"),
StrategyDslTimeframeNormalizer.resolveStartCandleTypes(root));
}
@Test
void normalize_preservesMultiCandleTypesOnSave() throws Exception {
String json = """
@@ -170,7 +220,11 @@ class StrategyDslTimeframeNormalizerTest {
"candleTypes": ["1m", "3m", "5m"],
"children": [{
"type": "CONDITION",
"condition": { "indicatorType": "RSI", "period": 14 }
"condition": {
"indicatorType": "RSI",
"period": 14,
"leftCandleType": "1m"
}
}]
}
""";