[Spring Boot] Spring Boot 한국투자증권 Open API (KIS Developers) 활용하기 - 1. 거래량순위[v1_국내주식-047]
이전 글에서는 초기 세팅을 하였습니다. 본격적으로 거래량순위 API를 활용해서 데이터를 가져오도록 하겠습니다. 아직 세팅이 안되신 분들은 아래 블로그 글을 참고해 주시면 감사하겠습니다.
한국투자증권 Open API - API 문서
한국투자증권 주식 관련 API를 활용하기 위한 홈페이지입니다. 여러 언어 별로 API샘플코드도 지원해주고 있어 누구나 쉽게 접근하고 사용할 수 있습니다.
접근토큰발급(P)
- 메뉴 : 한국투자증권 개발자센터 - API문서 - OAuth 인증 - 접근토큰발급(P)
본인 계좌에 필요한 인증 절차로, 인증을 통해 접근 토큰을 부여받아 오픈API 활용이 가능합니다.
'23.4.28 이후 지나치게 잦은 토큰 발급 요청건을 제어 하기 위해 신규 접근토큰발급 이후 일정시간 이내에 재호출 시에는 직전 토큰값을 리턴하게 되었습니다. 일정시간 이후 접근토큰발급 API 호출 시에는 신규 토큰값을 리턴합니다.
개인 고객 발급의 경우 유효기간은 하루(24H)입니다.
기본정보
Method | POST |
실전 Domain | https://openapi.koreainvestment.com:9443/ |
모의 Domain | https://openapivts.koreainvestment.com:29443/ |
URL | /oauth2/tokenP |
Format | JSON |
Content-Type | application/json; charset=UTF-8 |
LAYOUT
Request
</> Body
Element | 한글명 | Type | Required | Length | Description |
grant_type | 권한부여 Type | String | Y | 18 | client_credentials |
appkey | 앱키 | String | Y | 36 | 한국투자증권 홈페이지에서 발급받은 appkey (절대 노출되지 않도록 주의해주세요.) |
appsecret | 앱시크릿키 | String | Y | 180 | 한국투자증권 홈페이지에서 발급받은 appsecret (절대 노출되지 않도록 주의해주세요.) |
Response
</> Body
Element | 한글명 | Type | Required | Length | Description |
access_token | 접근토큰 | String | Y | 350 | OAuth 토큰이 필요한 API 경우 발급한 Access token ex) "eyJ0eXUxMiJ9.eyJz…..................................." - 일반개인고객/일반법인고객 1. Access token 유효기간 1일 2. 일정시간(6시간) 이내에 재호출 시에는 직전 토큰값을 리턴 3. OAuth 2.0의 Client Credentials Grant 절차를 준용 - 제휴법인 1. Access token 유효기간 3개월 2. Refresh token 유효기간 1년 3. OAuth 2.0의 Authorization Code Grant 절차를 준용 |
token_type | 접근토큰유형 | String | Y | 20 | 접근토큰유형 : "Bearer" ※ API 호출 시, 접근토큰유형 "Bearer" 입력. ex) "Bearer eyJ...." |
expires_in | 접근토큰 유효기간 | Number | Y | 10 | 유효기간(초) ex) 7776000 |
acess_token_token_expired | 접근토큰 유효기간(일시표시) | String | Y | 50 | 유효기간(년:월:일 시:분:초) ex) "2022-08-30 08:10:10" |
POSTMAN 호출 이미지
POSTMAN(포스트맨) 을 활용하여 접근토큰을 발급합니다. 추후 접근토큰발급 관련 코드도 추가할 예정입니다.
거래량순위[v1_국내주식-047]
- 메뉴 : 한국투자증권 개발자센터 - API문서 - 국내주식시세 - 거래량순위
국내주식 거래량순위 API 입니다. 한국투자 HTS(eFriend Plus) > [0171] 거래량 순위 화면의 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
기본정보
Method | GET |
실전 Domain | https://openapi.koreainvestment.com:9443/ |
모의 Domain | 모의투자 미지원 |
URL | /uapi/domestic-stock/v1/quotations/volume-rank |
Format | JSON |
Content-Type | application/json; charset=UTF-8 |
LAYOUT
Request
</> Header
Element | 한글명 | Type | Required | Length | Description |
content-type | 컨텐츠타입 | String | Y | 40 | application/json; charset=utf-8 |
authorization | 접근토큰 | String | Y | 40 | OAuth 토큰이 필요한 API 경우 발급한 Access token 일반고객(Access token 유효기간 1일, OAuth 2.0의 Client Credentials Grant 절차를 준용) 법인(Access token 유효기간 3개월, Refresh token 유효기간 1년, OAuth 2.0의 Authorization Code Grant 절차를 준용) |
appkey | 앱키 | String | Y | 36 | 한국투자증권 홈페이지에서 발급받은 appsecret (절대 노출되지 않도록 주의해주세요.) |
appsecret | 앱시크릿키 | String | Y | 180 | 한국투자증권 홈페이지에서 발급받은 appsecret (절대 노출되지 않도록 주의해주세요.) |
personalseckey | 고객식별키 | String | N | 180 | [법인 필수] 제휴사 회원 관리를 위한 고객식별키 |
tr_id | 거래ID | String | Y | 13 | FHPST01710000 |
tr_cont | 연속 거래 여부 | String | N | 1 | 공백 : 초기 조회 |
custtype | 고객 타입 | String | Y | 1 | B : 법인 P : 개인 |
seq_no | 일련번호 | String | N | 2 | [법인 필수] 01 |
mac_address | 맥주소 | String | N | 12 | 법인고객 혹은 개인고객의 Mac address 값 |
phone_number | 핸드폰번호 | String | N | 12 | [법인 필수] 제휴사APP을 사용하는 경우 사용자(회원) 핸드폰번호 ex) 01011112222 (하이픈 등 구분값 제거) |
ip_addr | 접속 단말 공인 IP | String | N | 12 | [법인 필수] 사용자(회원)의 IP Address |
hashkey | 해쉬키 | String | N | 256 | [POST API 대상] Client가 요청하는 Request Body를 hashkey api로 생성한 Hash값 * API문서 > hashkey 참조 |
gt_uid | Global UID | String | N | 32 | [법인 필수] 거래고유번호로 사용하므로 거래별로 UNIQUE해야 함 |
</> Query Parameter
Element | 한글명 | Type | Required | Length | Description |
FID_COND_MRKT_DIV_CODE | 조건 시장 분류 코드 | String | Y | 2 | J |
FID_COND_SCR_DIV_CODE | 조건 화면 분류 코드 | String | Y | 5 | 20171 |
FID_INPUT_ISCD | 입력 종목코드 | String | Y | 12 | 0000(전체) 기타(업종코드) |
FID_DIV_CLS_CODE | 분류 구분 코드 | String | Y | 2 | 0(전체) 1(보통주) 2(우선주) |
FID_BLNG_CLS_CODE | 소속 구분 코드 | String | Y | 2 | 0 : 평균거래량 1:거래증가율 2:평균거래회전율 3:거래금액순 4:평균거래금액회전율 |
FID_TRGT_CLS_CODE | 대상 구분 코드 | String | Y | 32 | 1 or 0 9자리 (차례대로 증거금 30% 40% 50% 60% 100% 신용보증금 30% 40% 50% 60%) ex) "111111111" |
FID_TRGT_EXLS_CLS_CODE | 대상 제외 구분 코드 | String | Y | 32 | 1 or 0 6자리 (차례대로 투자위험/경고/주의 관리종목 정리매매 불성실공시 우선주 거래정지) ex) "000000" |
FID_INPUT_PRICE_1 | 입력 가격 1 | String | Y | 12 | 가격 ~ ex) "0" 전체 가격 대상 조회 시 FID_INPUT_PRICE_1, FID_INPUT_PRICE_2 모두 ""(공란) 입력 |
FID_INPUT_PRICE_2 | 입력 가격 2 | String | Y | 12 | ~ 가격 ex) "1000000" 전체 가격 대상 조회 시 FID_INPUT_PRICE_1, FID_INPUT_PRICE_2 모두 ""(공란) 입력 |
FID_VOL_CNT | 거래량 수 | String | Y | 12 | 거래량 ~ ex) "100000" 전체 거래량 대상 조회 시 FID_VOL_CNT ""(공란) 입력 |
FID_INPUT_DATE_1 | 앱시크릿키 | String | Y | 10 | ""(공란) 입력 |
Response
</> Header
Element | 한글명 | Type | Required | Length | Description |
content-type | 컨텐츠타입 | String | Y | 40 | application/json; charset=utf-8 |
tr_id | 거래ID | String | Y | 13 | 요청한 tr_id |
tr_cont | 연속 거래 여부 | String | N | 1 | 공백 : 초기 조회 N : 다음 데이터 조회 (output header의 tr_cont가 M일 경우) |
gt_uid | Global UID | String | N | 32 | [법인 필수] 거래고유번호로 사용하므로 거래별로 UNIQUE해야 함 |
</> Body
Element | 한글명 | Type | Required | Length | Description |
rt_cd | 성공 실패 여부 | String | Y | 1 | |
msg_cd | 응답코드 | String | Y | 8 | |
msg1 | 응답메세지 | String | Y | 80 | |
Output | 응답상세 | Object Array | Y | Array | |
-hts_kor_isnm | HTS 한글 종목명 | String | Y | 40 | |
-mksc_shrn_iscd | 유가증권 단축 종목코드 | String | Y | 9 | |
-data_rank | 데이터 순위 | String | Y | 10 | |
-stck_prpr | 주식 현재가 | String | Y | 10 | |
-prdy_vrss_sign | 전일 대비 부호 | String | Y | 1 | |
-prdy_vrss | 전일 대비 | String | Y | 10 | |
-prdy_ctrt | 전일 대비율 | String | Y | 82 | |
-acml_vol | 누적 거래량 | String | Y | 18 | |
-prdy_vol | 전일 거래량 | String | Y | 18 | |
-lstn_stcn | 상장 주수 | String | Y | 18 | |
-avrg_vol | 평균 거래량 | String | Y | 18 | |
-n_befr_clpr_vrss_prpr_rate | N일전종가대비현재가대비율 | String | Y | 82 | |
-vol_inrt | 거래량 증가율 | String | Y | 84 | |
-vol_tnrt | 거래량 회전율 | String | Y | 82 | |
-nday_vol_tnrt | N일 거래량 회전율 | String | Y | 8 | |
-avrg_tr_pbmn | 평균 거래 대금 | String | Y | 18 | |
-tr_pbmn_tnrt | 거래대금회전율 | String | Y | 82 | |
-nday_tr_pbmn_tnrt | N일 거래대금 회전율 | String | Y | 8 | |
-acml_tr_pbmn | 누적 거래 대금 | String | Y | 18 |
POSTMAN 호출 이미지
거래량 순위 API는 저장한 접근토큰, APP Key, APP Secret Key를 헤더에 넣고, 파라미터들을 사용하여 호출할 수 있습니다.
Headers
Params
소스 코드
본격적으로 코드를 작성해보도록 하겠습니다. 모든 파일의 코드를 작성하지 않고 일부 로직의 코드만 추출하여, 설명하도록 하겠습니다. 전체 코드를 확인하시고 싶으시다면, 맨 아래 Github를 참고해주시면 감사하겠습니다.
application.properties
호출하여 사용할 값들을 저장합니다. 해당 변수 명들을 자유롭게 변경하셔도 상관 없습니다.
예) appkey -> app-key
# server port
server.port=8090
# log level
logging.level.root=debug
# api key
appkey= APP-KEY
appsecret= APP-SecretKey
# token
access_token= Access-Token
KisController.java
- Controller 파일에 매핑할 url을 작성합니다.
- localhost:8090/volume-rank 호출 시, 거래량 정보를 불러오도록 세팅합니다.
package com.seopport.kisvolumerank.controller;
import com.seopport.kisvolumerank.dto.ResponseOutputDTO;
import com.seopport.kisvolumerank.service.KisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import java.util.List;
@RestController
public class KisController {
private KisService kisService;
@Autowired
public KisController(KisService kisService) {
this.kisService = kisService;
}
@GetMapping("/volume-rank")
public Mono<List<ResponseOutputDTO>> getVolumeRank() {
return kisService.getVolumeRank();
}
}
KisService.java
단계적으로 서비스를 설명하도록 하겠습니다. 서비스에서는 본격적으로 거래량순위 API를 호출하여 데이터를 가져옵니다.
HttpHeaders
HttpHeaders에 각 값들을 세팅해 줍니다. 상단에 변수로 선언된 값들은 application.properties 파일에 작성한 값을 가져옵니다.
@Value("${appkey}")
private String appkey;
@Value("${appsecret}")
private String appSecret;
@Value("${access_token}")
private String accessToken;
private HttpHeaders createVolumeRankHttpHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(accessToken);
headers.set("appkey", appkey);
headers.set("appSecret", appSecret);
headers.set("tr_id", "FHPST01710000");
headers.set("custtype", "P");
return headers;
}
getVolumeRank()
거래량순위 API 호출에서는 Get 형식을 사용하므로, Params를 설정해 줍니다.
public Mono<List<ResponseOutputDTO>> getVolumeRank() {
HttpHeaders headers = createVolumeRankHttpHeaders();
return webClient.get()
.uri(uriBuilder -> uriBuilder.path("/uapi/domestic-stock/v1/quotations/volume-rank")
.queryParam("FID_COND_MRKT_DIV_CODE", "J")
.queryParam("FID_COND_SCR_DIV_CODE", "20171")
.queryParam("FID_INPUT_ISCD", "0002")
.queryParam("FID_DIV_CLS_CODE", "0")
.queryParam("FID_BLNG_CLS_CODE", "0")
.queryParam("FID_TRGT_CLS_CODE", "111111111")
.queryParam("FID_TRGT_EXLS_CLS_CODE", "000000")
.queryParam("FID_INPUT_PRICE_1", "0")
.queryParam("FID_INPUT_PRICE_2", "0")
.queryParam("FID_VOL_CNT", "0")
.queryParam("FID_INPUT_DATE_1", "0")
.build())
.headers(httpHeaders -> httpHeaders.addAll(headers))
.retrieve()
.bodyToMono(String.class)
.flatMap(response -> parseFVolumeRank(response));
}
parseFVolumeRank(String response)
response를 받아서, DTO 객체에 각 값들을 넣어줍니다. 메서드 명들을 자유롭게 사용하시면 됩니다.
private Mono<List<ResponseOutputDTO>> parseFVolumeRank(String response) {
try {
List<ResponseOutputDTO> responseDataList = new ArrayList<>();
JsonNode rootNode = objectMapper.readTree(response);
JsonNode outputNode = rootNode.get("output");
if (outputNode != null) {
for (JsonNode node : outputNode) {
ResponseOutputDTO responseData = new ResponseOutputDTO();
responseData.setHtsKorIsnm(node.get("hts_kor_isnm").asText());
responseData.setMkscShrnIscd(node.get("mksc_shrn_iscd").asText());
responseData.setDataRank(node.get("data_rank").asText());
responseData.setStckPrpr(node.get("stck_prpr").asText());
responseData.setPrdyVrssSign(node.get("prdy_vrss_sign").asText());
responseData.setPrdyVrss(node.get("prdy_vrss").asText());
responseData.setPrdyCtrt(node.get("prdy_ctrt").asText());
responseData.setAcmlVol(node.get("acml_vol").asText());
responseData.setPrdyVol(node.get("prdy_vol").asText());
responseData.setLstnStcn(node.get("lstn_stcn").asText());
responseData.setAvrgVol(node.get("avrg_vol").asText());
responseData.setNBefrClprVrssPrprRate(node.get("n_befr_clpr_vrss_prpr_rate").asText());
responseData.setVolInrt(node.get("vol_inrt").asText());
responseData.setVolTnrt(node.get("vol_tnrt").asText());
responseData.setNdayVolTnrt(node.get("nday_vol_tnrt").asText());
responseData.setAvrgTrPbmn(node.get("avrg_tr_pbmn").asText());
responseData.setTrPbmnTnrt(node.get("tr_pbmn_tnrt").asText());
responseData.setNdayTrPbmnTnrt(node.get("nday_tr_pbmn_tnrt").asText());
responseData.setAcmlTrPbmn(node.get("acml_tr_pbmn").asText());
responseDataList.add(responseData);
}
}
return Mono.just(responseDataList);
} catch (Exception e) {
return Mono.error(e);
}
}
ResponseOutputDTO.java
response를 담을 객체를 생성해 줍니다.
package com.seopport.kisvolumerank.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@NoArgsConstructor
@ToString
public class ResponseOutputDTO {
// HTS 한글 종목명
private String htsKorIsnm;
// 유가증권 단축 종목코드
private String mkscShrnIscd;
// 데이터 순위
private String dataRank;
// 주식 현재가
private String stckPrpr;
// 전일 대비 부호
private String prdyVrssSign;
// 전일 대비
private String prdyVrss;
// 전일 대비율
private String prdyCtrt;
// 누적 거래량
private String acmlVol;
// 전일 거래량
private String prdyVol;
// 상장 수주
private String lstnStcn;
// 평균 거래량
private String avrgVol;
// N일전종가대비현재가대비율
private String nBefrClprVrssPrprRate;
// 거래량 증가율
private String volInrt;
// 거래량 회전율
private String volTnrt;
// N일 거래량 회전율
private String ndayVolTnrt;
// 평균 거래 대액
private String avrgTrPbmn;
// 거래대금회전률
private String trPbmnTnrt;
// N일 거래대금 회전율
private String ndayTrPbmnTnrt;
// 누적 거래 대금
private String acmlTrPbmn;
}
결과 출력
실제 스프링 부트 실행 후 localhost:8090/volume-rank 접근 화면입니다.