From 44609b7fd7bf71b78d801014759fcccb7ef8797c Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Thu, 30 May 2024 13:42:18 +0900 Subject: [PATCH] =?UTF-8?q?#16=20[refactor]=20:=20=EC=86=90=EC=A0=88=20&?= =?UTF-8?q?=20=EC=9D=B5=EC=A0=88=20=EA=B3=84=EC=82=B0=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dgu/backend/dto/BackTestingDto.java | 15 +++++ .../service/BackTestingCalculator.java | 61 +++++++++---------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/backend/src/main/java/org/dgu/backend/dto/BackTestingDto.java b/backend/src/main/java/org/dgu/backend/dto/BackTestingDto.java index 7c9f34c..88a6727 100644 --- a/backend/src/main/java/org/dgu/backend/dto/BackTestingDto.java +++ b/backend/src/main/java/org/dgu/backend/dto/BackTestingDto.java @@ -74,6 +74,10 @@ public static class BackTestingResult { private Long income; private Integer tradingPeriod; + public void updateRate(Double rate) { + this.rate = rate; + } + public static BackTestingDto.BackTestingResult of(LocalDateTime date, String action, Double coinPrice, Double coin, Long capital, Double rate, Long income, Integer tradingPeriod) { return BackTestingDto.BackTestingResult.builder() .date(date) @@ -209,6 +213,17 @@ public static class TradingLog { private Double coin; private Long coinPrice; private Double rate; + + public static BackTestingDto.TradingLog of(BackTestingDto.BackTestingResult backTestingResult) { + return BackTestingDto.TradingLog.builder() + .type(!backTestingResult.getAction().equals("BUY") ? "매도" : "매수") + .date(backTestingResult.getDate()) + .capital(backTestingResult.getCapital()) + .coinPrice(backTestingResult.getCoinPrice().longValue()) + .coin(backTestingResult.getCoin()) + .rate(backTestingResult.getRate()) + .build(); + } } @Builder diff --git a/backend/src/main/java/org/dgu/backend/service/BackTestingCalculator.java b/backend/src/main/java/org/dgu/backend/service/BackTestingCalculator.java index e24db57..73ff36c 100644 --- a/backend/src/main/java/org/dgu/backend/service/BackTestingCalculator.java +++ b/backend/src/main/java/org/dgu/backend/service/BackTestingCalculator.java @@ -3,6 +3,8 @@ import lombok.RequiredArgsConstructor; import org.dgu.backend.domain.CandleInfo; import org.dgu.backend.dto.BackTestingDto; +import org.dgu.backend.exception.BackTestingErrorResult; +import org.dgu.backend.exception.BackTestingException; import org.dgu.backend.util.NumberUtil; import org.springframework.stereotype.Component; @@ -44,15 +46,15 @@ public List removeDuplicatedCandles(List candles) { public List calculateEMA(List candles, int date) { Double k = 2.0 / (date + 1); List emaValues = new ArrayList<>(); - for (int i = 0; i < candles.size(); i++) { + Double ema = candles.get(0).getTradePrice(); + emaValues.add(ema); + + for (int i = 1; i < candles.size(); i++) { Double price = candles.get(i).getTradePrice(); - if (i == 0) { - emaValues.add(price); - } else { - Double ema = k * price + (1 - k) * emaValues.get(i - 1); - emaValues.add(ema); - } + ema = k * price + (1 - k) * ema; + emaValues.add(ema); } + return IntStream.range(0, emaValues.size()) .mapToObj(i -> BackTestingDto.EMAInfo.builder() .date(candles.get(i).getDateTime()) @@ -99,14 +101,14 @@ public List run(List candles, Back for (LocalDateTime goldenCrossPoint : goldenCrossPoints) { int startIndex = findStartIndex(candles, goldenCrossPoint); - executeTrade(candles, startIndex, backTestingResults); + executeTrade(candles, startIndex, stepInfo.getTradingUnit(), backTestingResults); } return backTestingResults; } // 거래를 실행하는 메서드 - private void executeTrade(List candles, int startIndex, List backTestingResults) { + private void executeTrade(List candles, int startIndex, int tradingUnit, List backTestingResults) { // 초기 세팅 coin = 0.0; executeBuy(candles.get(startIndex).getDateTime(), candles.get(startIndex).getTradePrice(), backTestingResults); @@ -116,7 +118,7 @@ private void executeTrade(List candles, int startIndex, List candles, int startIndex, List avgPrice * (100 + sellingPoint) / 100) { + // 전체 자본 대비 수익률을 기준으로 한 익절 처리 + else if (((currentPrice * coin + capital) - (tradingUnit * buyingCnt)) / (tradingUnit * buyingCnt) * 100 > sellingPoint) { executeSell(currentDate, currentPrice, backTestingResults); break; } - // 손절 처리 - else if (currentPrice < avgPrice * (100 - stopLossPoint) / 100) { + // 전체 자본 대비 수익률을 기준으로 한 손절 처리 + else if (((currentPrice * coin + capital) - (tradingUnit * buyingCnt)) / (tradingUnit * buyingCnt) * 100 < -stopLossPoint) { executeStopLoss(currentDate, currentPrice, backTestingResults); break; } @@ -153,7 +151,7 @@ private void executeBuy(LocalDateTime currentDate, Double currentPrice, List candles, LocalDateTime goldenCrossPo return i; } } - return -1; + + throw new BackTestingException(BackTestingErrorResult.NOT_FOUND_START_INDEX); } // 백테스팅 결과를 집계하는 메서드 @@ -267,9 +266,9 @@ private BackTestingDto.Trading createTradingPart(Long capital, Long finalCapital private BackTestingDto.Performance createPerformancePart(Double initialCapital, Long finalCapital) { int totalTradeCount = positiveTradeCount + negativeTradeCount; Double totalRate = numberUtil.round(((finalCapital - initialCapital) / initialCapital) * 100, 2); - Double winRate = numberUtil.round((double) positiveTradeCount / totalTradeCount * 100, 2); - Double lossRate = numberUtil.round((double) negativeTradeCount / totalTradeCount * 100, 2); - Double winLossRatio = numberUtil.round((double) positiveTradeCount / negativeTradeCount * 100, 2); + Double winRate = positiveTradeCount != 0 ? numberUtil.round((double) positiveTradeCount / totalTradeCount * 100, 2) : 0.0; + Double lossRate = negativeTradeCount != 0 ? numberUtil.round((double) negativeTradeCount / totalTradeCount * 100, 2) : 0.0; + Double winLossRatio = negativeTradeCount != 0 ? numberUtil.round((double) positiveTradeCount / negativeTradeCount * 100, 2) : 0.0; return BackTestingDto.Performance.builder() .totalRate(totalRate) @@ -284,13 +283,9 @@ private BackTestingDto.Performance createPerformancePart(Double initialCapital, // 거래 로그 생성 메서드 private BackTestingDto.TradingLog createTradingLog(BackTestingDto.BackTestingResult backTestingResult) { - return BackTestingDto.TradingLog.builder() - .type(!backTestingResult.getAction().equals("BUY") ? "매도" : "매수") - .date(backTestingResult.getDate()) - .capital(backTestingResult.getCapital()) - .coinPrice(backTestingResult.getCoinPrice().longValue()) - .coin(backTestingResult.getCoin()) - .rate(backTestingResult.getRate() != null && !backTestingResult.getRate().isNaN() ? (numberUtil.round(backTestingResult.getRate(), 2)) : 0.0) - .build(); + Double rate = numberUtil.round(backTestingResult.getRate(), 2); + backTestingResult.updateRate(rate); + + return BackTestingDto.TradingLog.of(backTestingResult); } } \ No newline at end of file