R

[R + Python 연습] 증권사 Open API를 이용하여 종목의 일일 시세를 다운로드하라.

오렌지사과키위 2023. 12. 5. 19:40

문제

증권사 HTS를 이용하여 개별 종목의 일일 시세 데이터를 다운로드하는 것은 손이 많이 가는 작업이다. 증권사 Open API를 이용하여 이 작업을 자동화하라. 이렇게 다운로드한 데이터를 다른 곳에서 사용할 수 있도록 구글 드라이브를 이용하여 저장하라.

 

참고
실시간으로 주가를 파악해서 시스템 트레이딩을 하는 경우가 아니라면, 증권사 API를 이용하여 주가를 가져오는 것은 권하기 어렵습니다. 많은 종목 또는 장기간 주식 시세 데이터를 수집하고 분석하는 용도로는 파이썬 라이브러리를 이용하는 것이 간편하고 빠릅니다. 국내 주식 시세 데이터를 얻을 수 있는 파이썬 라이브러리의 종류와 특성에 대해서는 국내 주가 데이터 특성 비교 (FinanceDataReader, PyKrx, macap)를 참고하기 바랍니다.

 

코드 (파이썬 코드)

import datetime
import json
import pandas as pd
import requests

from google.colab import drive


APP_KEY = "<<<YOUR APP KEY HERE>>>"
APP_SECRET = "<<<YOUR APP SECRET KEY HERE>>>"
URL_BASE = "https://openapivts.koreainvestment.com:29443"  ## Pesudo


# Get authentication token.
# Refer https://apiportal.koreainvestment.com/apiservice/oauth2#L_fa778c98-f68d-451e-8fff-b1c6bfe5cd30
def auth():
  headers = {
      "content-type": "application/json"
  }

  body = {
      "grant_type":"client_credentials",
      "appkey":  APP_KEY,
      "appsecret": APP_SECRET
  }

  PATH = "oauth2/tokenP"
  URL = f"{URL_BASE}/{PATH}"

  res = requests.post(URL, headers = headers, data = json.dumps(body))

  global ACCESS_TOKEN
  ACCESS_TOKEN = res.json()["access_token"]


# Get price list for a ticker between from_date to to_date.
# Refer https://apiportal.koreainvestment.com/apiservice/apiservice-domestic-stock-quotations#L_a08c3421-e50f-4f24-b1fe-64c12f723c77
def get_prices_between(stock_no, from_date, to_date):
  PATH = "uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice"
  URL = f"{URL_BASE}/{PATH}"

  headers = {
      "Content-Type": "application/json",
      "authorization": f"Bearer {ACCESS_TOKEN}",
      "appKey": APP_KEY,
      "appSecret": APP_SECRET,
      "tr_id": "FHKST03010100"
  }

  params = {
      "fid_cond_mrkt_div_code": "J",
      "fid_input_iscd": stock_no,
      "fid_input_date_1": from_date,
      "fid_input_date_2": to_date,
      "fid_period_div_code": "D",
      "fid_org_adj_prc": "0"
  }

  res = requests.get(URL, headers = headers, params = params)

  if res.status_code == 200 and res.json()["rt_cd"] == "0" :
    return(res.json())
  elif res.status_code == 200 and res.json()["msg_cd"] == "EGW00123" :
    auth()
    get_prices_between(stock_no, from_date, to_date)
  else:
    print("Error Code : " + str(res.status_code) + " | " + res.text)
    return None


def get_prices(ticker):
  now = datetime.datetime.now()

  from_dates = []
  for year in range(2000, now.year + 1):
    for month in range(1, now.month + 1 if year == now.year else 13, 4):
      from_dates.extend([(year, month)])

  prices = []
  empty_count = 0
  for year, month in reversed(from_dates):
    from_date = "%d%02d01" % (year, month)
    to_date = "%d%02d31" % (year, month + 3)
    res = get_prices_between(ticker, from_date, to_date)
    some_prices = list(filter(None, res["output2"]))
    some_prices_count = len(some_prices)
    print(f"{some_prices_count}", end = " ")
    if some_prices_count == 0:
      empty_count = empty_count + 1
    if empty_count > 1:
      break
    prices.extend(some_prices)

  return prices
  
  tickers = (
    "069500",  # KODEX 200
    "114800",  # Samsung Kodex Inverse ETF
    "122630",  # Kodex Leverage
    "229200",  # KODEX KOSDAQ 150
    "233740",  # KODEX KOSDAQ150 Leverage
    "251340",  # KODEX KOSDAQ150 Inverse
    "252670",  # Samsung Kodex F Kospi 200 Inverse2x ETF
    "278530",  # Samsung Kodex 200 Total Return ETF
)

col_name_mapping = {
    "stck_bsop_date": "date",
    "stck_oprc": "open",
    "stck_hgpr": "high",
    "stck_lwpr": "low",
    "stck_clpr": "close",
    "acml_vol": "volume",
}

# Mount Google Drive to /content/drive/MyDrive
drive.mount("/content/drive")

# Get access token if required.
try: ACCESS_TOKEN
except NameError: auth()

for ticker in tickers:
  print(f"For ticker {ticker}:", end = " ")

  print("Getting prices:", end = " ")
  prices = get_prices(ticker)

  print("Processing:", end = " ")
  items = filter(lambda x: len(x) > 0, prices)
  df = pd.DataFrame(items)
  df = df[col_name_mapping.keys()]
  df = df.astype(int)
  df.rename(columns = col_name_mapping, inplace = True)
  df.sort_values(by = ["date"], inplace = True)
  print("%d items" % len(df), end = " ")

  print("Writing to csv", end = " ")
  with open(f"drive/MyDrive/stock_data/{ticker}.csv", "w") as f:
    f.write(df.to_csv(index = False))
  print("- done")

 

결과 및 해석

여러 증권사에서 Open API를 제공합니다만, 실행 환경에 제약이 있는 경우가 많습니다. 한국투자증권과 같은 경우 실행 환경에 제약이 거의 없는 REST API를 제공합니다. 이를 이용하면 웹 접속이 가능한 시스템이면 어디서나 사용할 수 있습니다.

 

한국투자증권 Open API 서비스 사용 신청은 KIS Developers에서 할 수 있습니다. 서비스 신청과 사용법에 대해서는 위키독스로 공유된 파이썬을 이용한 한국/미국 주식 자동매매 시스템을 참고하기 바랍니다.

 

일별 시세 데이터를 받아오는 파이썬 코드는 Google Colab에서 작성한 것입니다. 작성한 코드는 구글 드라이브에 저장되며 다른 사용자들과 공유할 수 있습니다. 위의 코드는 여기(Google Colab)에서 볼 수 있습니다.

 

구글 드라이브에서 stock_data라는 이름으로 폴더를 만들고, 위의 코드를 실행하면 아래와 같이 해당 폴더안에 종목별로 티커명.csv 파일이 하나씩 생성됩니다. 실행 시 구글 드라이브 접근 권한 허용을 위한 페이지가 뜰 수 있습니다.

 

 

R에서는 다음 코드와 같이 구글 드라이브에 저장된 파일을 사용할 수 있습니다. 가져온 데이터를 이용하여 KODEX 200과 KODEX 코스닥 150 ETF의 누적 수익률을 그래프로 나타낸 것입니다.

 

library(dplyr)  # for mutate()
library(lubridate)  # for ymd()
library(googledrive)  # for drive_get(), drive_read_string()
library(PerformanceAnalytics)  # for Return.calculate(), chart.CumReturns()

read_google_drive_csv_as_xts <- function(ticker) {
  file_name = paste0("~/stock_data/", ticker, ".csv")
  drive_read_string(drive_get(file_name)) %>%
    read.csv(text = .) %>%
    mutate(date = ymd(date)) %>%
    as.xts()
}

KO.069500.xts <- read_google_drive_csv_as_xts("069500")
KO.229200.xts <- read_google_drive_csv_as_xts("229200")

prices <- 
  cbind(KO.069500.xts$close,
        KO.229200.xts$close) %>%
  setNames(c("KODEX KOSPI 200", "KODEX KOSDAQ 150")) %>%
  na.omit()

rets <- Return.calculate(prices) %>% na.omit()
chart.CumReturns(rets, legend.loc = "topleft")

  

 

도움이 되었다면, 이 글을 친구와 공유하는 건 어떻까요?

facebook twitter kakaoTalk naver band