114 lines
4.7 KiB
Python
114 lines
4.7 KiB
Python
"""
|
|
Super Quality strategy page for the Streamlit Quant application.
|
|
"""
|
|
import streamlit as st
|
|
from datetime import datetime
|
|
from strategies.composite.super_quality import get_super_quality_top
|
|
from db.common import DBManager
|
|
|
|
def render_quality_page():
|
|
"""Render the Super Quality strategy page."""
|
|
st.title("슈퍼 퀄리티 전략")
|
|
|
|
with st.expander("전략 설명", expanded=False):
|
|
st.write("""
|
|
'신F-스코어 3점 + 고GP/A 전략'을 '강환국 슈퍼 퀄리티 전략'이라 명명한다.
|
|
이 전략은 신F-스코어가 3점인 종목을 매수하되, GP/A로 순위를 매겨서 순위가 높은 종목만 매수하는 것이다.
|
|
이 경우 한국에서 수익이 어땠을지 분석해보자.
|
|
연도별로 신F-스코어 3점을 충족하는 종목은 600-700개였다.
|
|
|
|
신F-스코어 3점 기업 내에서도 GP/A가 높은 종목이 3점 종목 평균보다 CAGR 기준으로 3-4% 더 높았다.
|
|
반대로 GP/A가 낮은 종목의 수익률은 상대적으로 저조했다.
|
|
|
|
---
|
|
투자 전략: 강환국 슈퍼 퀄리티 전략 1.0
|
|
레벨: 초, 중급
|
|
스타일: 퀄리티
|
|
기대 CAGR: 약 20%
|
|
매수 전략:
|
|
- 신F-스코어 3점 종목만 매수
|
|
- 여기에 GP/A 순위를 부여, 순위 높은 20-30종목을 매수
|
|
매도 전략: 연 1회 리밸런싱
|
|
|
|
---
|
|
지금까지 소개한 거의 모든 전략에서 소형주 전략이 전체 주식 수익률보다 높았다.
|
|
시가총액 하위 20% 종목의 CAGR을 분석해보았다.
|
|
|
|
---
|
|
투자 전략: 강환국 슈퍼 퀄리티 전략 2.0
|
|
레벨: 초, 중급
|
|
스타일: 퀄리티
|
|
기대 CAGR: 20% 이상
|
|
매수 전략:
|
|
아래 조건을 만족하는 20-30종목 매수
|
|
- 신F-스코어 3점 종목만 매수
|
|
- 여기에 GP/A 순위를 부여, 순위 높은 종목만 매수
|
|
- 단, 소형주(시가총액 최저 20%)만 매수
|
|
매도 전략: 연 1회 리밸런싱
|
|
---
|
|
소형주 중 신F-스코어가 3점인 종목을 찾아보니 2004-2016년 구간에 80-100개 종목이 남았다.
|
|
그 주식들을 통째로 매수해도 CAGR 34.55%를 벌수 있었다!
|
|
정말 상당한 수익이다.
|
|
이 종목들을 다 샀으면 총 1,159개 종목 중 14개가 파산했다.(1.2%)
|
|
또 1년간 마이너스 수익을 기록한 종목이 29.7%였다.
|
|
|
|
신F-스코어가 3점인 종목 중 GP/A가 높은 종목 위주로 매수했으면 (1) CAGR도 조금 개선되고 (2) 최상 30개 종목을 매수했을 경우 선택받은 종목 360개 중 파산한 기업은 단 1개였다.
|
|
F-스코어와 GP/A는 엄청난 잠재력을 지닌 콤비네이션임이 분명하다.
|
|
""")
|
|
|
|
# Strategy implementation
|
|
st.write("## 슈퍼 퀄리티 전략 2.0 포트폴리오")
|
|
|
|
# Get data
|
|
db = DBManager()
|
|
data = get_super_quality_top(db)
|
|
|
|
st.write("### 포트폴리오")
|
|
st.write(data)
|
|
|
|
# # Display options
|
|
# col1, col2 = st.columns([1, 2])
|
|
#
|
|
# with col1:
|
|
# st.write("### 설정")
|
|
# min_f_score = st.slider("최소 F-스코어", min_value=0, max_value=3, value=3)
|
|
# include_small_caps = st.checkbox("소형주만 포함", value=True)
|
|
# num_stocks = st.slider("포트폴리오 종목수", min_value=5, max_value=50, value=20)
|
|
#
|
|
# # Filter data
|
|
# filtered_data = data[data['f-score'] >= min_f_score].copy()
|
|
#
|
|
# if include_small_caps:
|
|
# # Sort by market cap and keep only the bottom 20%
|
|
# filtered_data = filtered_data.sort_values('시가총액')
|
|
# filtered_data = filtered_data.head(int(len(filtered_data) * 0.2))
|
|
#
|
|
# # Sort by GP/A in descending order
|
|
# filtered_data = filtered_data.sort_values('GP/A', ascending=False)
|
|
#
|
|
# # Get top N stocks
|
|
# portfolio = filtered_data.head(num_stocks)
|
|
#
|
|
# # Display portfolio
|
|
# with col2:
|
|
# st.write(f"### 선택된 {len(portfolio)} 종목")
|
|
# st.dataframe(portfolio, use_container_width=True)
|
|
#
|
|
# # Display metrics
|
|
# st.write("### 포트폴리오 지표")
|
|
# col1, col2, col3, col4 = st.columns(4)
|
|
#
|
|
# with col1:
|
|
# st.metric(label="평균 F-스코어", value=f"{portfolio['f-score'].mean():.2f}")
|
|
#
|
|
# with col2:
|
|
# st.metric(label="평균 GP/A", value=f"{portfolio['GP/A'].mean():.2f}%")
|
|
#
|
|
# with col3:
|
|
# avg_market_cap = portfolio['시가총액'].mean() / 1_000_000_000
|
|
# st.metric(label="평균 시가총액", value=f"{avg_market_cap:.1f}십억원")
|
|
#
|
|
# with col4:
|
|
# st.metric(label="종목 수", value=len(portfolio))
|
|
|