Programming/Java

(java) Wilder RSI(Relative Strength Index) 계산하는 방법

Jan92 2025. 7. 13. 15:33
반응형

Wilder RSI(Relative Strength Index) 계산하는 방법

해당 포스팅에서는 주식 자동매매 프로그램을 만드는 과정에서 상대강도지수인 RSI 지표를 구하는 방식 및 소스 코드 예시를 정리해 보았습니다.

RSI 값은 일별 종가 데이터를 기반으로 계산하며, 아래 예시에서는 전일 RSI 값과 현재 주가에 따른 실시간 RSI 값을 계산하는 예시가 구현되어 있습니다.

내용 참고하시고 궁금하신 부분은 댓글 주시면 답변드리도록 하겠습니다.

 

 


RSI란

RSI(Relative Strength Index)는 주어진 기간 동안 주가 가격 상승과 하락의 크기를 비교해 과매수 또는 과매도 상태를 판단하는 보조지표입니다.

일반적으로 14일을 기준 기간으로 사용하며, RSI는 0 ~ 100 사이의 값을 가지게 되는데 70 이상은 과매수, 30 이하는 과매도로 간주됩니다.

 

 


RSI 계산 방법

// AU(N일간의 평균 종가 상승폭), AD(N일간의 평균 종가 하락폭)

RS = AU / AD
RSI = 100 - (100 / (1 + RS))


// 예를 들어 다음과 같은 종가 데이터가 있다고 가정할 때
// (맨 오른쪽 데이터가 가장 최근 종가라고 가정하며, 맨 오른쪽 데이터 기준 RSI를 구하는 예시)

data = [11, 10, 15, 13, 8, 5, 9]

Day | Price | Change | Gain | Loss
----|-------|--------|------|-----
 1  | 11    |   -    |  -   |  -
 2  | 10    | -1     |  0   | 1
 3  | 15    | +5     |  5   | 0
 4  | 13    | -2     |  0   | 2
 5  | 8     | -5     |  0   | 5
 6  | 5     | -3     |  0   | 3
 7  | 9     | +4     |  4   | 0

AU = (5 + 4) / 7 = 9 / 7 ≈ 1.2857
AD = (1 + 2 + 5 + 3) / 7 = 11 / 7 ≈ 1.5714

RS = AU / AD ≈ 0.8182
RSI = 100 - (100 / 1 + 0.8182) = 45.0

 

RSI를 계산하는 일반적인 방식은 다음과 같습니다.

하지만 해당 방식으로 특정 종목의 RSI를 계산했을 때, 증권사 hts 등에서 표시되는 RSI 값과는 결과가 다를 수 있는데요.

 

이유는 위에서 계산한 방식은 단순 이동 평균(SMA) 기반의 Cutler 방식의 RSI 계산이고, 증권사에서 사용되는 RSI 계산은 지수평활법 기반의 Wilder 방식을 사용하기 때문입니다.

 

* RSI 계산 방식은 증권사마다 다를 수 있다는 점 참고 부탁드립니다.

 

 

// 14일을 기준 기간으로 하였을 때, 초기 14일 데이터는 동일한 방식으로 계산

AU = 14일 동안의 종가 상승 금액 합 / 14
AD = 14일 동안의 종가 하락 금액 합 / 14

// 14일 이후부터는 지수평활법을 사용하여 누적 업데이트된 종가 상승 금액을 기준으로 AU, AD 계산

AU = (전 거래일 기준 AU * 13 + 현재 U) / 14
AD = (전 거래일 기준 AD * 13 + 현재 D) / 14

RS = AU / AD
RSI = 100 - (100 / (1 + RS))

 

위 예시와 같이 Wilder RSI 계산 방식의 경우 기준일이 14일인 경우, 단순 14일 치의 종가 데이터가 아닌 이전 종가 데이터부터 누적된 데이터가 필요하다는 차이점이 있습니다.

 

 


Wilder RSI 계산 예시 코드

@Slf4j
@SpringBootTest
public class RSITestSimulator {

    int period = 14;
    double au;
    double ad;
    long lastClosePric;

    @Test
    void calculatingTheRSI() {
        // 종가 데이터 (날짜 기준 오름차순)
        List<Long> closePricList = new ArrayList<>();

        // 초기 계산 데이터 자르기 및 초기 계산 데이터 기준 마지막 가격 세팅
        List<Long> preCloses = closePricList.subList(0, period - 1);
        lastClosePric = preCloses.get(preCloses.size() - 1);
        // 초기 세팅 이후 계산될 데이터 자르기
        List<Long> afterCloses = closePricList.subList(period - 1, closePricList.size() - 1);

        // 초기 RSI 계산 AU, AD 세팅
        preAverageGainLoss(preCloses);
        // 초기 세팅 이후 데이터 AU, AD 세팅
        afterAverageGainLoss(afterCloses);

        // 전날 종가 기준 RSI Value
        log.info("beforeDayRsiValue: " + computeRSI(au, ad));

        // 실시간 주가 데이터
        List<Long> realTimePriceList = new ArrayList<>();

        for (Long price : realTimePriceList) {
            // 실시간 주가 RSI Value
            log.info("realTimeRsiValue: " + getRealTimeRsi(price));
        }
    }

    private void preAverageGainLoss(List<Long> prices) {
        double gainSum = 0.0;
        double lossSum = 0.0;
        for (int i = 1; i < prices.size(); i++) {
            double change = prices.get(i) - prices.get(i -1);
            if (change > 0) {
                gainSum += change;
            } else {
                lossSum -= change;
            }
        }

        this.au = gainSum / period - 1;
        this.ad = lossSum / period - 1;
    }

    private void afterAverageGainLoss(List<Long> prices) {
        for (Long price : prices) {
            long change = price - lastClosePric;
            if (change > 0) {
                this.au = ((au * (period -1)) + change) / period;
                this.ad = ((ad * (period -1)) + 0) / period;
            } else {
                this.au = ((au * (period -1)) + 0) / period;
                this.ad = ((ad * (period -1)) - change) / period;
            }
            this.lastClosePric = price;
        }
    }

    public double getRealTimeRsi(long nowPric) {
        long change = nowPric - lastClosePric;

        double realAu;
        double realAd;

        if (change > 0) {
            realAu = ((au * (period -1)) + change) / period;
            realAd = ((ad * (period -1)) + 0) / period;
        } else {
            realAu = ((au * (period -1)) + 0) / period;
            realAd = ((ad * (period -1)) - change) / period;
        }

        return computeRSI(realAu, realAd);
    }

    // RSI 계산 공식
    private double computeRSI(double au, double ad) {
        if (ad == 0) {
            return 100.0;
        }
        double rsi =  au / (au + ad) * 100;
        return Math.round(rsi * 100.0) / 100.0;
    }
}

 

단순 RSI 계산만을 위한 코드 예시이기 때문에 불필요한 부분은 생략하였으며, 코드 최적화 등의 부분은 부족한 점이 있을 수 있습니다.

 

 

 

 

< 주식 자동매매 관련 자료 >

2025.07.05 - [Programming/Java] - WebSocketClient 메시지 누락을 막기 위한 ExecutorService, BlockingQueue 도입

반응형