meta:
  title: "은퇴자산포트폴리오 — 포트폴리오 노출·현금 정책 분할 후보"
  parent_file: "RetirementAssetPortfolio.yaml"  # 2026-06-22 WBS-7.11: spec/03_risk_policy.yaml 삭제로 갱신
  version: "2026-05-18-F10_score_clamp_d2_fix"
  language: "ko-KR"
  timezone: "Asia/Seoul"
  role: "canonical"
  has_code_implementation: true
  code_path: ["spec/strategy/action_matrix.yaml"]
  migration_status: "canonical_split_active"
  authority_rule: "이 split 파일이 해당 섹션의 canonical source다. parent_file은 legacy compatibility index다."

portfolio_exposure_framework:
  principle: "비중 한도는 파산확률 제어 장치. 시장지배 주도주는 벤치마크와 비교해 판단."
  exposure_layers:
    direct_core_leaders: ["삼성전자", "SK하이닉스"]
    duplicate_beta: ["KODEX 반도체", "동일 섹터 ETF"]
    tactical_satellites: ["방산", "조선", "전력설비", "건설", "플랜트/EPC", "로보틱스", "기타 고베타"]
    cash: ["현금", "MMF", "RP", "단기채 ETF"]
  valid_trim_reasons:
    - "벤치마크 대비 초과비중이 허용밴드를 초과하고 가격 추세가 훼손됨"
    - "직접보유와 ETF가 같은 팩터를 중복 보유해 실질노출이 상한 초과"
    - "포트폴리오 현금이 최소 방어현금 이하이고 신규 기회 또는 손실방어 자금이 필요"
    - "외국인·기관 5D/20D 수급 동반 이탈 + 거래대금 동반 장대음봉"
  sell_priority_engine:
    purpose: >
      여러 보유 종목·ETF가 동시에 SELL/TRIM/ROTATE 후보가 되었을 때
      어떤 주문을 먼저 실행할지 결정하는 포트폴리오 단위 우선순위 규칙.
      stop_loss.sell_signal_priority는 종목 내부 매도 신호 우선순위이고,
      이 엔진은 계좌·현금·중복노출·상대약세를 종합한 후보 간 정렬 기준이다.
    activation_any:
      - "cash_floor.trim_required_when 충족"
      - "duplicate_exposure_rule 발동"
      - "동일 리포트에서 SELL/TRIM/ROTATE 후보가 2개 이상"
      - "risk_off 또는 event_week 현금 상향 필요"
      - "보유주 진단에서 축소/교체 후보가 2개 이상"
    required_inputs:
      - "capture_read_ledger.current_holding_quantity"
      - "average_cost"
      - "current_price"
      - "unrealized_return_pct"
      - "current_weight_pct"
      - "target_weight_pct 또는 target_band"
      - "duplicate_exposure_status"
      - "cash_floor_status"
      - "rw_partial 또는 relative_weakness_exit 합계"
      - "liquidity_status 또는 AvgTradeValue_5D_M"
      - "account_type"
      - "tax_cost_estimate 또는 세금 미확인 표시"
    hard_precedence:
      1: "법적·거래정지·상장폐지·회계 리스크 또는 stop_loss.emergency — 수량 확인 시 최우선"
      2: "hard stop 손절, trailing_stop 이탈, time_stop 만료, RW>=4 — stop_loss.sell_signal_priority 적용"
      3: "cash_floor 미달 또는 policy_event_week_escalation 현금 상향 — 현금 확보 목적 매도 우선"
      4: "중복노출 상한 초과 — duplicate_exposure_rule.funding_order 적용"
      5: "보유주 진단 교체/축소 후보 — current_holdings_score 낮은 순"
      6: "익절·리밸런싱 후보 — 세후 비용 대비 이탈폭 큰 순"
      7: "단순 수익 실현 — 다른 우선순위가 없을 때만"
    candidate_scoring:
      purpose: "동일 hard_precedence 단계 안에서 후보를 정렬하는 보조 점수. 점수가 높을수록 먼저 매도."
      score_range: "0~100"
      clamp: "Sell_Priority_Score = min(max(formula_result, 0), 100)"
      clamp_rationale: >
        컴포넌트 합산 시 이론상 최대 122점 초과 가능(예: hard_stop 50 + 중복ETF 20 + 상대약세 20 + ...).
        100 초과 점수는 LLM 임의 가중치 개입 증거로 오인되므로 반드시 min(result, 100) clamp 적용.
        clamp 발동 시 출력에 "[CLAMP 발동: raw={raw_score} → 100]" 표기 필수.
      formula: >
        Sell_Priority_Score = min(max(
          hard_precedence_points
          + duplicate_exposure_points
          + cash_relief_points
          + weakness_points
          + overweight_points
          + liquidity_points
          - tax_penalty_points
          - core_quality_protection_points
        , 0), 100)
      components:
        hard_precedence_points:
          emergency_or_hard_stop: 50
          cash_floor_trim_required: 40
          duplicate_exposure_required: 30
          holding_diagnosis_rotate_or_trim: 20
          take_profit_rebalance: 10
        duplicate_exposure_points:
          same_sector_etf: 20
          lower_quality_duplicate: 15
          no_duplicate: 0
        cash_relief_points:
          expected_net_cash_pct_ge_3: 15
          expected_net_cash_pct_1_to_3: 10
          expected_net_cash_pct_lt_1: 3
        weakness_points:
          rw_ge_4_or_rs_laggard: 35  # RS_RATIO_V1: rs_ratio < 0.80 (시장 대비 20%+ 약세)
          rw_3_or_momentum_decel: 25
          rw_2: 8
          flow_5d_20d_negative: 15
          price_below_ma20_ma60: 10
        weakness_formula_refs:  # [2026-05-19_ALPHA_SHIELD_V1] X3 rs_laggard 공식 확정
          rs_laggard:
            formula_id: "RS_RATIO_V1"
            definition: "rs_ratio = stock_5d_return / kospi_5d_return < 0.80"
            rs_leader_protection: "rs_ratio >= 1.20 -> sell_priority 후순위 보호"
            canonical_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.RS_RATIO_V1"
        overweight_points:
          above_target_by_5p_or_more: 12
          above_target_by_2_to_5p: 6
          within_band: 0
        liquidity_points:
          avg_trade_value_sufficient: 5
          low_liquidity: -10
        tax_penalty_points:
          high_tax_or_fee_drag: 10
          unknown_tax_cost: 3
          tax_loss_harvest_available: -5
        core_quality_protection_points:
          direct_core_leader_in_valid_uptrend: 20
          A_core_or_CSCs_ge_85: 12
          satellite_or_etf: 0
    tie_breakers:
      1: "더 높은 hard_precedence 단계"
      2: "Sell_Priority_Score 높은 후보"
      3: "cash_floor 개선 효과가 큰 후보"
      4: "중복노출 해소 효과가 큰 후보"
      5: "current_holdings_score 낮은 후보"
      6: "세후 비용이 낮은 후보"
      7: "유동성이 충분해 지정가 체결 가능성이 높은 후보"
    account_priority:
      principle: "계좌별 세금·한도·출금 가능성을 고려하되, hard stop 손절을 세금 이유로 지연하지 않는다."
      taxable_account: "세금비용이 크면 동일 우선순위 내에서 후순위. 단, 손절·hard stop은 예외."
      isa: "비과세/분리과세 한도와 납입 여력 확인 후 리밸런싱 후보 우선 가능."
      pension: "장기 코어·해외ETF 성격은 보호. 위성·중복 ETF만 우선 축소."
    output_required:
      table: "sell_priority_decision_table"
      columns: ["우선순위", "계좌", "종목명", "현재보유수량", "매도가능검산", "매도유형", "우선순위단계", "Sell_Priority_Score", "예상순현금", "세금/비용", "execution_style", "immediate_sell_qty", "rebound_wait_qty", "max_daily_qty", "매도사유", "보류사유", "다음확인사항"]
    smart_cash_raise_execution:  # [2026-05-20_APEX_V1] 현금확보 매도 품질 하네스
      purpose: >
        현금 부족 해소 목적의 매도를 단순 우선순위 정렬에서 끝내지 않고,
        과매도 반등 대기·분산 위험 즉시 감축·수익보호 감축·체결 품질 제한까지 연결한다.
      formula_refs:
        cash_raise_plan: "spec/13b_harness_formulas.yaml:SMART_CASH_RAISE_PLAN_V1"
        rebound_trigger: "spec/13b_harness_formulas.yaml:REBOUND_SELL_TRIGGER_V1"
        sell_quantity_allocator: "spec/13b_harness_formulas.yaml:SELL_QUANTITY_ALLOCATOR_V1"
        execution_quality: "spec/13b_harness_formulas.yaml:EXECUTION_QUALITY_GUARD_V1"
      execution_styles:
        URGENT_LIQUIDITY_TRIM:
          trigger: "cash_floor_status=HARD_BLOCK AND rsi14>=35 AND bb_position>=20"
          rule: "sell_priority rank 순서대로 immediate_sell_qty 배정. 단일 종목 일괄 전량 금지."
        OVERSOLD_REBOUND_SELL:
          trigger: "rsi14<35 OR bb_position<20 OR close < ma20 - 8%"
          rule: "즉시 매도는 cap 이하. 잔여는 rebound_wait_qty로 분리하고 REBOUND_SELL_TRIGGER_V1 충족 시 실행."
        DISTRIBUTION_EXIT:
          trigger: "distribution_risk_score>=70"
          rule: "반등 대기보다 감축 우선. 단 시장가 전량 매도와 불리한 추격 정정 금지."
        PROFIT_PROTECT_TRIM:
          trigger: "profit_preservation_state IN [PROFIT_LOCK_20, PROFIT_LOCK_30]"
          rule: "수익 포지션 일부 이익잠금. secular_leader_gate_active=true이면 하네스 defer 상태 우선."
      quantity_policy:
        allocator: "SELL_QUANTITY_ALLOCATOR_V1"
        immediate_qty_cap_default_pct: 25
        max_daily_qty_default_pct: 50
        rebound_wait_required_when: "execution_style=OVERSOLD_REBOUND_SELL"
      prohibition:
        - "현금 부족을 이유로 과매도 후보를 전량 즉시 매도 금지"
        - "rebound_wait_qty를 LLM이 immediate_sell_qty로 이동 금지"
        - "execution_quality_status != PASS인 행을 HTS 주문표 PASS로 출력 금지"
        - "코어 주도주 직접보유는 tier 1~8 후보 소진 전 현금확보 1순위 금지"
    prohibition:
      - "보유수량 미확인 상태에서 Sell_Priority_Score가 높아도 매도수량 숫자 출력 금지"
      - "세금 최적화를 이유로 hard stop·cash_floor hard stop 매도를 지연 금지"
      - "주도주 직접보유를 중복 ETF보다 먼저 매도하려면 hard stop 또는 명확한 thesis 훼손 근거 필요"
      - "매도 우선순위를 산문으로만 설명하고 표를 생략 금지"
      - "sell_priority_decision_table 없이 복수 매도 후보 중 특정 종목 수량을 확정 금지"
    # ── 현재 시장 국면 주도 섹터 종목 TRIM 방어 규칙 ────────────────────────────
    # 배경: REGIME_TRIM_50 등 현금 확보 목적 매도 시 sell_priority_engine을 거치지 않으면
    #       시장 주도 섹터 직접보유 종목(SK하이닉스 등)이 잘못 1순위로 선택되는 오류 발생.
    regime_leading_sector_protection:
      rule: >
        현재 market_regime_state가 LEADER_CONCENTRATION 또는 SECULAR_LEADER_RISK_ON이고
        sector_flow Rotation_Rank 1~2위 섹터의 직접보유 종목은
        현금 확보 목적 매도(REGIME_TRIM_50·cash_floor_raise)에서 sell_priority tier=9(마지막) 고정.
      override_conditions_all_required:
        - "해당 종목에 hard_stop(EXIT_100·TRAILING_STOP_BREACH) 발동"
        - "OR thesis 훼손 명시: 실적 하향·외국인+기관 20D 동반 순매도·MA20+MA60 동시 이탈"
        - "OR tier 1~8 후보가 모두 소진되어 남은 선택지가 없음"
      gas_check: >
        runSellPriority() 결과에서 해당 종목이 tier=9로 표시되는지 확인.
        tier=9가 아닌 상위 순위로 나타나면 calcSellPriorityScore_() 입력 데이터 오류.
      prohibition:
        - "현재 장 주도 섹터(반도체·AI전력 등) 직접보유를 '수익이 있어서' 이유로 1차 매도 선정 금지"
        - "ETF 중복노출(KODEX 반도체·TIGER AI전력기기) 미확인 상태에서 코어주도주 TRIM 금지"
        - "sell_priority_table 출력 전 '주도주를 먼저 팔자'는 판단 표현 금지"
  duplicate_exposure_rule:
    calculation: "반도체 실질노출 = 삼성전자 직접비중 + SK하이닉스 직접비중 + 반도체 ETF × ETF 반도체 순도"
    funding_order: ["①중복 섹터 ETF", "②20D 수급 이탈·20일선 하회 위성", "③동일 섹터 내 후순위", "④시장지배 코어 직접보유 (마지막)"]
    leader_quality_switch:  # [proposal_86 / 2026-05-16] ETF→반도체 주도주 직접 전환 가속
      purpose: >
        SECULAR_LEADER_RISK_ON 국면에서 반도체 ETF 성과가 직접 주도주 대비 열위일 때
        ETF 비중을 감축하고 직접 주도주로 노출 품질을 높인다.
        etf_staged_reduction(proposal_58)과 병행 가능. 이 규칙이 우선 발동 트리거 역할.
      trigger_required_all:
        - "market_regime_state == SECULAR_LEADER_RISK_ON"
        - "same_sector_etf_weight_pct > 0 (반도체 ETF 보유 중)"
        - "삼성전자 OR SK하이닉스 중 1개 이상이 SECULAR_LEADER_RISK_ON 조건 통과"
        - "ETF Ret10D <= 직접 주도주(삼성전자 OR SK하이닉스) Ret10D - 3%p"
      action:
        step_1: "ETF 30~50% 감축 검토 (etf_staged_reduction.step_1과 동일 규칙 적용)"
        step_2: "감축 대금은 동일일 즉시 전량 투입 금지. 2거래일 이내 대기."
        step_3: "2거래일 이내 주도주 staged_entry_v2 stage_1 OR stage_2 조건 충족 시 전환 매수 실행"
      prohibition:
        - "ETF 매도대금으로 anti_climax_buy_gate 미통과 주도주 추격매수 금지"
        - "ETF 감축 후 semiconductor_total_cap 초과 직접매수 금지"
        - "SECULAR_LEADER_RISK_ON 비활성 시 이 규칙 적용 금지 — etf_staged_reduction만 사용"
        - "ETF 성과 비교 데이터 미확인 시 트리거 발동 금지"
      output_table:
        columns:
          - "ETF종목명"
          - "ETF_Ret10D(%)"
          - "직접주도주_Ret10D(%)"
          - "차이(%p)"
          - "감축검토수량"
          - "전환대상주도주"
          - "전환실행예정일"

    etf_staged_reduction:  # [proposal_58 / 2026-05-15] ETF-개별주 중복 노출 단계적 감축
      purpose: >
        ETF-개별주 중복 노출 발생 시 즉각 전량 청산이 아닌 단계적 감축으로
        추세 지속 기회비용 손실과 중복 리스크를 동시에 관리한다.
      trigger: >
        duplicate_exposure_rule 발동 조건 충족:
        동일 섹터 ETF + 개별주 동시 보유 비중이 총자산 대비 20%p 이상
      step_1_immediate_reduction:
        action: "ETF 보유수량의 50% 즉시 매도 (지정가, 당일 장중)"
        rationale: "중복 노출 절반 즉시 해소. 개별주로의 집중 시작."
        order_type: "지정가 — 전일종가 -0.5% 이하 호가 우선. 시장가 금지."
        prohibition:
          - "패닉 시장가 전량 매도 금지"
          - "ETF를 매도하기 전 개별주 비중이 총자산 15% 미만인 경우 step_1 연기 가능"
      step_2_sector_trend_check:
        timing: "step_1 실행 후 2~5거래일 대기"
        conditions:
          continue_reduction:
            description: "잔여 50% 추가 매도 진행 조건"
            criteria:
              - "sector_flow.Rotation_Score 해당 섹터 순위가 3위 이하로 하락"
              - "주도섹터 ETF(대표지수) 대비 해당 ETF Ret10D <= -3%p"
              - "daily_leader_scan에서 해당 섹터 C5=0 (섹터 주도 상실)"
            action: "잔여 50% 매도. 섹터 ETF 포지션 완전 청산."
          hold_remainder:
            description: "잔여 50% 유지 조건"
            criteria:
              - "sector_flow.Rotation_Score 해당 섹터 순위 1~2위 유지"
              - "Ret10D_ETF >= 주도섹터ETF Ret10D -2%p 이내"
              - "daily_leader_scan에서 해당 섹터 C5=1 유지"
            action: >
              잔여 50% 30거래일 추가 보유. 30거래일 후 step_2 재점검.
              개별주 비중이 총자산 25% 이상 확대된 시점에 자동 매도 전환.
      step_3_full_exit_trigger:
        description: "단계 관계없이 즉시 전량 매도하는 비상 조건"
        conditions:
          - "해당 ETF ATR20_Status=DATA_MISSING AND 2거래일 이상 지속"
          - "섹터 crash_intraday_protocol tier_B 이상 발동"
          - "weekly_circuit_breaker.circuit_break 발동"
          - "portfolio_hard_stop_procedure 발동"
        action: "전량 지정가 분할 매도. 1거래일 내 완료."
      output_table:
        columns:
          - "ETF종목명"
          - "현재보유수량"
          - "1단계매도수량"
          - "잔여수량"
          - "섹터추세판정"
          - "2단계처리방향"
          - "완전청산예상일"
      prohibition:
        - "step_1 실행 없이 step_2로 바로 진행 금지 — 50% 우선 감축은 필수"
        - "ETF 매도 후 동일 섹터 다른 ETF 즉시 매수로 대체 금지 (중복 재발)"
        - "개별주 미확보 상태에서 ETF 전량 청산 금지 — 섹터 노출 완전 소멸 방지"
  target_allocation_structure:  # [proposal_55 / 2026-05-15] 3-버킷 목표 운영 구조
    purpose: >
      포트폴리오 운영 목표 비중을 3개 버킷으로 명시.
      실제 비중이 목표 밴드를 벗어나면 리밸런싱 신호 발생.
      각 버킷은 기존 규칙(special_exception·cash_floor·sector_cap)에 우선하지 않는다.
      목표 구조는 방향성 지침이며, 기존 risk_block이 항상 우선한다.
    buckets:
      core_bucket:
        label: "코어 (Core)"
        target_pct: 65
        band: "60~72%"
        definition: >
          CSCS >= 70 종목 또는 special_exception 대상.
          삼성전자·SK하이닉스·방산·조선 대장주·전력기기 주도주.
          60거래일 이상 보유 목표.
        rebalancing_trigger:
          trim: "코어 합산 비중 > 72% → 초과분 부분 익절 검토 (take_profit.rebalancing_trim 준용)"
          add:  "코어 합산 비중 < 60% AND orbit_state != unrealistic → A등급 코어 추가매수 검토"
        prohibition:
          - "코어 버킷 확대를 이유로 위성·현금 버킷 한도 이하로 압박 금지"
      tactical_satellite_bucket:
        label: "전술 위성 (Tactical Satellite)"
        target_pct: 20
        band: "10~25%"
        definition: >
          CSCS < 70 종목. staged_entry_v2 탐색매수→확인매수→주력편입 대상.
          평균 보유 목표 20~60거래일.
          이 버킷이 수익률 엔진이다. 목표 구조 내에서 최대 25%까지 허용.
        rebalancing_trigger:
          trim: "위성 합산 비중 > 25% → relative_weakness_exit 점수 높은 종목부터 감축"
          add:  "위성 합산 비중 < 10% AND daily_leader_scan 탐색 후보 존재 → 탐색매수 1건 검토"
        concentration_rule: "단일 위성 종목 최대 7% (stock_model.core_satellite_rule.max_weight 준수)"
      cash_fc_bucket:
        label: "현금 + 탐색 실패 허용 예산 (Cash + FC)"
        target_pct: 15
        band: "10~22% (MRS 연동 가변)"
        definition: >
          즉시 인출 가능 현금 + D+2 추정현금성자산.
          MRS 기반 목표현금(market_risk_score_based_cash) + FC 예산(explore_loss_budget) 합산.
          강한 상승장(MRS=0): 하한 10%. 리스크 이벤트(MRS=8~10): 상한 22%.
        fc_reserve:
          formula: "FC_reserve = 총자산 × 0.025 (월 탐색 실패 허용 예산)"
          note: "FC_reserve는 현금 버킷 내 별도 슬롯. 탐색매수 손절 시 FC_reserve에서 차감."
        rebalancing_trigger:
          raise: "현금 < 10% OR MRS 기반 목표현금 > 현재 즉시현금 → 위성 부분 매도 또는 코어 익절"
          deploy: "현금 > 22% AND orbit_state=achievable → 탐색매수·확인매수 적극 집행"
        orbit_adjustment_rule:  # [proposal_72 / 2026-05-15] orbit_monthly_tracker 오프셋 결과 하한 clamp
          purpose: "orbit_monthly_tracker의 cash_floor 하한 조정 오프셋이 현금 버킷을 10% 미만으로 밀어내는 것을 방지."
          clamp_rule: "최종 현금 목표 = max(orbit_adjusted_target, dynamic_cash_floor)"
          dynamic_cash_floor:  # [proposal_85 / 2026-05-16] SECULAR_LEADER_RISK_ON 시 clamp 하한 7%
            secular_leader_risk_on: "7% — SECULAR_LEADER_RISK_ON 조건 전부 충족 시만 적용"
            normal: "10% — 기본값"
            event_week_or_overheated: "12~15%"
            risk_off: "15~25%"
          secular_leader_risk_on_required_all:
            - "market_regime_state == SECULAR_LEADER_RISK_ON"
            - "MRS <= 3"
            - "Total_Heat < 7%"
          secular_leader_risk_on_note: >
            normal.min_cash_ratio = 7%(cash_floor.regime_numbers.normal)와 동일한 수준.
            즉 SECULAR_LEADER_RISK_ON에서도 기존 normal 하한(7%)을 벗어나지 않는다.
            이 완화는 orbit_clamp 10%와 normal min 7% 사이의 3%p 공간을 해소하는 것.
          calculation_sequence:
            step_1: "orbit_monthly_tracker.adjustment_rules에 따라 orbit_offset(±%p) 계산"
            step_2: "MRS 기반 목표현금(market_risk_score_based_cash) + orbit_offset = orbit_adjusted_target"
            step_3: "SECULAR_LEADER_RISK_ON 조건 충족 시 dynamic_cash_floor = 7%, 미충족 시 10%"
            step_4: "orbit_adjusted_target < dynamic_cash_floor → dynamic_cash_floor로 강제 clamp"
          prohibition:
            - "D+2 추정현금성자산을 즉시현금 cash_floor 충족으로 간주 금지"
            - "SECULAR_LEADER_RISK_ON 해제 즉시 clamp 하한 10%로 복귀 (중간값 유지 금지)"
          note: "clamp 발동 시 블록 5(현금 룰 판정) 출력에 [CLAMP 발동: orbit_offset 일부 무시, floor=X%] 표기 필수."
    bucket_rebalancing_workflow:
      step_1: "매주 수요일 정기점검: 3개 버킷 현재 비중 산출"
      step_2: "MRS 산출 → cash_fc_bucket 목표 조정"
      step_3: "각 버킷 벗어남 확인 → 리밸런싱 우선순위 결정"
      step_4: "리밸런싱 방향: trim 우선(위험 낮추기) → add 이후(기회 포착)"
      priority:
        1: "cash_fc_bucket.raise가 최우선 (risk_block 준수)"
        2: "tactical_satellite_bucket.trim 두 번째"
        3: "core_bucket 조정 마지막"
    output_table:
      columns:
        - "버킷"
        - "목표비중(%)"
        - "현재비중(%)"
        - "목표밴드"
        - "이탈여부"
        - "리밸런싱 방향"
        - "즉시실행 필요"
    prohibition:
      - "목표 구조를 이유로 master_prohibitions·cash_floor·risk_block 완화 금지"
      - "코어 65% 목표를 채우기 위해 위험 코어 종목을 무리하게 매수 금지"
      - "위성 20% 목표를 채우기 위해 anti_climax_buy_gate·minimalist_buy_gate 통과 없이 진입 금지"
    orbit_state_override_rule:  # [proposal_70 / 2026-05-15] orbit_state가 target_allocation_structure add 트리거보다 항상 우선
      purpose: "orbit_state_action_map이 target_allocation_structure의 add 트리거보다 항상 우선함을 명시."
      override_rule:
        unrealistic:
          condition: "orbit_state = unrealistic"
          action: "target_allocation_structure의 모든 add 트리거 무시. orbit_state_action_map.unrealistic 규칙(A등급 1건/월)이 최종 제한."
        stretch:
          condition: "orbit_state = stretch"
          action: "tactical_satellite_bucket.add 트리거 발동 시에도 orbit_state_action_map.stretch 기준(A등급만, 주 최대 3건) 내에서만 실행."
        achievable:
          condition: "orbit_state = achievable"
          action: "target_allocation_structure 버킷 트리거 정상 발동. override 없음."
      priority_order:
        "1": "orbit_state_action_map (최상위)"
        "2": "target_allocation_structure (하위 지침)"
      prohibition:
        - "orbit_state=unrealistic 상태에서 버킷 add 트리거만으로 신규 매수 집행 금지"
        - "orbit_state 확인 없이 버킷 트리거 실행 금지"
  # ── [2026-05-21_CLA_HARNESS_V1] 반도체 클러스터 상태 분리 ───────────────────
  cluster_states:
    purpose: >
      CLA(CONCENTRATED_LEADER_ADVANCE) 레짐 발동 시 반도체 클러스터의 기존보유 유지(HOLD)와
      신규추가매수(BUY)를 분리해 엔진의 '리더 팔고 위성 추가매수' 오판을 구조적으로 차단한다.
    CLUSTER_OPEN:
      description: "기본 상태 (CLA 레짐 비발동). 반도체 O2 섹터 기본 25% 상한 적용."
      trigger: "market_regime != CLA"
      new_buy_allowed: true
      cap_pct: 25
      harness_field: cluster_state
      harness_value: "CLUSTER_OPEN"
    CLUSTER_HOLD_ONLY:
      description: >
        CLA 레짐 발동 시 클러스터 상태. 기존 보유분 HOLD는 허용.
        신규 BUY는 RAG_V1=PASS AND cluster_combined_pct < CLUSTER_CAP_CLA_REGIME_PER 조건 모두 충족 시만 허용.
        O2 반도체 섹터 상한을 기본 25%에서 60%로 상향하여 유동성 보호.
        CLA 해제 시 기본 상한 복귀. (spec/calibration_registry.yaml:CLUSTER_CAP_CLA_REGIME_PER 참조)
      trigger: "market_regime == CLA"
      hold_allowed: true
      new_buy_conditions:
        - rag_v1: PASS
        - cluster_combined_pct_max: calibration_registry.CLUSTER_CAP_CLA_REGIME_PER
      new_buy_blocked_action: HOLD
      cap_pct: 60
      harness_field: cluster_state
      harness_value: "CLUSTER_HOLD_ONLY"
    regime_based_o2_rule:
      rule: >
        O2 반도체 섹터 상한은 cluster_state=CLUSTER_OPEN 에서 기본 25%,
        SECULAR_LEADER_RISK_ON 에서 35%, cluster_state=CLUSTER_HOLD_ONLY 에서 60%까지 적용한다.
        cluster_state=CLUSTER_HOLD_ONLY 에서는 기존 보유분이 25% 초과해도 강제매도 없음.
        신규 추가매수는 RAG_V1 통과 + 60% 상한 적용.
      original_o2: "spec/03_risk_policy.yaml:semiconductor_total_cap"
      cla_override: "CLA 발동 시 O2 적용 범위를 CLUSTER_OPEN 상태로 한정"
    prohibition:
      - "CLA 레짐에서 코어 반도체 직접보유를 현금확보 1순위로 선정 금지"
      - "cluster_state 필드 없이 반도체 종목 SELL 판단 금지"
      - "CLUSTER_HOLD_ONLY 상태를 LLM이 임의로 CLUSTER_OPEN으로 변경 금지"

  # ── [P0-1.1 / governance/todo/v8_9_p0_adoption_plan.yaml] v8.9 제안 신규 cap 필드 ──
  # 배경: v8.9 제안서(suggest/quant_investment_engine_v8_9...)는 단일종목·top3 cap을 제시했으나
  #       기존 spec에는 이름 지정 예외(삼성전자/SK하이닉스 spec/05_position_sizing.yaml,
  #       반도체 클러스터 spec/10_portfolio_rules.yaml)만 있고 "이름 없는 일반 종목"의
  #       기본 상한과 top3 합산 상한이 없었다. 기존 이름 지정 예외를 덮어쓰지 않고
  #       비어있던 두 칸만 채운다.
  concentration_caps_v8_9_supplement:
    note: >
      이 항목은 기존 regime/cluster_state 연동 cash_floor·반도체 cap을 대체하지 않는다.
      spec/05_position_sizing.yaml의 삼성전자(soft 45%/hard 48%)·SK하이닉스(soft 22%/hard 25%)
      named exception과 spec/risk/portfolio_exposure.yaml:target_allocation_structure.
      tactical_satellite_bucket.concentration_rule(단일 위성 7%)이 항상 우선한다.
    default_single_stock_cap_pct:
      applies_when: "named exception(삼성전자·SK하이닉스 등 spec/05_position_sizing.yaml) 또는 위성 7% 규칙이 없는 종목"
      soft_cap_pct: 15
      hard_cap_pct: 20
      over_cap_action: "no_additional_buy; evaluate partial_trim_or_core_ETF_transition_after_tax_cost"
    top3_combined_cap_pct:
      definition: "포트폴리오 내 비중 상위 3개 종목(직접보유, named exception 포함)의 합산 비중"
      soft_cap_pct: 50
      hard_cap_pct: 65
      over_cap_action: "no_additional_buy_increasing_concentration; transition_optimizer가 신규 매수 후보를 hard_constraint_fail로 거부"
    source_proposal: suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml:portfolio_policy_v8_9.concentration_limits

  cash_floor:  # ── 구조 수정: duplicate_exposure_rule 자식(4칸) → portfolio_exposure_framework 직속(2칸) (2026-05-14)
    normal: "총자산 7~10%"
    overheated_or_event_week: "총자산 10~15%"
    risk_off: "총자산 15~25%"
    rule: >
      cash_floor는 immediate_cash + eligible_settlement_cash_d2 (즉시현금 + D+2 정산예정현금)을 현금 방어선으로 준수하되, 계좌 범위와 제한을 엄격히 적용한다.
      일반계좌의 D+2 현금은 cash_defense_eligible=true로 인정하며 일반계좌의 immediate_cash와 합산하여 방어선으로 취급한다.
      ISA/연금저축 계좌의 D+2 및 즉시현금은 일반계좌 신규매수 재원으로 합산하는 것을 금지한다.
      cash_shortfall 계산 로그에는 immediate_cash, settlement_cash_d2, restricted_cash를 명확히 분리 출력한다.
    settlement_rule: "일반계좌의 즉시현금 + D+2 정산현금을 유동성 방어선(cash_floor) 및 매수 가능 자금의 원장으로 인정한다."
    buying_power_rule: "매수가능현금 = (일반계좌 즉시현금 + D+2 정산현금) - 예약된 주문금액."
    numeric_definitions:
      settlement_cash_ratio: "D+2 정산현금 / 총자산 × 100"
      total_cash_ratio: "D+2 정산현금 / 총자산 × 100"
      buy_power_ratio: "(D+2 정산현금 - 예약된 주문금액) / 총자산 × 100"
      post_trade_total_cash_ratio: "(D+2 정산현금 - 신규매수예상금액 + 매도대금정산분) / 총자산 × 100"
    regime_numbers:
      normal:
        min_cash_ratio: 7
        target_cash_ratio: 10
      overheated_or_event_week:
        min_cash_ratio: 10
        target_cash_ratio: 15
      risk_off:
        min_cash_ratio: 15
        target_cash_ratio: 25
    rebalancing_formula:
      buy_allowed_when: "buy_power_ratio >= target_cash_ratio + rebalancing_buy_ratio AND post_trade_total_cash_ratio >= min_cash_ratio"
      trim_required_when: "post_trade_total_cash_ratio < min_cash_ratio OR buy_power_ratio < rebalancing_buy_ratio"
    policy_event_week_escalation:
      base_condition: "미중 정상회담·FOMC·CPI/PCE·고용·금통위·지정학 이벤트가 해당 주에 예정됨"
      raise_to_12pct_if_any: ["USD/KRW 1450원 초과", "VIX 18 초과", "KOSPI/KOSDAQ 장중 고점 대비 -2% 이상", "SOXX -4% 이상 하락", "외국인 KOSPI 일간 순매도 1조원 이상 또는 5D 누적 순매도"]
      raise_to_15pct_if_any: ["USD/KRW 1500원 돌파", "VIX 25 이상", "정책 이벤트 결과가 관세·희토류·반도체 규제·호르무즈 중 2개 이상 악화", "KOSPI 20일선 종가 이탈"]
      execution_priority: ["중복 ETF", "손실 중 약한 위성", "유동성 낮은 테마 ETF", "코어 직접보유"]
      required_output: ["현재 즉시현금비중", "D+2 추정현금성자산", "매수가능현금", "목표 즉시현금비중", "리밸런싱 후 최소 즉시현금비중", "추가 필요 현금", "매도 후보별 예상 순현금", "실행 후 즉시현금비중"]


  # ── [2026-05-18_ROUTING_OPTIMIZATION_V1] 팩터 과집중 수치 임계치 ─────────────
  # 배경: 포트폴리오.yaml 원칙에 "고베타·고모멘텀 과집중 차단"이 추가됐으나
  #       "얼마가 과한 것인가?"에 대한 수치 기준이 없어 AI가 주관적 판단에 의존.
