5  API를 이용한 데이터 수집

API 제공자는 본인이 가진 데이터베이스를 다른 누군가가 쉽게 사용할 수 있는 형태로 가지고 있으며, 해당 데이터베이스에 접근할 수 있는 열쇠인 API 주소를 가진 사람은 이를 언제든지 사용할 수 있습니다.

API는 API 주소만 가지고 있다면 데이터를 언제, 어디서, 누구나 쉽게 이용할 수 있다는 장점이 있습니다. 또한 대부분의 경우 사용자가 필요한 데이터만을 가지고 있으므로 접속 속도가 빠르며, 데이터를 가공하는 번거로움도 줄어듭니다. 해외에는 금융 데이터를 API의 형태로 제공하는 업체가 많으므로, 이를 잘만 활용한다면 매우 손쉽게 퀀트 투자에 필요한 데이터를 수집할 수 있습니다.

6 야후 파이낸스 이용하기

야후 파이낸스에서는 주가 데이터를 무료로 제공하며, quantmod 패키지의 getSymbols() 함수는 해당 API에 접속해 데이터를 다운로드합니다.

6.1 주가 다운로드

getSymbols() 함수의 기본적인 사용법은 매우 간단합니다. 괄호 안에 다운로드하려는 종목의 티커를 입력하면 됩니다.

library(quantmod)
getSymbols('AAPL')
[1] "AAPL"
head(AAPL)
           AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume AAPL.Adjusted
2007-01-03  3.081786  3.092143 2.925000   2.992857  1238319600      2.551165
2007-01-04  3.001786  3.069643 2.993571   3.059286   847260400      2.607790
2007-01-05  3.063214  3.078571 3.014286   3.037500   834741600      2.589219
2007-01-08  3.070000  3.090357 3.045714   3.052500   797106800      2.602006
2007-01-09  3.087500  3.320714 3.041071   3.306071  3349298400      2.818154
2007-01-10  3.383929  3.492857 3.337500   3.464286  2952880000      2.953019

먼저 getSymbols() 함수 내에 애플의 티커인 AAPL을 입력합니다. 티커와 동일한 변수인 AAPL이 생성되며, 주가 데이터가 다운로드된 후 xts 형태로 입력됩니다.

다운로드 결과로 총 6개의 열이 생성됩니다. Open은 시가, High는 고가, Low는 저가, Close는 종가를 의미합니다. 또한 Volume은 거래량을 의미하며, Adjusted는 배당이 반영된 수정주가를 의미합니다. 이 중 가장 많이 사용되는 데이터는 Adjusted, 즉 배당이 반영된 수정주가입니다.

chart_Series(Ad(AAPL))

Ad() 함수를 통해 다운로드한 데이터에서 수정주가만을 선택한 후 chart_Series() 함수를 이용해 시계열 그래프를 그릴 수도 있습니다. 시계열 기간을 입력하지 않으면 2007년 1월부터 현재까지의 데이터가 다운로드되며, 입력 변수를 추가해서 원하는 기간의 데이터를 다운로드할 수도 있습니다.

data = getSymbols('AAPL',
                  from = '2000-01-01', to = '2018-12-31',
                  auto.assign = FALSE)
head(data)
           AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume AAPL.Adjusted
2000-01-03  0.936384  1.004464 0.907924   0.999442   535796800      0.851942
2000-01-04  0.966518  0.987723 0.903460   0.915179   512377600      0.780115
2000-01-05  0.926339  0.987165 0.919643   0.928571   778321600      0.791530
2000-01-06  0.947545  0.955357 0.848214   0.848214   767972800      0.723033
2000-01-07  0.861607  0.901786 0.852679   0.888393   460734400      0.757282
2000-01-10  0.910714  0.912946 0.845982   0.872768   505064000      0.743963

from에는 시작시점을 입력하고 to에는 종료시점을 입력하면 해당 기간의 데이터가 다운로드됩니다.

getSymbols() 함수를 통해 다운로드한 데이터는 자동으로 티커와 동일한 변수명에 저장됩니다. 만일 티커명이 아닌 원하는 변수명에 데이터를 저장하려면 auto.assign 인자를 FALSE로 설정해주면 다운로드한 데이터가 원하는 변수에 저장됩니다.

ticker = c('META', 'NVDA') 
getSymbols(ticker)
[1] "META" "NVDA"
head(META)
           META.Open META.High META.Low META.Close META.Volume META.Adjusted
2012-05-18     42.05     45.00    38.00      38.23   573576400         38.23
2012-05-21     36.53     36.66    33.00      34.03   168192700         34.03
2012-05-22     32.61     33.59    30.94      31.00   101786600         31.00
2012-05-23     31.37     32.50    31.36      32.00    73600000         32.00
2012-05-24     32.95     33.21    31.77      33.03    50237200         33.03
2012-05-25     32.90     32.95    31.11      31.91    37149800         31.91
head(NVDA)
           NVDA.Open NVDA.High NVDA.Low NVDA.Close NVDA.Volume NVDA.Adjusted
2007-01-03  6.178333  6.253333 5.798333   6.013333   115482000      5.518750
2007-01-04  5.991667  6.013333 5.838333   5.985000    79729800      5.492745
2007-01-05  5.843333  5.866667 5.570000   5.610000   124334400      5.148591
2007-01-08  5.630000  5.760000 5.533333   5.651667    65727000      5.186830
2007-01-09  5.660000  5.698333 5.535000   5.541667    76416600      5.085878
2007-01-10  5.483333  5.866667 5.400000   5.815000   110874600      5.336729

한 번에 여러 종목의 주가를 다운로드할 수도 있습니다. 위 예제와 같이 메타와 엔비디아의 티커인 META와 NVDA를 ticker 변수에 입력하고 getSymbols() 함수에 티커를 입력한 변수를 넣으면 두 종목의 주가가 순차적으로 다운로드됩니다.

6.2 국내 종목 주가 다운로드

getSymbols() 함수를 이용하면 미국뿐 아니라 국내 종목의 주가도 다운로드할 수 있습니다. 국내 종목의 티커는 총 6자리로 구성되어 있으며, 해당 함수에 입력되는 티커는 코스피 상장 종목의 경우 티커.KS, 코스닥 상장 종목의 경우 티커.KQ의 형태로 입력해야 합니다.

다음은 코스피 상장 종목인 삼성전자 데이터의 다운로드 예시입니다.

getSymbols('005930.KS')
[1] "005930.KS"
tail(Ad(`005930.KS`))
           005930.KS.Adjusted
2023-01-12              60500
2023-01-13              60800
2023-01-16              61100
2023-01-17              61000
2023-01-18              60400
2023-01-19              61100

Cl() 함수는 Close, 즉 종가만을 선택하며, 사용 방법은 Ad() 함수와 동일합니다. 비록 배당을 고려할 수는 없지만, 전반적으로 오류가 없는 데이터를 사용할 수 있습니다.

다음은 코스닥 상장종목인 셀트리온제약의 예시이며, 티커인 068670에 .KQ를 붙여 함수에 입력합니다. 역시나 데이터가 다운로드되어 티커명의 변수에 저장됩니다.

getSymbols("068760.KQ")
[1] "068760.KQ"
tail(Cl(`068760.KQ`))
           068760.KQ.Close
2023-01-12              NA
2023-01-13              NA
2023-01-16              NA
2023-01-17              NA
2023-01-18              NA
2023-01-19           66200

6.3 SQL에 데이터 저장하기

API나 크롤링을 통해 수집한 데이털르 SQL에 저장해보도록 하겠다. 먼저 SQL에서 다음과 같이 데이터베이스와 주가가 들어갈 테이블을 만든다.

create database stock_db;
use stock_db;

create table price
(
    날짜 date,
    시가 double,
    고가 double,
    저가 double,
    종가 double,
    거래량 double,
    종목코드 varchar(6),
    primary key(날짜, 종목코드)
);

이제 삼성전자 주가를 SQL에 저장해보도록 하자.

library(magrittr)

df = `005930.KS` %>% fortify.zoo() %>%
  select(1:6) %>%
  mutate(종목코드 = '005930') %>%
  set_colnames(c('날짜', '시가', '고가', '저가', '종가', '거래량', '종목코드'))

데이터를 받은 후 클렌징 처리를 해준다.

library(DBI)
library(RMySQL)

con = dbConnect(
  drv = MySQL(),
  user = 'root',
  password = '1234', 
  host = '127.0.0.1',
  dbname = 'stock_db'
)

dbSendQuery(con,
  "SET GLOBAL local_infile = TRUE;"
)

dbWriteTable(con, "price", df,
             overwrite = TRUE, row.names = FALSE)
  1. R과 SQL을 연결한다.

  2. local_infile를 TRUE로 설정한다.

  3. dbWriteTable() 함수를 이용해 데이터를 price 테이블에 저장한다.

SQL을 확인해보면 주가 데이터가 저장되어 있습니다.

7 유료 데이터 벤더 이용하기

미국 시장의 데이터만 필요할 경우 유료 데이터 벤더를 이용하는 것도 좋은 방법입니다. 미국에는 금융 데이터를 API로 제공하는 수많은 업체가 있으며, tiingo의 경우는 월 $10만 지불하면 미국과 중국의 4만여개 종목에 대한 데이터를 API 형태로 받을 수 있습니다. 이는 상장폐지된 종목을 커버할 뿐만 아니라, API를 이용하므로 크롤링과는 비교할 수 없는 속도로 데이터를 받을 수 있다는 장점이 있습니다. 이 외에도 Alpha Vantage, Quandl, Polygon 등 수많은 데이터 벤더가 존재합니다.

tiingo는 무료 계정도 하루 1,000회까지 API 요청을 할 수 있으며, R/파이썬에서 사용할 수 있는 패키지도 있어 쉽게 사용이 가능합니다.

7.1 가입 및 API token 받기

먼저 https://api.tiingo.com/ 사이트에 접속하여 우측 상단의 [Sign-up]을 클릭해 회원가입을 합니다. 그 후 로그인을 한 후 우측 상단에서 본인의 ID를 클릭한 후 [Account]를 선택, 좌측 메뉴의 [API] 부분에서 [Token]을 클릭하면 본인의 API token을 확인할 수 있습니다.

다음으로 발급받은 API Key를 .Renviron 파일에 추가하도록 합니다. 해당 파일에는 여러 패스워드를 추가해 안전하게 관리할 수 있습니다.

file.edit("~/.Renviron")

.Renviron 파일이 열리면 다음과 같이 입력을 해줍니다.

RIINGO_TOKEN = '발급받은 API'

파일을 저장한 후 해당 파일을 적용하기 위해 R의 Session을 재시작(ctrl+shift+F10)합니다. 그 후 아래 명령어를 실행하여 API Key를 불러오도록 합니다. (재시작하지 않으면 Key를 불러올 수 없습니다.)

다시 시작한 후 API Key를 불러옵니다.

RIINGO_TOKEN = Sys.getenv("RIINGO_TOKEN")

7.2 데이터 다운로드

R에서 tiingo를 사용할 수 있게 해주는 riingo 패키지를 이용해 데이터를 받아보도록 하겠습니다. 먼저 tiingo에서 제공하는 종목은 어떠한 것이 있는지 티커 정보들을 확인해봅니다.

# install.packages("riingo")
library(riingo)

tickers = supported_tickers()
tickers
# A tibble: 106,998 × 6
   ticker exchange assetType priceCurr…¹ startDate           endDate            
   <chr>  <chr>    <chr>     <chr>       <dttm>              <dttm>             
 1 000001 SHE      Stock     CNY         2007-01-04 00:00:00 2023-01-18 00:00:00
 2 000002 SHE      Stock     CNY         2007-01-04 00:00:00 2023-01-18 00:00:00
 3 000003 SHE      Stock     CNY         NA                  NA                 
 4 000004 SHE      Stock     CNY         2007-08-31 00:00:00 2023-01-18 00:00:00
 5 000005 SHE      Stock     CNY         2007-08-31 00:00:00 2023-01-18 00:00:00
 6 000006 SHE      Stock     CNY         2007-01-04 00:00:00 2023-01-18 00:00:00
 7 000007 SHE      Stock     CNY         2007-08-31 00:00:00 2023-01-18 00:00:00
 8 000008 SHE      Stock     CNY         2007-01-04 00:00:00 2023-01-18 00:00:00
 9 000009 SHE      Stock     CNY         2007-01-05 00:00:00 2023-01-18 00:00:00
10 000010 SHE      Stock     CNY         2007-08-31 00:00:00 2023-01-18 00:00:00
# … with 106,988 more rows, and abbreviated variable name ¹​priceCurrency

ticker(티커), exchange(거래소), assetType(주식 종류), priceCurrency(거래 통화), startDate(시작일), endDate(마감일) 정보가 표시됩니다. 거래소와 통화 별 종목이 몇개가 있는지 확인해보도록 하겠습니다.

library(dplyr)

tickers %>% group_by(exchange, priceCurrency) %>% summarize(n = n()) %>%
  arrange(priceCurrency, desc(n))
# A tibble: 27 × 3
# Groups:   exchange [24]
   exchange priceCurrency     n
   <chr>    <chr>         <int>
 1 ASX      AUD             169
 2 SHE      CNY            2570
 3 SHG      CNY            1936
 4 SHEB     HKD              42
 5 SHE      HKD              12
 6 NMFQS    USD           46245
 7 PINK     USD           14416
 8 NASDAQ   USD           13113
 9 NYSE     USD            8170
10 OTCGREY  USD            4011
# … with 17 more rows

이 중 마이너 거래소나 장외 거래소의 경우 정보를 받아도 우리나라의 증권사를 통해서는 실제로 거래를 할 수 없을수도 있습니다. 따라서 실제 거래가 가능한 거래소 데이터만 필터링한 후 해당 종목들을 받는 것이 효율적입니다.

각 종목의 상세 정보를 확인해보도록 하겠습니다.

riingo_meta("AAPL")
# A tibble: 1 × 6
  ticker name      description   startDate           endDate             excha…¹
  <chr>  <chr>     <chr>         <dttm>              <dttm>              <chr>  
1 AAPL   Apple Inc Apple Inc. (… 1980-12-12 00:00:00 2023-01-18 00:00:00 NASDAQ 
# … with abbreviated variable name ¹​exchangeCode

riingo_meta() 함수 내에 티커를 입력하면 티커, 종목명, 사업내역 등 대략적인 정보를 받아올 수 있습니다.

이제 주가를 받아보도록 합시다.

riingo_prices("AAPL")
# A tibble: 251 × 14
   ticker date                close  high   low  open    volume adjClose adjHigh
   <chr>  <dttm>              <dbl> <dbl> <dbl> <dbl>     <int>    <dbl>   <dbl>
 1 AAPL   2022-01-19 00:00:00  166.  171.  166.  170   92914792     165.    170.
 2 AAPL   2022-01-20 00:00:00  165.  170.  164.  167.  91420515     163.    168.
 3 AAPL   2022-01-21 00:00:00  162.  166.  162.  164. 122848858     161.    165.
 4 AAPL   2022-01-24 00:00:00  162.  162.  155.  160. 162706686     160.    161.
 5 AAPL   2022-01-25 00:00:00  160.  163.  157.  159. 115798367     159.    162.
 6 AAPL   2022-01-26 00:00:00  160.  164.  158.  164. 108275308     159.    163.
 7 AAPL   2022-01-27 00:00:00  159.  164.  158.  162. 121954638     158.    163.
 8 AAPL   2022-01-28 00:00:00  170.  170.  163.  166. 179935660     169.    169.
 9 AAPL   2022-01-31 00:00:00  175.  175   170.  170. 115541590     174.    174.
10 AAPL   2022-02-01 00:00:00  175.  175.  172.  174.  86213911     173.    174.
# … with 241 more rows, and 5 more variables: adjLow <dbl>, adjOpen <dbl>,
#   adjVolume <int>, divCash <dbl>, splitFactor <dbl>

riingo_prices() 함수 내에 티커를 입력하면 close(종가), high(고가), low(저가), open(시가), volumne(거래량) 및 수정주가와 divCash(현금 배당), splitFactor(주식분할 조정계수)까지 데이터를 받을 수 있습니다.

이번에는 일별 가치지표를 받아보도록 합니다. (무료 계정의 경우 다우존스 30 지수에 포함되는 종목 정보만 제공합니다.)

riingo_fundamentals_metrics('AAPL')
# A tibble: 254 × 7
   ticker date                    marketCap enterprise…¹ peRatio pbRatio trail…²
   <chr>  <dttm>                      <dbl>        <dbl>   <dbl>   <dbl>   <dbl>
 1 AAPL   2022-01-19 00:00:00 2727235373310      2.81e12    27.1    37.9    1.12
 2 AAPL   2022-01-20 00:00:00 2699016370470      2.78e12    26.8    37.5    1.11
 3 AAPL   2022-01-21 00:00:00 2664562936770      2.75e12    26.5    37.0    1.10
 4 AAPL   2022-01-24 00:00:00 2651601883140      2.74e12    26.4    36.9    1.09
 5 AAPL   2022-01-25 00:00:00 2621414112660      2.71e12    26.1    36.4    1.08
 6 AAPL   2022-01-26 00:00:00 2619937536930      2.71e12    26.1    36.4    1.08
 7 AAPL   2022-01-27 00:00:00 2612226530340      2.70e12    26.0    36.3    1.08
 8 AAPL   2022-01-28 00:00:00 2779690385530      2.87e12    27.6    38.6    1.15
 9 AAPL   2022-01-31 00:00:00 2852311897980      2.94e12    28.4    39.7    1.18
10 AAPL   2022-02-01 00:00:00 2849537593010      2.94e12    28.3    39.6    1.17
# … with 244 more rows, and abbreviated variable names ¹​enterpriseVal,
#   ²​trailingPEG1Y

riingo_fundamentals_metrics() 함수 내에 티커를 입력하면 일간 시가총액, 기업가치, PER, PBR, PEG 정보가 받아집니다.

마지막으로 재무제표를 받아보도록 합니다.

df = riingo_fundamentals_statements('AAPL')
df
# A tibble: 4 × 8
  ticker date                 year quarter balanceSheet  cashF…¹ incom…² overv…³
  <chr>  <dttm>              <int>   <int> <list>        <list>  <list>  <list> 
1 AAPL   2022-09-24 00:00:00  2022       4 <df [26 × 2]> <df>    <df>    <df>   
2 AAPL   2022-09-24 00:00:00  2022       0 <df [26 × 2]> <df>    <df>    <df>   
3 AAPL   2022-06-25 00:00:00  2022       3 <df [26 × 2]> <df>    <df>    <df>   
4 AAPL   2022-03-26 00:00:00  2022       2 <df [26 × 2]> <df>    <df>    <df>   
# … with abbreviated variable names ¹​cashFlow, ²​incomeStatement, ³​overview

티블 내에 다시 데이터프레임이 들어간 형태입니다. 이 중 필요한 데이터만 선택해 묶음을 풀어주면 됩니다.

library(tidyr)

df %>%
  select(ticker, date, year, quarter, balanceSheet) %>%
  unnest(cols = c('balanceSheet'))
# A tibble: 104 × 6
   ticker date                 year quarter dataCode                     value
   <chr>  <dttm>              <int>   <int> <chr>                        <dbl>
 1 AAPL   2022-09-24 00:00:00  2022       4 liabilitiesCurrent    153982000000
 2 AAPL   2022-09-24 00:00:00  2022       4 equity                 50672000000
 3 AAPL   2022-09-24 00:00:00  2022       4 taxLiabilities                   0
 4 AAPL   2022-09-24 00:00:00  2022       4 liabilitiesNonCurrent 148101000000
 5 AAPL   2022-09-24 00:00:00  2022       4 retainedEarnings       -3068000000
 6 AAPL   2022-09-24 00:00:00  2022       4 debtCurrent            21110000000
 7 AAPL   2022-09-24 00:00:00  2022       4 taxAssets                        0
 8 AAPL   2022-09-24 00:00:00  2022       4 assetsNonCurrent      217350000000
 9 AAPL   2022-09-24 00:00:00  2022       4 acctPay                64115000000
10 AAPL   2022-09-24 00:00:00  2022       4 debt                  120069000000
# … with 94 more rows