본문 바로가기
AyoProject/AT 매매 실험실

OPEN DART API를 통한 재무제표 활용 시 필드 에러

by 청양호박이 2021. 1. 4.

안녕하세요. 오늘은 OPEN DART에서 제공하는 API를 사용해서 상장기업 재무정보를 가져올 때, 발생가능성이 있는 문제에 대해서 알아보고자 합니다. 재무제표에는 수많은 데이터 항목이 있고, 수많은 정보들이 있습니다. 하지만 이 정보들을 API를 통해서 가져왔을때... 100% 성공율로 가져오지 않는 문제가 있습니다.

 

예를들어서... Json( )으로 받아온 데이터를 보자면...

[{'rcept_no': '20160330003887', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '00525882', 'sj_div': 'BS',
  'sj_nm': '재무상태표', 'account_id': 'ifrs_CurrentAssets', 'account_nm': '유동자산', 'account_detail': '-',
  'thstrm_nm': '제 14 기', 'thstrm_amount': '49788196944', 'frmtrm_nm': '제 13 기', 'frmtrm_amount': '40164423775',
  'bfefrmtrm_nm': '제 12 기', 'bfefrmtrm_amount': '52650296355', 'ord': '1'},
 {'rcept_no': '20160330003887', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '00525882', 'sj_div': 'BS',
  'sj_nm': '재무상태표', 'account_id': 'ifrs_CashAndCashEquivalents', 'account_nm': '현금및현금성자산', 'account_detail': '-',
  'thstrm_nm': '제 14 기', 'thstrm_amount': '18278538023', 'frmtrm_nm': '제 13 기', 'frmtrm_amount': '12969699256',
  'bfefrmtrm_nm': '제 12 기', 'bfefrmtrm_amount': '18731384806', 'ord': '2'},
 {'rcept_no': '20160330003887', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '00525882', 'sj_div': 'BS',
  'sj_nm': '재무상태표', 'account_id': '-표준계정코드 미사용-', 'account_nm': '매출채권', 'account_detail': '-', 'thstrm_nm': '제 14 기',
  'thstrm_amount': '20508386587', 'frmtrm_nm': '제 13 기', 'frmtrm_amount': '21410916124', 'bfefrmtrm_nm': '제 12 기',
  'bfefrmtrm_amount': '21223358739', 'ord': '13'},

이러한 형태로 구성이 되어 있습니다. 여기서 account_id와 account_name이 재무제표에서 내가 찾고자 하는 항목에 대한 이름이고, thstrm_amount가 원하는 항목에 해당하는 값 입니다. 따라서 우리는 해당 id 혹은 name을 Json( )에서 찾아서 값을 가져오게 됩니다.

 

하지만 이 account_id와 account_name이 모든 종목에 대해서 동일한 값으로 들어오는 것이 아닙니다. 실제로 확인해 보니 그렇네요... 따라서 여러가지 조치를 할 필요성이 있습니다. 단적으로 예를들어 보자면...

 

[매출액]

 

매출액은 연결포괄손익계산서에서 가장 상단에 위치하는 값 입니다. 해당 값은 종목별로 다른 형태를 띄는 것을 확인할 수 있습니다. 몇가지를 찾아보면...

('005930', '0', '삼성전자', '005930', '삼성전자', '00126380') 

{'rcept_no': '20200330003851', 'reprt_code': '11011', 'bsns_year': '2019', 'corp_code': '00126380', 'sj_div': 'IS',
  'sj_nm': '손익계산서', 'account_id': 'ifrs-full_Revenue', 'account_nm': '수익(매출액)', 'account_detail': '-',
  'thstrm_nm': '제 51 기', 'thstrm_amount': '230400881000000', 'thstrm_add_amount': '', 'frmtrm_nm': '제 50 기',
  'frmtrm_amount': '243771415000000', 'bfefrmtrm_nm': '제 49 기', 'bfefrmtrm_amount': '239575376000000', 'ord': '0'},

  ==============================================================

('096240', '10', '청담러닝', '096240', '청담러닝', '00525882')

 {'rcept_no': '20160330003887', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '00525882', 'sj_div': 'CIS',
  'sj_nm': '포괄손익계산서', 'account_id': '-표준계정코드 미사용-', 'account_nm': '영업수익', 'account_detail': '-', 'thstrm_nm': '제 14 기',
  'thstrm_amount': '135508996479', 'thstrm_add_amount': '', 'frmtrm_nm': '제 13 기', 'frmtrm_amount': '130461469777',
  'bfefrmtrm_nm': '제 12 기', 'bfefrmtrm_amount': '125262570783', 'ord': '1'},
  
  ==============================================================
  
('039340', '10', '한국경제TV', '039340', '한국경제TV', '00263371')
  
 {'rcept_no': '20160330004534', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '00263371', 'sj_div': 'CIS',
  'sj_nm': '포괄손익계산서', 'account_id': 'ifrs_GrossProfit', 'account_nm': '영업수익', 'account_detail': '-',
  'thstrm_nm': '제 17 기', 'thstrm_amount': '72163979576', 'thstrm_add_amount': '', 'frmtrm_nm': '제 16 기',
  'frmtrm_amount': '58361506347', 'bfefrmtrm_nm': '제 15 기', 'bfefrmtrm_amount': '55454164771', 'ord': '16'},
  
    ==============================================================
    
(한국전자인증, 00361169)
    
 {'rcept_no': '20160330002816', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '00361169', 'sj_div': 'CIS',
  'sj_nm': '포괄손익계산서', 'account_id': '-표준계정코드 미사용-', 'account_nm': 'Ⅰ. 영업수익', 'account_detail': '-',
  'thstrm_nm': '제 17 기', 'thstrm_amount': '25223248375', 'thstrm_add_amount': '', 'frmtrm_nm': '제 16 기',
  'frmtrm_amount': '24409197410', 'bfefrmtrm_nm': '제 15 기', 'bfefrmtrm_amount': '23372236705', 'ord': '8'},

account_id가 "-표준계정코드 미사용-" 이라는 애들도 있고... 정상적인 "ifrs-full_Revenue" 이라는 애들도 있습니다. 한글명도 당연히 다름을 확인 할 수 있습니다. 이 외에도 참으로 많은 항목이 서로 다릅니다. 

 

[영업활동 현금흐름]

{'rcept_no': '20160520000534', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '01010110', 'sj_div': 'CF',
  'sj_nm': '현금흐름표', 'account_id': 'ifrs_CashFlowsFromUsedInOperatingActivities', 'account_nm': '영업활동현금흐름',
  'account_detail': '-', 'thstrm_nm': '제 4 기', 'thstrm_amount': '', 'frmtrm_nm': '제 3 기', 'frmtrm_amount': '',
  'bfefrmtrm_nm': '제 2 기', 'bfefrmtrm_amount': '11621091856', 'ord': '0'},
  
{'rcept_no': '20160330004084', 'reprt_code': '11011', 'bsns_year': '2015', 'corp_code': '00872984', 'sj_div': 'CF',
  'sj_nm': '현금흐름표', 'account_id': '-표준계정코드 미사용-', 'account_nm': '영업활동으로부터의 순현금유입', 'account_detail': '-',
  'thstrm_nm': '제 5 기', 'thstrm_amount': '733913332899', 'frmtrm_nm': '제 4 기', 'frmtrm_amount': '700957666771',
  'bfefrmtrm_nm': '제 3 기', 'bfefrmtrm_amount': '802697687700', 'ord': '21'},

영업활동 현금흐름에 대해서도 모든 종목이 종일한 이름으로 데이터를 제공하는 것이 아닙니다. 다양한 용어로 자신만의 동일한 데이터를 표현하고 있습니다. 하지만 개발을 수행하는 입장에서는 다양성은 결국 통일적인 데이터를 표출하는데 있어서 코드의 복잡성을 야기하게 됩니다.

 

이와 같이, 어떤 종목이 어떤 필드명에 어떤값이 들어가 있는지는 통일성있게 작업이 불가능 할 수 있습니다. 따라서... 전체 종목에 대해서 DB에 저장해 보고, 값이 0으로 나오는 종목이 있을경우 확인을 해보면서 자신의 코드를 구체화 해야 합니다. 

 

누구는 account_id가 있으니, 그 코드를 가지고 나올 수 있는 몇가지 가지수를 적용해 놓으면 되는게 아닐까?? 라고 하겠지만... 치명적으로 해당 id가 "-표준계정코드 미사용-" 이라고 나올 수 도 있기 때문에 그 방법도 올바르게 모든 종목정보를 저장할 수 있는 방법은 아닙니다.

 

 

사용한 해결책


그래서 저는 다소 무식해보이지만 그래도 적절할 수 있어 보이는 방법으로 진행해 보았습니다. 우선 각 종목은 자신의 재무제표 항목을 자주 바꾸지 않는다는 전체하에, 해당 정보를 저장하는 테이블 내 "0" 정보를 가지고 있는 모든 종목을 뒤져서 예외사항을 찾아서 코드에 적용하는 방법으로 개발하였습니다. 그래서 탄생한 코드가 아래와 같습니다.

 

[0 데이터를 찾는 쿼리]

SELECT * FROM fin_stat_info WHERE revenue='0';

하아... 그렇게 많이 찾아냈는데도 다수의 데이터가 존재하네요....ㅠㅠ

 

[모든 항목 정상 반영 코드]

# account_id를 통한 값 추출
if res.json()['list'][k]['account_id'] == 'ifrs_Revenue' or res.json()['list'][k]['account_id'] == 'ifrs-full_Revenue' or res.json()['list'][k]['account_id'] == 'ifrs_RevenueFromSaleOfGoods':
revenue = res.json()['list'][k]['thstrm_amount']
revenue_add = res.json()['list'][k]['thstrm_add_amount']
if res.json()['list'][k]['account_id'] == 'dart_OperatingIncomeLoss':
operating_income = res.json()['list'][k]['thstrm_amount']
operating_income_add = res.json()['list'][k]['thstrm_add_amount']
if res.json()['list'][k]['account_id'] == 'ifrs-full_CashFlowsFromUsedInOperatingActivities' or res.json()['list'][k]['account_id'] == 'ifrs_CashFlowsFromUsedInOperatingActivities':
operating_cashflow = res.json()['list'][k]['thstrm_amount']
if res.json()['list'][k]['account_id'] == 'ifrs-full_CashFlowsFromUsedInInvestingActivities' or res.json()['list'][k]['account_id'] == 'ifrs_CashFlowsFromUsedInInvestingActivities':
investing_cashflow = res.json()['list'][k]['thstrm_amount']
if res.json()['list'][k]['account_id'] == 'ifrs-full_CashFlowsFromUsedInFinancingActivities' or res.json()['list'][k]['account_id'] == 'ifrs_CashFlowsFromUsedInFinancingActivities':
financing_cashflow = res.json()['list'][k]['thstrm_amount']

# account_nm을 통한 값 추출 (기타 예외사항 존재)
if res.json()['list'][k]['account_nm'] == '수익(매출액)' or res.json()['list'][k]['account_nm'] == '매출' or \
res.json()['list'][k]['account_nm'] == '매출액' or res.json()['list'][k]['account_nm'] == '영업수익' or res.json()['list'][k]['account_nm'] == 'Ⅰ. 영업수익':
revenue = res.json()['list'][k]['thstrm_amount']
revenue_add = res.json()['list'][k]['thstrm_add_amount']
if res.json()['list'][k]['account_nm'] == '영업이익' or res.json()['list'][k]['account_nm'] == '영업이익(손실)' or \
res.json()['list'][k]['account_nm'] == '영업 이익' or res.json()['list'][k]['account_nm'] == 'Ⅲ. 영업이익':
operating_income = res.json()['list'][k]['thstrm_amount']
operating_income_add = res.json()['list'][k]['thstrm_add_amount']
if res.json()['list'][k]['account_nm'] == '영업활동 현금흐름' or res.json()['list'][k][
'account_nm'] == '영업활동현금흐름' or res.json()['list'][k]['account_nm'] == '영업활동으로 인한 현금흐름' or \
res.json()['list'][k]['account_nm'] == '영업활동으로인한현금흐름' or res.json()['list'][k][
'account_nm'] == '영업활동으로 인한 순현금흐름' or res.json()['list'][k]['account_nm'] == '영업활동순현금흐름' or res.json()['list'][k]['account_nm'] == '영업활동으로부터의 순현금유입' or res.json()['list'][k]['account_nm'] == 'Ⅰ.영업활동 현금흐름':
operating_cashflow = res.json()['list'][k]['thstrm_amount']
if res.json()['list'][k]['account_nm'] == '투자활동 현금흐름' or res.json()['list'][k][
'account_nm'] == '투자활동현금흐름' or res.json()['list'][k]['account_nm'] == '투자활동으로 인한 현금흐름' or \
res.json()['list'][k]['account_nm'] == '투자활동으로인한현금흐름' or res.json()['list'][k][
'account_nm'] == '투자활동으로 인한 순현금흐름' or res.json()['list'][k]['account_nm'] == '투자활동순현금흐름' or res.json()['list'][k]['account_nm'] == '투자활동으로부터의 순현금유입' or res.json()['list'][k]['account_nm'] == 'Ⅱ.투자활동 현금흐름':
investing_cashflow = res.json()['list'][k]['thstrm_amount']
if res.json()['list'][k]['account_nm'] == '재무활동 현금흐름' or res.json()['list'][k][
'account_nm'] == '재무활동현금흐름' or res.json()['list'][k]['account_nm'] == '재무활동으로 인한 현금흐름' or \
res.json()['list'][k]['account_nm'] == '재무활동으로인한현금흐름' or res.json()['list'][k][
'account_nm'] == '재무활동으로 인한 순현금흐름' or res.json()['list'][k]['account_nm'] == '재무활동순현금흐름' or res.json()['list'][k]['account_nm'] == '재무활동으로부터의 순현금유입' or res.json()['list'][k]['account_nm'] == 'Ⅲ.재무활동 현금흐름':
financing_cashflow = res.json()['list'][k]['thstrm_amount']

주기적으로 데이터 미취합이 발생하면 반영을 해서 데이터를 현실화 해야 할 듯 합니다.

 

- Ayotera Lab -

댓글