HEROJOON 블로그(히로블)

업비트 코인 자동매매 RSI 값 구하기 (Java) 본문

Backend

업비트 코인 자동매매 RSI 값 구하기 (Java)

herojoon 2022. 12. 2. 01:42
반응형

목표

업비트에서 제공하는 RSI 값 계산식Java언어를 이용하여 구현해본다.

 

"파이썬의 경우 Pandas 라이브러리를 이용하여 해당 라이브러리에서 제공하는 ewm 함수로 쉽게 RSI값을 구할 수 있으나 Java의 경우 계산식 라이브러리가 오래되었고, 업비트에서 제공하는 RSI값과 차이가 있었습니다.

그래서 RSI에 관련된 공식들에 대한 이론 공부와 Pandas 라이브러리의 ewm 함수를 참고하여 업비트 RSI 계산식을 만들어보았습니다. 결과는 해당 계산식으로 업비트의 RSI 값과 일치하는 값을 가져올 수 있습니다."

 

 

먼저 이해하기

RSI (Relative Strength Index: 상대강도지수) 란?

 

주식, 선물, 옵션 등의 기술적 분석에 사용되는 보조 지표이다. RSI는 가격의 상승압력과 하락압력 간의 상대적인 강도를 나타낸다. 1978년 미국의 월레스 와일더가 개발했다.

 

RSI는 일정 기간 동안 주가가 전일 가격에 비해 상승한 변화량과 하락한 변화량의 평균값을 구하여, 상승한 변화량이 크면 과매수로, 하락한 변화량이 크면 과매도로 판단하는 방식이다.

 

참고: https://ko.wikipedia.org/wiki/RSI_(%ED%88%AC%EC%9E%90%EC%A7%80%ED%91%9C) 

 

RSI (투자지표) - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 상대강도지수(相對强度指數, 영어: relative strength index, RSI)는 주식, 선물, 옵션 등의 기술적 분석에 사용되는 보조 지표이다. RSI는 가격의 상승압력과 하락압력

ko.wikipedia.org

 

- RSI 설명 요약 -

  • RSI 30 이하: 과매도 구간 (매수 타이밍)
  • RSI 70 이상: 과매수 구간 (매도 타이밍)

 

 

RSI 값 계산식

일반 RSI 값 계산식

RSI = RS / (1 + RS) 또는 AU / (AU + AD)

 

RS = AU / AD

 

AU (Average Ups): 일정 기간 동안 종가가 이전 종가보다 상승한 값의 평균
AD (Average Downs): 일정 기간 동안 종가가 이전 종가보다 하락한 값의 평균

RSI는보통 14일 기준으로 구하지만 다른 기간으로 설정하여 구해도 됩니다.

RSI (14) 라는 것은 RSI를 14일 기준으로 구한다는 뜻입니다.

 

 

업비트 RSI 값 계산식

RSI = RS / (1 + RS) 또는 AU / (AU + AD)

 

RS = AU / AD

 


★★이 부분이 중요합니다.★★

보통 "단순 이동 평균"을 이용하여 아래 부분을 구하는데, 업비트의 RSI는 "지수 이동 평균"을 이용하여 아래 AU와 AD를 구해주어야 합니다.

 

- 단순 이동 평균 (Simple Moving Average: SMA)

: 값을 모두 더해서 값의 개수로 나눠 평균값을 구합니다. ex) (20 + 10 + 30) / 3

 

- 지수 이동 평균 (Exponential Moving Average: EMA)

: 최근 값에 좀 더 큰 가중치를 부여하여 평균값을 구합니다.

 

<지수 이동 평균(EMA) 계산식>

a = 2 / (1 + Day)

(종가 * a) + (이전 EMA * (1- a))

 

<업비트 RSI 값 구하기 위해 수정한 지수 이동 평균(EMA) 계산식>

: 파이썬 Pandas 라이브러리의 ewm함수를 참고하여 기존 지수 이동 평균 계산식을 변경하였습니다.

a = 1 / (1 + (Day - 1))

(종가 * a) + (이전 EMA * (1- a))

 

Day: RSI 기준일자

이전 EMA: EMA 공식으로 구한 값. 최초 이전 EMA의 값은 이전 종가의 값을 그대로 사용합니다.

 

AU (Average Ups): 일정 기간 동안 종가가 이전 종가보다 상승한 값의 평균
AD (Average Downs): 일정 기간 동안 종가가 이전 종가보다 하락한 값의 평균


 

업비트 RSI 값 구하기 Java 코드

public Double getRsiByMinutes() {
    final int minutes = 30;
    final String market = "KRW-BTC";
    final int maxCount = 200;
    // 업비트 캔들 API 호출 (Docs: https://docs.upbit.com/reference/%EB%B6%84minute-%EC%BA%94%EB%93%A4-1)
    List<MinuteCandleRes> candleResList = marketPriceReaderService.getCandleMinutes(minutes, market, maxCount);
    if (CollectionUtils.isEmpty(candleResList)) {
        return null;
    }
    
    // 지수 이동 평균은 과거 데이터부터 구해주어야 합니다.
    candleResList = candleResList.stream()
            .sorted(Comparator.comparing(CandleRes::getTimestamp))  // 오름차순 (과거 순)
            .collect(Collectors.toList());  // Sort

    double zero = 0;
    List<Double> upList = new ArrayList<>();  // 상승 리스트
    List<Double> downList = new ArrayList<>();  // 하락 리스트
    for (int i = 0; i < candleResList.size() - 1; i++) {
        // 최근 종가 - 전일 종가 = gap 값이 양수일 경우 상승했다는 뜻 / 음수일 경우 하락이라는 뜻
        double gapByTradePrice = candleResList.get(i + 1).getTradePrice().doubleValue() - candleResList.get(i).getTradePrice().doubleValue();
        if (gapByTradePrice > 0) {  // 종가가 전일 종가보다 상승일 경우
            upList.add(gapByTradePrice);
            downList.add(zero);
        } else if (gapByTradePrice < 0) {  // 종가가 전일 종가보다 하락일 경우
            downList.add(gapByTradePrice * -1);  // 음수를 양수로 변환해준다.
            upList.add(zero);
        } else {  // 상승, 하락이 없을 경우 종가 - 전일 종가 = gap은 0이므로 0값을 넣어줍니다.
            upList.add(zero);
            downList.add(zero);
        }
    }

    double day = 14;  // 가중치를 위한 기준 일자 (보통 14일 기준)
    double a = (double) 1 / (1 + (day - 1));  // 지수 이동 평균의 정식 공식은 a = 2 / 1 + day 이지만 업비트에서 사용하는 수식은 a = 1 / (1 + (day - 1))
    
    // AU값 구하기
    double upEma = 0;  // 상승 값의 지수이동평균
    if (CollectionUtils.isNotEmpty(upList)) {
        upEma = upList.get(0).doubleValue();
        if (upList.size() > 1) {
            for (int i = 1 ; i < upList.size(); i++) {
                upEma = (upList.get(i).doubleValue() * a) + (upEma * (1 - a));
            }
        }
    }

    // AD값 구하기
    double downEma = 0;  // 하락 값의 지수이동평균
    if (CollectionUtils.isNotEmpty(downList)) {
        downEma = downList.get(0).doubleValue();
        if (downList.size() > 1) {
            for (int i = 1; i < downList.size(); i++) {
                downEma = (downList.get(i).doubleValue() * a) + (downEma * (1 - a));
            }
        }
    }

    // rsi 계산
    double au = upEma;
    double ad = downEma;
    double rs = au / ad;
    double rsi = 100 - (100 / (1 + rs));

    return rsi;
}

 

코드 설명

1. RSI값을 구하기 위해 여러 날의 종가 정보들을 조회한다. 해당 정보는 캔들 조회 API를 이용하여 최대 200개까지 조회할 수 있다.

- 업비트 캔들 조회 API: https://docs.upbit.com/reference/%EB%B6%84minute-%EC%BA%94%EB%93%A4-1

 

업비트 개발자 센터

업비트 Open API 사용을 위한 개발 문서를 제공 합니다.업비트 Open API 사용하여 다양한 앱과 프로그램을 제작해보세요.

docs.upbit.com

-> RSI값을 14일 기준으로 구하고 싶었기때문에 14일 기준을 구한다면 캔들 조회 API의 값도 14개를 가져와야 하나?

아니면 종가와 전일 종가의 상승분, 하락분을 구하면 차액 값이기 때문에 13개의 값이 나올텐데 애초에 14개에 1개를 더 가져와서 총 15개를 가져와야하나 헷갈렸었다. 리서치를 해보니 지수 이동 평균을 구할 때 데이터의 값이 어느정도 많아야 한다고 보았고, 업비트에서 최대로 가져올 수 있는 200개를 가져오니 업비트 RSI값과 일치하였다. 여기서 14일은 14일 기준의 가중치를 가진 지수 이동 평균을 구한다. 라는 의미로 보면 조금 덜 헷갈릴 것 같다.

 

2. AU는 종가가 전일 종가보다 얼마나 상승했는지 상승한 값들을 가져와 평균을 내는 것이고, AD는 종가가 전일 종가보다 얼마나 하락했는지 하락한 값들을 가져와 평균을 내는 것이다.

for문을 돌려서 상승값의 리스트와 하락값의 리스트를 만들어 준다.

-> 종가 - 전일 종가 = gap을 구하다 보면 상승이나 하락이 없어 0이 나올 때가 있다. 0의 값도 지수 이동 평균을 구하기 위해 상승값 리스트, 하락값 리스트에 넣어야 하는지 고민이었다. 파이썬으로 구현한 RSI 값 로직을 참고해보니 0의 값도 함께 넣어서 값을 구하고 있었고 Java코드에서도 0 값을 넣어 지수 이동 평균을 구하도록 하였다.

 

3. 지수 이동 평균을 이용하여 AU, AD 값을 구해준다.

-> 지수 이동 평균을 구할때 가중치에 대한 기준 값을 a = 2 / 1 + Day라는 공식으로 대게 구한다.

하지만 이렇게 테스트할 경우 RSI 값이 정확하지 않았기 때문에 Day를 14가 아닌 13이나 15로 넣어야 하나 헷갈렸었다.

나중에 알고보니 공식 자체를 수정하는게 맞아서 파이썬 Pandas 라이브러리의 ewm 함수 공식을 대입해 해결하였다.

 

4. 위에서 구한 AU, AD값을 이용하여 RSI값을 구해준다.

-> RSI값이 잘 나오는 것을 확인할 수 있다.

 

결과

-- 테스트 1 --

 

<Java 코드 RSI 값>

 

<업비트 RSI 값>

 

 

-- 테스트 2 --

 

<Java 코드 RSI 값>

 

<업비트 RSI 값>

 

결과를 보면 Java코드의 RSI 값과 업비트 RSI값이 소수점 4자리까지 정확하게 일치하는 것을 볼 수 있다.

소수점 5번째 이상부터는 미세한 차이가 있지만 이것까진 생각하지 않는 것으로..ㅎ

반응형
Comments