본문 바로가기
Programming/Quant

[퀀트투자를 위한 툴 만들기 3] 대신증권API로 추출한 종목 정보와 Company Guide(Fn Guide)에서 추출한 재무 정보 합치기(Merging)

by 지표덕후 2021. 8. 4.

파이선과 엑셀 Xlwings 패키지를 활용해 퀀트투자를 위한 종목선정 툴을 만들었습니다.
http://mokeya.tistory.com/67?category=914399

 

이 작업은 크게 네 가지 과업으로 이루어지는데,
이 포스팅은 그 세 번째 단계에 대한 내용을 담고 있습니다.
첫 단계에서 대신증권API로 한국 증시에 상장된 종목들에 대한 다양한 지표들을 추출하고요,
두 번째 단계에서는 첫 단계에서 확보하지 못 한 재무정보들을 Fn Gudie 웹에서 긁어(크롤링)옵니다.
지금의 세 번째 단계는 이 두 데이터를 합치는(merging) 작업입니다.
이 머징작업에 키(key)로 사용되는 값은 여섯자리 숫자로 이루어진 종목코드입니다.

1. 대신증권API로 주식시장 전종목에 대한 정보 추출
2. Company Guide(Fn Gudie) 웹을 크롤링해 전종목에 대한 재무정보 추출
3. 1과 2의 결과물을 종목코드를 기준으로 합친 후 투자지표 필드 추가(현재 글)
4. 3의 결과물을 가지고 xlwings 패키지 활용하여 엑셀툴 제작

코딩의 결과


컬럼이 많고 컬럼값으로 들어가는 숫자도 크다보니 표가 옆으로 너무 길어지네요.
본 코딩의 결과로 추출되는 테이블은 아래 첨부파일 참고해주세요.

mergedTable.xlsx
0.66MB


작업환경


1. 파이썬3.7(32bit)
2. 파이썬IDE는 JetBrain의 Pycharm


코드


대신증권 API를 통해 추출한 정보가 담긴 csv와
Fn Gudie 웹에서 크롤링한 정보가 담긴 csv를 불러옵니다.
불러온 두 테이블 모두 여섯자리 숫자로 이루어진 종목코드 컬럼(각 테이블의 첫 번째 컬럼)을 가지고 있습니다.
이 컬럼을 key값으로 하여  두 테이블을 합쳐줍니다.
합쳐진 테이블은 dfMerge라는 변수명으로 지정해주었습니다.

import csv
import pandas as pd
from pandas import DataFrame

# 과정1(대신증권API 활용)의 결과로 생성된 csv 읽기
dfMarketEye = pd.read_csv("subMarketEye.csv", sep=",", encoding='cp949') # 한글이 깨지지 않도록 인코딩 방식 'cp949'

# 과정2(FnCompany 크롤링)의 결과로 생성된 csv 읽기
dfFNGuide = pd.read_csv("FNGuide.csv", sep=",", encoding='cp949') # 한글이 깨지지 않도록 인코딩 방식 'cp949'

# 두 데이터를 종목코드를 key로 merging
dfMerge = pd.merge(dfMarketEye.drop(dfMarketEye.columns[[0]], axis='columns')
                   , dfFNGuide.drop(dfFNGuide.columns[[0]], axis='columns'), how = "left", on = "종목코드")

 

 

 

 

dfMerge를 그대로 csv로 저장할 수도 있지만
그 전에 테이블에 담긴 숫자들을 가지고 투자 의사결정을 하는 데 도움이 될 만한
몇 가지 지표들을 계산해 추가하였습니다.

PER, PBR 등의 소위 가치 지표와
ROS, ROE 등 수익성 지표, 부채비율 등의 안정성 지표, 그리고 시총순위입니다.
(이것 외에도 사람들이 투자에 참고하는 지표들이 몇 있는데, 
이 지표들을 계산하는 산식에 대해서도 별도로 포스팅할 예정입니다)

# 종목의 가치를 판단하는 데 필요한 투자지표 계산하여 컬럼 추가
## 가치 지표
dfMerge["PER"] = round((dfMerge["시가총액"]/dfMerge["당기순이익"]), 2)
dfMerge["PBR"] = round((dfMerge["시가총액"]/dfMerge["자본(순자산)"]), 2)
dfMerge["PSR"] = round((dfMerge["시가총액"]/dfMerge["매출액"]), 2)
dfMerge["PCR"] = round((dfMerge["시가총액"]/dfMerge["영업현금흐름"]), 2)

## 수익성 지표
dfMerge["ROS"] = round((dfMerge["당기순이익"]/dfMerge["매출액"]), 4) * 100
dfMerge["ROE"] = round((dfMerge["당기순이익"]/dfMerge["자본(순자산)"]), 4) * 100
dfMerge["ROA"] = round((dfMerge["당기순이익"]/dfMerge["자산"]), 4) * 100
dfMerge["EBIT"] = dfMerge["영업이익"]
dfMerge["GP/A"] = round((dfMerge["매출총이익"]/dfMerge["자산"]), 2)

## 안정성 지표
dfMerge["유동비율"] = round((dfMerge["유동자산"]/dfMerge["유동부채"]), 2)
dfMerge["부채비율"] = round((dfMerge["부채"]/dfMerge["자본(순자산)"]), 2)

## 시총순위
dfMerge["시총순위"] = dfMerge.groupby(["종목구분"])['시가총액'].rank(method = "min", ascending=False)

 

 

 

저는 이 시총순위를 가지고 시총 상위 10% 종목, 혹은 하위 10% 종목 등
종목 추출 필터링을 걸 때 활용할 예정입니다.
따라서 제가 최종적으로 필요한 정보는 특정 종목의 '시총순위'가 아니라
특정 종목이, 자신이 속한 풀(pool) 내에서 시총규모 기준으로 어디에 위치해 있는지에 대한 정보입니다.
이 목적을 위해 '시총백분위'라는 컬럼을 추가하였고요,
풀(pool)을 정의하기 위해 대신증권API에서 정의해둔 '종목구분'을 사용했습니다.
아래 코드 스니펫에 주석을 달아두었으니 참고하세요.

# 해당 종목이 속해 있는 종목구분에 총 몇 개 종목이 있는지를 알려주는 "종목수" 컬럼 추가
dfStocks = dfMerge.groupby(["종목구분"]).size()
dfStocks = pd.DataFrame(dfStocks)
dfStocks.columns = ["종목수"]
dfMerge = pd.merge(dfMerge, dfStocks, how = "left", on = "종목구분")

"""
종목구분(대신API에서 제공하는 종목구분)별 상장종목 수
(1은 주권을 의미. 투자회사, 리츠, ETF 등을 의미하는 0~17까지의 구분 코드가 존재)
  [helpstring("구분없음")]   CPC_KSE_SECTION_KIND_NULL= 0,
  [helpstring("주권")]   CPC_KSE_SECTION_KIND_ST   = 1,
  [helpstring("투자회사")]   CPC_KSE_SECTION_KIND_MF    = 2,
  [helpstring("부동산투자회사"]   CPC_KSE_SECTION_KIND_RT    = 3,
  [helpstring("선박투자회사")]   CPC_KSE_SECTION_KIND_SC    = 4,
  [helpstring("사회간접자본투융자회사")]CPC_KSE_SECTION_KIND_IF = 5,
  [helpstring("주식예탁증서")]   CPC_KSE_SECTION_KIND_DR    = 6,
  [helpstring("신수인수권증권")]   CPC_KSE_SECTION_KIND_SW    = 7,
  [helpstring("신주인수권증서")]   CPC_KSE_SECTION_KIND_SR    = 8,
  [helpstring("주식워런트증권")]   CPC_KSE_SECTION_KIND_ELW = 9,
  [helpstring("상장지수펀드(ETF)")]CPC_KSE_SECTION_KIND_ETF = 10,
  [helpstring("수익증권")]    CPC_KSE_SECTION_KIND_BC    = 11,
  [helpstring("해외ETF")]      CPC_KSE_SECTION_KIND_FETF   = 12,
  [helpstring("외국주권")]    CPC_KSE_SECTION_KIND_FOREIGN = 13,
  [helpstring("선물")]      CPC_KSE_SECTION_KIND_FU    = 14,
  [helpstring("옵션")]      CPC_KSE_SECTION_KIND_OP    = 15,    
  [helpstring("KONEX")]      CPC_KSE_SECTION_KIND_KN    = 16,
  [helpstring("ETN")]      CPC_KSE_SECTION_KIND_ETN     = 17
"""

# 시총백분위수, 소수 셋째 자리에서 반올림
dfMerge["시총백분위"] = round((dfMerge["시총순위"]/dfMerge["종목수"]), 2)

 

 

 

합쳐진 테이블에 필요한 정보를 추가하는 작업까지 끝났습니다.
이제 컬럼의 순서를 재배치한 후 csv로 출력하는 작업만 남았습니다.
저는 아래와 같은 순서로 재배치했지만, 여러분이 원하시는 순서대로 재배치하셔도 됩니다.

print(dfMerge.columns)

# 데이터프레임(DF)의 컬럼 재배치
dfMergedFinal = dfMerge.loc[:, ["종목구분", "종목코드", "종목명", "상장주식수", "현재가", "시가총액", "시총백분위", "외국인보유비율(%)", "52주최고가", "52주최저가", "배당수익률(%)"
                        , "유동비율", "부채비율"
                        , "매출액", "영업이익", "당기순이익", "자산", "자본(순자산)", "영업현금흐름"
                        , "GP/A", "EBIT", "ROS", "ROE", "ROA"
                        , "매출증가율(%)", "순이익증가율(%)", "분기년월", "분기매출액증가율(%)", "분기순이익증가율(%)"
                        , "PER", "PBR", "PSR", "PCR"]]

# csv로 출력
dfMergedFinal.to_csv('mergedTable.csv', encoding="cp949")

 

 

 

의사결정에 필요한 지표를 최대한 많이 확보하기 위해
에프앤가이드의 재무정보를 크롤링했지만,
투자지표에 대한 공부를 해보니 증권사API가 제공하는 정보만으로도 지표를 계산해낼 수 있더군요.
다음 포스팅에서는 그 이야기를 좀 해볼까 합니다.
결론적으로 FnGuide에서 재무정보를 추출하여 테이블을 머징하는 작업은 이후에 생략하게 되었습니다.

댓글