schema_version: formula_domain.v1 source: C:\Temp\data_feed\spec\13_formula_registry.yaml domain: sector meta: note: > governance/todo/v8_9_p1_adoption_plan.yaml P1-A. source: suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml:sector_graph_engine_v8_9 formulas: SECTOR_EXPOSURE_GRAPH_V1: purpose: > 섹터를 단일 텍스트 라벨이 아니라 L1:L2:L3:L4 canonical ID로 분류하고, ETF 구성종목을 lookthrough하여 직접보유와 합산한 실질노출을 계산하며, AI/반도체/전력 등 테마 간 중복 베타를 residualize해 과집중 진단의 이중계산을 막는다. (governance/todo/v8_9_p1_adoption_plan.yaml P1-A.1, source: suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml:sector_graph_engine_v8_9) canonical_sector_id_format: 'L1:L2:L3:L4, 예: EQ:TECH:SEMIS:HBM' applicable: portfolio_exposure.concentration_caps_v8_9_supplement, PORTFOLIO_TRANSITION_UTILITY_V1.concentration_reduction_benefit_krw 계산 직전. inputs: - field: direct_weight_pct unit: percent note: 종목 직접보유 비중 - field: etf_constituents_json unit: json note: 'ETF 구성종목 리스트 [{ticker, weight_pct}]. ETF_quality_gate(NAV_asof_valid, constituents_asof_valid) 통과 필요.' - field: etf_weight_pct unit: percent note: 해당 ETF의 포트폴리오 내 비중 - field: sector_id unit: string note: canonical_sector_id_format 준수 - field: peer_sector_betas unit: list_of_ratio note: 동일 macro_driver(AI_capex, semiconductor 등)를 공유하는 다른 섹터의 베타 목록 expression: lookthrough_etf_weight_pct: "sum(constituent.weight_pct * etf_weight_pct / 100 for constituent in etf_constituents_json if constituent.sector_id == sector_id)" sector_family_total_pct: "direct_weight_pct + lookthrough_etf_weight_pct" factor_beta_residualized: "factor_beta_raw - sum(shared_variance_with(peer) for peer in peer_sector_betas if peer.macro_driver == self.macro_driver)" output: field: sector_family_total_pct unit: percent additional_outputs: - lookthrough_etf_weight_pct - factor_beta_residualized - theme_overlap_pct gates: - if: sector_family_total_pct > concentration_caps_v8_9_supplement.top3_combined_cap_pct.hard_cap_pct action: HARD_CONCENTRATION_BLOCK missing_policy: etf_constituents_json: ETF_BUY_BLOCKED — constituents_missing (v8.9 V89_016). lookthrough를 0으로 추정 금지. peer_sector_betas: factor_beta_residualized를 raw 값 그대로 사용하고 PARTIAL 표기. 0으로 추정 금지. canonical_ref: spec/risk/portfolio_exposure.yaml:duplicate_exposure_rule implementation: tools/build_sector_exposure_graph_v1.py owner: quant_team lifecycle_state: shadow input_fields: - direct_weight_pct - etf_constituents_json - etf_weight_pct - sector_id - peer_sector_betas output_fields: - sector_family_total_pct - lookthrough_etf_weight_pct - factor_beta_residualized golden_cases: - V89_044_sector_overlap - V89_045_ETF_direct_overlap activation_threshold: min_t20_sample: 30 retirement_condition: performance_degradation LEADER_LIFECYCLE_GATE_V1: purpose: > 종목의 시장 주도력을 CAPTAIN/CORE_LEADER/ENABLER/CYCLICAL_BETA/LAGGARD/DISTRIBUTION_RISK 6개 role로 분류하고, 승급·강등 조건을 결정론적으로 평가한다. LLM이 '주도주라서 산다'는 서사로 role을 임의 부여하는 것을 금지한다. (governance/todo/v8_9_p1_adoption_plan.yaml P1-A.2, source: suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml:sector_graph_engine_v8_9.leader_lifecycle) applicable: SECTOR_EXPOSURE_GRAPH_V1 산출 직후. PORTFOLIO_TRANSITION_UTILITY_V1 candidate_action 생성 전. roles: [CAPTAIN, CORE_LEADER, ENABLER, CYCLICAL_BETA, LAGGARD, DISTRIBUTION_RISK] inputs: - field: relative_strength_leads_sector unit: boolean - field: volume_quality_confirmed unit: boolean - field: above_ma60_or_reclaim_confirmed unit: boolean - field: earnings_revision_status unit: 'enum: positive | neutral | negative' - field: institutional_flow_status unit: 'enum: accumulation | neutral | distribution' - field: current_role unit: enum note: 직전 평가에서 결정된 role. 최초 평가 시 LAGGARD로 시작. promotion_requires_all: - relative_strength_leads_sector == true - volume_quality_confirmed == true - above_ma60_or_reclaim_confirmed == true - earnings_revision_status != negative - institutional_flow_status != distribution demotion_triggers_any: - break_ma60_with_distribution: "above_ma60_or_reclaim_confirmed == false AND institutional_flow_status == distribution" - underperform_sector_20d: relative_strength_leads_sector == false (20거래일 연속) - earnings_revision_negative: earnings_revision_status == negative - crowded_flow_reversal: institutional_flow_status == distribution AND current_role IN [CAPTAIN, CORE_LEADER] role_transition_table: promotion_path: [LAGGARD, CYCLICAL_BETA, ENABLER, CORE_LEADER, CAPTAIN] demotion_path: [CAPTAIN, CORE_LEADER, ENABLER, CYCLICAL_BETA, LAGGARD, DISTRIBUTION_RISK] rule: 승급은 promotion_requires_all 충족 시 promotion_path 다음 단계로 1단계만 이동. 강등은 demotion_triggers_any 발동 시 즉시 DISTRIBUTION_RISK로 직행(단계적 강등 아님 — 보수적 원칙). output: field: leader_role unit: enum additional_outputs: - role_transition_reason - role_changed gates: - if: leader_role == DISTRIBUTION_RISK action: NEW_BUY_BLOCKED missing_policy: 입력 필드 중 하나라도 null이면 role 유지(current_role) + role_transition_reason=DATA_MISSING. 임의 승급/강등 금지. canonical_ref: spec/strategy/leader_scan.yaml implementation: tools/build_sector_exposure_graph_v1.py owner: quant_team lifecycle_state: shadow input_fields: - relative_strength_leads_sector - volume_quality_confirmed - above_ma60_or_reclaim_confirmed - earnings_revision_status - institutional_flow_status - current_role output_fields: - leader_role - role_transition_reason golden_cases: - V89_046_leader_distribution activation_threshold: min_t20_sample: 30 retirement_condition: performance_degradation