본문 바로가기

프로젝트/SpringBoot 업비트 자동매매 프로젝트

[자동매매, SpringBoot] 2. 계좌 조회

2024.05.11 - [프로젝트/SpringBoot 업비트 자동매매 프로젝트] - [자동매매, SpringBoot] 1. 프로젝트 생성 및 계획

 

이전 글에서 프로젝트 생성 및 계획을 마쳤고, 계좌 조회 기능을 추가할 것 이다.

 

업비트 개발자센터에서 API Reference를 보면

 

https://api.upbit.com/v1/accounts GET 요청을 보내면 내가 보유한 자산 리스트를 Response 받을 수 있다고 한다.

 

화폐, 수량, 묶여있는 수량, 매수평균가, 매수평균가 수정 여부, 평단가 기준 화폐 정보를 받을 수 있는데

 

화폐, 수량 정보만 있으면 될 것 같다. 

 

1. Util Class

API 요청을 보내려면 Header로 Authorization token을 보내야 하는데

 

업비트에서 지급 받은 accessKey, secretKey를 가지고 token을 만들 수 있다.

 

이 토큰은 앞으로 다른 요청을 보낼 때도 계속 쓰일 것 같으니 Util 클래스를 만들어서 재사용 할 것이다.

 

Util 클래스에는 token을 만드는 함수와, 요청을 보낼 때 쓸 restTemplate을 Bean으로 등록 해 주었다.

 

앞으로 재사용 할 함수들은 Util에 추가할 예정

 

Dependency에 jwt 라이브러리도 추가

 

 

2. AccountService

계좌관련 서비스는 AccountService에 추가를 할 것이다.

 

계좌 조회 요청을 보내면 화폐, 수량, 묶여있는 수량, 매수평균가, 매수평균가 수정 여부, 평단가 기준 화폐 정보를 받을 수 있는데

 

화폐, 수량, 평균 매수가 정도가 필요 할 것 같아서

 

보유한 화폐의 수량을 return 해주는 getBalances, getBalance

 

보유한 화폐의 평균 매수가를 return 해주는 getAvgBuyPrices, getAvgBuyPrice 를 만들었다.

 

Util Class 의 generateAuthenticationToken 함수로 JWT Token을 만들고

 

이 토큰을 담아서 https://api.upbit.com/v1/accounts  GET 요청을 보낸다.

 

@Slf4j
@Service
@RequiredArgsConstructor
public class AccountService {
    private final Util util;
    private final RestTemplate restTemplate;

    /**
     * 보유한 화폐들의 수량
     * @return Map<화폐, 수량>
     * @throws IOException
     */
    public Map<String, Double> getAllBalances() throws IOException {
        Map<String, Double> balances = new HashMap<>();

        String authenticationToken = util.generateAuthenticationToken();

        HttpHeaders headers = new HttpHeaders();

        headers.set("Authorization", authenticationToken);
        headers.set("Content-Type", "application/json");

        HttpEntity request = new HttpEntity(headers);

        ResponseEntity<String> response = restTemplate.exchange(SERVER_URL + "/v1/accounts", HttpMethod.GET, request, String.class);

        List<Map<String, Object>> list = new ObjectMapper().readValue(response.getBody(), new TypeReference<List<Map<String, Object>>>() {});

        for( Map<String, Object> account : list) {
            String currency = account.get("currency").toString();
            double balance = Double.parseDouble(account.get("balance").toString());

            balances.put(currency, balance);

            log.info("[" + currency + "] 수량 : " + balance);
        }

        return balances;
    }

    /**
     * 보유한 화폐들의 평균 매수가
     * @return Map<화폐, 평균 매수가>
     * @throws JsonProcessingException
     */
    public Map<String, Double> getAllAvgBuyPrices() throws JsonProcessingException {
        Map<String, Double> avgBuyPrices = new HashMap<>();

        String authenticationToken = util.generateAuthenticationToken();

        HttpHeaders headers = new HttpHeaders();

        headers.set("Authorization", authenticationToken);
        headers.set("Content-Type", "application/json");

        HttpEntity request = new HttpEntity(headers);

        ResponseEntity<String> response = restTemplate.exchange(SERVER_URL + "/v1/accounts", HttpMethod.GET, request, String.class);

        List<Map<String, Object>> list = new ObjectMapper().readValue(response.getBody(), new TypeReference<List<Map<String, Object>>>() {});

        for( Map<String, Object> account : list) {
            String currency = account.get("currency").toString();
            double avgBuyPrice = Double.parseDouble(account.get("avg_buy_price").toString());

            avgBuyPrices.put(currency, avgBuyPrice);

            log.info("[" + currency + "] 평균 매수가 : " + avgBuyPrice);
        }

        return avgBuyPrices;
    }

    /**
     * currency 수량
     * @param currency 화폐
     * @return 수량
     */
    public double getBalance(String currency) throws JsonProcessingException {
        String authenticationToken = util.generateAuthenticationToken();

        HttpHeaders headers = new HttpHeaders();

        headers.set("Authorization", authenticationToken);
        headers.set("Content-Type", "application/json");

        HttpEntity request = new HttpEntity(headers);

        ResponseEntity<String> response = restTemplate.exchange(SERVER_URL + "/v1/accounts", HttpMethod.GET, request, String.class);

        List<Map<String, Object>> list = new ObjectMapper().readValue(response.getBody(), new TypeReference<List<Map<String, Object>>>() {});

        for( Map<String, Object> account : list) {
            if (currency.equals(account.get("currency").toString())) {
                double balance = Double.parseDouble(account.get("balance").toString());
                log.info("[" + currency + "] 수량 : " + balance);
                return balance;
            }
        }

        return 0.0;
    }

    /**
     * currency 평균 매수가
     * @param currency 화폐
     * @return 평균 매수가
     */
    public double getAvgBuyPrices(String currency) throws JsonProcessingException {
        String authenticationToken = util.generateAuthenticationToken();

        HttpHeaders headers = new HttpHeaders();

        headers.set("Authorization", authenticationToken);
        headers.set("Content-Type", "application/json");

        HttpEntity request = new HttpEntity(headers);

        ResponseEntity<String> response = restTemplate.exchange(SERVER_URL + "/v1/accounts", HttpMethod.GET, request, String.class);

        List<Map<String, Object>> list = new ObjectMapper().readValue(response.getBody(), new TypeReference<List<Map<String, Object>>>() {});

        for( Map<String, Object> account : list) {
            if (currency.equals(account.get("currency").toString())) {
                double avgBuyPrice = Double.parseDouble(account.get("avg_buy_price").toString());
                log.info("[" + currency + "] 평균 매수가 : " + avgBuyPrice);
                return avgBuyPrice;
            }
        }

        return 0.0;
    }
}

3. 결과

함수를 실행해서 로그를 확인해보면 나의 계좌가 출력 된다.

 

스케줄링 함수에 넣거나 테스트를 만들어서 테스트 해보면 된다.