본문 바로가기

코딩/퀀트 투자

[파이썬/ 웹 스크래핑] 벤저민 그레이엄의 투자 전략에 따른 종목 선정 - 공격 투자자(1)

 

 

Photo by Stephen Dawson on Unsplash

 

 

1. 목표

 

벤저민 그레이엄의 방어 투자자를 위한 투자 전략에 이어 공격 투자자를 위한 투자 전략을 사용해 종목 선정하는 것을 목표로 한다.

 

방어 투자자를 위한 투자 전략에 대한 정보가 궁금하다면 아래 두 개의 글을 참조하면 된다.

 

 

[파이썬/ 웹 스크래핑] 벤저민 그레이엄의 투자 전략에 따른 종목 선정 - 방어 투자자(1)

1. 목표 미국 주식을 대상으로 벤저민 그레이엄의 투자 전략에 따라 종목 선정을 하는 것이다. 투자 전략은 <현명한 투자자>라는 책의 내용을 토대로 설정했으며, 투자 성향에 따라 방어 투자자,

coding-gongbu.tistory.com

 

[파이썬/ 웹 스크래핑] 벤저민 그레이엄의 투자 전략에 따른 종목 선정 - 방어 투자자(2)

1. 목표 저번 포스팅에 이어서 벤저민 그레이엄의 방어 투자자를 위한 7가지 투자 전략 중 나머지 3가지 전략을 통한 종목 선정을 할 것이다. 앞에서 다룬 4가지 전략에 대한 내용 및 코드는 아래

coding-gongbu.tistory.com

 

 

 

 

2. 공격 투자자를 위한 6가지 투자 전략

 

(1) 적정 PER:

 

- PER이 9 이하인 종목

 

 

(2) 재무 상태:

 

- 유동자산이 유동부채의 150% 이상인 종목

("유동자산 / 유동부채 = 유동비율(current ratio) >= 1.5"인 종목)

- 부채(total debt)가 순 유동자산의 110% 이하인 종목

(순 유동자산 = 총 유동자산(total current assets) - 총 유동부채(total current liabilities))

 

 

(3) 이익의 안정성:

 

- 지난 5년 동안 적자를 기록한 적 없는 종목

(즉, 5년간 당기순이익(net income)이 마이너스가 없는 종목)

 

 

(4) 배당 실적:

 

- 당기에 조금이라도 배당을 지급한 종목

 

 

(5) 이익 증가:

 

- 작년 이익이 4년 전 이익보다 많은 종목

 

 

(6) 주가:

 

- 주가가 순유형자산의 120% 미만

(순유형자산(net tangible assets) = 총 자산(total assets) - 총 부채(total liabilities) - 무형자산(intangible assets) - 영업권(goodwill))

 

 

 

 

 

Photo by Ilya Pavlov on Unsplash

 

 

첫 번째, PER이 9 이하이며, 유동자산이 유동부채의 150% 이상인 종목 선정 (투자 전략 (1), (2) - 1)

 

먼저 investing.com에서 제공하는 stock screener를 통해 위 조건을 만족하는 종목을 필터링한다.

 

이때 유동자산이 유동부채의 150% 이상이라는 것은 유동비율(current ratio)가 1.5 이상이라는 것을 의미한다.

 

 

Stock Screener - Investing.com

Stock Screener - research and filter stocks based on key parameters and metrics such as stock price, market cap, dividend yield and more.

www.investing.com

 

다음으로 selenium을 이용한 웹 스크래핑으로 필터링 된 종목들의 ticker를 추출해 csv 파일에 dataframe의 형태로 저장한다.

 

코드는 다음과 같다.

 

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import pandas as pd
import time

driver = webdriver.Chrome('C:/Program Files/chromedriver_win32/chromedriver.exe')
driver.implicitly_wait(5)

tickers = []

for i in range(1, 34):
    driver.get(f'https://www.investing.com/stock-screener/?sp=country::5|sector::a|industry::a|equityType::a|peexclxor_us::9,250000|qcurratio_us::1.5,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')

 

 

 

 

두 번째, 부채가 순유동자산의 110% 이하인 종목 선정 (투자 전략 (2) - 2)

 

먼저 finviz.com에서 위에서 추출한 ticker들의 총 부채(total debt), 총 유동자산(total current assets)과 총 유동부채(total current liabilities)을 웹 스크래핑으로 추출한다.

 

 

FINVIZ.com - Stock Screener

× Ever heard of Finviz*Elite? Our premium service offers you real-time quotes, advanced visualizations, technical studies, and much more. Become Elite and make informed financial decisions. Find out more --> Upgrade your FINVIZ experience Join thousands o

finviz.com

 

코드는 다음과 같다.

 

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_1.csv')
tickers = tickers['Symbol'].tolist()

columns = ['Total Current Assets', 'Total Current Liabilities', 'Total Debt']
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_2.csv')

 

다음으로 위에서 추출한 정보로 부채가 순유동자산의 110% 이하인 종목들을 필터링해 dataframe의 형태로 csv 파일에 저장한다.

 

이때 부채는 총 부채(total debt) 값을 사용하고, 순유동자산은 "총 유동자산(total current assets) - 총 유동부채(total current liabilities)"로 계산한다.

 

코드는 다음과 같다.

 

import pandas as pd

df = pd.read_csv('df_filter_2.csv', index_col = 0)
df = df.dropna()
list = []

for i in range(len(df)):
    comp = df.iloc[i, 0] - df.iloc[i, 1]
    if (1.1 * comp) >= df.iloc[i, 2]:
        list.append(df.index[i])

df_result = pd.DataFrame({'Symbol' : list})
df_result.to_csv('tickers_result_2.csv')

 

 

 

 

세 번째, 지난 5년 동안 적자를 기록한 적이 없고, 작년 이익이 4년 전의 이익보다 많은 종목 선정 (투자 전략 (2), (4))

 

(두 투자 전략에 대한 정보가 같으므로 한 번에 필터링해서 분석했다.)

 

먼저 위에서 필터링 한 종목들의 5년 치 당기순이익(net income) 정보를 macrotrends.net에서 웹 스크래핑을 통해서 추출해 csv 파일에 저장한다.

 

 

Macrotrends | The Long Term Perspective on Markets

High quality interactive historical charts covering global stock, bond, commodity and real estate markets as well as key economic and demographic indicators.

www.macrotrends.net

 

코드는 다음과 같다.

 

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

df = pd.read_csv('tickers_result_2.csv')
tickers = df['Symbol'].tolist()

df_net_income = pd.DataFrame(index=tickers, columns=column(5))

driver = webdriver.Chrome('C:/Program Files/chromedriver_win32/chromedriver.exe')

for ticker in tickers:
    driver.get(f'https://www.macrotrends.net/stocks/charts/{ticker}/apple/financial-statements')
    driver.implicitly_wait(10)

    #net income 크롤링
    html = driver.page_source
    soup = BeautifulSoup(html, 'lxml')
    s1 = soup.select('#row15jqxgrid > div > div')

    for i in range(1, 6):
        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

df_net_income.to_csv('df_net_income_3.csv')

 

다음으로 위에서 구한 5년간의 net income 정보를 통해 5년간 net income 값이 마이너스가 아니고, 1년 전의 net income 값이 4년 전의 net income 값에 비해 큰 종목들을 필터링한다.

 

코드는 다음과 같다.

 

import pandas as pd

df_net_income = pd.read_csv('df_net_income_3.csv', index_col=0)
df_net_income = df_net_income.dropna()

list = []

for i in range(len(df_net_income)):
    for j in range(5):
        if (df_net_income.iloc[i, j]) >= 0:
            pass
        else:
            break

        if j == 4:
            list.append(df_net_income.index[i])

list1 = []

for i in list:
    if df_net_income.loc[i, '1년 전'] > df_net_income.loc[i, '4년 전']:
        list1.append(i)

df_result = pd.DataFrame({'Symbol' : list1})
df_result.to_csv('tickers_result_3.csv')

 

 

 

 

<a href="https://kr.freepik.com/vectors/illustration">Illustration 벡터는 stories - kr.freepik.com가 제작함</a>

 

 

3. 요약

 

이 글에서는 벤저민 그레이엄의 공격 투자자를 위한 6가지 투자 전략 중 4가지의 투자 전략을 통해 필터링해보았다.

 

다음 글에서는 나머지 2가지 전략과 결과적으로 필터링 된 종목에 대해서 알아볼 것이다.