From 095b0d7db74d574ce4f7cd9c56da78c2a1c8e5e2 Mon Sep 17 00:00:00 2001 From: Macbook Date: Fri, 29 May 2026 23:13:28 +0900 Subject: [PATCH] =?UTF-8?q?TRIX=20=EC=97=85=EB=B9=84=ED=8A=B8=EC=99=80=20?= =?UTF-8?q?=EA=B3=B5=EC=8B=9D=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../goldenchart/service/IndicatorService.java | 8 ++++---- .../service/StrategyDslToTa4jAdapter.java | 19 +++++++++---------- frontend/src/utils/customIndicators.ts | 14 ++++++++------ frontend/src/utils/indicatorRegistry.ts | 2 +- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/backend/src/main/java/com/goldenchart/service/IndicatorService.java b/backend/src/main/java/com/goldenchart/service/IndicatorService.java index 5d4401d..cfe404c 100644 --- a/backend/src/main/java/com/goldenchart/service/IndicatorService.java +++ b/backend/src/main/java/com/goldenchart/service/IndicatorService.java @@ -678,14 +678,14 @@ public class IndicatorService { return Map.of("plot0", toPlot(s, obv), "plot1", toPlot(s, maInd)); } - /** 업비트 TRIX: ln(종가) → 3×EMA → 1봉 차분 × 10000, 신호선 = TRIX EMA */ + /** 업비트 TRIX: 종가 3×EMA ROC%, TRMA = TRIX EMA */ private Map> calcTRIX(BarSeries s, Map p) { int len = intP(p, "length", 12); int sigLen = intP(p, "signalLength", 9); - Indicator logClose = UnaryOperationIndicator.log(new ClosePriceIndicator(s)); - EMAIndicator e3 = new EMAIndicator(new EMAIndicator(new EMAIndicator(logClose, len), len), len); + ClosePriceIndicator close = new ClosePriceIndicator(s); + EMAIndicator e3 = new EMAIndicator(new EMAIndicator(new EMAIndicator(close, len), len), len); PreviousValueIndicator prev3 = new PreviousValueIndicator(e3); - NumericIndicator trix = NumericIndicator.of(e3).minus(prev3).multipliedBy(10_000); + NumericIndicator trix = NumericIndicator.of(e3).minus(prev3).dividedBy(prev3).multipliedBy(100); EMAIndicator signal = new EMAIndicator(trix, sigLen); return Map.of("plot0", toPlot(s, trix), "plot1", toPlot(s, signal)); } diff --git a/backend/src/main/java/com/goldenchart/service/StrategyDslToTa4jAdapter.java b/backend/src/main/java/com/goldenchart/service/StrategyDslToTa4jAdapter.java index 17b9077..bf15593 100644 --- a/backend/src/main/java/com/goldenchart/service/StrategyDslToTa4jAdapter.java +++ b/backend/src/main/java/com/goldenchart/service/StrategyDslToTa4jAdapter.java @@ -654,24 +654,24 @@ public class StrategyDslToTa4jAdapter { SMAIndicator slowK = new SMAIndicator(new StochasticOscillatorKIndicator(s, kLen), smooth); return new SMAIndicator(slowK, dSmooth); } - // TRIX — ln(종가) → 3×EMA → Δ×10000 (업비트) + // TRIX — 종가 3×EMA ROC% (업비트·키움) if (field.startsWith("TRIX_VALUE_")) { int len = parseTrailingInt(field, "TRIX_VALUE_", intP(p, "length", 12)); - EMAIndicator e3 = tripleLogEma(close, len); + EMAIndicator e3 = tripleEma(close, len); PreviousValueIndicator prev3 = new PreviousValueIndicator(e3); - return NumericIndicator.of(e3).minus(prev3).multipliedBy(10_000); + return NumericIndicator.of(e3).minus(prev3).dividedBy(prev3).multipliedBy(100); } if (field.equals("TRIX_VALUE")) { int len = intP(p, "length", 12); - EMAIndicator e3 = tripleLogEma(close, len); + EMAIndicator e3 = tripleEma(close, len); PreviousValueIndicator prev3 = new PreviousValueIndicator(e3); - return NumericIndicator.of(e3).minus(prev3).multipliedBy(10_000); + return NumericIndicator.of(e3).minus(prev3).dividedBy(prev3).multipliedBy(100); } if (field.equals("TRIX_SIGNAL")) { int len = intP(p, "length", 12); - EMAIndicator e3 = tripleLogEma(close, len); + EMAIndicator e3 = tripleEma(close, len); PreviousValueIndicator prev3 = new PreviousValueIndicator(e3); - NumericIndicator trix = NumericIndicator.of(e3).minus(prev3).multipliedBy(10_000); + NumericIndicator trix = NumericIndicator.of(e3).minus(prev3).dividedBy(prev3).multipliedBy(100); return new EMAIndicator(trix, intP(p, "signalLength", 9)); } // OBV @@ -865,9 +865,8 @@ public class StrategyDslToTa4jAdapter { // ── TRIX 헬퍼 ───────────────────────────────────────────────────────────── - private EMAIndicator tripleLogEma(ClosePriceIndicator close, int len) { - Indicator logClose = UnaryOperationIndicator.log(close); - return new EMAIndicator(new EMAIndicator(new EMAIndicator(logClose, len), len), len); + private EMAIndicator tripleEma(ClosePriceIndicator close, int len) { + return new EMAIndicator(new EMAIndicator(new EMAIndicator(close, len), len), len); } private Indicator maOfSource(Indicator source, BarSeries s, String maType, int len) { diff --git a/frontend/src/utils/customIndicators.ts b/frontend/src/utils/customIndicators.ts index 2e5dea0..3084ead 100644 --- a/frontend/src/utils/customIndicators.ts +++ b/frontend/src/utils/customIndicators.ts @@ -183,8 +183,10 @@ export function calcInvestPsychological( } /** - * TRIX + 신호선 (업비트·TradingView 동일) - * 1) ln(종가) → 2) EMA × 3 → 3) 1봉 차분 × 10000 → 4) 신호선 = TRIX의 EMA(signalLength) + * TRIX + TRMA(시그널) — 업비트·키움 HTS + * EMA3 = EMA(EMA(EMA(종가, N), N), N) + * TRIX = (EMA3 - EMA3[1]) / EMA3[1] × 100 + * TRMA = TRIX의 EMA(M) */ export function calcTRIXWithSignal( bars: OHLCVBar[], @@ -192,15 +194,15 @@ export function calcTRIXWithSignal( ): Record { const len = Number(params.length ?? 12); const sigLen = Number(params.signalLength ?? 9); - const logClose = bars.map(b => (b.close > 0 ? Math.log(b.close) : NaN)); + const closes = bars.map(b => b.close); - const e1 = ema(logClose, len); + const e1 = ema(closes, len); const e2 = ema(e1.map(v => (Number.isFinite(v) ? v : NaN)), len); const e3 = ema(e2.map(v => (Number.isFinite(v) ? v : NaN)), len); const trix = e3.map((v, i) => { - if (i === 0 || !Number.isFinite(v) || !Number.isFinite(e3[i - 1])) return NaN; - return (v - e3[i - 1]) * 10_000; + if (i === 0 || !Number.isFinite(v) || !Number.isFinite(e3[i - 1]) || e3[i - 1] === 0) return NaN; + return (v - e3[i - 1]) / e3[i - 1] * 100; }); const signal = ema(trix.map(v => (Number.isFinite(v) ? v : NaN)), sigLen); diff --git a/frontend/src/utils/indicatorRegistry.ts b/frontend/src/utils/indicatorRegistry.ts index ac64cf2..fa0b39d 100644 --- a/frontend/src/utils/indicatorRegistry.ts +++ b/frontend/src/utils/indicatorRegistry.ts @@ -315,7 +315,7 @@ export const INDICATOR_REGISTRY: IndicatorDef[] = [ { type:'Momentum', name:'Momentum', koreanName:'모멘텀', shortName:'MOM', category:'Momentum', overlay:false, defaultParams:{length:10, src:'close'}, plots:[{id:'plot0',title:'MOM', color:'#26A69A',type:'line',lineWidth:2}], hlines:[{price:0,color:'#607D8B'}], description:'모멘텀 지표' }, { type:'ROC', name:'Rate of Change', koreanName:'가격변화율', shortName:'ROC', category:'Momentum', overlay:false, defaultParams:{length:9, src:'close'}, plots:[{id:'plot0',title:'ROC', color:'#00BCD4',type:'line',lineWidth:2}], hlines:[{price:10,color:'#EF535080'},{price:0,color:'#607D8B'},{price:-10,color:'#4CAF5080'}], description:'가격 변화율 (%)' }, { type:'TSI', name:'True Strength Index', koreanName:'참강도지수', shortName:'TSI', category:'Momentum', overlay:false, defaultParams:{longLength:25, shortLength:13, signalLength:13, src:'close'}, plots:[{id:'plot0',title:'TSI', color:'#2196F3',type:'line',lineWidth:2},{id:'plot1',title:'Signal',color:'#FF6D00',type:'line',lineWidth:1}], hlines:[{price:25,color:'#EF5350'},{price:0,color:'#607D8B'},{price:-25,color:'#4CAF50'}], description:'참 강도 지수' }, - { type:'TRIX', name:'TRIX', koreanName:'삼중지수변화율', shortName:'TRIX', category:'Momentum', overlay:false, defaultParams:{length:12, signalLength:9}, plots:[{id:'plot0',title:'TRIX', color:'#26A69A',type:'line',lineWidth:2},{id:'plot1',title:'Signal',color:'#FF6D00',type:'line',lineWidth:1}], hlines:[{price:0,color:'#607D8B', label:'중앙선'}], description:'삼중 지수 변화율 (ln→3×EMA→Δ×10000)' }, + { type:'TRIX', name:'TRIX', koreanName:'삼중지수변화율', shortName:'TRIX', category:'Momentum', overlay:false, defaultParams:{length:12, signalLength:9}, plots:[{id:'plot0',title:'TRIX', color:'#26A69A',type:'line',lineWidth:2},{id:'plot1',title:'TRMA',color:'#FF6D00',type:'line',lineWidth:1}], hlines:[{price:0,color:'#607D8B', label:'중앙선'}], description:'삼중 지수 변화율 (종가 3×EMA ROC%)' }, { type:'KnowSureThing', name:'Know Sure Thing (KST)', koreanName:'KST모멘텀', shortName:'KST', category:'Momentum', overlay:false, defaultParams:{}, plots:[{id:'plot0',title:'KST', color:'#9C27B0',type:'line',lineWidth:2},{id:'plot1',title:'Signal',color:'#FF9800',type:'line',lineWidth:1}], hlines:[{price:0,color:'#607D8B'}], description:'KST 모멘텀 지표' }, { type:'CoppockCurve', name:'Coppock Curve', koreanName:'콥독커브', shortName:'Copp', category:'Momentum', overlay:false, defaultParams:{}, plots:[{id:'plot0',title:'Coppock',color:'#E91E63',type:'line',lineWidth:2}], hlines:[{price:0,color:'#607D8B'}], description:'콥독 커브 장기 모멘텀' }, { type:'BullBearPower', name:'Bull Bear Power', koreanName:'매수매도세력', shortName:'BBP', category:'Momentum', overlay:false, defaultParams:{length:13, src:'close'}, plots:[{id:'plot0',title:'BBP', color:'#26A69A',type:'histogram'}], hlines:[{price:0,color:'#607D8B'}], description:'매수/매도 세력 비교' },