신심리도 추가, 투자심리도 수정

This commit is contained in:
Macbook
2026-05-29 23:03:47 +09:00
parent 320675ea6b
commit 359e6c9653
19 changed files with 226 additions and 74 deletions
@@ -80,7 +80,7 @@ public class StrategyDslToTa4jAdapter {
Map.entry("ICHIMOKU", "IchimokuCloud"),
Map.entry("VOLUME_OSC", "VolumeOscillator"),
Map.entry("PSYCHOLOGICAL", "Psychological"),
Map.entry("NEW_PSYCHOLOGICAL", "Psychological"),
Map.entry("NEW_PSYCHOLOGICAL", "NewPsychological"),
Map.entry("INVEST_PSYCHOLOGICAL", "InvestPsychological"),
Map.entry("BWI", "BBBandWidth"),
Map.entry("VR", "VR"),
@@ -700,17 +700,22 @@ public class StrategyDslToTa4jAdapter {
}
if (field.startsWith("PSY_VALUE_")) {
int period = parseTrailingInt(field, "PSY_VALUE_", intP(p, "length", 12));
return newPsychIndicator(s, period, false);
return psychIndicator(s, period, false);
}
if (field.equals("PSY_VALUE") || field.equals("NEW_PSY_VALUE")
|| indType.equals("PSYCHOLOGICAL") || indType.equals("NEW_PSYCHOLOGICAL"))
return newPsychIndicator(s, intP(p, "length", 12), false);
if (field.equals("PSY_VALUE") || indType.equals("PSYCHOLOGICAL"))
return psychIndicator(s, intP(p, "length", 12), false);
if (field.startsWith("NEW_PSY_VALUE_")) {
int period = parseTrailingInt(field, "NEW_PSY_VALUE_", intP(p, "length", 10));
return newSentPsyIndicator(s, period);
}
if (field.equals("NEW_PSY_VALUE") || indType.equals("NEW_PSYCHOLOGICAL"))
return newSentPsyIndicator(s, intP(p, "length", 10));
if (field.startsWith("INVEST_PSY_VALUE_")) {
int period = parseTrailingInt(field, "INVEST_PSY_VALUE_", intP(p, "length", 10));
return newPsychIndicator(s, period, true);
return psychIndicator(s, period, false);
}
if (field.equals("INVEST_PSY_VALUE") || indType.equals("INVEST_PSYCHOLOGICAL"))
return newPsychIndicator(s, intP(p, "length", 10), true);
return psychIndicator(s, intP(p, "length", 10), false);
// Williams %R
if (field.startsWith("WILLIAMS_R_VALUE_")) {
int period = parseTrailingInt(field, "WILLIAMS_R_VALUE_", intP(p, "length", 14));
@@ -894,10 +899,14 @@ public class StrategyDslToTa4jAdapter {
return new VrIndicator(s, period);
}
private Indicator<Num> newPsychIndicator(BarSeries s, int period, boolean volumeWeighted) {
private Indicator<Num> psychIndicator(BarSeries s, int period, boolean volumeWeighted) {
return new PsychologicalIndicator(s, period, volumeWeighted);
}
private Indicator<Num> newSentPsyIndicator(BarSeries s, int period) {
return new NewSentimentPsychologicalIndicator(s, period);
}
/** VR(거래량비율) = 상승일 거래량합 / 하락일 거래량합 × 100 */
private static final class VrIndicator extends CachedIndicator<Num> {
private final int period;
@@ -933,7 +942,7 @@ public class StrategyDslToTa4jAdapter {
}
}
/** 심리도 / 투자심리도 */
/** 심리도 / 투자심리도 */
private static final class PsychologicalIndicator extends CachedIndicator<Num> {
private final int period;
private final boolean volumeWeighted;
@@ -979,6 +988,47 @@ public class StrategyDslToTa4jAdapter {
}
}
/** 신심리도 — 상승·하락 일수와 등락폭 반영 */
private static final class NewSentimentPsychologicalIndicator extends CachedIndicator<Num> {
private final int period;
private final ClosePriceIndicator close;
NewSentimentPsychologicalIndicator(BarSeries series, int period) {
super(series);
this.period = period;
this.close = new ClosePriceIndicator(series);
}
@Override
protected Num calculate(int index) {
if (index < period) return null;
double upSize = 0, downSize = 0;
int upDay = 0, downDay = 0;
for (int j = index - period + 1; j <= index; j++) {
Num c = close.getValue(j);
Num cPrev = close.getValue(j - 1);
if (c == null || cPrev == null) continue;
double diff = c.doubleValue() - cPrev.doubleValue();
if (diff > 0) {
upSize += diff;
upDay++;
} else if (diff < 0) {
downSize += -diff;
downDay++;
}
}
double total = upSize + downSize;
if (total == 0) return null;
double val = (upDay * (upSize / total) - downDay * (downSize / total)) * 100.0 / period;
return getBarSeries().numFactory().numOf(val);
}
@Override
public int getCountOfUnstableBars() {
return period + 1;
}
}
/**
* 직전 N봉 고가 중 최고값 (현재 봉 제외).
* index t 에서 max(high[t-N]..high[t-1]).