이 알고리즘은 "채권 쟁이 서준식의 다시 쓰는 주식 투자 교과서"에 나온 가치투자 기법을 바탕으로 개발하였습니다.
기업의 재무제표 항목 중 주당순이익(EPS), 주당순자산(BPS), 자기 자본 이익률(ROE), 주당 순이익률(PER) 그리고 주가를 활용하여 5년 뒤의 기대 BPS를 구한 뒤 연간 기대 수익률 15%로 계산하여 현재 기대 주가를 산출해내는 기법입니다.
이번 포스팅에서는 파이썬 코드와 결과값을 보여주며 그 방식에 대해 소개하겠습니다.
1. 크롤링
def get_html(jcode): #재무재표 크롤링
url = 'http://comp.fnguide.com/SVO2/ASP/SVD_main.asp?pGB=1&gicode=A%s' % jcode
html = urlopen(url).read()
html = bs4.BeautifulSoup(html,'lxml')
table = html.find('div',{'id':'highlight_D_Y'})
crt_data = html.find('div',{'id':'corp_group2'}).find_all('dl')
crt_PER = crt_data[0].find_all('dd')[1].text #PER
crt_around_PER = crt_data[4].find_all('dd')[1].text #업종 PER
crt_PBR = float(crt_data[6].find_all('dd')[1].text)
crt_price = int(html.find('span',{'id' : 'svdMainChartTxt11'}).text.replace(',',''))
name = html.find('h1',{'id':'giName'}).text
date_col = table.find_all('tr')[1].find_all('th')[:5]
date_col=list(i.text for i in date_col) #dataframe의 column
j = table.find_all('td',{'class','r'})
tr = table.find_all('tr')[18:]
df = pd.DataFrame()
for i in tr: #테이블 항목 가져오기
category = i.find('span',{'class':'txt_acd'})
if(category==None):
category=i.find('th')
category = category.text.strip()
value_list = []
j = i.find_all('td',{'class','r'})[:5]
for value in j:
temp = value.text.replace(',','').strip()
try:
temp = float(temp)
value_list.append(temp)
except:
value_list.append(0)
data = {category:value_list}
data = pd.DataFrame(data)
df = pd.concat([df,data],axis=1)
df.index=date_col
return df
이 메서드의 실행 결과는 다음의 데이터프레임입니다.
Fnguide에서 제공하는 재무제표를 크롤링하여 최근 5년간의 재무제표로 재구성하였습니다.
2. 기대 BPS 계산
def df_extension(df): #데이터테이블 확장(2025년까지)
for i in range(1,7):
ROE_mean = df['ROE'].mean()
new_eps = df['BPS'][-1]*ROE_mean/100
new_bps = new_eps + df['BPS'][-1]
PER = df['PER'][-1]
PBR = df['PBR'][-1]
stock_ret = df['배당수익률'][-1]
df.loc['202'+str(i)+'/12'] = [df['ROA'].mean(),ROE_mean,new_eps,new_bps, np.nan, PER, PBR, np.nan, stock_ret]
return df
이 메서드의 실행 결과는 다음과 같습니다.
반복문을 통해 5년 뒤의 기대 BPS를 계산하였습니다.
계산식
Expected BPS = BPS + BPS*(ROE/100)
3. 현재 가치로 환산
def report(self):
self.new_price = int(self.df['BPS'][-1]/((1.15)**5))
self.all_datas = pd.DataFrame(index = ['종목이름','종목코드','평균ROE','PER','업종PER','현재가치','주가','평가'],
data = [self.name,self.jcode,self.ROE_mean,self.crt_PER,self.crt_around_PER,self.new_price,self.crt_price,\
'적합' if self.new_price>=1.5*self.crt_price else '부적합']).T
return self.all_datas
이 메서드의 실행 결과는 다음과 같습니다.
현재 기대 주가는 5년 뒤 기대 BPS를 기대수익률 15%로 계산하여 현재 가치를 구합니다. 현재 가치가 주가의 1.5배보다 크다면 적합, 아니면 부적합으로 평가됩니다. 삼성전자의 경우 현재 가치가 주가를 한참 밑돌고 있고 PER로 업종 PER보다 높게 나와 부적합으로 평가되었습니다.
4. 고배당주와 시가총액 상위 순으로 기업 조회
def get_excel_high_dividend(self): #고배당주 엑셀 read
load_wb = load_workbook("stock_div.xlsx", data_only=True)
load_ws = load_wb['Sheet1']
row_num = 0
jcode=[]
for row in load_ws.rows:
row_num+=1
if row_num==1:
continue
elif row_num==100: #100개만
break
jcode.append(row[2].value)
return jcode
def get_excel_siga_top(self): #시가총액 엑셀 read
load_wb = open('siga.csv','r',encoding='utf-8')
load_ws = csv.reader(load_wb)
row_num = 0
jcode=[]
for row in load_ws:
row_num+=1
if row_num==1:
continue
jcode.append(row[1])
return jcode
이 두 메서드는 시가총액 순서대로 기업코드가 적혀있는 엑셀과 고배당주 순서대로 기업코드가 적혀있는 엑셀을 읽어와 기업코드를 리턴하는 메서드입니다. 따라서 반복문을 시행하면서 위 알고리즘을 적용해 데이터 프레임을 산출해내면 다음과 같습니다.
이 프로그램의 실행결과 적합 판정을 받은 기업이 소수입니다. 현재 그만큼 자산시장이 고평가 되어있다고 볼 수도 있을 것 같습니다.
'개발 프로젝트' 카테고리의 다른 글
[SwitfUI] 깨워줘요 앱 개발 (0) | 2023.03.07 |
---|---|
파이썬으로 업무 프로그램 개발하기 (0) | 2022.11.11 |
Portfolio Optimization & Text Mining (2) | 2021.06.15 |
[PHP] 경희대학교 중앙동아리 평가 사이트 개발 (0) | 2021.06.13 |
[Python] 포트폴리오 최적화 전략 구현과 Plotly로 시각화하기 (0) | 2021.06.13 |