""" Super Quality strategy page for the Streamlit Quant application. """ import streamlit as st from datetime import datetime from strategies.factors.f_score import get_f_score 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 date = get_last_year_end() db = DBManager() data = get_f_score(db, date) # 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)) def get_last_year_end(): """Get the last year's end date.""" today = datetime.now() last_year = today.year - 1 last_year_end = datetime(last_year, 12, 31) return last_year_end.date()