들어가기에 앞서
이번 학기에 과제로 진행한 프로젝트(?)인데 그냥 묵혀두기에 아까워서 리마인드 겸 혹시나 귀인이 찾아와 조언을 해줄까 싶어서(dcf, 파이썬 둘 다 엉성합니다 ㅎ) 블로그에 업로드합니다.
프로젝트의 목적
네이버나 에프엔가이드 등에서 제공해주는 요약재무제표 + 투자자의 전망 => 회사가치 도출
도출된 회사가치를 참고자료 삼아 투자에 활용할 수 있게하는 것이 기본적인 목적입니다.
사실 타당성 있는 DCF를 구현하기 위해서는 해당 회사의 일반적인 서치 이상의 정보조사를 바탕으로 향후 어떻게 사업을 전개할 지, 비용은 어떻게 감당할 건지, 재무구조는 어떻게 개선할 건지 등등 할 것이 많습니다.
그래서 일반인 수준에서 혼자 DCF로 밸류에이션을하고 투자로 연결시키는건 상당한 무리일겁니다.
그렇다고 아무것도 안하는 것 보다는 어느 정도 기초정보들을 바탕으로 대략적으로 예측해두는 것이 투자하는데 참고가 될것이라 생각합니다.
또한 이는 자동화, 간소화 되어있기에 참고자료로써는 편리하고 괜찮을 것이라 생각해서 프로젝트를 진행했습니다.
프로젝트 구조
코드 구현의 순서는 다음과 같습니다.
1. 요약 재무제표 크롤링
- FCFF 도출에 필요한 항목들만 추려내서 데이터 프레임으로 정리하기
- 각 항목들의 추정치 도출하기 (ex 매출액의 연평균성장률, 평균 매출원가율)
2. 정제된 재무제표를 바탕으로 미래 FCFF 추정하기
- 1번에서 구한 추정치를 바탕으로 FCFF추정하기
- 사용자의 시나리오에 따라 FCFF 추정하기
3. WACC 값 도출을 위한 크롤링
- Rf, Rm, B 등등 크롤링해오기
4. 주가가치 산출하기
- 앞선 과정으로 얻어진 값들로 회사가치, 주가가치 계산하기
- wacc, 영구성장률 변화에 따른 민감도 차이 보여주기
5. 시각화
- 민감도별 시각화하기
오늘은 1번 코드를 공유하며 마무리해보겠습니다. 얘가 왜 이렇게 한건가 질문해주시거나 잘못된 것에 대해 지적해주시면 정말정말 큰 도움이 될 것 같습니다.
아직 메인함수가 없어서 바로 코드 복붙해보시면 아무것도 안나올 수 있습니다. 다른 시리즈로 코드를 완성하고 실행해보세요
결과화면
import pandas as pd
import requests
from bs4 import BeautifulSoup
import re
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
def load_FS():
print("자료의 소스는 FnGuide, 네이버금융, Yahoo Finance입니다.해당 사이트들에 필요한 정보가 누락되어있으면 valuation을 중단합니다.\n")
company = input("종목코드를 입력하세요: ")
source_url = '''https://comp.fnguide.com/SVO2/asp/SVD_Finance.asp?pGB=1&gicode={}&cID=&MenuYn=Y&ReportGB=&NewMenuID=103&stkGb=701'''.format(company)
tables = pd.read_html(source_url, encoding='utf-8')
IS = tables[0]
BS = tables[2]
CF = tables[4]
return IS, BS, CF, company
class step1:
def __init__(self, company, IS, BS, CF):
self._company = company
self._IS = IS
self._BS = BS
self._CF = CF
self._tax = 0
self._sales = []
self._cogs = []
self._sgna = []
self._op = []
self._capex_l = []
self._dep_l = []
self._amo_l = []
self._dNWC = [0]
self._FCFF = []
self._NWC = []
def Basic_IS(self):
IS_core = self._IS.loc[[0,1,3],['IFRS(연결)','2018/12','2019/12','2020/12']]
self._sales = list(IS_core.loc[0])
self._cogs = list(IS_core.loc[1])
self._sgna = list(IS_core.loc[3])
self._sales.pop(0)
self._cogs.pop(0)
self._sgna.pop(0)
for i in range(3):
if self._sales[i] > 3000:
self._tax = 0.275
elif 200 < self._sales[i] <= 3000:
self._tax = 0.242
elif 2 < self._sales[i] <= 200:
self._tax = 0.22
else :
self._tax = 0.11
self._op.append((self._sales[i]-self._cogs[i]-self._sgna[i])*(1-self._tax))
return self._tax
def Basic_CF(self): ##sales
url = 'https://comp.fnguide.com/SVO2/asp/SVD_Finance.asp?pGB=1&gicode={}&cID=&MenuYn=Y&ReportGB=&NewMenuID=103&stkGb=701'.format(self._company)
response = requests.get(url)
if response.status_code == 200:
html = response.text
soup = BeautifulSoup(html, 'html.parser')
capex = soup.select_one('#divCashY > table > tbody > tr:nth-child(108)')
dep = soup.select_one('#divCashY > table > tbody > tr:nth-child(9)')
amo = soup.select_one('#divCashY > table > tbody > tr:nth-child(10)')
else :
print(response.status_code)
capex = capex.get_text()
dep = dep.get_text()
amo = amo.get_text()
n = re.compile('\d+\,?\d+')
self._capex_l = n.findall(capex)
self._dep_l = n.findall(dep)
self._amo_l = n.findall(amo)
for i in range(4):
self._capex_l[i] = int(self._capex_l[i].replace(',',''))
self._dep_l[i] = int(self._dep_l[i].replace(',',''))
self._amo_l[i] = int(self._amo_l[i].replace(',',''))
self._capex_l.pop()
self._dep_l.pop()
self._amo_l.pop()
def Dnwc(self):
url = 'https://comp.fnguide.com/SVO2/asp/SVD_Finance.asp?pGB=1&gicode={}&cID=&MenuYn=Y&ReportGB=&NewMenuID=103&stkGb=701'.format(self._company)
response = requests.get(url)
if response.status_code == 200:
html = response.text
soup = BeautifulSoup(html, 'html.parser')
AR_r = soup.select_one('#divDaechaY > table > tbody > tr:nth-child(6)').get_text()
Inventory_r = soup.select_one('#divDaechaY > table > tbody > tr:nth-child(3)').get_text()
CA_etc_r = soup.select_one('#divDaechaY > table > tbody > tr:nth-child(11)').get_text()
AP_r = soup.select_one('#divDaechaY > table > tbody > tr:nth-child(35)').get_text()
CL_etc_r = soup.select_one('#divDaechaY > table > tbody > tr:nth-child(42)').get_text()
else :
print(response.status_code)
ft = re.compile('\d+\,?\d+')
AR = ft.findall(AR_r)
Inventory = ft.findall(Inventory_r)
CA_etc = ft.findall(CA_etc_r)
AP = ft.findall(AP_r)
CL_etc = ft.findall(CL_etc_r)
for i in range(len(AR)):
AR[i] = int(AR[i].replace(',',''))
Inventory[i] = int(Inventory[i].replace(',',''))
CA_etc[i] = int(CA_etc[i].replace(',',''))
AP[i] = int(AP[i].replace(',',''))
CL_etc[i] = int(CL_etc[i].replace(',',''))
self._NWC.append((AR[i]+Inventory[i]+CA_etc[i]) - (AP[i]+CL_etc[i]))
self._NWC.pop()
for i in range(len(self._op)):
if i < 2:
self._dNWC.append(self._NWC[i+1]-self._NWC[i])
def FCFF(self):
for i in range(3):
self._FCFF.append(self._op[i]+self._dep_l[i]+self._amo_l[i]-self._capex_l[i]-self._dNWC[i])
def mktable(self):
d_main_info = {'매출액': self._sales,'매출원가': self._cogs,'판관비': self._sgna
,'영업이익': self._op,'+유형자산상각비':self._dep_l,'+무형자산상각비': self._amo_l,
'-Capex': self._capex_l,'-dNwc': self._dNWC, 'FCFF': self._FCFF, '참고용NWC': self._NWC}
yr1 = (datetime.datetime.today() - relativedelta(years = 1)).strftime("%Y")
yr2 = (datetime.datetime.today() - relativedelta(years = 2)).strftime("%Y")
yr3 = (datetime.datetime.today() - relativedelta(years = 3)).strftime("%Y")
Main_info = pd.DataFrame(d_main_info,index=[yr3, yr2 ,yr1])
S_BS = Main_info.transpose()
print("최근 3개년 FCFF(단위: 억)")
display(S_BS)
print("첫 번째 열의 dNWC의 경우 해당 페이지에서 얻을 수 없으며, 미래 추정에 영향을 주지 않음\n ")
return S_BS
파이썬으로 DCF 구현하기 3: 주가 산출 (0) | 2021.06.29 |
---|---|
파이썬으로 DCF 구현하기 2: 추정 및 WACC값 구하기 (0) | 2021.06.29 |
기업 가치평가 : DCF평가법 실습3 - FCF완결 (0) | 2021.02.28 |
기업 가치평가 : DCF평가법 실습2 (0) | 2021.02.28 |
기업 가치평가 : DCF평가법 - 네이버 금융으로 가치평가해보기 (0) | 2021.02.28 |