직접입력 평가로직 오류
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
package com.goldenchart.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.ta4j.core.BarSeries;
|
||||
import org.ta4j.core.BaseBarSeriesBuilder;
|
||||
import org.ta4j.core.Rule;
|
||||
import org.ta4j.core.bars.TimeBarBuilderFactory;
|
||||
import org.ta4j.core.num.DoubleNumFactory;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* RSI CROSS_UP — HL_MID(중앙선) vs K_50(직접입력) 동일 임계값 시그널 일치.
|
||||
*/
|
||||
class RsiThresholdCrossSignalTest {
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
private StrategyDslToTa4jAdapter adapter;
|
||||
private BarSeries series;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
adapter = new StrategyDslToTa4jAdapter();
|
||||
series = buildOscillatingSeries(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rsiCrossUp_hlMid_equalsK50Override() throws Exception {
|
||||
assertSignalHitsEqual(
|
||||
"""
|
||||
{
|
||||
"indicatorType": "RSI",
|
||||
"conditionType": "CROSS_UP",
|
||||
"leftField": "RSI_VALUE_9",
|
||||
"rightField": "HL_MID",
|
||||
"period": 9,
|
||||
"valuePeriodOverride": true,
|
||||
"thresholdOverride": false,
|
||||
"candleRange": 1
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{
|
||||
"indicatorType": "RSI",
|
||||
"conditionType": "CROSS_UP",
|
||||
"leftField": "RSI_VALUE_9",
|
||||
"rightField": "K_50",
|
||||
"period": 9,
|
||||
"valuePeriodOverride": true,
|
||||
"thresholdOverride": true,
|
||||
"targetValue": 50,
|
||||
"candleRange": 1
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rsiCrossUp_hlMid_equalsK50WithoutOverride() throws Exception {
|
||||
assertSignalHitsEqual(
|
||||
"""
|
||||
{
|
||||
"indicatorType": "RSI",
|
||||
"conditionType": "CROSS_UP",
|
||||
"leftField": "RSI_VALUE_9",
|
||||
"rightField": "HL_MID",
|
||||
"period": 9,
|
||||
"valuePeriodOverride": true,
|
||||
"candleRange": 1
|
||||
}
|
||||
""",
|
||||
"""
|
||||
{
|
||||
"indicatorType": "RSI",
|
||||
"conditionType": "CROSS_UP",
|
||||
"leftField": "RSI_VALUE_9",
|
||||
"rightField": "K_50",
|
||||
"period": 9,
|
||||
"valuePeriodOverride": true,
|
||||
"candleRange": 1
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
private void assertSignalHitsEqual(String condA, String condB) throws Exception {
|
||||
Map<String, Map<String, Object>> indicatorParams = Map.of(
|
||||
"RSI", Map.of("length", 9, "maLength", 3, "maType", "SMA", "src", "close"));
|
||||
Map<String, Map<String, Object>> visual = Map.of(
|
||||
"RSI", Map.of("hlines", List.of(
|
||||
Map.of("label", "과열선", "price", 70),
|
||||
Map.of("label", "중앙선", "price", 50),
|
||||
Map.of("label", "침체선", "price", 30))));
|
||||
|
||||
List<Integer> hitsA = scanHits(condA, indicatorParams, visual);
|
||||
List<Integer> hitsB = scanHits(condB, indicatorParams, visual);
|
||||
assertEquals(hitsA, hitsB,
|
||||
"HL_MID and K_50 should produce identical CROSS_UP bars, A=" + hitsA.size() + " B=" + hitsB.size());
|
||||
}
|
||||
|
||||
private List<Integer> scanHits(String condJson,
|
||||
Map<String, Map<String, Object>> indicatorParams,
|
||||
Map<String, Map<String, Object>> visual) throws Exception {
|
||||
JsonNode buyDsl = MAPPER.readTree("""
|
||||
{
|
||||
"type": "TIMEFRAME",
|
||||
"candleTypes": ["3m"],
|
||||
"candleType": "3m",
|
||||
"children": [{
|
||||
"type": "CONDITION",
|
||||
"condition": %s
|
||||
}]
|
||||
}
|
||||
""".formatted(condJson.trim()));
|
||||
|
||||
StrategyDslToTa4jAdapter.RuleBuildContext ctx =
|
||||
new StrategyDslToTa4jAdapter.RuleBuildContext(
|
||||
series, indicatorParams, visual, null, null, false, Map.of());
|
||||
Rule rule = adapter.toRule(buyDsl, ctx);
|
||||
|
||||
java.util.ArrayList<Integer> hits = new java.util.ArrayList<>();
|
||||
for (int i = series.getBeginIndex(); i <= series.getEndIndex(); i++) {
|
||||
if (rule.isSatisfied(i, null)) hits.add(i);
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
private static BarSeries buildOscillatingSeries(int count) {
|
||||
BaseBarSeriesBuilder builder = new BaseBarSeriesBuilder()
|
||||
.withName("test")
|
||||
.withBarBuilderFactory(new TimeBarBuilderFactory(Duration.ofMinutes(3)))
|
||||
.withNumFactory(DoubleNumFactory.getInstance());
|
||||
BarSeries s = builder.build();
|
||||
Instant t = Instant.parse("2024-01-01T00:00:00Z");
|
||||
double price = 100;
|
||||
for (int i = 0; i < count; i++) {
|
||||
double swing = Math.sin(i * 0.15) * 8 + Math.sin(i * 0.04) * 4;
|
||||
price = 100 + swing + (i * 0.02);
|
||||
double o = price - 0.5;
|
||||
double h = price + 1.5;
|
||||
double l = price - 1.5;
|
||||
double c = price;
|
||||
s.addBar(t, o, h, l, c, 1000);
|
||||
t = t.plus(Duration.ofMinutes(3));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user