1. 목표
미국 주식을 대상으로 벤저민 그레이엄의 투자 전략에 따라 종목 선정을 하는 것이다.
투자 전략은 <현명한 투자자>라는 책의 내용을 토대로 설정했으며, 투자 성향에 따라 방어 투자자, 공격 투자자로 분리하여 투자 전략을 설정해 종목을 선정했다.
투자 전략에 대한 더 자세한 분석은 아래의 글을 참조하면 된다.
2. 방어 투자자를 위한 7가지 투자 전략
(1) 기업의 적정 규모:
- 연 매출액(revenue)이 3억 5천 달러 이상인 종목
(책에서는 연 매출액 1억 달러 이상을 기준으로 삼았으나, 인플레이션을 고려하여 3억 5천 달러를 기준으로 삼았다. )
(2) 재무 구조의 건전성:
- 유동비율(current ratio)이 2 이상인 종목
- 장기부채가 운전자본 이하인 종목
(장기부채 = 총 부채(total liabilities) - 총 유동부채(total current liabilities),
운전자본 = 총 유동 자산(total current assets) - 총 유동 부채(total current liabilities))
(3) 이익의 안정성:
- 지난 10년 동안 해마다 이익을 낸 종목, 즉 10년간 당기순이익(net income)이 마이너스가 없는 종목
(4) 배당 실적:
- 지난 20년간 빠짐없이 배당을 지급한 종목
(5) 이익 증가:
- 지난 10년간 3년 평균 EPS 증가율이 33% 이상인 종목
(1 ~ 3년 전 3년 평균 EPS가, 8 ~ 10년 전 3년 평균 EPS보다 33% 이상 많은 종목)
(6) 적정 PER:
- 현재 주가가 최근 3년 평균 이익의 15배 이하인 종목
(최근 3년 평균 이익은 주당 순이익인 EPS의 평균으로 구한다.
따라서 "현재 주가 / 3년 평균 EPS <= 15"를 만족하는 종목을 선정한다.)
(7) 적정 PBR:
- 현재 PBR이 1.5 이하인 종목
- PBR * PER이 22.5 이하인 종목
(PER은 (6)에서 구한 PER(= 현재 주가 / 3년 평균 EPS) 값을 사용했다.)
첫 번째, 매출액(revenue)이 3억 5천 달러 이상이며, 유동비율(current ratio)이 2 이상인 종목 탐색 (투자 전략 (1), (2) - 1)
첫 번째 조건은 investing.com에서 제공하는 stock screener를 통해 만족하는 종목을 쉽게 필터링할 수 있다.
그 후 아래의 코드와 같이 selenium을 이용한 웹 스크래핑으로 필터링 된 종목들의 ticker를 추출해 csv 파일에 dataframe의 형태로 저장했다.
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import pandas as pd
driver = webdriver.Chrome('C:/Program Files/chromedriver_win32/chromedriver.exe')
driver.implicitly_wait(3)
tickers = []
for i in range(1, 20):
driver.get(f'https://www.investing.com/stock-screener/?sp=country::5|sector::a|industry::a|equityType::a|eq_revenue::350000000,27839630000000|qcurratio_us::2,3921.01%3Ceq_market_cap;{i}')
try:
driver.find_element_by_link_text('Overview').send_keys(Keys.ENTER)
except:
continue
time.sleep(5)
html = driver.page_source
soup = BeautifulSoup(html, 'lxml')
select = soup.select('.symbol.left.bold.elp')
for s in select:
text = s.find_next(class_='left').text
tickers.append(text)
tickers_df = pd.DataFrame({'Symbol' : tickers})
tickers_df.to_csv('tickers_result_1.csv')
두 번째, 장기 부채가 운전자본 이하인 종목 탐색 (투자 전략 (2))
먼저 첫 번째 조건을 만족하는 종목들의 총 유동자산, 총 유동부채, 총 부채 정보를 finviz.com에서 웹 스크래핑을 통해 추출하고, 추출한 정보를 dataframe의 형태로 csv 파일에 저장했다.
코드는 다음과 같다.
from bs4 import BeautifulSoup
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
tickers = pd.read_csv('tickers_result.csv')
tickers = tickers['Symbol'].tolist()
columns = ['Total Current Assets', 'Total Current Liabilities', 'Total Liabilities']
df_filter = pd.DataFrame(index=tickers, columns=columns)
driver = webdriver.Chrome('C:/Program Files/chromedriver_win32/chromedriver.exe')
driver.implicitly_wait(3)
for ticker in tickers:
driver.get(f'https://finviz.com/quote.ashx?t={ticker}')
try:
driver.find_element_by_link_text('balance sheet').send_keys(Keys.ENTER)
except:
continue
time.sleep(5)
html = driver.page_source
soup = BeautifulSoup(html, 'lxml')
for column in columns:
r = soup.find(text=column).find_next(class_='snapshot-td2').find_next(class_='snapshot-td2').text
try:
df_filter.loc[ticker, column] = float(r.replace(',', ''))
except:
df_filter.loc[ticker, column] = None
df_filter.to_csv('df_filter_1.csv')
다음으로 위에서 가져온 정보들을 바탕으로 각 종목별로 장기부채(=총 부채 - 총 유동부채)와 운전자본(= 총 유동자산 - 총 유동부채)를 계산하고, 장기부채가 운전자본 이하인 종목들을 필터링해 dataframe의 형태로 csv 파일에 저장한다.
코드는 다음과 같다.
import pandas as pd
df = pd.read_csv('df_filter_result.csv', index_col = 0)
df = df.dropna()
list = []
for i in range(len(df)):
comp = (df.iloc[i, 0] - df.iloc[i, 1])
if comp > (df.iloc[i, 2] - df.iloc[i, 1]):
list.append(df.index[i])
df_result = pd.DataFrame({'Symbol' : list})
df_result.to_csv('tickers_result_2.csv')
세 번째, 지난 10년간 당기 순이익(net income)이 마이너스가 없고, 지난 10년간 3년 평균 EPS 증가율이 33% 이상인 종목 탐색 (투자 전략 (3), (5))
(두 투자전략에 필요한 정보를 같은 사이트의 같은 페이지에서 스크래핑하므로 같이 분석했다. )
먼저 두 번째 조건을 통해 필터링 된 종목들의 10년 간의 당기 순이익과 EPS 정보를 macrotrends.net에서 웹 스크래핑을 통해서 추출하고, 두 가지 정보를 각각 dataframe의 형태로 csv 파일에 저장한다.
코드는 다음과 같다.
from bs4 import BeautifulSoup
import pandas as pd
from selenium import webdriver
def column(num):
column = []
for i in range(1, num+1):
column.append(f'{i}년 전')
return column
driver = webdriver.Chrome('C:/Program Files/chromedriver_win32/chromedriver.exe')
df = pd.read_csv('tickers_result_2.csv')
tickers = df['Symbol'].tolist()
df_net_income = pd.DataFrame(index=tickers, columns=column(10))
df_eps = pd.DataFrame(index=tickers, columns=column(10))
for ticker in tickers:
driver.get(f'https://www.macrotrends.net/stocks/charts/{ticker}/apple/financial-statements')
driver.implicitly_wait(20)
#net income 웹 스크래핑
try:
button = driver.find_element_by_id('jqxScrollBtnDownhorizontalScrollBarjqxgrid')
for i in range(40):
button.click()
except:
pass
html = driver.page_source
soup = BeautifulSoup(html, 'lxml')
s1 = soup.select('#row15jqxgrid > div > div')
for i in range(1, 11):
try:
df_net_income.loc[ticker, f'{i}년 전'] = float(s1[i + 1].get_text().replace(',', '')[1:])
except:
df_net_income.loc[ticker, f'{i}년 전'] = None
#eps 웹 스크래핑
s3 = soup.select('#row21jqxgrid > div > div')
for i in range(1, 11):
try:
df_eps.loc[ticker, f'{i}년 전'] = float(s3[i + 1].get_text()[1:])
except:
df_eps.loc[ticker, f'{i}년 전'] = None
df_net_income.to_csv('df_net_income.csv')
df_eps.to_csv('df_eps.csv')
다음으로 위에서 가져온 정보들을 바탕으로 10년간 당기 순이익이 마이너스가 없고, 10년간 EPS 성장률이 33% 이상인 종목들을 필터링해 dataframe의 형태로 csv 파일에 저장한다.
이때 10년간 EPS 성장률의 경우에는 1~3년 전 3년 평균 EPS와 8~10년 전 평균 EPS를 비교해서 구하게 된다.
수식으로 나타내면, ( 1~3년 전 3년 평균 EPS - 8~10년 전 평균 EPS ) / abs( 8~10년 전 평균 EPS ) >= 0.33이다. (abs는 절댓값을 의미한다.)
코드는 다음과 같다.
import pandas as pd
import math
def column(num):
columns = []
for i in range(1, num+1):
columns.append(f'{i}년 전')
return columns
df_net_income = pd.read_csv('df_net_income.csv', index_col=0)
df_eps = pd.read_csv('df_eps.csv', index_col=0)
list = []
#10년간 당기 순이익이 마이너스가 아닌 종목
for i in range(len(df_net_income)):
for j in range(10):
if math.isnan(df_net_income.iloc[i, j]) :
pass
elif (df_net_income.iloc[i, j]) >= 0:
pass
else:
break
if j == 4:
list.append(df_net_income.index[i])
list1 = []
#10년간 3년 평균 EPS 성장률이 33% 이상인 종목
for i in list:
total = 0
count = 0
for j in range(1, 4):
if math.isnan(df_eps.loc[i, f'{j}년 전']):
pass
else:
total = total + df_eps.loc[i, f'{j}년 전']
count = count + 1
try:
recent_eps = total/count
except:
recent_eps = float('nan')
total = 0
count = 0
for j in range(8, 11):
if math.isnan(df_eps.loc[i, f'{j}년 전']):
pass
else:
total = total + df_eps.loc[i, f'{j}년 전']
count = count + 1
try:
past_eps = total/count
except:
recent_eps = float('nan')
if (math.isnan(recent_eps) == False) & (math.isnan(past_eps) == False):
if (recent_eps - past_eps) / abs(past_eps) >= 0.33:
list1.append(i)
else:
pass
df_result = pd.DataFrame({'Symbol' : list1})
df_result.to_csv('tickers_result_3.csv')
3. 요약
이번 글에서는 방어 투자자를 위한 벤저민 그레이엄의 7가지 투자 전략 중 4가지 투자 전략을 미국 주식에 적용해보았다.
다음 글에서는 나머지 3가지 투자 전략을 미국 주식에 적용해보고 적용한 결과 선정되는 종목에 대해서 알아볼 것이다.
'코딩 > 퀀트 투자' 카테고리의 다른 글
[파이썬/ 웹 스크래핑] 벤저민 그레이엄의 투자 전략에 따른 종목 선정 - 공격 투자자(2) (0) | 2021.09.11 |
---|---|
[파이썬/ 웹 스크래핑] 벤저민 그레이엄의 투자 전략에 따른 종목 선정 - 공격 투자자(1) (1) | 2021.09.10 |
[파이썬/ 웹 스크래핑] 벤저민 그레이엄의 투자 전략에 따른 종목 선정 - 방어 투자자(2) (0) | 2021.09.04 |
파이썬으로 S&P 500 지수 투자 분석하기(TIGER ETF 편) (0) | 2021.08.28 |
파이썬으로 S&P 500 지수 투자 분석하기 (VOO ETF 편) (0) | 2021.08.27 |