시간이 없어서 계속 못봤다. 얼른 마저 가보자.
● PER차트와 밴드차트
드디어 Chat GPT로 그렇게 그려하고 싶던, PER차트와 밴드차트가 나왔다. 어서 해보자! 우선 스톨고우에서 코스트코 페이지를 보라고 한다. 사이트에 들어가보면 PER이 나와있어서 웹크롤링을 통해 그려도 되지만 Finterstellar API를 통해 그려보도록 한다.
크롤링에 관심이 있다면 '파이썬을 활용한 금융공학 레시피'를 맛보기로 해볼 수 있다고 한다. (같은 저자이다.)
핀터스텔라 API는 https://www.finterstellar.com/에서 받을 수 있다.
Sign up을 눌러서 아래와 같이 간단하게 회원가입을 진행해보자.
가입하면 메일로 임시비밀번호가 발송되고 그것으로 로그인하면 된다. 들어가서 비밀번호를 바꿔주자. finterstellar OTP는 한 번 받으면 1000회 까지 사용할 수 있고 사용한도를 소진하면 다시 받으면 된다고 한다.
전 종목 별 실시간 크롤링을 통해 가공된 데이터를 제공하고 있다. 이 정도면 거의 퀀트계의 마더테레사가 아니신지..
메인 창에서 아이디를 누르면 밑에 OTP가 있다. 이것을 클릭해보자. 그리고 나서 간단하게 Renew를 눌러주면 OTP가 생성된다. Current Call은 현재 OTP의 사용량은 Total call은 API의 누적사용량을 나타낸다.
finterstellar 모듈을 불러오고 fn_single()함수를 이용해본다. OTP코드 값 otp와 종목코드 symbol, 데이터 산출 기준 window를 (분기, 연간 등)을 내보내준다. 분기 데이터는 window='Q', 분기데이터를 합쳐서 1년치로 만든 데이터를 가져오려면 window='T'를 선택하면 된다. (연간 데이터는 제공하지 않음.)
df=fs.fn_single(otp='발급받은 otp',symbol='cost',window='T')
df.tail(3)
코스트코에 대한 dataframe을 형성하였고, 맨 끝에 3행만 보이게 했다.
총 매출(Revenue)부터 여러가지 재무제표에 관련한 사항이 쭉 표기가 된다. 가져온 데이터가 뭔지 너무 많으니, 데이터프레임.columns 라는 명령어를 사용해서 열 정보를 가져와보자.
df.columns
주로 쓰는 재무제표의 데이터와 가격 데이터가 거의 다 들어있다고 보면 될 것 같다. PER계산에 필요한 Price(가격)과 EPS(Earning per Share, 주당 순이익)을 가져와보자.
PER은 Price earning Ratio로써, 시가총액 ÷ 이익 = PER이고, EPS는 이익 ÷ 주식 수 이다. 이익이 분자와 분모에 있으니, PER*EPS를 해보면 (시가총액 ÷ 이익) × (이익 ÷주식수) = 시가총액 ÷ 주식수 = (주식 한 개당 지금 가격) = 주식가격이다.
즉, PER * EPS = 주식가격이므로, 주식가격 ÷ EPS 를하면 PER이 된다. 이를 이용하여 코스트코의 PER 추이를 차트로 그려본다.
df['PER']=df['Price']/df['EPS']
fs.draw_chart(df,left='PER',right='Price')
df의 PER의 열을 만드는데 그건 df의 Price열을 df의 EPS로 나눈 값이라는 것이다. 그것을 통해 왼쪽 축은 PER 값 오른쪽 값은 Price에 대한 차트를 그려보자.
와우~ 역시 필수 소비재인 코스트코는 전쟁의 여파에도 불구하고 전고점을 돌파하려하고 있다. 그런데, 재미있는건 PER도 코스트코의 가격을 적절히 따라서 올라오고 있다. 상승세에 있는 주식은 역시 프리미엄이 비싸다는 것을 알 수 있다.
이때, PER을 통해 주가가 움직일 수 있었던 범위의 시나리오를 그려볼 수 있다. PER밴드를 구하기 위해서는 multiple과 acct를 이용한다. 예컨데, 현재 가격이 500이고 PER이 40이다. 근데, 역사적으로 PER이 25였던 적이 있었다? 한다면 지금 주가는 500이 아니라 더 떨어져있을 것이다. 이를 가정한 그래프를 그리는 것이다.
fs.draw_price_multiple_band(df,multiple='PER',acct='EPS')
PER의 최고치와 PER의 최소치를 기준으로 5등분해서 이를 보여주는 것으로 판단된다. 와우 ~ 이러면 현재 주가가 과거 대비 어느정도의 프리미엄에 있는지 한 눈에 볼 수 있다. 지금 코스트코는 지난 10년대비 굉장히 높은 프리미엄을 받고 있는 주식이다. 고평가 되었다고 말할 수도 있는 위치이다.
● 투자전략
- 전체 상장 종목을 PER 순서대로 줄 세워두고 앞에서부터 n 개를 사서 보유하고 다음 재무제표가 발표되면 이 과정을 반복한다.
일단 fn_consolidated()함수를 이용하여 분기 재무정보를 한번에 초회한다. OPT와 term(분기 값), vol(일평균 거래량)이 있다.
df=fs.fn_consolidated(otp='otp', term='2022Q4',vol=100000)
df.head(3)
22년도 4분기에 거래량이 10만주 이상인 전체 상장 종목을 불러왔다. head는 맨 위에 행 3개만 불러오는 명령이다.
2022년 4분기의 일 평균 거래량이 10만 주 이상인 재무 정보가 모두 불러졌다. 엄청나다. 위에서 한 대로 df.column을 통해 무슨 열이 있는지 확인해보자.
df.colums
아까 했던거랑 비슷하다. 자세한 항목은 필요할 때 꺼내 쓰도록 하자. Price_M1, M2, M3는 1개월 후 2개월 후 3개월 후의 주가라고 한다. 예를들어, 2022년 4분기 data라면 Price는 2022년 12월 data, Price_M1은 1월말, Price_M2는 2월말, Price_M3는 3월말 주가를 의미한다고 한다.
PER과 PBR같은 프리미엄을 산정하는 가치들은 분기말 주가를 기준으로 한다. 그래서 마더 테레서 핀터스텔라는 M1~M3을 모두 제공하는 것이다.
df['PER']=df['Price']/df['EPS']
df['PER']
아까 불러온 전체 상장종목에 대해 PER을 구해보자.
323개 밖에 없다니.. 2022년 하락장을 통해 거래량이 정말 급감하긴 한 것 같긴하다. 하긴 나도 물리고 난 다음부터는 거래를 거의 실행하지 않았다.
● 백테스팅
PER가 낮은 순서대로 상위 30개 종목에 투자하는 전략을 실행해본다. set_terms()함수를 이용하여 거래 시작 시점인 trade_start와 종료 시점인 trade_end를 보내면 데이터 산출 기간이 돌아온다.
terms = fs.set_terms(trade_start='2020Q1',trade_end='2022Q4')
terms
코로나가 터지는 시점인 2020Q1부터 지금까지 Q4까지 테스트 해보도록 하자.
그러면, 2019Q4부터 2022Q3까지의 데이터를 가져오게 된다. 재무 데이터는 한 분기 빠른 값으로 설정하게 된다. 2019Q4부터 2022Q3까지의 재무데이터를 받아오자.
data={}
for t in terms :
data[t] = fs.fn_consolidated(otp='OTP', term=t)
첫 행에서 딕셔너리 data를 만들어준다. 딕셔너리는 아래 포스팅에서 간단히 다루어보았었다.
그리고 2번 째 행에서 for문을 통해 앞에 terms에서 구해놓았던 기간들을 집어넣어서 반복 수행한다. 그래서 t in terms이다.
그러고 3번 째 행에서는 data[t] = fs.consolidated(otp='OTP', term=t) 해서 t가 하나씩 추가되면서 data에 2019Q4부터 2022Q3까지 쭉 data를 불러오는 과정을 수행하는 것이다.
data의 량이 꽤 많다보니 받아들이는데 시간이 꽤 걸린다. 여기서 각각의 재무데이터에서 PER를 계산해보자.
for t in terms :
data[t]['PER']=data[t]['Price']/data[t]['EPS']
마찬가지로 data가 꽤 많으니, for문을 통해 PER를 각각 편하게 구해준다. [t]에 대한 data에서 ['PER']열을 구하는 것이다. 이제 각 분기별로 트레이딩 할 종목을 선정하여 시그널을 저장할 딕셔너리 's'에 담아본다.
fn_filter()함수를 이용하여, data[t], 필터링 기준이 되는 항목 by, 항목의 최저값 floor, 최대값 cap, 상위 몇종목을 선정할지 표시하는 n 오른차순 또는 내림차순을 결정하는 asc를 전달한다.
s = {}
signal = {}
for t in terms :
s[t] = fs.fn_filter(data[t], by='PER', floor=1, cap=10, n=30, asc=True)
signal[t] = list(s[t].index)
그러면, PER에 의해 1에서 10사이의 종목 30개를 오름차순으로 가져온다. (PER가 낮은 순서부터 높은순서로) 그렇게 뽑은 종목을 signal[t]에 각각 저장하는 것이다. 임의로 2021Q1의 종목을 뽑아보면 아래와 같다.
이제 이걸로 백테스팅을 드디어 수행하는데, 트레이딩 시그널 signal과 재무데이터가 들어있는 data를 함께 보내주고 리벨런싱 일자를 지정하는 m값을 보내는데, 아까 말한 Price M1,M2,M3와 관련이 있다. 재무제표 발표 기준일로 하려면 0이다. cost라는 함수로 거래비용도 지정할 수 있다. 0.1%로 해보자.
df=fs.backtest(signal=signal, data=data, m=2, cost= 0.001)
역시 세월은 지나봐야 아는 것 같다.
fs.draw_return(df)
df에 대한 return을 확인해보니 재미있는 결과를 얻을 수 있었다. 빨간색 선은 포트폴리오의 누적 수익률, 하늘색 점선은 S&P500의 누적수익률, 빨간색 막대는 분기수익률, 파란색 막대는 벤치마크 지수의 분기수익률이다.
흔히 코로나 사이클이라고 불리었던 지난 3년을 우라가미 구니오의 계절장세로 나누면
- 2020Q1부터 2021Q1까지 금융장세였던 대세 상승장이었을 땐 전략이 잘 먹혔다.
- 2021Q2부터 2022Q1까지의 실적장세에는 S&P500의 성과가 더 좋다.
- 2022Q1부터 2022Q4까지의 역금융장세에는 전략이 쓰레기가 되어버렸다... ㅠㅠ (오히려 S&P500은 반등)
핀터스텔라 페이지의 퀀트모델 - 가치모델 만들기에서 비슷하게 전략을 수행해볼 수 있는데, 얼추 비슷하다.
'미래걱정 > 주식' 카테고리의 다른 글
가치지표 결합하기 (PER , PBR 등) - 파이썬 Finterstellar 모듈 활용하기 (0) | 2023.03.31 |
---|---|
PBR PSR PCR Band 전략 및 백테스트 - 미국주식으로 시작하는 슬기로운 퀀트투자 5장 (2) (2) | 2023.03.05 |
Finterstellar (핀터스텔라)를 활용한 3년간 S&P500에 대한 단기투자 백테스팅 (0) | 2023.02.13 |
단기 투자의 기술 편 (보조지표를 통한 백테스팅) - 슬기로운 퀀트투자 3장 (0) | 2023.02.12 |
슬기로운 퀀트투자 - 2장 일단 해보자 편 (1) | 2023.02.08 |
댓글