- 저자
- 김용환, Yubin Kim
- 출판
- 한빛미디어
- 출판일
- 2021.09.30
미국주식으로 시작하는 슬기로운 퀀트투자 5장(1) - 가치주를 찾는 기술 (feat. PER Band Chart 핀터스
미국 주식으로 시작하는 슬기로운 퀀트투자 이 책은 서학개미를 위한 미국 주식 퀀트투자 입문+실습서이다. 퀀트투자는 수학적, 통계적 기법을 활용해 투자 종목을 발굴하는 투자 방법이다. 퀀
jhhistory.tistory.com
● 가치지표를 섞어서 백테스팅을 해보자
책에서는 5장 '가치주를 찾는 기술' 후반 부이다. PER과 PBR을 합쳐서 백 테스팅을 해보자. 교집합을 통해 종목 개수를 추릴 수도 있고, 나름의 점수를 매겨서 줄 세우는 방법이 있을 수 있다고 한다.
후자의 경우 PER이 낮은 순서대로 100점 99점 이렇게 판단할 수도 있고, PER이 1배~10배 면 100점 이런 식으로 판단할 수도 있겠다. 후자는 상황에 따른 본인의 점수 표준표 세팅이 엄청 중요하겠다.
● PER과 PBR의 교집합
지난 5장의 (1)에서 했던 것처럼 일단 PER과 PBR에 대한 퀀트 머신의 판단 시그널을 만들어 준다. 구글 콜랩 키고 pip install finterstellar 해준다음에,
import finterstellar as fs
t='2022Q4'
data = fs.fn_consolidated(otp='otp',term=t,vol=0)
data['PER']=data['Price_M3']/data['EPS']
data['PBR']=data['Price_M3']/(data['Shareholders Equity']/data['Shares'])
s1=fs.fn_filter(data,by='PER',floor=1,cap=10,n=10,asc=True)
s2=fs.fn_filter(data,by='PBR',floor=0.1,cap=1,n=10,asc=True)
자세한 내용은 반복이라 생략한다. 책과 다르게 2022Q4까지로 하였다. s1과 s2에 어떤 종목이 있는지 살펴보면
PER
symbol
STAR 1.071542
IMPP 1.142857
ESEA 1.156144
ZEV 1.230769
IREN 1.241667
SBOW 1.364997
AIHS 1.383562
UHAL 1.410104
AZTA 1.427900
SOHO 1.428571
PBR
symbol
IMPP 0.107932
CS 0.121102
RKT 0.124655
ROOT 0.128281
KEP 0.128969
COHN 0.132144
BBGI 0.134380
NXTP 0.134399
ACOR 0.134717
TUYA 0.135764
이렇게 출력이 된다. 진짜 진심 난생 첨 보는 티커들이다. s1과 s2를 합칠 건데, 교집함이니까 'and'로 간다.(합집합이면 or) 합치는 명령어는 combine_signal() 함수이다.
PER PBR
symbol
IMPP 1.142857 0.107932
이러면 한 종목 밖에 산출되지 않는다. or이면 19개 종목이 합산되겠지.. 점수방식으로 섞는 다면 아래와 같이 진행하면 된다.
s1=fs.fn_score(data, by='PER', method='absolute', floor=1,cap=10, asc=True)
s2=fs.fn_score(data, by='PBR', method='absolute', floor=0.1, cap=1, asc=True)
그러면 PER이 1에 가까운 순서부터 높은 순서대로 100점에서 점수를 나눠먹고 PBR도 마찬가지로 형성이 된다.
PER Score
symbol
STAR 1.071542 99.2
IMPP 1.142857 98.4
ESEA 1.156144 98.3
ZEV 1.230769 97.4
IREN 1.241667 97.3
... ... ...
NBTB 9.960452 0.4
FCCO 9.963731 0.4
EEX 9.970588 0.3
MBCN 9.980843 0.2
GS 9.991442 0.1
[746 rows x 2 columns]
PBR Score
symbol
IMPP 0.107932 99.1
CS 0.121102 97.7
RKT 0.124655 97.3
ROOT 0.128281 96.9
KEP 0.128969 96.8
... ... ...
OMI 0.997372 0.3
AZTA 0.998183 0.2
BY 0.998897 0.1
FIBK 0.999545 0.1
LMND 0.999879 0.0
[868 rows x 2 columns]
이 점수를 가지고 높은 순으로 잘라보자 10개로 추려보자. 10개를 추려내는 기준은 PER과 PBR로 추려낸 signal에서 점수가 둘 다 높은 형태로 교집합을 이루고 있는 방식으로 추려내는 것으로 보인다.
fs.combine_score(s1,s2,n=10)
Score Score_ Sum
symbol
IMPP 49.20 49.55 98.75
CRESY 47.35 44.30 91.65
REI 47.60 38.25 85.85
GTN 45.00 40.10 85.10
DISH 43.95 40.60 84.55
HT 44.50 38.05 82.55
STAR 49.60 32.80 82.40
FRC 40.20 42.15 82.35
PACW 40.30 40.20 80.50
EDRY 47.55 32.40 79.95
만약 상대 값으로 가져가면 가장 PER이 낮은 종목이 100인 것을 기준으로 PER의 배수에 따라 점수를 균등 배분하는 식으로 가져가는 것 같다.
s1=fs.fn_score(data, by='PER', method='relative', floor=1,cap=10, asc=True)
s2=fs.fn_score(data, by='PBR', method='relative', floor=0.1, cap=1, asc=True)
print(s1), print(s2)
그럼 아래와 같은 결과가 출력된다.
PER Score
symbol
STAR 1.071542 100.0
IMPP 1.142857 99.9
ESEA 1.156144 99.7
ZEV 1.230769 99.6
IREN 1.241667 99.5
... ... ...
NBTB 9.960452 0.7
FCCO 9.963731 0.5
EEX 9.970588 0.4
MBCN 9.980843 0.3
GS 9.991442 0.1
[746 rows x 2 columns]
PBR Score
symbol
IMPP 0.107932 100.0
CS 0.121102 99.9
RKT 0.124655 99.8
ROOT 0.128281 99.7
KEP 0.128969 99.5
... ... ...
OMI 0.997372 0.6
AZTA 0.998183 0.5
BY 0.998897 0.3
FIBK 0.999545 0.2
LMND 0.999879 0.1
[868 rows x 2 columns]
(None, None)
마찬가지로 시그널 2개에 대해 합쳐보면
fs.combine_score(s1,s2,n=10)
아래와 같은 결과가 도출된다. 절댓값과 상대값과 순위차이는 있지만 거의 비슷한 느낌이다.
Score Score_ Sum
symbol
IMPP 49.95 50.00 99.95
CRESY 49.05 48.55 97.60
REI 49.35 45.95 95.30
GTN 48.40 46.90 95.30
DISH 47.80 47.20 95.00
HT 48.10 45.90 94.00
FRC 46.00 47.60 93.60
PACW 46.05 47.00 93.05
STAR 50.00 42.90 92.90
EDRY 49.25 42.50 91.75
● 가치투자 지표 4개 합쳐보기
내가 좋아하는 할 수 있다 알고 투자 강환국 작가님은 '슈퍼가치전략'이라는 이름으로 PER PBR PCR PSR의 지표를 다 섞어서 소형주에 투자하는 전략을 사용하면 2004년부터 2016년까지 이 전략으로 투자하면 연간 38%의 수익률을 기록한다고 언급한 적이 있다고 한다.
- 시가총액 하위 20% 주식을 뽑아냄.
- 뽑아낸 주식을 대상으로 PER PBR PCR PSR 지표 순위를 매김
- 4 지표의 순위를 더해 종합 순위를 매긴다.
- 종합 순위가 높은 상위 50개 종목에 투자한다.
- 연 1회 리밸런싱
순서대로 진행해 보자.
#기간별 data를 뽑아옴
terms=fs.set_terms(trade_start='2011Q1',trade_end='2022Q4')
data={}
for t in terms:
data[t]=fs.fn_consolidated(otp='otp',term=t)
#PER PBR PSR PCR을 구함
for t in terms:
data[t]['Market Cap']=data[t]['Price_M3']*data[t]['Shares']
data[t]['PER']=data[t]['Price_M3']/data[t]['EPS']
data[t]['PBR']=data[t]['Price_M3']/(data[t]['Shareholders Equity']/data[t]['Shares'])
data[t]['PSR']=data[t]['Price_M3']/(data[t]['Revenue']/data[t]['Shares'])
data[t]['PCR']=data[t]['Price_M3']/((data[t]['Net Income']+data[t]['Depreciation'])/data[t]['Shares'])
#시가총액 계산이 잘 되어있는 지 확인, 2021년 1분기를 시가총액 순위로 내림차순 정리
data['2021Q1'].sort_values(by='Market Cap', ascending=False).head()
그다음은 시그널을 변수에 담고 시가총액 순으로 오름차순으로 솎아내고 (작은 것부터 위로), PER PBR PSR PCR에 대한 시그널도 낮은 것부터 추려낸다. PER만 1부터 나머지는 0.1부터
s1={}
s2={}
s3={}
s4={}
s5={}
#20%시가총액 뽑을 수가 없어서 5000개 종목 중 1000개로 선정
for t in terms:
s1[t]=fs.fn_filter(data[t],by='Market Cap', floor=0, n=1000, asc=True)
s2[t]=fs.fn_score(data[t], by='PER', method='relative', floor=1, asc=True)
s3[t]=fs.fn_score(data[t], by='PBR', method='relative', floor=0.1, asc=True)
s4[t]=fs.fn_score(data[t], by='PSR', method='relative', floor=0.1, asc=True)
s5[t]=fs.fn_score(data[t], by='PCR', method='relative', floor=0.1, asc=True)
상대평가 처리했고, score에 대한 합치기 해보자.
s6={}
for t in terms:
s6[t] = fs.combine_score(s2[t], s3[t], s4[t], s5[t])
그러면 아래와 같이 2011 Q3 기준으로 점수가 합산되어 있다. 각 항목(PER PBR PSR PCR)에 대한 점수를 합산한 대로 내림차순으로 나열된다.
[1535 rows x 5 columns], '2011Q3': Score Score_ Score_ Score_ Sum
symbol
SOL 25.000 24.850 24.925 24.850 99.625
GOOG 24.975 24.875 23.425 24.825 98.100
CAAS 24.850 24.075 23.625 24.575 97.125
HPQ 24.675 23.350 24.450 24.600 97.075
SPXC 24.275 24.500 24.300 23.800 96.875
... ... ... ... ... ...
HALO NaN 0.175 0.425 NaN 0.600
VHC NaN 0.375 0.025 NaN 0.400
INVA NaN NaN 0.225 NaN 0.225
GSG NaN NaN 0.075 NaN 0.075
MNKD NaN NaN 0.025 NaN 0.025
분기별로 50개 종목을 선정해 본다.
s={}
signal = {}
for t in terms:
s[t]=fs.combine_signal(s6[t], s1[t], how='and',n=50)
signal[t] =list(s[t].index)
s 리스트에 대해서 시가총액 리스트와 교집합 시켜서 signal을 만들어본다. 예를 들어 2021Q4에 대해 종목이 필터 된 것을 확인해 보면
print(signal['2021Q4'])
['OPFI', 'LX', 'XYF', 'SSY', 'SALM', 'LDI', 'BZH', 'TPC', 'CLPS', 'COWN', 'CONN', 'DHC', 'QIWI', 'RYAM', 'BBAR', 'PANL', 'NLS', 'BEST', 'CRESY', 'AAN', 'LAZY', 'GLBS', 'TCS', 'QD', 'ADES', 'VIOT', 'CTRM', 'JOB', 'FHS', 'CVLG', 'BGFV', 'PFSW', 'ACCO', 'MITT', 'CPOP', 'UONEK', 'KIRK', 'OVID', 'MOD', 'BXC', 'SB', 'ACTG', 'TLYS', 'EMKR', 'CBAT', 'RCON', 'USX', 'OPRT', 'KOP', 'RVP']
이 정도가 추려졌는데, 정말 한 번도 들어 본 적이 없는 종목이다. 이제 백테스트를 돌려보면 아래와 같다. 가격은 재무제표 발표 후 3개월 후의 주가를 이용하는 것이다.
df=fs.backtest(signal=signal, data=data, m=3, cost=0.001)
결과를 보면 아래와 같다.
CAGR: 5.68%
Accumulated return: 91.54%
Investment period: 11.8yrs
Sharpe ratio: 0.25
MDD: -51.66%
미국 주식으로는 연평균 수익률 5.68% 밖에 되지 않는다. 역시 미국에서 초보자는 S&P500이 최고 ~!라고 생각할지는 모르겠지만, 만약 대형주로 바꿔서 백테스트를 해보면 (asc=False로 변경) 어떨까?라는 생각을 해볼 수도 있다.

s1만 시가총액의 순서이므로 False로 해서 순서만 돌려주자 (상위 20% 종목 발굴)
for t in terms:
s1[t]=fs.fn_filter(data[t],by='Market Cap', floor=0, n=1000, asc=False)
s2[t]=fs.fn_score(data[t], by='PER', method='relative', floor=1, asc=True)
s3[t]=fs.fn_score(data[t], by='PBR', method='relative', floor=0.1, asc=True)
s4[t]=fs.fn_score(data[t], by='PSR', method='relative', floor=0.1, asc=True)
s5[t]=fs.fn_score(data[t], by='PCR', method='relative', floor=0.1, asc=True)
s6[t] = fs.combine_score(s2[t], s3[t], s4[t], s5[t])
s[t]=fs.combine_signal(s6[t], s1[t], how='and',n=50)
signal[t] =list(s[t].index)
df=fs.backtest(signal=signal, data=data, m=3,cost=0.001)
for 문 돌렸던 것을 한 번에 처리하고 백테스트를 돌렸다. 더 구리다. 시가총액이 높은 순서로 사서 저 PER, PBR, PSR, PCR의 순서대로 점수를 먹여서 리벨런싱 하는 것도 답이 아니라는 말이다.
CAGR: 4.11%
Accumulated return: 60.61%
Investment period: 11.8yrs
Sharpe ratio: 0.22
MDD: -45.61%
2011년 1분기부터 2022년 4분기까지의 포트폴리오의 수익률과 벤치마크 수익률의 비교이다.

상승장에서 소형주가 대형주보다 상승률이 더 높았다는 뜻인데, 어줍잖은 전략을 쓸바에는 그냥 S&P500사자.
슬기로운 퀀트투자 - 2장 일단 해보자 편
미국 주식으로 시작하는 슬기로운 퀀트투자 이 책은 서학개미를 위한 미국 주식 퀀트투자 입문+실습서이다. 퀀트투자는 수학적, 통계적 기법을 활용해 투자 종목을 발굴하는 투자 방법이다. 퀀
jhhistory.tistory.com
슬기로운 퀀트투자 3장 - 단기 투자의 기술 편 (보조지표를 통한 백테스팅)
미국 주식으로 시작하는 슬기로운 퀀트투자 이 책은 서학개미를 위한 미국 주식 퀀트투자 입문+실습서이다. 퀀트투자는 수학적, 통계적 기법을 활용해 투자 종목을 발굴하는 투자 방법이다. 퀀
jhhistory.tistory.com
댓글