YOGYUI

MySQL - 2021 국내주식 공모주(IPO) 데이터베이스 만들기 (2) 본문

Software/SQL

MySQL - 2021 국내주식 공모주(IPO) 데이터베이스 만들기 (2)

요겨 2021. 9. 19. 22:24
반응형

4. DART 공시 관련 속성 추가

신규상장 관련 기업의 정보를 가장 정확하게 파악할 수 있는 것은 금융감독원의 전자공시시스템(DART : Data Analysis, Retrieval and Transfer System)이다

2020년 4월부터 Open API 형식으로 누구든지 DART 홈페이지에 접속하지 않고서도 쉽게 접근할 수 있게 OpenDART를 서비스하고 있으므로 이를 활용해서 전에 만들어둔 IPO2021 테이블에 DART 관련 속성(칼럼)을 추가해보도록 하자

※ OpenDART는 회원 가입 후 API Key를 발급받아야 한다. OpenDART 활용법에 대한 개요는 링크 참고

4.1. 기업 DART 고유번호 테이블 추가

상장기업의 유가증권 시장에서의 증권코드(ex: 삼성전자 = 005930)와는 별개로 DART에서 관리하는 8자리의 기업 고유번호가 따로 존재한다 (ex: 삼성전자 = 00126380)

이 코드가 있어야 공시자료 검색을 수월하게 할 수 있으므로, 관련 정보를 담은 테이블을 STOCK 데이터베이스에 만들어주자

(CREATE TABLE 쿼리 사용)

MySQL [STOCK]> CREATE TABLE IF NOT EXISTS `DARTLIST` (
    ->    `기업코드` varchar(8) NOT NULL,
    ->    `기업명` varchar(32) NOT NULL,
    ->    `증권코드` varchar(6),
    ->    `수정일자` DATE,
    ->    PRIMARY KEY (`기업코드`) 
    ->  );
Query OK, 0 rows affected (0.122 sec)
MySQL [STOCK]> DESC DARTLIST;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| 기업코드     | varchar(8)  | NO   | PRI | NULL    |       |
| 기업명       | varchar(32) | NO   |     | NULL    |       |
| 증권코드     | varchar(6)  | YES  |     | NULL    |       |
| 수정일자     | date        | YES  |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
4 rows in set (0.046 sec)

이제 파이썬으로 DART 상장법인 전체 리스트를 OpenDART를 사용해 가져온 뒤, DB 테이블에 레코드를 등록하는 코드를 만들어보자 (DART 기업 리스트 얻는 자세한 내용은 링크 참고)

import io
import os
import requests
import zipfile
import pandas as pd
import xml.etree.ElementTree as ET
import pymysql
from datetime import datetime

url = "https://opendart.fss.or.kr/api/corpCode.xml"
api_key = "API Key from OpenDART"
params = {
    'crtfc_key': api_key
}

xmlpath = os.path.abspath('./CORPCODE.xml')
if not os.path.isfile(xmlpath):
    # get corp code xml file from open API
    response = requests.get(url, params=params)
    iostream = io.BytesIO(response.content)
    zf = zipfile.ZipFile(iostream)  # just 1-file (name = CORPCODE.xml)
    infolist = zf.infolist()
    zf.extract(infolist[0])
    zf.close()

# read xml file and make pandas dataframe
tree = ET.parse(xmlpath)
root = tree.getroot()
tags_list = root.findall('list')

def convert(tag: ET.Element) -> dict:
    conv = {}
    for child in list(tag):
        if child.tag == 'modify_date':
            conv[child.tag] = datetime.strptime(child.text, '%Y%m%d')
        else:
            conv[child.tag] = child.text
    return conv

tags_list_dict = [convert(x) for x in tags_list]
df_dartlist = pd.DataFrame(tags_list_dict)

# update database table
db_conn = pymysql.connect(host='my.sql.server', port=3306, user='yogyui', passwd='password', db='STOCK')
cursor = db_conn.cursor()
# query records already in table
cursor.execute('SELECT * FROM DARTLIST;')
result = cursor.fetchall()
dartcodes = [x[0] for x in result]
print('select table done')

insert_data = []
update_data = []
for i in range(len(df_dartlist)):
    dr = df_dartlist.iloc[i]
    corp_code = dr['corp_code']
    corp_name = dr['corp_name']
    stock_code = dr['stock_code']
    modi_date = dr['modify_date']
    if corp_code in dartcodes:  # already in table
        update_data.append((corp_name, stock_code, modi_date.strftime('%Y-%m-%d'), corp_code))
    else:
        insert_data.append((corp_code, corp_name, stock_code, modi_date.strftime('%Y-%m-%d')))
print('make list done (update: {}, insert: {})'.format(len(update_data), len(insert_data)))

if len(update_data) > 0:
    sql = "UPDATE DARTLIST SET `기업명` = %s, `증권코드` = %s, `수정일자` = %s WHERE `기업코드` = %s"
    result = cursor.executemany(sql, update_data)

if len(insert_data) > 0:
    sql = "INSERT INTO DARTLIST (`기업코드`, `기업명`, `증권코드`, `수정일자`) VALUES (%s, %s, %s, %s)"
    result = cursor.executemany(sql, insert_data)

db_conn.commit()
db_conn.close()
select table done
make list done (update: 0, insert: 88127)

8만개가 넘는 레코드를 insert 혹은 update해야하므로 pymysql cursor 객체의 executemany 메서드를 사용했다

테이블에 레코드가 제대로 입력되었는지 확인해보자

MySQL [STOCK]> SELECT COUNT(*) FROM DARTLIST;
+----------+
| COUNT(*) |
+----------+
|    88127 |
+----------+
1 row in set (0.089 sec)

MySQL [STOCK]> SELECT * FROM DARTLIST LIMIT 3;
+--------------+-----------------------------+--------------+--------------+
| 기업코드     | 기업명                      | 증권코드     | 수정일자     |
+--------------+-----------------------------+--------------+--------------+
| 00100090     | LG스포츠                    |              | 2021-02-22   |
| 00100115     | MBC아카데미                 |              | 2019-03-19   |
| 00100124     | 샤프에비에이션케이          |              | 2021-02-23   |
+--------------+-----------------------------+--------------+--------------+
3 rows in set (0.011 sec)

MySQL [STOCK]> SELECT * FROM DARTLIST ORDER BY `기업코드` DESC LIMIT 3;
+--------------+-----------------------+--------------+--------------+
| 기업코드     | 기업명                | 증권코드     | 수정일자     |
+--------------+-----------------------+--------------+--------------+
| 99999999     | 금감원(테스트)        | 999980       | 2020-01-29   |
| 01587171     | 푸오레                |              | 2021-09-17   |
| 01587162     | 심원태양광발전        |              | 2021-09-17   |
+--------------+-----------------------+--------------+--------------+
3 rows in set (0.010 sec)

MySQL [STOCK]> SELECT * FROM DARTLIST WHERE `기업명` LIKE '%삼성전자%';
+--------------+--------------------------------+--------------+--------------+
| 기업코드     | 기업명                         | 증권코드     | 수정일자     |
+--------------+--------------------------------+--------------+--------------+
| 00126380     | 삼성전자                     | 005930       | 2021-06-10   |
| 00252074     | 삼성전자판매                  |              | 2021-02-26   |
| 00258999     | 삼성전자서비스                 |              | 2021-02-26   |
| 00366997     | 삼성전자로지텍                 |              | 2021-02-26   |
| 01345812     | 삼성전자서비스씨에스             |              | 2021-02-26   |
+--------------+--------------------------------+--------------+--------------+
5 rows in set (0.172 sec)

MySQL [STOCK]> SELECT * FROM DARTLIST WHERE `증권코드` != '' ORDER BY `수정일자` DESC LIMIT 5;
+--------------+--------------+--------------+--------------+
| 기업코드     | 기업명       | 증권코드     | 수정일자     |
+--------------+--------------+--------------+--------------+
| 00266943     | 넥슨지티     | 041140       | 2021-09-17   |
| 00370255     | 바른전자     | 064520       | 2021-09-17   |
| 00205687     | 휴먼엔       | 032860       | 2021-09-17   |
| 00609634     | 아이엠       | 101390       | 2021-09-17   |
| 00146427     | 일야         | 058450       | 2021-09-17   |
+--------------+--------------+--------------+--------------+
5 rows in set (0.175 sec)

금융감독원에서 테스트를 위해 넣어둔 최후 레코드를 제외하면 9월 19일 기준 총 88126개 기업의 기업코드, 기업명, 증권코드(상장되었을 경우), 수정일자 레코드가 제대로 등록되었다

IPO2021 테이블과 DARTLIST 테이블의 관계는 다음과 같다

(나중에 조인 연산을 위해 증권코드를 외래키로 등록할까말까 고민중)

4.2. DART 공시문서 (증권신고서) 데이터 속성 추가

상장시 공시하는 '증권신고서'를 통해 기업 공모에 관한 수많은 정보를 획득할 수 있다

http://dart.fss.or.kr/dsaf001/main.do?rcpNo=20210810000389

신규 상장의 경우 증권신고서 종류중에서도 '지분증권' 문서를 살펴봐야 한다

예를 들기 위해 9월 29일에 상장되는 실리콘투 종목의 문서를 검색해보자

해당 문서를 클릭하면 본문 팝업이 뜬다

문서번호는 20210913000265 이다

※주의: 발행조건확정 후 수정된 문서는 이 API로 알 수 없다 (발행조건확정 전 최종 기재정정 문서 번호를 넘겨주는 것 같다)

 

OpenDART를 활용하면 위와 같이 번거롭게 수동으로 검색하지 않고 쉽게 자동화할 수 있다

OpenDART의 '증권신고서 주요정보' - '지분증권 개발가이드'를 참고해서 코드를 작성해보자

https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS006&apiId=2020054

 

전자공시 OPENDART 시스템 | 개발가이드 | 상세

증권신고서 주요정보 개발가이드 증권신고서 주요정보 지분증권 지분증권 개발가이드 기본 정보 기본 정보 메서드 요청URL 인코딩 출력포멧 GET https://opendart.fss.or.kr/api/estkRs.json UTF-8 JSON GET https:/

opendart.fss.or.kr

import pymysql
import requests

corp_name = "실리콘투"

# DART에 등록된 기업 코드를 가져온다
db_conn = pymysql.connect(host="my.sql.server", port=3306, user="yogyui", passwd="password", db='STOCK')
cursor = db_conn.cursor()
cursor.execute(f"SELECT * FROM DARTLIST WHERE `기업명`='{corp_name}';")
result = cursor.fetchall()
corp_code = result[0][0]

# 증권신고서 주요정보 - 지분증권 OpenAPI 호출
url = "https://opendart.fss.or.kr/api/estkRs.json"
params = {
    "crtfc_key": "Your API Key from OpenDART",
    "corp_code": corp_code,
    "bgn_de": "20210101",
    "end_de": "20211231"
}
response = requests.get(url, params=params)

# 그룹별로 나누기
json = response.json()
group_list = []
for group in json.get('group'):
    title = group.get('title')
    tag_list = group.get('list')
    group_list.append({'title': title, 'list': tag_list})
db_conn.close()
In [1]: response.text
Out[1]: '{"status":"000","message":"정상","group":[{"title":"일반사항","list":[{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","sbd":"2021년 09월 14일 ~ 2021년 09월 15일","pymd":"2021년 09월 17일","sband":"2021년 09월 14일","asand":"2021년 09월 17일","asstd":"-","exstk":"-","exprc":"-","expd":"-","rpt_rcpn":"-"}]},{"title":"증권의종류","list":[{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","stksen":"기명식보통주","stkcnt":"1,654,000","fv":"500","slprc":"27,200","slta":"44,988,800,000","slmthn":"일반공모"}]},{"title":"인수인정보","list":[{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","stksen":"기명식보통주","actsen":"대표","actnmn":"삼성증권","udtcnt":"992,400","udtamt":"26,993,280,000","udtprc":"944,764,800","udtmth":"총액인수"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","stksen":"기명식보통주","actsen":"대표","actnmn":"미래에셋증권","udtcnt":"661,600","udtamt":"17,995,520,000","udtprc":"404,899,200","udtmth":"총액인수"}]},{"title":"자금의사용목적","list":[{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","se":"시설자금","amt":"30,000,000,000"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","se":"운영자금","amt":"3,000,000,000"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","se":"채무상환","amt":"6,588,780,800"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","se":"타법인증권취득자금","amt":"5,000,000,000"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","se":"발행제비용","amt":"1,400,000,000"}]},{"title":"매출인에관한사항","list":[{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","hdr":"SON ROBERT INHO","rl_cmp":"등기임원","bfsl_hdstk":"444,320","slstk":"40,000","atsl_hdstk":"404,320"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","hdr":"최진호","rl_cmp":"등기임원","bfsl_hdstk":"257,067","slstk":"40,000","atsl_hdstk":"217,067"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","hdr":"송건호","rl_cmp":"미등기임원","bfsl_hdstk":"24,000","slstk":"10,000","atsl_hdstk":"14,000"},{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","hdr":"박종엽","rl_cmp":"직원","bfsl_hdstk":"16,000","slstk":"4,000","atsl_hdstk":"12,000"}]},{"title":"일반청약자환매청구권","list":[{"rcept_no":"20210913000265","corp_cls":"E","corp_code":"00982023","corp_name":"실리콘투","exprc":"-","expd":"-","grtrs":"-","exavivr":"-","grtcnt":"-"}]}]}'

In [2]: len(group_list)
Out[2]: 6

총 6개의 'group' 정보가 반환되었다 (좀 더 코드 간결화를 위해 request 결과를 json으로 변환했다)

 

각 그룹에 어떤 정보가 담겨있는지 살펴보자

(실제 아래 나오는 정보들은 공시자료 원문에 동일하게 기재되어 있다)

[일반사항]

In [3]: group_list[0]
Out[3]:
{'title': '일반사항',
 'list': [{'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'sbd': '2021년 09월 14일 ~ 2021년 09월 15일',
   'pymd': '2021년 09월 17일',
   'sband': '2021년 09월 14일',
   'asand': '2021년 09월 17일',
   'asstd': '-',
   'exstk': '-',
   'exprc': '-',
   'expd': '-',
   'rpt_rcpn': '-'}]}

rcept_no로 증권신고서 문서의 DART 문서번호인데, 위에서 수동으로 검색한 20210913000265와 같은 것을 알 수 있다 -> 실제 공시자료 원문을 검색할 때 이 값을 사용하면 된다!

corp_cls는 법인구분으로, E는 기타를 나타낸다 (글을 작성하고 있는 9월 27일 기준 아직 상장되지 않았으므로 유가증권이나 코스닥으로 분류되지 않는다)

corp_code는 DART에 등록된 기업코드, corp_name은 회사명이다

sbd는 청약기일, pymd는 청약금 납입기일, sband는 청약공고일, asand는 배정공고일, asstd는 배정기준일, exstk, exprc, expd는 신주인수권에 관한 세부사항이다 (값이 없는 건 무시하자 ㅎㅎ)

[증권의 종류]

In [4]: group_list[1]
Out[4]: 
{'title': '증권의종류',
 'list': [{'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'stksen': '기명식보통주',
   'stkcnt': '1,654,000',
   'fv': '500',
   'slprc': '27,200',
   'slta': '44,988,800,000',
   'slmthn': '일반공모'}]}

(동일한 속성이 반복된다)

stksen은 증권의 종류, stkcnt는 증권수량, fv는 액면가액, slprc는 모집(매출)가액 = 공모가, slta는 모집(매출)총액, slmthm은 모집(매출)방법이다

[인수인 정보]

In [5]: group_list[2]
Out[5]: 
{'title': '인수인정보',
 'list': [{'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'stksen': '기명식보통주',
   'actsen': '대표',
   'actnmn': '삼성증권',
   'udtcnt': '992,400',
   'udtamt': '26,993,280,000',
   'udtprc': '944,764,800',
   'udtmth': '총액인수'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'stksen': '기명식보통주',
   'actsen': '대표',
   'actnmn': '미래에셋증권',
   'udtcnt': '661,600',
   'udtamt': '17,995,520,000',
   'udtprc': '404,899,200',
   'udtmth': '총액인수'}]}

인수인 정보는 공모주 청약 주관 증권사에 대한 정보를 담고 있다

삼성증권과 미래에셋증권이 주관한 것을 알 수 있다

actsen은 인수인구분, actnmn은 인수인명, udtcnt는 인수수량, udtamt는 인수금액, udtprc는 인수대가, udtmth는 인수방법이다

[자금의 사용 목적]

In [6]:group_list[3]
Out[6]: 
{'title': '자금의사용목적',
 'list': [{'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'se': '시설자금',
   'amt': '30,000,000,000'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'se': '운영자금',
   'amt': '3,000,000,000'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'se': '채무상환',
   'amt': '6,588,780,800'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'se': '타법인증권취득자금',
   'amt': '5,000,000,000'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'se': '발행제비용',
   'amt': '1,400,000,000'}]}

어떤 목적을 위해 공모를 진행하는지 항목별로 구분되어 있다

se는 구분, amt는 금액을 가리킨다

시설자금, 운영자금 등 회사 운용에 일부, 채무상환에 일부 사용하고자 하는 등 다양한 목적으로 자금을 사용하고자 한다는 것을 알 수 있다

더 자세히 알고자 하면 공시 원문을 보면 된다

[매출인에 관한 사항]

In [7]: group_list[4]
Out[7]: 
{'title': '매출인에관한사항',
 'list': [{'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'hdr': 'SON ROBERT INHO',
   'rl_cmp': '등기임원',
   'bfsl_hdstk': '444,320',
   'slstk': '40,000',
   'atsl_hdstk': '404,320'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'hdr': '최진호',
   'rl_cmp': '등기임원',
   'bfsl_hdstk': '257,067',
   'slstk': '40,000',
   'atsl_hdstk': '217,067'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'hdr': '송건호',
   'rl_cmp': '미등기임원',
   'bfsl_hdstk': '24,000',
   'slstk': '10,000',
   'atsl_hdstk': '14,000'},
  {'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'hdr': '박종엽',
   'rl_cmp': '직원',
   'bfsl_hdstk': '16,000',
   'slstk': '4,000',
   'atsl_hdstk': '12,000'}]}

주요임원들의 (구주)매출에 대한 정보가 있다

hdr은 보유자 이름, rl_cmp는 회사와의 관계, bfsl_hdstk는 매출 전 보유 증권 수, slstk는 매출 증권 수, atsl_hdstk는 매출 후 보유 증권 수를 가리킨다

(박종엽씨는 누구시길래 직원인데 4천주나 내신걸까 ㅋㅋ)

[일반 청약자 환매청구권]

In [8]: group_list[5]
Out[8]: 
{'title': '일반청약자환매청구권',
 'list': [{'rcept_no': '20210913000265',
   'corp_cls': 'E',
   'corp_code': '00982023',
   'corp_name': '실리콘투',
   'exprc': '-',
   'expd': '-',
   'grtrs': '-',
   'exavivr': '-',
   'grtcnt': '-'}]}

풋백옵션이 있을리가... ㅋㅋ 무시해주자

 

순리대로 가려면 공시 원문을 가져와서 HTML 크롤링을 해야되지만, OpenDART에서 증권신고서 주요 내용을 간추려서 간단하게 제공해주기 때문에 활용도가 높다

 

간단하게 예를 들기 위해 기존 IPO2021 테이블에 DART 문서번호 속성을 추가하고 모든 레코드에 대해 업데이트해보자

사실 지분증권 OpenAPI로 얻을 수 있는 정보는 거의 없다고 봐도 된다 ㅎㅎ

4.2.1. 테이블 속성 추가

DART 기업코드는 8자리 고정 길이 문자열, 문서번호는 14자리 고정 길이 문자열이다

MySQL [stock]> ALTER TABLE IPO2021 ADD COLUMN `기업코드` CHAR(8) ;
Query OK, 0 rows affected (0.313 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [stock]> ALTER TABLE IPO2021 ADD CONSTRAINT uniqueCode UNIQUE (`기업코드`);
Query OK, 0 rows affected (0.074 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [stock]> ALTER TABLE IPO2021 ADD COLUMN `문서번호` CHAR(14);
Query OK, 0 rows affected (0.245 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [stock]> DESC IPO2021;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| 종목코드   | varchar(6)  | NO   | PRI | NULL    |       |
| 이름       | varchar(32) | NO   |     | NULL    |       |
| 상장일     | date        | YES  |     | NULL    |       |
| 공모가     | int(11)     | YES  |     | NULL    |       |
| 상장주식수 | int(11)     | YES  |     | NULL    |       |
| 기업코드   | char(8)     | YES  | UNI | NULL    |       |
| 문서번호   | char(14)    | YES  |     | NULL    |       |
+------------+-------------+------+-----+---------+-------+
7 rows in set (0.043 sec)

4.2.2. 테이블 UPDATE

앞서 구현해둔 updateIPODatabase 함수에 OpenDART 관련 구문을 추가해주면 된다

from get_ipo_list import getIPOAllList
from get_code import getKrxStockCsvFile
from datetime import datetime
import pymysql
import requests

def updateIPODatabase():
    db_conn = pymysql.connect(host='yogyui.ipdisk.co.kr', port=3307, user='lee2002w', passwd='Lsh312453124%', db='STOCK')
    cursor = db_conn.cursor()
    # 테이블 SELECT 쿼리로 저장되어 있는 레코드들 불러오기
    cursor.execute('SELECT * FROM IPO2021;')
    result = cursor.fetchall()
    # 저장되어있는 코드들만 따로 추출
    codes = [x[0] for x in result]
    
    # 2021년 신규상장 종목 리스트업 (스팩 제외)
    df_new = getIPOAllList(2021)
    # 오늘 이후의 데이터 (상장예정)는 제거
    now = datetime.now()
    df_new = df_new[df_new['신규상장일'] < now]
    # 상장일 기준으로 오름차순 정렬
    df_new = df_new.sort_values(by=['신규상장일'], ascending=True)
    
    # KRX 상장종목 데이터 가져오기
    df_all = getKrxStockCsvFile()
    
    # 모든 신규상장종목에 대해 루프
    for i in range(len(df_new)):
        try:
            dr = df_new.iloc[i]
            name = dr['기업명']
            date = dr['신규상장일']
            value1 = dr['공모가(원)']
            # 단축코드 가져오기
            if name == '에이치케이이노엔':
                find = df_all[df_all['한글 종목약명'] == 'HK이노엔'].iloc[0]
            else:
                find = df_all[df_all['한글 종목약명'] == name].iloc[0]
            code = find['단축코드']
            value2 = find['상장주식수']
            
            # DARTLIST 테이블로부터 기업코드를 얻어온다
            cursor.execute(f'SELECT * FROM DARTLIST WHERE `증권코드` = {code};')
            result = cursor.fetchall()
            corp_code = result[0][0]
            
            # 증권신고서 주요정보 - 지분증권 OpenAPI 호출
            url = "https://opendart.fss.or.kr/api/estkRs.json"
            params = {
                "crtfc_key": "Your API Key from OpenDART",
                "corp_code": corp_code,
                "bgn_de": "20200101",
                "end_de": "20211231"
            }
            response = requests.get(url, params=params)
            json = response.json()
            if json.get('status') == '000':
                group_list = []
                for group in json.get('group'):
                    title = group.get('title')
                    tag_list = group.get('list')
                    group_list.append({'title': title, 'list': tag_list})
                doc_no = group_list[0].get('list')[0].get('rcept_no')
            else:
                doc_no = ''
            
            # 종목코드가 이미 저장되었는지 여부에 따라 쿼리 분기
            if code in codes:
                sql = "UPDATE IPO2021 SET "
                sql += "`이름` = '{}',".format(name)
                sql += "`상장일` = '{}',".format(date.strftime('%Y-%m-%d'))
                sql += "`공모가` = '{}',".format(value1)
                sql += "`상장주식수` = '{}',".format(value2)
                sql += "`기업코드` = '{}',".format(corp_code)
                sql += "`문서번호` = '{}',".format(doc_no)
                sql = sql[:-1] + " WHERE `종목코드` = '{}';".format(code)
            else:
                sql = "INSERT INTO IPO2021 (`종목코드`, `이름`, `상장일`, `공모가`, `상장주식수`, `기업코드`, `문서번호`) VALUES ("
                sql += "'{}',".format(code)
                sql += "'{}',".format(name)
                sql += "'{}',".format(date.strftime('%Y-%m-%d'))
                sql += "'{}',".format(value1)
                sql += "'{}',".format(value2)
                sql += "'{}',".format(corp_code)
                sql += "'{}',".format(doc_no)
                sql = sql[:-1] + ");"
            result = cursor.execute(sql)
            print('({}) {}'.format(result, sql))
        except Exception:
            continue
    
    db_conn.commit()
    db_conn.close()

 

함수를 호출해보면, 일부기업(ex: 선진뷰티사이언스, 프레스티지바이오파마, 오로스테크놀로지 등)은 DART 기업코드는 정상적으로 조회되지만, OpenDART로부터 지분증권 문서 조회가 제대로 되지 않는 문제가 있는 것을 알 수 있다 (원인은 모르겠다... DART측 문제인 것 같긴 한데 ㅎㅎ)

MySQL [stock]> SELECT  * FROM IPO2021 ORDER BY `상장일`;
+----------+------------------------+------------+--------+------------+----------+----------+
| 종목코드 | 이름                   | 상장일     | 공모가 | 상장주식수 | 기업코드 | 문서번호 |
+----------+------------------------+------------+--------+------------+----------+----------+
| 236810   | 엔비티                 | 2021-01-21 |  19000 |    8487713 | 01051065 |          |
| 086710   | 선진뷰티사이언스       | 2021-01-27 |  11500 |    6115650 | 00132202 |          |
| 352700   | 씨앤투스성진           | 2021-01-28 |  32000 |    9995531 | 00875237 |          |
| 348030   | 모비릭스               | 2021-01-28 |  14000 |    9304000 | 01210190 |          |
| 163730   | 핑거                   | 2021-01-29 |  16000 |    9239014 | 01142075 |          |
| 248070   | 솔루엠                 | 2021-02-02 |  17000 |   50005551 | 01159233 |          |
| 321820   | 와이더플래닛           | 2021-02-03 |  16000 |    6897482 | 01116502 |          |
| 277810   | 레인보우로보틱스       | 2021-02-03 |  10000 |   16135214 | 01261644 |          |
| 262840   | 아이퀘스트             | 2021-02-05 |  11000 |    9860000 | 01146822 |          |
| 950210   | 프레스티지바이오파마   | 2021-02-05 |  32000 |   60096155 | 01510489 |          |
| 239890   | 피엔에이치테크         | 2021-02-16 |  18000 |    4516016 | 01112889 |          |
| 322310   | 오로스테크놀로지       | 2021-02-24 |  21000 |    9309542 | 01016479 |          |
| 189330   | 씨이랩                 | 2021-02-24 |  35000 |    3013642 | 00991298 |          |
| 340930   | 유일에너테크           | 2021-02-25 |  16000 |   10674744 | 01368637 |          |
| 338220   | 뷰노                   | 2021-02-26 |  21000 |   11168656 | 01344202 |          |
| 247660   | 나노씨엠에스           | 2021-03-09 |  20000 |    4208920 | 00660459 |          |
| 334970   | 프레스티지바이오로직스 | 2021-03-11 |  12400 |   47206389 | 01258428 |          |
| 356890   | 싸이버원               | 2021-03-11 |   9500 |    4640830 | 01293953 |          |
| 950220   | 네오이뮨텍             | 2021-03-16 |   7500 |   98417010 | 01511558 |          |
| 314930   | 바이오다인             | 2021-03-17 |  30000 |    6168195 | 01117413 |          |
| 302440   | SK바이오사이언스       | 2021-03-18 |  65000 |   76500000 | 01319899 |          |
| 347700   | 라이프시맨틱스         | 2021-03-23 |  12500 |   10119770 | 01336762 |          |
| 361390   | 제노코                 | 2021-03-24 |  36000 |    2442140 | 01014718 |          |
| 289220   | 자이언트스텝           | 2021-03-24 |  11000 |    9655765 | 01264438 |          |
| 333620   | 엔시스                 | 2021-04-01 |  19000 |   10556344 | 01310773 |          |
| 351330   | 이삭엔지니어링         | 2021-04-21 |  11500 |    8288520 | 01135640 |          |
| 059270   | 해성티피씨             | 2021-04-21 |  13000 |    4630000 | 00453442 |          |
| 294570   | 쿠콘                   | 2021-04-28 |  45000 |    8077071 | 01311055 |          |
| 361610   | SK아이이테크놀로지     | 2021-05-11 | 105000 |   71297592 | 01386916 |          |
| 357230   | 에이치피오             | 2021-05-14 |  22200 |   19938160 | 01068737 |          |
| 352480   | 씨앤씨인터내셔널       | 2021-05-17 |  47500 |    6675961 | 01226410 |          |
| 252990   | 샘씨엔에스             | 2021-05-20 |   6500 |   50153846 | 01199727 |          |
| 361670   | 삼영에스앤씨           | 2021-05-21 |  11000 |    5557158 | 00405506 |          |
| 363250   | 진시스템               | 2021-05-26 |  20000 |    6811080 | 01437858 |          |
| 276730   | 제주맥주               | 2021-05-26 |   3200 |   56485461 | 01230255 |          |
| 187660   | 에이디엠코리아         | 2021-06-03 |   3800 |   21836250 | 01409022 |          |
| 376190   | 엘비루셈               | 2021-06-11 |  14000 |   24600000 | 00527011 |          |
| 232680   | 라온테크               | 2021-06-17 |  18000 |    4886005 | 00911229 |          |
| 303530   | 이노뎁                 | 2021-06-18 |  18000 |    7302549 | 01264872 |          |
| 357580   | 아모센스               | 2021-06-25 |  12400 |   11200076 | 01012987 |          |
| 352910   | 오비고                 | 2021-07-13 |  14300 |   11410834 | 00494546 |          |
| 137310   | 에스디바이오센서       | 2021-07-16 |  52000 |  103271353 | 00854997 |          |
| 365270   | 큐라클                 | 2021-07-22 |  25000 |   13582968 | 01397772 |          |
| 377030   | 맥스트                 | 2021-07-27 |  15000 |    8533491 | 01409095 |          |
| 270660   | 에브리봇               | 2021-07-28 |  36700 |    6095671 | 01201590 |          |
| 323410   | 카카오뱅크             | 2021-08-06 |  39000 |  475100237 | 01133217 |          |
| 195940   | 에이치케이이노엔       | 2021-08-09 |  59000 |   28904499 | 01012066 |          |
| 259960   | 크래프톤               | 2021-08-10 | 498000 |   48951445 | 00760971 |          |
| 376980   | 원티드랩               | 2021-08-11 |  35000 |    4703068 | 01441611 |          |
| 367000   | 플래티어               | 2021-08-12 |  11000 |    8269707 | 01454341 |          |
| 058970   | 엠로                   | 2021-08-13 |  22600 |    5426060 | 00396925 |          |
| 372910   | 한컴라이프케어         | 2021-08-17 |  13700 |   27674406 | 00534598 |          |
| 315640   | 딥노이드               | 2021-08-17 |  42000 |    4290997 | 01344831 |          |
| 099390   | 브레인즈컴퍼니         | 2021-08-19 |  25000 |    4108796 | 00642897 |          |
| 089860   | 롯데렌탈               | 2021-08-19 |  59000 |   36634063 | 00545716 |          |
| 139990   | 아주스틸               | 2021-08-20 |  15100 |   26452189 | 00486705 |          |
| 308080   | 바이젠셀               | 2021-08-25 |  52700 |    9435680 | 01335790 |          |
| 377190   | 디앤디플랫폼리츠       | 2021-08-27 |   5000 |   64400000 | 01475609 |          |
| 271940   | 일진하이솔루스         | 2021-09-01 |  34300 |   36313190 | 00972503 |          |
| 203400   | 에이비온               | 2021-09-08 |  17000 |   15249795 | 00977377 |          |
| 273640   | 와이엠텍               | 2021-09-10 |  28000 |    5393000 | 01215618 |          |
| 395400   | SK리츠                 | 2021-09-14 |   5000 |  155020532 | 01535150 |          |
| 329180   | 현대중공업             | 2021-09-17 |  60000 |   88773116 | 01390344 |          |
+----------+------------------------+------------+--------+------------+----------+----------+
63 rows in set (0.002 sec)

어차피 지분증권 API로는 할 수 있는게 많지 않았으니, 공시문서검색 API로 빠르게 전환하는 게 좋을 것 같다 ㅠㅠ


다음 포스트에서는 공시원문에서 중요한 정보(ex: 기관 의무보유확약비율)를 추출해서 테이블에 추가해보도록 하자

 

반응형