YOGYUI

공공데이터포털::한국예탁결제원 주식정보서비스 (REST API) 본문

Data Analysis/Data Engineering

공공데이터포털::한국예탁결제원 주식정보서비스 (REST API)

요겨 2021. 12. 19. 21:38
반응형

공공데이터포털(data.go.kr)에서 한국예탁결제원이 제공하는 국내 주식시장과 관련된 다양한 정보들을 가져와보자 (KSD 증권정보포털인 SEIBro의 API, api.seibro.or.kr)

1. 공공데이터포털 API 활용신청

데이터 타이틀은 "한국예탁결제원_주식정보서비스", URL은 아래 링크 참고

https://www.data.go.kr/data/15001145/openapi.do

1.1 활용신청

활용신청 버튼을 클릭

1.2. 활용목적 기입

1.3. 상세기능정보 선택

특별한 사유가 아니라면 전부 선택해주자

(서버 과부하를 우려해서 그런가, 일일 트래픽이 기능별로 100회로 한정되어 있다 ㅠ)

1.4. 활용신청

저작자표시 동의 체크박스 클릭 수 활용신청

 

2. 인증키 확인

[마이페이지 - 오픈API - 개발계정]

신청한 서비스(한국예탁결제원_주식정보서비스) 클릭 

개발계정 상세보기에서 인증키를 확인할 수 있다

API 호출시에 사용해야 하니 메모장에 복붙해두자

 

중요: 상세기능정보에서 API 호출 테스트를 할 수 있다

만약 아래와 같이 "SERVICE KEY IS NOT REGISTERED ERROR" 결과가 반환된다면 아직 API 활용 신청에 대한 서버 동기화가 이루어지지 않은 것이니, 신청 후 여유롭게 하루정도는 기다려주자

 

3. 활용 예시 (배당순위조회)

"찬바람이 불면 배당주를 사라"는 투자 격언은 많은 주식 투자자들이 한번은 들어봤을 것 같은데, 정작 배당을 많이 주는 종목이 뭔지는 경제 기사에서 집계해주는 게 아니면 직접 찾아보기가 약간은 번거로웠을텐데 (포털사이트 통계 혹은 증권사 서비스 이용 등) 과연 공공데이터포털의 Open API로는 제대로 된 정보를 얻을 수 있을지 확인해보자

 

페이지에서 제공하는 API 가이드 문서

4. [한국예탁결제원 주식정보서비스] 기술문서 20211020.docx
0.42MB

를 따라 테스트 코드를 작성해보자

(다른 포스팅 글과 마찬가지로 여기서도 Python을 활용)

한국예탁결제원_주식정보서비스 API의 공통 서비스 URL은
<http://api.seibro.or.kr/openapi/service/StockSvc> 이고 서비스별로 서로 다른 상세주로를 가진다 
예를 들어 배당순위조회의 상세 URL은 getDividendRankN1 이므로 호출 API URL은 
http://api.seibro.or.kr/openapi/service/StockSvc/getDividendRankN1
이 된다

요청변수 중 필수 파라미터, 옵션 파라미터 등을 참고해서 테스트 코드를 작성해보자

작년 (2020년) 기준으로 모든 시장에 대한 상위 배당률 10개 종목을 쿼리해보자

import requests

url_base = "http://api.seibro.or.kr/openapi/service/StockSvc"
url_spec = "getDividendRankN1"
url = url_base + "/" + url_spec
api_key_utf8 = "Your API Key form data.go.kr"
api_key_decode = requests.utils.unquote(api_key_utf8, encoding='utf-8')

params = {
    "serviceKey": api_key_decode,
    "rankTpcd": 1,
    "year": 2020,
    "numOfRows": 10,
    "pageNo": 1,
}

response = requests.get(url, params=params)
In [1]: print(response.text)
Out[1]:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><response><header><resultCode>00</resultCode><resultMsg>NORMAL SERVICE.</resultMsg></header><body><items><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>9900</divAmtPerStk><divRateCpri>29.5</divRateCpri><divRatePval>1980</divRatePval><issucoCustno>2345</issucoCustno><korSecnNm>동남합성</korSecnNm><num>1</num><pval>500</pval><secnKacd>보통주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>023450</shotnIsin></item><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>16750</divAmtPerStk><divRateCpri>21.14</divRateCpri><divRatePval>335</divRatePval><issucoCustno>1739</issucoCustno><korSecnNm>서울도시가스</korSecnNm><num>2</num><pval>5000</pval><secnKacd>보통주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>017390</shotnIsin></item><item><caltotMartTpcd>코스닥시장</caltotMartTpcd><divAmtPerStk>3244.52</divAmtPerStk><divRateCpri>19.8</divRateCpri><divRatePval>0</divRatePval><issucoCustno>34462</issucoCustno><korSecnNm>에스앤케이 KDR</korSecnNm><num>3</num><pval>0</pval><secnKacd>보통주</secnKacd><setaccMm>07</setaccMm><setaccMmdd>0731</setaccMmdd><shotnIsin>950180</shotnIsin></item><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>500</divAmtPerStk><divRateCpri>16.2</divRateCpri><divRatePval>100</divRatePval><issucoCustno>811</issucoCustno><korSecnNm>대동전자</korSecnNm><num>4</num><pval>500</pval><secnKacd>보통주</secnKacd><setaccMm>03</setaccMm><setaccMmdd>0331</setaccMmdd><shotnIsin>008110</shotnIsin></item><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>0</divAmtPerStk><divRateCpri>12.8</divRateCpri><divRatePval>7.03835617</divRatePval><issucoCustno>18894</issucoCustno><korSecnNm>바다로19호선박투자회사</korSecnNm><num>5</num><pval>5000</pval><secnKacd>보통주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>155900</shotnIsin></item><item><caltotMartTpcd>코스닥시장</caltotMartTpcd><divAmtPerStk>800</divAmtPerStk><divRateCpri>11.88</divRateCpri><divRatePval>160</divRatePval><issucoCustno>1270</issucoCustno><korSecnNm>리드코프</korSecnNm><num>6</num><pval>500</pval><secnKacd>보통주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>012700</shotnIsin></item><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>1250</divAmtPerStk><divRateCpri>10.9</divRateCpri><divRatePval>25</divRatePval><issucoCustno>354</issucoCustno><korSecnNm>대신증권1우</korSecnNm><num>7</num><pval>5000</pval><secnKacd>우선주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>003545</shotnIsin></item><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>1200</divAmtPerStk><divRateCpri>8.6</divRateCpri><divRatePval>24</divRatePval><issucoCustno>354</issucoCustno><korSecnNm>대신증권</korSecnNm><num>8</num><pval>5000</pval><secnKacd>보통주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>003540</shotnIsin></item><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>900</divAmtPerStk><divRateCpri>8.6</divRateCpri><divRatePval>180</divRatePval><issucoCustno>16786</issucoCustno><korSecnNm>메리츠금융지주</korSecnNm><num>9</num><pval>500</pval><secnKacd>보통주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>138040</shotnIsin></item><item><caltotMartTpcd>유가증권시장</caltotMartTpcd><divAmtPerStk>500</divAmtPerStk><divRateCpri>8.6</divRateCpri><divRatePval>20</divRatePval><issucoCustno>70</issucoCustno><korSecnNm>유수홀딩스</korSecnNm><num>10</num><pval>2500</pval><secnKacd>보통주</secnKacd><setaccMm>12</setaccMm><setaccMmdd>1231</setaccMmdd><shotnIsin>000700</shotnIsin></item></items><numOfRows>10</numOfRows><pageNo>1</pageNo><totalCount>1441</totalCount></body></response>

XML 출력을 보기 좋게 바꿔보자 (BeautifulSoup - prettify)

from bs4 import BeautifulSoup

xml = BeautifulSoup(response.text, "lxml")
print(xml.prettify())
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html>
 <body>
  <response>
   <header>
    <resultcode>00</resultcode>
    <resultmsg>NORMAL SERVICE.</resultmsg>
   </header>
   <items>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>9900</divamtperstk>
     <divratecpri>29.5</divratecpri>
     <divratepval>1980</divratepval>
     <issucocustno>2345</issucocustno>
     <korsecnnm>동남합성</korsecnnm>
     <num>1</num>
     <pval>500</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>023450</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>16750</divamtperstk>
     <divratecpri>21.14</divratecpri>
     <divratepval>335</divratepval>
     <issucocustno>1739</issucocustno>
     <korsecnnm>서울도시가스</korsecnnm>
     <num>2</num>
     <pval>5000</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>017390</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>코스닥시장</caltotmarttpcd>
     <divamtperstk>3244.52</divamtperstk>
     <divratecpri>19.8</divratecpri>
     <divratepval>0</divratepval>
     <issucocustno>34462</issucocustno>
     <korsecnnm>에스앤케이 KDR</korsecnnm>
     <num>3</num>
     <pval>0</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>07</setaccmm>
     <setaccmmdd>0731</setaccmmdd>
     <shotnisin>950180</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>500</divamtperstk>
     <divratecpri>16.2</divratecpri>
     <divratepval>100</divratepval>
     <issucocustno>811</issucocustno>
     <korsecnnm>대동전자</korsecnnm>
     <num>4</num>
     <pval>500</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>03</setaccmm>
     <setaccmmdd>0331</setaccmmdd>
     <shotnisin>008110</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>0</divamtperstk>
     <divratecpri>12.8</divratecpri>
     <divratepval>7.03835617</divratepval>
     <issucocustno>18894</issucocustno>
     <korsecnnm>바다로19호선박투자회사</korsecnnm>
     <num>5</num>
     <pval>5000</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>155900</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>코스닥시장</caltotmarttpcd>
     <divamtperstk>800</divamtperstk>
     <divratecpri>11.88</divratecpri>
     <divratepval>160</divratepval>
     <issucocustno>1270</issucocustno>
     <korsecnnm>리드코프</korsecnnm>
     <num>6</num>
     <pval>500</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>012700</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>1250</divamtperstk>
     <divratecpri>10.9</divratecpri>
     <divratepval>25</divratepval>
     <issucocustno>354</issucocustno>
     <korsecnnm>대신증권1우</korsecnnm>
     <num>7</num>
     <pval>5000</pval>
     <secnkacd>우선주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>003545</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>1200</divamtperstk>
     <divratecpri>8.6</divratecpri>
     <divratepval>24</divratepval>
     <issucocustno>354</issucocustno>
     <korsecnnm>대신증권</korsecnnm>
     <num>8</num>
     <pval>5000</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>003540</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>900</divamtperstk>
     <divratecpri>8.6</divratecpri>
     <divratepval>180</divratepval>
     <issucocustno>16786</issucocustno>
     <korsecnnm>메리츠금융지주</korsecnnm>
     <num>9</num>
     <pval>500</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>138040</shotnisin>
    </item>
    <item>
     <caltotmarttpcd>유가증권시장</caltotmarttpcd>
     <divamtperstk>500</divamtperstk>
     <divratecpri>8.6</divratecpri>
     <divratepval>20</divratepval>
     <issucocustno>70</issucocustno>
     <korsecnnm>유수홀딩스</korsecnnm>
     <num>10</num>
     <pval>2500</pval>
     <secnkacd>보통주</secnkacd>
     <setaccmm>12</setaccmm>
     <setaccmmdd>1231</setaccmmdd>
     <shotnisin>000700</shotnisin>
    </item>
   </items>
   <numofrows>10</numofrows>
   <pageno>1</pageno>
   <totalcount>1441</totalcount>
  </response>
 </body>
</html>

각 <item> 태그별로 다음 하위태그들의 정보를 확인할 수 있다

  • caltotMartTpcd: 시장구분 (유가증권/코스닥/K-OTC/코넥스/기타비상장)
  • divAmtPerStk: 주당배당금 (단위: 원)
  • divRateCpri: 시가배당률 (단위: %)
  • divRatePval: 액면가배당율 (단위: %)
  • issucoCustno: 발행회사번호
  • korSecnNm: 국문 종목명
  • num: 배당 순위
  • pval: 액면가 (단위: 원)
  • secnKacd: 주식종류 (보통주/우선주)
  • setaccMm: 결산월
  • setaccMmdd: 결산월일
  • shotnIsin: 단축코드 (6자리, 주식거래앱 등에서 활용)

이제 XML의 아이템들을 pandas 데이터프레임으로 변환해보자

import pandas as pd

items = xml.find("items")
item_list = []
for item in items:
    item_dict = {
        'caltotmarttpcd': item.find("caltotmarttpcd").text.strip(),
        'divamtperstk': float(item.find("divamtperstk").text.strip()),
        'divratecpri': float(item.find("divratecpri").text.strip()),
        'divratepval': float(item.find("divratepval").text.strip()),
        'issucocustno': item.find("issucocustno").text.strip(),
        'korsecnnm': item.find("korsecnnm").text.strip(),
        'num': int(item.find("num").text.strip()),
        'pval': int(item.find("pval").text.strip()),
        'secnkacd': item.find("secnkacd").text.strip(),
        'setaccmm': int(item.find("setaccmm").text.strip()),
        'setaccmmdd': item.find("setaccmmdd").text.strip(),
        'shotnisin': item.find("shotnisin").text.strip()
    }
    item_list.append(item_dict)
df = pd.DataFrame(item_list)
In [3]: print(df[['korsecnnm', 'divratecpri', 'divamtperstk', 'caltotmarttpcd']])
Out[3]:
      korsecnnm  divratecpri  divamtperstk caltotmarttpcd
0          동남합성        29.50       9900.00         유가증권시장
1        서울도시가스        21.14      16750.00         유가증권시장
2     에스앤케이 KDR        19.80       3244.52          코스닥시장
3          대동전자        16.20        500.00         유가증권시장
4  바다로19호선박투자회사        12.80          0.00         유가증권시장
5          리드코프        11.88        800.00          코스닥시장
6        대신증권1우        10.90       1250.00         유가증권시장
7          대신증권         8.60       1200.00         유가증권시장
8       메리츠금융지주         8.60        900.00         유가증권시장
9         유수홀딩스         8.60        500.00         유가증권시장

KSD 증권정보포털 SEIBro에서 확인할 수 있는 정보와 동일한 것을 알 수 있다

https://seibro.or.kr/websquare/control.jsp?w2xPath=/IPORTAL/user/company/BIP_CNTS01042V.xml&amp;menuNo=286#

 

바다로19호선박투자회사의 경우 배당금이 아니라 '분배금'으로 매달 배당금 형식으로 선박 투자에 대한 용선료를 주주들에게 지급하는데, 배당금으로 집계되지 않아 0원으로 표기되어 있다 (그런데 시가배당률은 또 집계가 되어있다...)

 

또한, 에스앤케이 KDR과 같이 주식예탁증서(Depositary Receipt)에 대해서도 종목명에 명확히 기재되어 있으니 확인이 필요하다 (보통 국내시장에서 KDR은 유상증자에 대한 내용인데, SNK같은 해외법인이 국내시장에 상장할 때 사용하는 방식 중 하나, 워낙 고배당이었기에 논란이 일어서 주식하는 사람 대부분이 한번쯤은 들어봤을 듯?)

 

또한, 분기배당하는 기업의 경우 1년간 배당 총액을 합산하여 주당배당금과 시가배당률을 산정하니 참고해야 한다 (대부분 기업은 연말 1회 배당이긴 하지만 ㅎㅎ)

 

다음과 같이 함수로 만들어보자 (데이터프레임 칼럼명도 보기쉽게 한글로 바꿔주자)

import requests
from bs4 import BeautifulSoup
import pandas as pd

def getDevidendRank(year: int, count: int = 100, market: int = None) -> pd.DataFrame:
    """
    market: 상장구분 (11=유가증권, 12=코스닥, 13=K-OTC, 14=코넥스, 50=기타비상장)
    """
    url_base = "http://api.seibro.or.kr/openapi/service/StockSvc"
    url_spec = "getDividendRankN1"
    url = url_base + "/" + url_spec
    api_key_utf8 = "Your key from data.go.kr"
    api_key_decode = requests.utils.unquote(api_key_utf8, encoding='utf-8')
    
    params = {
        "serviceKey": api_key_decode,
        "rankTpcd": 1,  # 1 = 시가배당률, 2 = 액면가배당률
        "year": year,
        "numOfRows": count,
        "pageNo": 1,
    }
    if market is not None:
        params['listTpcd'] = market
    
    response = requests.get(url, params=params)
    xml = BeautifulSoup(response.text, "lxml")
    items = xml.find("items")
    item_list = []
    for item in items:
        item_dict = {
            'caltotmarttpcd': item.find("caltotmarttpcd").text.strip(),
            'divamtperstk': float(item.find("divamtperstk").text.strip()),
            'divratecpri': float(item.find("divratecpri").text.strip()),
            'divratepval': float(item.find("divratepval").text.strip()),
            'issucocustno': item.find("issucocustno").text.strip(),
            'korsecnnm': item.find("korsecnnm").text.strip(),
            'num': int(item.find("num").text.strip()),
            'pval': int(item.find("pval").text.strip()),
            'secnkacd': item.find("secnkacd").text.strip(),
            'setaccmm': int(item.find("setaccmm").text.strip()),
            'setaccmmdd': item.find("setaccmmdd").text.strip(),
            'shotnisin': item.find("shotnisin").text.strip()
        }
        item_list.append(item_dict)
    df = pd.DataFrame(item_list)
    df.columns = ['시장구분', '주당배당금', '시가배당률', '액면가배당률', '발행회사번호', '종목명', '순위', '액면가', '주식종류', '결산월', '결산월일', '종목코드']    
    df = df[['순위', '종목코드', '종목명', '주식종류', '시장구분', '주당배당금', '시가배당률', '액면가배당률', '액면가', '결산월', '결산월일', '발행회사번호']]
    return df

위와 같이 함수로 작성해서 시장별로 구분된 순위를 따로 쿼리할 수 있다

In [4]: df1 = getDevidendRank(2020, 10, 11)
In [5]: df1
Out[5]:
   순위    종목코드             종목명 주식종류    시장구분  ...       액면가배당률   액면가  결산월  결산월일  발행회사번호
0   1  023450            동남합성  보통주  유가증권시장  ...  1980.000000   500   12  1231    2345
1   2  017390          서울도시가스  보통주  유가증권시장  ...   335.000000  5000   12  1231    1739
2   3  008110            대동전자  보통주  유가증권시장  ...   100.000000   500    3  0331     811
3   4  155900    바다로19호선박투자회사  보통주  유가증권시장  ...     7.038356  5000   12  1231   18894
4   5  003545          대신증권1우  우선주  유가증권시장  ...    25.000000  5000   12  1231     354
5   6  003540            대신증권  보통주  유가증권시장  ...    24.000000  5000   12  1231     354
6   7  138040         메리츠금융지주  보통주  유가증권시장  ...   180.000000   500   12  1231   16786
7   8  000700           유수홀딩스  보통주  유가증권시장  ...    20.000000  2500   12  1231      70
8   9  153360  하이골드오션3호선박투자회사  보통주  유가증권시장  ...     2.358387  5000   12  1231   18460
9  10  001755          한양증권1우  우선주  유가증권시장  ...    16.000000  5000   12  1231     175

[10 rows x 12 columns]

In [6]: df2 = getDevidendRank(2020, 10, 12)
In [7]: df2
Out[7]:
   순위    종목코드        종목명 주식종류   시장구분  ...  액면가배당률   액면가  결산월  결산월일  발행회사번호
0   1  950180  에스앤케이 KDR  보통주  코스닥시장  ...     0.0     0    7  0731   34462
1   2  012700       리드코프  보통주  코스닥시장  ...   160.0   500   12  1231    1270
2   3  078020   이베스트투자증권  보통주  코스닥시장  ...    11.0  5000   12  1231    4481
3   4  040420    정상제이엘에스  보통주  코스닥시장  ...    86.0   500   12  1231    6393
4   5  085910       네오티스  보통주  코스닥시장  ...    50.0   500   12  1231   11365
5   6  007330   푸른상호저축은행  보통주  코스닥시장  ...    55.0  1000   12  1231     733
6   7  190650  코리아에셋투자증권  보통주  코스닥시장  ...     6.0  5000    3  0331    6332
7   8  225190      삼양옵틱스  보통주  코스닥시장  ...   120.0   500   12  1231   25874
8   9  031330     에스에이엠티  보통주  코스닥시장  ...    30.0   500   12  1231    3133
9  10  029960        코엔텍  보통주  코스닥시장  ...   102.0   500   12  1231    2996

[10 rows x 12 columns]

2021년 자료의 경우 아직 공시자료(배당을 위한 주주명부폐쇠 등)들을 다 정리하지 못했는지, KSD를 통해서는 의미있는 자료를 얻기 힘들다

In [8]: df3 = getDevidendRank(2021, 50)
In [8]: df3[['순위', '종목명', '주당배당금', '시가배당률']]
Out[8]:
    순위                   종목명         주당배당금  시가배당률
0    1          바다로19호선박투자회사      0.000000  12.60
1    2                신영증권1우   4050.000000   6.98
2    3                  신영증권   4000.000000   6.88
3    4                  동남합성   4300.000000   6.20
4    5   이지스밸류플러스위탁관리부동산투자회사    303.000000   5.80
5    6     신한알파위탁관리부동산투자회사1우    825.000000   5.46
6    7             코리아에셋투자증권    450.000000   5.15
7    8       신한알파위탁관리부동산투자회사    341.000000   4.36
8    9                 쌍용씨앤이    330.000000   4.10
9   10                    대덕    300.000000   3.80
10  11        하이골드오션3호선박투자회사      0.000000   3.80
11  12               아이마켓코리아    450.000000   3.80
12  13                   포스코  12000.000000   3.70
13  14     제이알글로벌위탁관리부동산투자회사    190.000000   3.60
14  15              마크로밀엠브레인    280.000000   3.20
15  16  미래에셋맵스제1호위탁관리부동산투자회사    153.000000   3.05
16  17                  청담러닝   1000.000000   2.90
17  18                   파트론    350.000000   2.89
18  19   이리츠코크렙기업구조조정부동산투자회사      0.000000   2.80
19  20                 삼양옵틱스    300.000000   2.80
20  21  코람코에너지플러스위탁관리부동산투자회사    166.000000   2.80
21  22                  리드코프    300.000000   2.80
22  23    엔에이치프라임위탁관리부동산투자회사    125.000000   2.70
23  24                  기신정기    150.000000   2.70
24  25                  대덕1우    305.000000   2.70
25  26                씨엠에스에듀    200.000000   2.70
26  27               현대중공업지주   1850.000000   2.58
27  28    이지스레지던스위탁관리부동산투자회사    130.000000   2.50
28  29                  미원상사   5500.000000   2.44
29  30              디티알오토모티브   1000.000000   2.40
30  31                씨앤투스성진    500.000000   2.36
31  32                    금비   1600.000000   2.33
32  33         롯데위탁관리부동산투자회사    139.759519   2.30
33  34                 효성ITX    450.000000   2.20
34  35                    풍강    100.000000   2.10
35  36               대덕전자 1우    305.000000   2.10
36  37                상신브레이크     80.000000   2.10
37  38           SBI 핀테크솔루션즈    174.821200   2.00
38  39  이에스알켄달스퀘어위탁관리부동산투자회사      0.000000   2.00
39  40               이씨에스텔레콤    130.000000   1.87
40  41                    방림     45.000000   1.70
41  42               에스앤티에너지    400.000000   1.70
42  43               케이씨씨글라스   1000.000000   1.60
43  44                 한온시스템    270.000000   1.60
44  45               S-OIL1우   1000.000000   1.60
45  46                 SK텔레콤   5000.000000   1.57
46  47     디앤디플랫폼위탁관리부동산투자회사     81.000000   1.53
47  48                 진양홀딩스     50.000000   1.50
48  49                하나금융지주    700.000000   1.50
49  50                고려신용정보    150.000000   1.50

2위와 3위에 나란히 랭크되어 있는 '신영증권1우' 및 '신영증권'의 21년도 반기보고서 공시자료를 보면

https://dart.fss.or.kr/dsaf001/main.do?rcpNo=20211108000273

우선주 주당 4,050원 - 시가배당률 6.98%

보통주 주당 4,000원 - 시가배당률 6.88%

로 공시가 되어 있는 것을 알 수 있다

 

이와 같이 주주총회를 통해 배당에 대한 의결이 이미 결정된 경우 OpenAPI를 통해서도 쉽게 확인할 수 있지만 아직 주주총회를 하지 않은 종목들은 집계되지 않은 것을 알 수 있다 (즉, 당장의 투자에 활용하기는 힘들다... ㅠㅠ)

4. 정리

공공데이터포털의 주식정보서비스 API는 KSD SEIBro OpenAPI 중 일부를 소개해주는 것에 불과하기 때문에 관심있는 사람은 아래 SEIBro 오픈플랫폼 사이트에서 직접 가입할 경우  훨씬 더 많은 정보를 얻을 수 있다

https://openplatform.seibro.or.kr/websquare/startup_control.jsp?w2xPath=/startup/index.xml 

 

세이브로 오픈플랫폼

 

openplatform.seibro.or.kr

참고로 위에서 구현한 코드의 requests - get 결과물의 url을 찍어보면 다음과 같이 api.seibro.or.kr이 도메인이 찍히는 것을 확인할 수 있다

In [9]: response.url
Out[9]: 'http://api.seibro.or.kr/openapi/service/StockSvc/getDividendRankN1?serviceKey=SV5xOdOCXxDs5yZxOsanMwoY9Jeht7UPM%2BnKqBY3tFIpAivokjm4XMm7Dr9QvXMgWXdwkxIeFRDHpgQaSQtmig%3D%3D&rankTpcd=1&year=2021&numOfRows=10&pageNo=1'

별도로 사이트를 가입하지 않고, 공공데이터포털에서 통합으로 서비스를 관리하고자 할 경우 유용하게 사용할 수 있는 API이지만, 운영계정으로 변경하지 않을 경우 기능별로 일일 트래픽이 100회로 제한되기 때문에 사용성이 떨어질 수 있으니 유의해야 한다

 

본 포스팅에서는 내가 관심있게 지켜보는 배당에 관한 항목만 살펴보았는데, 배당뿐만 아니라 다양한 정보를 쿼리할 수 있으니 직접 구현해보기 바란다

 

마지막으로 유가증권시장의 전체 종목의 단축코드(6자리)를 얻는 예제 코드를 작성해봤다 (시장별 단축코드 전체 조회, url 상세 = getShotnByMartN1)

def getStockCode(market: int = 11) -> pd.DataFrame:
    """
    market: 상장구분 (11=유가증권, 12=코스닥, 13=K-OTC, 14=코넥스, 50=기타비상장)
    """
    url_base = "http://api.seibro.or.kr/openapi/service/StockSvc"
    url_spec = "getShotnByMartN1"
    url = url_base + "/" + url_spec
    api_key_utf8 = "Your key from data.go.kr"
    api_key_decode = requests.utils.unquote(api_key_utf8, encoding='utf-8')
    
    params = {
        "serviceKey": api_key_decode,
        "pageNo": 1,
        "numOfRows": 100000,
        "martTpcd": market
    }
    
    response = requests.get(url, params=params)
    xml = BeautifulSoup(response.text, "lxml")
    items = xml.find("items")
    item_list = []
    for item in items:
        item_dict = {
            'korsecnnm': item.find("korsecnnm").text.strip(),
            'shotnisin': item.find("shotnisin").text.strip()
        }
        item_list.append(item_dict)
    df = pd.DataFrame(item_list)
    df.columns = ['종목명', '단축코드']
    return df

(넉넉하게 1페이지에 최대 10만개 종목을 모두 불러오도록 구현)

In [10]: df1 = getStockCode(11)

In [11]: df1.head(5)
Out[11]: 
         종목명    단축코드
0       동화약품  000020
1     케이알모터스  000040
2         경방  000050
3  메리츠화재해상보험  000060
4      삼양홀딩스  000070

In [12]: df1.tail(5)
Out[12]:
                    종목명    단축코드
938     에스케이위탁관리부동산투자회사  395400
939   엔에이치올원위탁관리부동산투자회사  400760
940                 케이카  381970
941  미래에셋글로벌위탁관리부동산투자회사  396690
942  신한서부티엔디위탁관리부동산투자회사  404990

In [13]: len(df1)
Out[13]: 943

In [14]: df2 = getStockCode(12)

In [15]: df2.head(5)
Out[15]: 
                   종목명    단축코드
0  이스트아시아홀딩스인베스트먼트리미티드  900110
1                삼천당제약  000250
2               중앙에너비스  000440
3                 신라섬유  001000
4                 안국약품  001540

In [16]: df2.tail(5)
Out[16]: 
                  종목명    단축코드
1529       유진기업인수목적7호  388800
1530  대신밸런스제11호기업인수목적  397500
1531      교보11호기업인수목적  397880
1532           해성산업1우  03481K
1533      하이제7호기업인수목적  400840

In [17]: len(df2)
Out[87]: 1534

유가증권시장(코스피) 및 코스닥 시장에에 상장된 종목은 2021년 12월 21일 기준 각각 943개, 1534개인 것을 확인할 수 있다

증권사 프로그램이나 앱, 포털사이트 등을 이용하면 종목 개별 종목코드정도는 쉽게 얻을 수 있지만 이렇게 전체 상장종목들에 대해서 단축코드를 개인 DB등에 저장해놓고 필요할 때마다 쿼리해서 활용하면 훨씬 스마트한 투자(?)를 할 수 있다

 

끝~!

반응형