- backend/app/seed/themes.py: themes_for_code(code) 헬퍼 추가 (코드 → 소속 테마들)
- backend/app/api/themes.py: GET /api/themes/peer/{code} 신규
· 종목이 속한 모든 테마의 코드 합집합을 peer 풀로
· 단일 SQL (ROW_NUMBER OVER PARTITION BY code) 로 21영업일 누적수익률 일괄 계산
· 본인 / peer 평균 / 상·하위 5종목 반환
- web/lib/api.ts: PeerCompare 타입 + api.peerCompare()
- web/components/PeerComparePanel.tsx: 테마 태그 + 3-칸 요약 (본인/평균/격차) + 상·하위 5
- [code]/page.tsx 에 PeriodReturns 아래 부착. 테마 무소속 종목은 패널 자동 숨김.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
237 lines
7.3 KiB
Python
237 lines
7.3 KiB
Python
"""테마/카테고리 → 종목코드 정적 매핑.
|
|
|
|
토스 '주식 골라보기' 의 테마 분류와 비슷한 결을 흉내. 종목/테마 페어링은 공개
|
|
정보 기준이며, 시장 사정에 따라 합병/분할/사명변경 등으로 stale 될 수 있다.
|
|
KRX 정상화 + sector 컬럼 채우기가 끝나면 DB-driven 분류로 전환 가능.
|
|
|
|
각 테마는 KRX_STATIC_MAJORS 안의 코드 위주 — symbols 테이블에 무조건 존재해
|
|
검색/카드 표시가 깨지지 않음.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Theme:
|
|
slug: str
|
|
name: str
|
|
description: str
|
|
codes: tuple[str, ...]
|
|
|
|
|
|
THEMES: tuple[Theme, ...] = (
|
|
Theme(
|
|
slug="semiconductor",
|
|
name="반도체",
|
|
description="메모리·파운드리·장비 핵심.",
|
|
codes=(
|
|
"005930", # 삼성전자
|
|
"000660", # SK하이닉스
|
|
"042700", # 한미반도체
|
|
"240810", # 원익IPS
|
|
"357180", # LX세미콘
|
|
"403870", # HPSP
|
|
"039030", # 이오테크닉스
|
|
"089030", # 테크윙
|
|
"095610", # 테스
|
|
"067310", # 하나마이크론
|
|
"000990", # DB하이텍
|
|
),
|
|
),
|
|
Theme(
|
|
slug="battery",
|
|
name="2차전지",
|
|
description="셀·소재·장비 전반.",
|
|
codes=(
|
|
"373220", # LG에너지솔루션
|
|
"006400", # 삼성SDI
|
|
"051910", # LG화학
|
|
"247540", # 에코프로비엠
|
|
"086520", # 에코프로
|
|
"066970", # 엘앤에프
|
|
"003670", # 포스코퓨처엠
|
|
"121600", # 나노신소재
|
|
"010130", # 고려아연
|
|
"213420", # 덕산네오룩스
|
|
),
|
|
),
|
|
Theme(
|
|
slug="bio",
|
|
name="바이오·헬스케어",
|
|
description="제약·바이오시밀러·CDMO.",
|
|
codes=(
|
|
"207940", # 삼성바이오로직스
|
|
"068270", # 셀트리온
|
|
"091990", # 셀트리온헬스케어
|
|
"196170", # 알테오젠
|
|
"145020", # 휴젤
|
|
"086900", # 메디톡스
|
|
"069620", # 대웅제약
|
|
"128940", # 한미약품
|
|
"000100", # 유한양행
|
|
"328130", # 루닛
|
|
"237690", # 에스티팜
|
|
"141080", # 리가켐바이오
|
|
"000250", # 삼천당제약
|
|
"028300", # HLB
|
|
"078160", # 메디포스트
|
|
"085660", # 차바이오텍
|
|
),
|
|
),
|
|
Theme(
|
|
slug="game",
|
|
name="게임",
|
|
description="모바일·PC·콘솔 게임사.",
|
|
codes=(
|
|
"035720", # 카카오 (게임즈 모회사)
|
|
"036570", # 엔씨소프트
|
|
"251270", # 넷마블
|
|
"263750", # 펄어비스
|
|
"293490", # 카카오게임즈
|
|
"112040", # 위메이드
|
|
"194700", # 네오위즈
|
|
"078340", # 컴투스
|
|
),
|
|
),
|
|
Theme(
|
|
slug="entertainment",
|
|
name="엔터테인먼트",
|
|
description="K-팝/엔터/플랫폼.",
|
|
codes=(
|
|
"352820", # 하이브
|
|
"035900", # JYP Ent.
|
|
"041510", # 에스엠
|
|
"122870", # 와이지엔터테인먼트
|
|
"067160", # 아프리카TV
|
|
"376300", # 디어유
|
|
"035760", # CJ ENM
|
|
"196300", # 쇼박스
|
|
),
|
|
),
|
|
Theme(
|
|
slug="auto",
|
|
name="자동차",
|
|
description="완성차·부품·타이어.",
|
|
codes=(
|
|
"005380", # 현대차
|
|
"000270", # 기아
|
|
"012330", # 현대모비스
|
|
"086280", # 현대글로비스
|
|
"161390", # 한국타이어앤테크놀로지
|
|
"000240", # 한국앤컴퍼니
|
|
),
|
|
),
|
|
Theme(
|
|
slug="finance",
|
|
name="금융",
|
|
description="은행·증권·보험·지주.",
|
|
codes=(
|
|
"105560", # KB금융
|
|
"055550", # 신한지주
|
|
"086790", # 하나금융지주
|
|
"316140", # 우리금융지주
|
|
"138040", # 메리츠금융지주
|
|
"175330", # JB금융지주
|
|
"138930", # BNK금융지주
|
|
"024110", # 기업은행
|
|
"032830", # 삼성생명
|
|
"088350", # 한화생명
|
|
"000810", # 삼성화재
|
|
"005830", # DB손해보험
|
|
"001450", # 현대해상
|
|
"071050", # 한국금융지주
|
|
),
|
|
),
|
|
Theme(
|
|
slug="shipbuilding-defense",
|
|
name="조선·방산",
|
|
description="조선·중공업·항공우주·방산.",
|
|
codes=(
|
|
"329180", # HD현대중공업
|
|
"009540", # HD한국조선해양
|
|
"267260", # HD현대일렉트릭
|
|
"010620", # 현대미포조선
|
|
"042660", # 한화오션
|
|
"010140", # 삼성중공업
|
|
"012450", # 한화에어로스페이스 (SEED)
|
|
"047810", # 한국항공우주
|
|
"064350", # 현대로템
|
|
"267250", # HD현대
|
|
),
|
|
),
|
|
Theme(
|
|
slug="platform-ai",
|
|
name="플랫폼·AI",
|
|
description="검색·메신저·콘텐츠·로봇.",
|
|
codes=(
|
|
"035420", # NAVER
|
|
"035720", # 카카오
|
|
"277810", # 레인보우로보틱스
|
|
"304100", # 솔트룩스
|
|
"388790", # 케이엠더블유
|
|
),
|
|
),
|
|
Theme(
|
|
slug="consumer-retail",
|
|
name="유통·소비재",
|
|
description="대형마트·백화점·식음료·뷰티.",
|
|
codes=(
|
|
"139480", # 이마트
|
|
"023530", # 롯데쇼핑
|
|
"069960", # 현대백화점
|
|
"004170", # 신세계
|
|
"282330", # BGF리테일
|
|
"007070", # GS리테일
|
|
"097950", # CJ제일제당
|
|
"271560", # 오리온
|
|
"005180", # 빙그레
|
|
"049770", # 동원F&B
|
|
"005300", # 롯데칠성
|
|
"000080", # 하이트진로
|
|
"002790", # 아모레G
|
|
"090430", # 아모레퍼시픽
|
|
"008770", # 호텔신라
|
|
),
|
|
),
|
|
Theme(
|
|
slug="energy-utility",
|
|
name="에너지·유틸리티",
|
|
description="전력·정유·가스·풍력.",
|
|
codes=(
|
|
"015760", # 한국전력공사
|
|
"017670", # SK텔레콤 (필수통신 묶음)
|
|
"030200", # KT
|
|
"010950", # S-Oil
|
|
"047050", # 포스코인터내셔널
|
|
"036460", # 한국가스공사 (SEED)
|
|
"034020", # 두산에너빌리티 (SEED)
|
|
"088980", # 맥쿼리인프라
|
|
),
|
|
),
|
|
)
|
|
|
|
|
|
def themes_index() -> list[dict]:
|
|
return [
|
|
{
|
|
"slug": t.slug,
|
|
"name": t.name,
|
|
"description": t.description,
|
|
"code_count": len(t.codes),
|
|
}
|
|
for t in THEMES
|
|
]
|
|
|
|
|
|
def theme_by_slug(slug: str) -> Theme | None:
|
|
for t in THEMES:
|
|
if t.slug == slug:
|
|
return t
|
|
return None
|
|
|
|
|
|
def themes_for_code(code: str) -> list[Theme]:
|
|
"""주어진 종목 코드가 속한 테마들. 한 종목이 여러 테마에 동시 등재될 수 있음."""
|
|
return [t for t in THEMES if code in t.codes]
|