투자자/알고리즘

파이썬으로 비상장법인 가치평가 퀀트전략 구현하기 feat. 상속세 및 증여세법

나그네_즈브즈 2021. 5. 20. 13:21

2021.05.13 - [투자자/투자전략] - 퀀트전략 : 비상장기업 가치평가 공식 적용하기 feat. 상속세 및 증여세법

 

상속세 및 증여세법이 제안하는 비상장법인의 가치평가 공식을 모든 주식 종목에 적용해보자. 계산 과정에 차트 정보는 전혀 필요하지 않고 대신 정리된 재무 데이터가 필요하다. 아직 전자공시시스템의 자료가 모두 정리되지 않았기 때문에, 우선은 검색 포털이 제공하는 금융 정보라든가 에프엔가이드의 도움에 의존해야만 한다.

 

import urllib.request
from bs4 import BeautifulSoup
import pandas as pd
import time

def value(c) :
    if c[0] != 'A': return 0
    url="http://comp.fnguide.com/SVO2/ASP/SVD_Finance.asp?pGB=1&gicode="+c+"&cID=&MenuYn=Y&ReportGB=D&NewMenuID=103&stkGb=701"
    print(url)
    ### 웹페이지에 접근할 URL을 완성했다.
    
    soup=BeautifulSoup(urllib.request.urlopen(url).read(), 'html.parser')
    cells = soup.find_all('tr', {'class': "rwf rowBold"})
    if len(cells) < 1 : return 0
    income = [cell for cell in cells if cell.find("th").find("div").string == "당기순이익"]
    ### URL의 소스코드를 열어서 soup에 저장한 다음, 표 중에서 '당기순이익'이 포함된 덩어리를 income에 담자
    
    tds = income[0].find_all('td', {'class': "r"})
    ### 연도별 행을 리스트 tds로 모아온다. income[0]은 연간, income[1]은 분기 재무제표에 대응된다.
    
    def bs4_to_int(x) :
        x = x.string
        if x == '\xa0' : return 0
        return int(x.replace(',','').replace(')','').replace('(','-'))
    ### bs4 클래스의 html 구조에서 내용을 추출해 숫자로 바꿔주는 함수를 만들었다.
        
    x = bs4_to_int(tds[1]) + bs4_to_int(tds[2])*2
    ### 최최최근 당기순이익과 최최근 당기순이익을 계산해준다.
    
    tds = income[1].find_all('td', {'class': "r"})
    x += (bs4_to_int(tds[0])+bs4_to_int(tds[1])+bs4_to_int(tds[2])+bs4_to_int(tds[3]))*3
    ### 가장 최근 사업연도의 당기순이익은 분기별 자료를 더해줘서 계산한다. 
    
    asset = [cell for cell in cells if cell.find("th").find("div").string == "자본"][1]
    y = bs4_to_int(asset.find_all('td', {'class': "r"})[-1])
    ### 자본총계를 얻어오는 과정이다. x를 구해낼 때와 비슷하다.
    
    return max(x+0.4*y, 0.8*y)

 

위에서 소개한 코드는 필요한 패키지를 임포트하고, 어떤 종목의 기업가치를 계산하는 함수를 디자인했다. 이 함수에 종목코드(A+숫자6자리)를 입력하면, 기업가치가 계산되어 반환된다. 함수는 세 층위의 구조로 이루어져 있다. ▲일단 에프엔가이드의 웹페이지에 접근할 URL을 만든다. ▲이 URL에 접근해 당기순이익을 참조해 순손익가치를 계산하고, ▲역시 같은 페이지 안에서 자본총계를 가지고 순자산가치를 계산한다. 상세한 설명은 주석을 참고하자.

 

def total_price(c) : 
    if c[0] != 'A' : return 0
    url = "http://comp.fnguide.com/SVO2/ASP/SVD_Main.asp?pGB=1&gicode=" + c + "&cID=&MenuYn=Y&ReportGB=&NewMenuID=101&stkGb=701"
    print(url)
    ### 불러올 페이지의 URL을 완성했다.
    
    soup = BeautifulSoup(urllib.request.urlopen(url).read(), 'html.parser')
    cells = soup.find_all('tr')
    dt = [cell for cell in cells if len(cell.find_all('dt')) != 0]
    if len(dt) < 1 : return 0
    ### URL을 열게 해서 모든 행(tr=table row)을 모은 뒤에, 하위 속성은 dt가 0인 것들을 덜어냈다.
    
    price = [cell for cell in dt if cell.find('dt').string == '시가총액(보통주)']
    ### dt 태그의 내용이 시가총액(보통주)인 덩어리를 찾아 저장한다.
    
    price_sum = price[0].find('td', {'class': 'r'}).string.replace(',', '')
    저장한 덩어리 안의 td태그(td=table data)에서 내용(문자열)을 가져와 쉼표를 지워준다.
    if price_sum == '\xa0' : return 0
    return int(price_sum)

시가총액을 발췌해 내는 함수다. 재무제표 페이지와는 태그트리가 다르지만, URL을 만들고 거기 접근해서 원하는 항목을 찾아 내용을 읽어오는 구조는 동일하다. 

 

value 함수에서 접근한 재무제표 탭의 페이지는 계정과목별로 연도별 혹은 분기별 숫자가 기록된 형태였다. 시가총액이 나타나 있는 snapshot 탭의 페이지에는 기업개요의 항목들이 간단히 표로 정리돼 있다. 태그 트리가 다를 수밖에 없다. 당기순이익처럼 상위~하위로 층을 이룬 태그 트리는 복잡해 보여도 접근하기엔 수월하다. 특정 클래스나 스타일을 공통적으로 지닌 덩어리들 사이에서 원하는 단어를 골라 찾아내면 되니까. 하지만 간단한 형태로 정리된 표는 죄다 tr 태그로 되어 있고 특별한 태그 속성이 부여되어 있지 않아서, 오히려 다루기 어렵다.

 

value 함수의 결과를 total_price에서 계산된 시가총액으로 나눈 결과가 클수록 주식은 저평가되어 있는 것으로 본다. 물론 절대적인 기준이 없기 때문에 ,투자에 참고하려면 여러 변수들을 종합적으로 고려해야 한다. 

 

에프엔가이드에서 시가총액을 확인하는 화면. 예시가 된 기업은 삼성전자.

 

 

반응형