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


factor_risk_management:
  factor_risk_limit:
    purpose: >
      포트폴리오 전체의 팩터 집중도를 수치로 통제한다.
      개별 종목 리스크 게이트(Total_Heat, stop_loss)와 직교하는
      포트폴리오 레벨 팩터 가드레일이다.
    portfolio_beta:
      formula_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.PORTFOLIO_BETA_V1"
      warning_threshold: 1.2
      hard_cap: 1.3
      action_warning: >
        PORTFOLIO_BETA >= 1.2 → 보고서에 [팩터경보: 포트폴리오 베타 상승] 출력.
        신규 위성 매수 시 고베타(Beta >= 1.5) 종목 추가 검토 신중.
      action_hard_cap: >
        PORTFOLIO_BETA >= 1.3 → 고베타(Beta >= 1.5) 신규 위성 매수 보류.
        기존 고베타 위성은 RW 점수 우선 재점검.
      missing_policy: "Beta 미확인 종목 제외 후 산출. 제외 비중 > 30% 시 PARTIAL 표기."
    single_stock_beta:
      high_beta_threshold: 1.5
      treatment: >
        Beta >= 1.5 종목은 trailing_stop ATR 배수 1.8배 적용
        (take_profit.trailing_stop.high_beta_leader 준용).
      very_high_beta_threshold: 2.0
      action_very_high: >
        Beta >= 2.0 위성은 최대 보유 기간 20거래일 이내로 제한.
        time_stop.satellite 기준 단축 적용.
    momentum_concentration:
      definition: "직전 20거래일 수익률(Ret20D) 기준 상위 2개 종목의 포트폴리오 비중 합산"
      warning_threshold: "상위 2종목 합산 비중 >= 50%"
      hard_cap: "상위 2종목 합산 비중 >= 60%"
      action_warning: "보고서에 [모멘텀 집중 경보] 출력. 신규 고모멘텀 종목 추가 자제."
      action_hard_cap: "상위 종목 중 약한 쪽(RW 점수 높은 쪽)의 비중 5%p 축소 검토."
    sector_beta_concentration:
      definition: "단일 섹터에 Beta >= 1.5 종목이 2개 이상이고 합산 비중 >= 25%"
      action: "해당 섹터 고베타 중 RW 점수 낮은 종목 먼저 비중 조정."
    calculation_frequency: "주간 정기점검(수요일) 및 신규 매수 직전"
    output_table:
      columns: ["항목", "현재값", "경보임계치", "하드캡", "상태", "조치"]
      rows:
        - ["포트폴리오 베타", "[PORTFOLIO_BETA_V1]", "1.2", "1.3", "[OK/경보/차단]", ""]
        - ["모멘텀 집중도(상위2종목비중)", "[산출]", "50%", "60%", "[OK/경보/차단]", ""]
        - ["섹터 고베타 집중", "[산출]", "—", "25%", "[OK/차단]", ""]
    prohibition:
      - "PORTFOLIO_BETA 미산출 상태에서 고베타 신규 위성 추가 허용 금지"
      - "Beta 미확인을 이유로 팩터 리스크 점검 전체를 생략 금지 (부분 산출 PARTIAL로 진행)"
      - "팩터 과집중 경보를 이유로 core 주도주(삼성전자·SK하이닉스) 우선 축소 금지"


  position_count_limit:
    id: "PCL_PORTFOLIO_EXPOSURE_ENFORCEMENT"
    canonical_ref: "spec/01_objective_profile.yaml:position_count_limit"
    purpose: >
      포트폴리오 노출 레이어에서 계좌별 종목 수 상한을 집행한다.
      계좌 성격이 다르므로 통합 합산 한도는 두지 않는다.
      연금저축은 ETF 전용 계좌 — 개별주 카운트 완전 제외.
    counting_rule:
      include: "개별주 직접 보유 (코어 + 위성)"
      exclude: ["ETF", "국내상장 해외ETF", "MMF", "RP", "단기채 ETF", "현금성 상품"]
      pension_scope: "연금저축 전체 제외 — ETF 전용 계좌"
    enforcement:
      PCL001_TAXABLE_HARD_BLOCK:
        condition: "taxable_individual_count >= 10"
        action: "ROTATE_REQUIRED (일반계좌)"
        output: >
          일반계좌 개별주 10종목 도달. 신규 매수 제안 시 교체 후보 1종목 이상 명시.
          교체 후보 선정 기준: financial_health_score 최저 → RW 점수 최고 → 보유 기간 최장.
      PCL002_TAXABLE_CAUTION:
        condition: "taxable_individual_count == 9"
        action: "CAUTION_FLAG (일반계좌)"
        output: "일반계좌 9종목. 신규 매수 전 포트폴리오 전체 검토 권고."
      PCL003_ISA_HARD_BLOCK:
        condition: "isa_individual_count >= 4"
        action: "ROTATE_REQUIRED (ISA)"
        output: "ISA 개별주 4종목 도달. 신규 매수 시 교체 후보 명시."
      PCL004_ISA_CAUTION:
        condition: "isa_individual_count == 3"
        action: "CAUTION_FLAG (ISA)"
        output: "ISA 3종목. 추가 진입 전 검토 권고."
      PCL005_SATELLITE_ROTATION_REVIEW:
        condition: "satellite_count >= 3 AND new_buy_target_bucket == satellite"
        action: "SATELLITE_ROTATION_REVIEW"
        output: >
          위성 3종목 이상 보유 중 추가 위성 진입 검토 시
          financial_health_score 가장 낮은 기존 위성 종목을 교체 후보로 우선 표시.
    output_table:
      columns: ["계좌", "현재 개별주 수", "경보", "하드차단", "상태"]
      rows:
        - ["일반계좌", "[taxable_individual_count]", "9종목", "10종목 이상", "[PASS/CAUTION/ROTATE_REQUIRED]"]
        - ["ISA",     "[isa_individual_count]",     "3종목", "4종목 이상", "[PASS/CAUTION/ROTATE_REQUIRED]"]
        - ["연금저축", "카운트 제외",               "—",     "—",         "ETF 전용"]
    prohibition:
      - "PCL 미집계 상태에서 신규 매수 수량 출력 금지"
      - "ETF를 개별주 카운트에 포함해 한도 여유 있는 것처럼 계산 금지"
      - "연금저축 ETF를 개별주로 오분류해 카운트 금지"
      - "종목 수 초과를 이유로 손절 기준 미도달 종목 강제 매도 금지"

  tactical_cash_buffer:
    amount: "총자산의 5% 고정 (방어용 cash_floor와 완전 별개)"
    purpose: "VIX 30 이상 돌파 또는 KOSPI 1일 -4% 이상 폭락 시에만 집행 가능한 긴급 실탄"
    activation_trigger:
      vix_shock: "VIX >= 30 돌파 확인 (macro 탭 VIX_Close 기준)"
      kospi_crash: "KOSPI 전일 대비 Ret1D <= -4%"
    execution_rule:
      - "A등급 주도주 중 낙폭 과대 종목 한정"
      - "1~2회 분할 지정가 투입"
      - "cash_floor 충족 상태에서만 사용 가능"
    prohibition:
      - "일상적 물타기·평단 낮추기 용도 사용 금지"
      - "B등급 이하 또는 관찰 중 종목 사용 금지"
      - "activation_trigger 미발동 시 사용 금지"
      - "cash_floor와 합산하여 운용 금지. 버퍼는 별도 현금 계선으로 관리."

  executable_rules:
    field_dictionary_ref: "spec/12_field_dictionary.yaml:field_dictionary"
    formula_refs:
      cash_ratios: "spec/13_formula_registry.yaml:formula_registry.formulas.CASH_RATIOS_V1"
      portfolio_band_status: "spec/13_formula_registry.yaml:formula_registry.formulas.PORTFOLIO_BAND_STATUS_V1"
    rules:
      - id: "PE001_CASH_RATIOS"
        inputs: ["immediate_cash", "settlement_cash", "reserved_order_amount", "planned_buy_amount", "sell_cash_proceeds_immediate", "total_asset"]
        formula_ref: "CASH_RATIOS_V1"
        output_fields: ["immediate_cash_ratio", "settlement_cash_ratio", "buy_power_cash", "buy_power_ratio", "post_trade_immediate_cash_ratio"]
        on_missing: "NO_CASH_CHECK"
      - id: "PE002_CASH_FLOOR_GATE"
        inputs: ["post_trade_immediate_cash_ratio", "min_cash_ratio", "buy_power_ratio"]
        rules:
          - {if: "post_trade_immediate_cash_ratio < min_cash_ratio", action: "BUY_BLOCKED_TRIM_REQUIRED"}
          - {if: "post_trade_immediate_cash_ratio >= min_cash_ratio", action: "CASH_GATE_PASS"}
        output_field: "cash_floor_status"
        on_missing: "BUY_BLOCKED_NO_CASH_CHECK"
      - id: "PE003_TARGET_BUCKET_BAND"
        inputs: ["current_weight_pct", "target_band_min_pct", "target_band_max_pct"]
        formula_ref: "PORTFOLIO_BAND_STATUS_V1"
        output_field: "portfolio_band_status"
        on_missing: "DATA_MISSING_NO_ADD_TRIM_DECISION"
      - id: "PE004_DUPLICATE_EXPOSURE"
        inputs: ["same_sector_etf_weight_pct", "single_stock_sector_weight_pct", "etf_purity_ratio"]
        derived_field:
          duplicate_exposure_pct: "same_sector_etf_weight_pct * etf_purity_ratio + single_stock_sector_weight_pct"
        rules:
          - {if: "duplicate_exposure_pct >= 20", action: "ETF_STAGED_REDUCTION_REQUIRED"}
          - {if: "duplicate_exposure_pct < 20", action: "DUPLICATE_EXPOSURE_PASS"}
        output_field: "duplicate_exposure_status"
        on_missing: "DATA_MISSING_REVIEW"
