이번에는 우량주 종목에 대해서 데이터 기반으로 자동예측하며, 종목에 대한 최적 선정을 통해 투자를 위한 기초 데이터로 활용하기 위한 프로그램을 개발해 보도록 하겠습니다. 우선 가장 간단한 방법에 대해서 구현하기로 하겠습니다. 아마 좀더 뒤로갈수록 많은 부분에 대한 로직이 들어가겠죠??
지난번 EMA 기반의 MACD를 통한 매매시점을 예측해 보았을때도, 모든 종목에... 그리고 모든 시점에 해당 예측이 정확이 맞는 것은 단정할 수 없었습니다. 비록 소정의 이익을 실현할 수 있었지만 말이죠. 오늘 알아볼 우량주에 대한 선정도 매우 일반화 된, 하지만 안정적으로 일반화 된 방법에 대해서... 수천개의 종목을 모두 가내수공업으로 분석이 불가능하기 때문에, 도움이 되는 부분에 대해서 개발을 하는 것 입니다.
결국, 이런 도구들도 결국 내가 투자를 하기 위한 기반 자료에 불과하다는 것은 유념해야 합니다. 그렇다면, 이렇게 거창하게 우량수 종목을 선정해보고자 하는데... 전체적인 단계는 아래와 같습니다.
- 우량주 종목 선정을 위한 기반 데이터 수집 방안 (전자공시시스템 활용) - 1개 종목기준
- 정리된 데이터 수집 방안에 대해서 코스피, 코스닥 전체 종목에 대한 데이터를 DBMS에 적재
- 우량주 종목 선정 로직을 적용하여 대상 종목 추출
- 추출된 우량주 종목에 대해서 저평가 종목 추출
- 추출된 종목에 대해서 실험실 진행
이렇게 총 5개 chapter로 진행해 보고자 합니다. 어쩌면 각 chapter는 여러개의 글로 구성될 수 있습니다. 그럼 한번 진행해 볼까요??
1. 전자공시시스템 (DART)
어떠한 기업에 대해서 가장 기본적이며, 중요한 정보를 얻기위해서는 별도의 기관에서 제공하는 정보들을 활용해야 합니다. 어쩌면 그 기관이라고 하는 곳이... 우리가 매일 접속하는 포털사이트가 될 수 있기도 하고, 아니면 그 외 다른 수단일 수 있습니다.
하지만 가장 정확한정보는 금융감독원에서 분기별로 모든기업에 대해서 정보를 제공하는 아래의 사이트를 통해서 얻을 수 있습니다.
기존에 다른 기업의 재무상태를 조사할 일이 있어서 처음 접하게 되었는데, DART에서 제공하는 사업보고서에 모든정보가 다 있었습니다. 제공하는 정보는 크게 아래의 목차로 구성되어 있습니다.
- 회사의 개요
- 사업의 내용
- 재무에 관한 사항
- 이사의 경영진단 및 분석의견
- 감사인의 감사의견 등 여러가지 내용
- [전문가의 확인]
언뜻 보기에도 많은 정보를 제공함을 알 수 있습니다. 그 중에서도 우량주 종목 선정을 위해서 가장 많이 확인할 부분은 바로 [재무에 관한 사항] 부분입니다. 여기에는 재무정보에 대한 내용이 모두 들어있기 때문에 이 정보들을 수집하여 프로그램을 구성해 보겠습니다.
그렇다면 수천개에 달하는 코스닥/코스피 종목에 대해서 어떻게 재무정보를 내 시스템으로 가져올 수 있을까요?? 여러가지 방법이 있을 수 있습니다. 해당 정보들은 web page로 구성되어 있기 때문에... web crawler로 긁어다가 내가 원하는 항목을 추출해서 DB에 저장하는 방법도 있고...
그럼 좀더 구체화를 한다면....
(1) 우선 코스닥/코스피 종목을 모두 받아와서 list화 한다.
(2) 개별 종목에 대해서 재무정보를 제공하는 DART web 주소를 추출하여 준비한다.
(3) 추출에 성공하면, loop로 종목별로 정보를 가져오고 원하는 항목을 뽑아내서 DB에 저장한다.
간단해 보이네요?? 그럼 일단 DART web 주소를 확인해 볼까요??
해당 정보는 제가 사랑하는 [삼성전자] 종목에 대해서 DART로 부터 분기보고서를 찾아서 캡쳐한 화면입니다. 보시면... 동일한 주소로 각 항목별 정보가 제공됨을 확인할 수 있습니다.
dartdart.fss.or.kr/dsaf001/main.do?rcpNo=20201116001248
이렇게 되면 문제가... 해당 주소로 접속하고 crawler를 통해서 크롤링을 하게되면 원하는 정보가 없게 됩니다. 따라서 다른 방법을 찾아야 합니다.
2. Open DART (전자공시 API 제공 시스템)
rawler를 통해서 크롤링을 하는 방법이 안된다고 좌절은 금물입니다. 곰곰이 생각해보면... 제가 기억이 나는 시점부터 해도 근 5년 이전부터 전자정부, 공공데이터와 같이 정부기관에서 제공이 가능한 정보에 대해서는 API를 통해서 제공하는 것들이 꽤 많습니다.
예를들어, 서울버스 시간표와 같은 어플들도 다 공공데이터를 통해서 받아와 서비스를 제공하는 것이고... 당장 최근에 코로나19 (COVID-19)때문에도 마스크 정보에 대해서 해당 공공데이터 API로 제공해 줬었습니다.
왠지 느낌적인 느낌으로 DART 홈페이지를 잘 살펴보니... 역시나 두둥!!!
위의 그림과 같이 오른쪽 하단에 [오픈다트] 라는 메뉴가 있습니다. 바로 클릭하여 들어가 보겠습니다.
보시다시피... 오픈API에 대한 소개부터 인증키 신청/관리, 개발가이드 등 공시정보를 활용하기 위한 상세한 정보와 수단을 제공하고 있었습니다. 기본사용법에 대해서는 아래의 링크를 통해서 확인해보겠습니다.
[OPEN DART 사용법]
2020/12/16 - [AyoProject/Ayotera-Trade] - Open DART (전자공시 API) 사용해보기
3. 공시정보 코드 수집 (1개종목)
내가 원하는 종목에 대해서 공시정보를 가져오기 위해서는 몇가지 단계를 거쳐야 합니다. 전체 종목을 모두 가져와서 한번에 DB화 하는 방법은 다음에 알아보기로 하고... 이번에는 내가 원하는 종목 1개에 대해서 공시정보를 정리해서 표출하는 방법을 알아보겠습니다.
한개 종목을 할 수 있다는 것은 결국 전체 종목이 가능하다는 말이겠죠?? 우선 위의 목적을 달성하기 위해서는 아래의 2가지를 보고 오시면 좋을 듯 합니다.
[공시정보 - 고유번호]
2020/12/18 - [Python] - Get 방식의 웹 서비스(Rest API) 호출하기 - Zip FILE (binary)편
[상장기업 재무정보 - 단일회사 전체 재무제표]
2020/12/17 - [Python] - Get 방식의 웹 서비스(Rest API) 호출하기 - json편
우선 이 2가지를 보고 오신다면 이제 기반이 갖추어 졌습니다.
그럼 두가지 글을 모두 확인해 보셨다는 가정하에... 저의 가장 favorite종목...!!! [삼성전자] 에 대한 전자공시시스템 내 코드를 확인해 보겠습니다.
[소스 코드]
# xml 호출하여 읽어오기
xmlTree = parse('c:\\corpCode\corpCode.xml')
root = xmlTree.getroot()
list = root.findall('list')
for i in range(len(list)):
if list[i].findtext('corp_name') == '삼성전자':
my_fav_code = list[i].findtext('corp_code')
print('대상종목 코드 : ', my_fav_code)
어차피 corp_name이 기업명 이기 때문에, [삼성전자]인 항목의 corp_code를 얻어내면 됩니다.
[결과]
대상종목 코드 : 00126380
4. 기간별 공시 데이터 수집 (1개종목)
얻어온 [삼성전자]에 대해서 년도별 1분기, 반기, 3분기, 년도 별 데이터를 가져오겠습니다. 제가 가져오고자 하는 데이터는 우선 영업이익과 영업활동 현금흐름... 이렇게 2가지 데이터 입니다. OPEN DART에서는 2015년 데이터부터 제공한다고 적혀있기 때문에, 해당 년도부터 쭉 긁어오겠습니다.
[소스 코드]
# 1개 종목에 대한 정보 가지고 오기
this_year = int(format(datetime.today().year))
reprt_code = ['11013', '11012', '11014', '11011']
select_info = ['']
for i in range(2015, this_year+1):
for j in reprt_code:
url = 'https://opendart.fss.or.kr/api/fnlttSinglAcntAll.json'
params = {'crtfc_key': '<my api key>',
'corp_code': my_fav_code, 'bsns_year': i, 'reprt_code': j, 'fs_div': 'CFS'}
res = requests.get(url, params)
if res.json()['status'] == '013':
print(i, j, "데이터 없음")
elif res.json()['status'] == '000':
for k in range(len(res.json()['list'])):
if res.json()['list'][k]['account_nm'] == '영업이익' or res.json()['list'][k]['account_nm'] == '영업이익(손실)':
print(i, j, '영업이익 3개월 : ', res.json()['list'][k]['thstrm_amount'])
print(i, j, '영업이익 누적 : ', res.json()['list'][k]['thstrm_add_amount'])
if res.json()['list'][k]['account_nm'] == '영업활동 현금흐름':
print(i, j, '영업활동 현금흐름 : ', res.json()['list'][k]['thstrm_amount'])
보고서 코드는 총 4가지가 존재하기 때문에, list형태로 미리 작성해 놓습니다. 그리고 현재 시점까지의 데이터를 가져와야 하기 때문에, 올해 정보를 this_year변수에 저장합니다.
소스 코드에 보이다시피, 년도별 loop와 보고서 코드별 loop를 이중으로 구성하여 진행하며, 각 loop마다 조건에 맞게 params를 작성하여 API를 호출하게 됩니다. 여기서 주의할 점은, API통신한 결과 json에서 status값을 통해서 세부정보가 있는지 판단이 가능하기 때문에...
status = 013일때 예외처리를 해줍니다. 그리고, 아닌경우 원하는 항목일 경우 출력을 해줍니다. 그렇게 하면 결과는 아래와 같이 추출이 됩니다.
[결과]
대상종목 코드 : 00126380
2015 11013 데이터 없음
2015 11012 데이터 없음
2015 11014 데이터 없음
2015 11011 영업이익 3개월 : 26413442000000
2015 11011 영업이익 누적 :
2015 11011 영업활동 현금흐름 : 40061761000000
2016 11013 영업이익 3개월 : 6675812000000
2016 11013 영업이익 누적 : 6675812000000
2016 11013 영업활동 현금흐름 : 8718290000000
2016 11012 영업이익 3개월 : 8143950000000
2016 11012 영업이익 누적 : 14819762000000
2016 11012 영업활동 현금흐름 : 22063135000000
2016 11014 영업이익 3개월 : 5200089000000
2016 11014 영업이익 누적 : 20019851000000
2016 11014 영업활동 현금흐름 : 36399732000000
2016 11011 영업이익 3개월 : 29240672000000
2016 11011 영업이익 누적 :
2016 11011 영업활동 현금흐름 : 47385644000000
2017 11013 영업이익 3개월 : 9898361000000
2017 11013 영업이익 누적 : 9898361000000
2017 11013 영업활동 현금흐름 : 10597271000000
2017 11012 영업이익 3개월 : 14066547000000
2017 11012 영업이익 누적 : 23964908000000
2017 11012 영업활동 현금흐름 : 23023433000000
2017 11014 영업이익 3개월 : 14533159000000
2017 11014 영업이익 누적 : 38498067000000
2017 11014 영업활동 현금흐름 : 40470495000000
2017 11011 영업이익 3개월 : 53645038000000
2017 11011 영업이익 누적 :
2017 11011 영업활동 현금흐름 : 62162041000000
2018 11013 영업이익 3개월 : 15642170000000
2018 11013 영업이익 누적 : 15642170000000
2018 11013 영업활동 현금흐름 : 15616352000000
2018 11012 영업이익 3개월 : 14869035000000
2018 11012 영업이익 누적 : 30511205000000
2018 11012 영업활동 현금흐름 : 29054126000000
2018 11014 영업이익 3개월 : 17574865000000
2018 11014 영업이익 누적 : 48086070000000
2018 11014 영업활동 현금흐름 : 44603783000000
2018 11011 영업이익 3개월 : 58886669000000
2018 11011 영업이익 누적 :
2018 11011 영업활동 현금흐름 : 67031863000000
2019 11013 영업이익 3개월 : 6233282000000
2019 11013 영업이익 누적 : 6233282000000
2019 11013 영업활동 현금흐름 : 5244311000000
2019 11012 영업이익 3개월 : 6597065000000
2019 11012 영업이익 누적 : 12830347000000
2019 11012 영업활동 현금흐름 : 11839176000000
2019 11014 영업이익 3개월 : 7777892000000
2019 11014 영업이익 누적 : 20608239000000
2019 11014 영업활동 현금흐름 : 25665775000000
2019 11011 영업이익 3개월 : 27768509000000
2019 11011 영업이익 누적 :
2019 11011 영업활동 현금흐름 : 45382915000000
2020 11013 영업이익 3개월 : 6447345000000
2020 11013 영업이익 누적 : 6447345000000
2020 11013 영업활동 현금흐름 : 11829879000000
2020 11012 영업이익 3개월 : 8146292000000
2020 11012 영업이익 누적 : 14593637000000
2020 11012 영업활동 현금흐름 : 26628032000000
2020 11014 영업이익 3개월 : 12353238000000
2020 11014 영업이익 누적 : 26946875000000
2020 11014 영업활동 현금흐름 : 40772425000000
2020 11011 데이터 없음
2020년 사업보고서는 년 결산이 끝나지 않았기 때문에 없는게 맞고, 나머지 데이터도 전자공시시스템의 내용과 일치합니다. 이렇게 정상적으로 작성된 결과를 가지고 이후에는 모든 종목에 대해서 가져오고 저장해 보도록 하겠습니다.
- Ayotera Lab -
댓글