"""테마/카테고리 → 종목코드 정적 매핑. 토스 '주식 골라보기' 의 테마 분류와 비슷한 결을 흉내. 종목/테마 페어링은 공개 정보 기준이며, 시장 사정에 따라 합병/분할/사명변경 등으로 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]