[Python] 퀀트 투자 기법 적용하기 Part 1. (미국편)
지난 포스팅에서 yahoo finance 에서 미국 주식 데이터를 긁어오는 내용을 다뤘었다.
이번 포스팅에서는 퀀트 기법에 적용해보기 위해, 어떤 데이터들을 먼저 수집해야하는지를 다뤄보자.
우선 적용시키고자 하는 퀀트 기법을 정리하면 아래와 같다.
방법론 | 조건 | 판단 기준 |
Graham | - ROA 5% 이상 - Debt ratio 50% 이하 - PBR 0.2 이상 |
PBR 가 낮은 종목부터 매수 |
NCAV | - PER 0.2 이상 5 이하 - (현 자산 - 총 부채) > 시가총액 OR (현 자산 - 총 부채) * 0.85 > 시가총액 - Net income > 0 |
순유동자산(2번째 항목)이 높은 종목부터 매수 |
3P Combo | - PER / PBR / PSR rank (낮은 순) | 3P (PER / PBR / PSR) rank 가 낮은 종목부터 매수 |
PBR + GP/A | - PBR rank (낮은 순) - GP/A rank (높은 순) |
두 조건의 종합 rank 가 낮은 종목부터 매수 |
참고) https://thisiswhoiam.tistory.com/3
[책 review] 할 수 있다! 퀀트 투자
나는 이제껏 주식을 할 때, 그렇게 많은 생각을 하지 않고 종목을 골랐었다. 위험 부담이 덜한 우량주, 배당주 위주로 종목을 선택했었고, 그렇게 대단한 수익률을 올리지 못한 것도 사실이다.
thisiswhoiam.tistory.com
위의 4가지 기법에서 활용하는 정보들을 취합해보면, 아래 표처럼 정리할 수 있다.
Measures | 적용 기법 |
PER | NCAV, 3P Combo |
PBR | Graham, 3P Combo, PBR + GP/A |
PSR | 3P Combo |
Market cap (시가 총액) | NCAV |
Current assets (현 자산) | NCAV |
Total liabilities (총 부채) | NCAV |
Total debt/equity (자본 대비 부채 비율) | Graham |
ROA | Graham |
Net income | NCAV |
GP/A | PBR + GP/A |
이번 포스팅에서는 이 정보들을 한 번에 불러오는 함수를 만들어보려고 한다.
우선 우리가 필요로 하는 정보들은 하나의 ticker(종목) 별로 불러와야하는 정보들이다.
그럼 여기서 우리가 코드를 짜기 전에 생각해야할 포인트는?
어떤 ticker 들에 대해 정보를 수집할 것인지 ticker 정보가 필요하다.
ticker 별로 필요한 정보를 수집하는 함수를 정의해야 한다.
ticker 정보는 yahoo finance 에서 제공하는 package 를 이용하면 알 수 있다. 아래 코드를 이용하면 yahoo finance 에서 제공하는 nasdaq 종목들의 정보를 불러올 수 있다.
# packages
import yahoo_fin.stock_info as si
# nasdaq 종목 불러오기
tickers = si.tickers_nasdaq()
위의 코드를 돌려보면, 아래 그림처럼 tickers 라는 list 형 변수에 nasdaq 종목들의 코드가 저장되어 있는 걸 확인할 수 있다.
그럼 다음으로 각 ticker 별로 우리가 필요로 하는 정보를 긁어오는 함수를 만들어보자.
지난 번 포스팅에서 다루었듯이, 필요한 정보들이 어디에 있는지 파악하는 것이 필요하다.
yahoo finance 에서 제공하는 package documentation 을 살펴보면 다양한 함수를 제공하고 있는데 그 중에서도 우리는 몇 가지만 사용할 예정이다. (get_stats_valuation, get_balance_sheet, get_income_statement, get_stats)
참고) http://theautomatic.net/yahoo_fin-documentation/#stock_info
Yahoo_fin Documentation - Open Source Automation
Python's yahoo_fin package lets you download historical stock price data, real-time prices, fundamentals data, option prices, cryptocurrency info, and more!
theautomatic.net
# 넷플릭스 ticker
ticker = 'nflx'
valuation = si.get_stats_valuation(ticker)
balance = si.get_balance_sheet(ticker)
income = si.get_income_statement(ticker)
stats = si.get_stats(ticker)
이렇게 각 함수 호출을 통해 우리가 얻고자 하는 데이터를 확인할 수 있다.
- valuation : Market cap, PER, PBR, PSR
- balance: Current assets, Total assets (GP/A 계산에 활용), Total liabilities
- income: Net income, Gross profit (GP/A 계산에 활용)
- stats: Deb ratio, ROA
그럼 이제 각 함수마다 어떤 정보를 포함하고 있는지 파악이 됐으니, 하나의 함수로 필요한 모든 정보를 불러와 취합하는 함수를 만들어보자.
def get_values(ticker):
market_cap, per, pbr, psr, ca, tl, netIncome, debt_ratio, roa, gpa = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
try: # 정보가 제공되지 않는 ticker 는 제외하려는 예외 처리
#Stats 1
valuation = si.get_stats_valuation(ticker)
market_cap = valuation.iloc[0,1]
per = valuation.iloc[2,1]
pbr = valuation.iloc[5,1]
psr = valuation.iloc[6,1]
#Balance sheet
balance = si.get_balance_sheet(ticker)
ca = balance.loc['totalCurrentAssets'].iloc[0]
tl = balance.loc['totalCurrentLiabilities'].iloc[0]
total_assets = balance.loc['totalAssets'].iloc[0]
#Income statement
income = si.get_income_statement(ticker)
netIncome = income.loc['netIncome'].iloc[0]
grossProfit = income.loc['grossProfit'].iloc[0]
#Stats 2
stats = si.get_stats(ticker)
debt_ratio = stats[stats['Attribute']=='Total Debt/Equity (mrq)']['Value']
roa = stats[stats['Attribute'] == 'Return on Assets (ttm)']['Value']
gpa = total_assets / grossProfit
except:
pass
return [ticker, market_cap, per, pbr, psr, ca, tl, netIncome, debt_ratio, roa, gpa]
이렇게 불러온 (+ 불러와서 계산한) 데이터들을 list 로 반환하는 함수를 만들 수 있다. list 형으로 반환하는 이유는 해당 함수는 하나의 ticker 에 대한 데이터만 추출하는 함수로 여러 개 ticker 들에 대한 정보들을 취합하여 dataframe 으로 만들기 위함이다.
아래는 반복문을 활용해 nasdaq 종목들의 데이터들을 모두 불러와 dataframe 화 시키는 코드이다.
df = []
for ticker in tickers:
val_list = get_values(ticker)
df.append(val_list)
df = pd.DataFrame(df, columns=['Ticker', 'MarketCap', 'PER', 'PBR', 'PSR', 'CurrentAssets', 'TotalLiabilities', 'netIncome', 'DebtRatio', 'ROA', 'GP/A'])
그리고 위에서 정의한 함수를 그대로 쓰게 되면 연산의 오류가 발생할 수 있다. 예를 들어 gpa 를 계산할 때, grossProfit 은 0 이 될 수 없기 때문에 해당 부분들은 조건문이나 예외 처리문을 활용해서 처리해주면 좋다. 맨 위에서 반환하는 모든 변수들을 0 으로 초기화했기 때문에 if 문에 걸리지 않는다면 0 을 반환하게 된다.
#gpa = total_assets / grossProfit
if grossProfit > 0:
gpa = total_assets / grossProfit
아마 코드를 돌리다보면 오류가 발생하는 부분이 생길 수도 있다. 제공되는 데이터의 형태가 대부분 통일화되어 있지만 간혹 통일되지 않은 예외가 튀어나올 때도 있기 때문이다. 그런 경우에는 어떻게 데이터가 반환이 되기에 오류가 발생하는지 확인하고 예외처리를 해주도록 하자.