주식 데이터를 가져올 때 많이 사용하는 한국투자증권 OpenAPI 사용법에 대해 정리해보고자 한다
(평소 주식투자에서도 잘 활용하고 있는 한국투자증권... 최고!)
한국투자증권 OPENAPI 개발자센터
해당 주소로 이동하면 한국투자증권 OPENAPI 개발자센터로 이동한다
한국투자증권 OPENAPI 개발자센터 샘플코드
한투에서는 친절하게 샘플코드를 준다
참고해서 개발을 해도 좋을 것 같다
해당 과정을 통해 APP key와 APP Secret를 발급받을 수 있습니다
국내주식기간별시세(일/주/월/년)[v1_국내주식-016]을 예시로 정보를 받아오고 데이터베이스에 저장하는 코드를 작성해보겠습니다
hantu-openapi:
domain: https://openapivts.koreainvestment.com:29443 #모의 도메인
#https://openapi.koreainvestment.com:9443 #실전 도메인
appkey: [발급받은 app key] #${api_appkey}
appsecret: [발급받은 app secret] #${api_appsecret}
application.yml
발급받은 키와 비밀번호를 yml에 등록시켜줍니다
// DB에 주식 정보 저장하는 코드
@PostMapping("/fetch-stock-data")
public String fetchStockData(
@RequestParam String marketCode,
@RequestParam String stockCode, // SK하이닉스 000660, 삼성전자: 005930
@RequestParam String startDate,
@RequestParam String endDate,
@RequestParam String periodCode) {
stockService.fetchAndSaveStockData(marketCode, stockCode, startDate, endDate, periodCode);
return stockCode+" Stock data fetched and saved successfully";
}
Controller에 다음과 같이 선언해주었습니다
필요한 파라미터는 Request의 Query Parameter에서 확인 할 수 있습니다
public class Stock {
// 날짜, 상한가, 하한가, 누적거래량
@Id
@Column(name = "stock_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String stockCode;
private LocalDate date;
private String maxPrice;
private String minPrice;
private String accumTrans;
private String openPrice;
private String closePrice;
}
결과로 받는 Response 중 저장하고 싶은 데이터를 Entity로 만들어줍니다
@Service
@Slf4j
public class StockService {
// https://apiportal.koreainvestment.com/apiservice/apiservice-domestic-stock-quotations2#L_a08c3421-e50f-4f24-b1fe-64c12f723c77 // 한투 openapi 문서
// https://www.ktb.co.kr/trading/popup/itemPop.jspx // 종목코드 검색
@Autowired
private StockRepository stockRepository;
@Value("${hantu-openapi.domain}")
private String apiDomain;
@Value("${hantu-openapi.appkey}")
private String appKey;
@Value("${hantu-openapi.appsecret}")
private String appSecret;
// api 받아와서 db에 저장
public void fetchAndSaveStockData(String marketCode, String stockCode, String startDate, String endDate, String periodCode) {
TokenResponse accessToken = getAccessToken();
String responseBody = getStockPriceData(marketCode, stockCode, startDate, endDate, periodCode, accessToken.getAccess_token());
//log.info("responseBody: " + responseBody);
saveStockData(responseBody, stockCode);
}
// api 데이터 받아옴
private String getStockPriceData(String marketCode, String stockCode, String startDate, String endDate, String periodCode, String accessToken) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("authorization", "Bearer " + accessToken);
headers.set("appkey", appKey);
headers.set("appsecret", appSecret);
headers.set("tr_id", "FHKST03010100");
// 국내주식기간별시세(일/주/월/년)[v1_국내주식-016]
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(apiDomain + "/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice")
.queryParam("FID_COND_MRKT_DIV_CODE", marketCode)
.queryParam("FID_INPUT_ISCD", stockCode)
.queryParam("FID_INPUT_DATE_1", startDate)
.queryParam("FID_INPUT_DATE_2", endDate)
.queryParam("FID_PERIOD_DIV_CODE", periodCode)
.queryParam("FID_ORG_ADJ_PRC", "0");
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(
builder.toUriString(),
HttpMethod.GET,
entity,
String.class);
return response.getBody();
}
// db에 저장함
private void saveStockData(String responseBody, String stockCode) {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
JsonNode output2 = rootNode.get("output2");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
for (JsonNode node : output2) {
Stock stock = new Stock();
stock.setStockCode(stockCode);
stock.setDate(LocalDate.parse(node.get("stck_bsop_date").asText(), formatter));
stock.setMaxPrice(node.get("stck_hgpr").asText());
stock.setMinPrice(node.get("stck_lwpr").asText());
stock.setAccumTrans(node.get("acml_vol").asText());
stock.setOpenPrice(node.get("stck_oprc").asText());
stock.setClosePrice(node.get("stck_clpr").asText());
stockRepository.save(stock);
}
} catch (Exception e) {
e.printStackTrace();
// 에러 처리 로직 추가
}
}
}
데이터를 받아와서 데이터베이스에 추가하는 코드입니다
위 과정에서 접근토큰을 넣어줘야합니다
토큰의 유효기간은 24시간이고 갱신발급주기는 6시간입니다
// 접근토큰발급(P)[인증-001]
public TokenResponse getAccessToken() {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String requestBody = String.format(
"{\"grant_type\":\"client_credentials\",\"appkey\":\"%s\",\"appsecret\":\"%s\"}",
appKey, appSecret
);
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<TokenResponse> response = restTemplate.exchange(
apiDomain + "/oauth2/tokenP",
HttpMethod.POST,
entity,
TokenResponse.class
);
if (response.getStatusCode() == HttpStatus.OK) {
return response.getBody();
} else {
throw new RuntimeException("Failed to obtain access token");
}
}
다음과 같이 토큰을 발급받고 저장해놓은 후, api를 불러올때마다 확인/갱신해줍니다
정상적으로 DB에 저장되는 것을 확인할 수 있습니다