meta:
  title: "은퇴자산포트폴리오 — Final_Action 결정 매트릭스"
  version: "2026-05-17-action_matrix_v1"
  language: "ko-KR"
  role: "canonical"
  purpose: >
    gas_data_feed.gs의 Allowed_Action(매수 게이트) + calcSellDecision_(매도) +
    calcFinalDecision_(통합)이 어떤 조건에서 어떤 Final_Action을 출력하는지
    단일 진실 소스로 문서화한다.
    "왜 지금 매수/매도인가"의 패턴을 Action_Reason 컬럼과 함께 읽으면 된다.

# ─────────────────────────────────────────────────────────────────────────────
# canonical_fields
# ─────────────────────────────────────────────────────────────────────────────
canonical_fields:
  Final_Action:
    role: "외부 소비 canonical field — getDailyBrief, API, ChatGPT 모두 이 값을 참조"
    allowed_values:
      SELL_READY:              "Sell_Validation=PASS — 즉시 HTS 주문 가능"
      SELL_CHECK_QTY:          "Sell_Action 있으나 보유수량 미확인 — 사용자가 확인 후 주문"
      EXIT_SIGNAL:             "Allowed_Action=EXIT_SIGNAL (RW>=3 또는 STOP_OR_TIME_EXIT_READY)"
      EXIT_REVIEW:             "Allowed_Action=REVIEW_EXIT (RW=2 또는 EXIT_REVIEW)"
      BUY_STAGE1_READY:        "SS001_Grade A + Entry_Mode_Gate=PASS + Timing=BUY_STAGE1_READY"
      BUY_BREAKOUT_PILOT_ONLY: "SS001_Grade A + 돌파 파일럿 진입 조건 충족"
      BUY_PULLBACK_WAIT:       "SS001_Grade A/B + 눌림목 대기 (진입 타이밍 준비)"
      WATCH_TIMING_SETUP:      "SS001_Grade A/B이지만 타이밍 미충족 — Allowed_Action=WATCH_CANDIDATE"
      NO_BUY_OVERHEATED:       "과열 지표 발동 (AC_Gate=BLOCK 또는 Val_Surge 과도)"
      HOLD:                    "그 외 전체 — Allowed_Action(NO_ADD/HOLD/HOLD_NO_ADD/OBSERVE_ONLY)으로 세분"

  Action_Params:
    role: "실행 파라미터 압축 — 외부 소비자(getDailyBrief·API·ChatGPT)가 즉시 사용 가능한 실행 정보"
    format:
      SELL_READY:              "{ratio}% {qty}주 @{price}원 | {executionWindow} | {orderType}"
      SELL_CHECK_QTY:          "{sellAction} | 보유수량 미확인"
      EXIT_SIGNAL/EXIT_REVIEW: "보유수량 미확인 — HTS 확인 필요"
      BUY_*:                   "목표 {posSizeQty}주 | 손절 {stopPriceEst}원 | TP1 {tp1Price}원({tp1Qty}주)"
      WATCH_TIMING_SETUP:      "대기 — {timingBlockReason}"
      HOLD/etc:                ""  # 빈 문자열

  Action_Reason:
    role: "왜 이 Final_Action인가를 한 문자열로 요약 — 사람이 읽는 컬럼"
    format:
      SELL_READY:              "{sellDetailLabel} {qty}주 @{limitPrice}원 [{sellReason}]"
      EXIT_SIGNAL/EXIT_REVIEW: "RW{n}({RW1+RW2...}) {exitSignalDetail}"
      BUY_*:                   "SS001:{grade}{normScore}점 RSI{rsi} 이격{disp}% FC{fc}"
      WATCH_TIMING_SETUP:      "SS001:{grade}{normScore}점 타이밍미충족({timingBlockReason})"
      HOLD:                    "HeatBlock({heatPct}%) | {regime} | SS001:{grade}"
      NO_ADD:                  "수급이탈 | 거래대금{억}억 | 스프레드{pct}% | {regime}"
      HOLD_NO_ADD:             "DART:{risk} | 과열({acGate})"
      OBSERVE_ONLY:            "PRICE_MISSING({priceStatus})"

  Allowed_Action:
    role: "내부 계산 중간값 — 매수 게이트 판정. 외부 소비 시 Final_Action 우선."
    allowed_values:
      OBSERVE_ONLY:   "가격 데이터 없음 — 모든 계산 불가"
      HOLD:           "HF005 BLOCK, 레짐 차단, 또는 SS001_Grade C"
      NO_ADD:         "수급이탈 / 거래대금 부족 / 스프레드 과도 / 레짐 차단(미보유)"
      HOLD_NO_ADD:    "DART 리스크 또는 과열 게이트"
      EXIT_SIGNAL:    "RW_Partial >= 3 또는 Timing_Action=STOP_OR_TIME_EXIT_READY"
      REVIEW_EXIT:    "RW_Partial >= 2 또는 Timing_Action=EXIT_REVIEW"
      WATCH_CANDIDATE: "SS001_Grade A/B이지만 타이밍 미충족"
      BUY_STAGE1_READY:        "SS001_Grade A + 타이밍 충족"
      BUY_BREAKOUT_PILOT_ONLY: "SS001_Grade A + 돌파 파일럿"
      BUY_PULLBACK_WAIT:       "SS001_Grade A/B + 눌림목 대기"

# ─────────────────────────────────────────────────────────────────────────────
# 매수 패턴 매트릭스
# ─────────────────────────────────────────────────────────────────────────────
buy_action_matrix:
  purpose: "SS001_Grade × Timing_Action × 제약 → Final_Action 결정 규칙"

  BUY_STAGE1_READY:
    required_all:
      - SS001_Grade: "A"
      - Timing_Action: "BUY_STAGE1_READY"
      - Entry_Mode_Gate: "PASS"
    blocked_if_any:
      - heatBlock: true         # globalHeatPct >= 10%
      - isRiskOffRegime: true   # RISK_OFF or RISK_OFF_CANDIDATE
      - dartRisk: true
      - liquidityFail: true     # flow.ok=F or avgTV5D<50 or spread>0.8%
    caution_if:
      - heatCaution: true       # 7~10% → Pos_Size_Qty × 0.5 & Action_Reason에 표기
    action_reason_template: "SS001:A{score}점 RSI{rsi} 이격{disp}% FC{fc}"

  BUY_BREAKOUT_PILOT_ONLY:
    required_all:
      - SS001_Grade: "A"
      - Timing_Action: "BUY_BREAKOUT_PILOT_ONLY"
      - Entry_Mode_Gate: "PASS"
    blocked_if_any: [heatBlock, isRiskOffRegime, dartRisk, liquidityFail]
    note: "돌파 파일럿 — 전체 수량의 30~50%만 진입. Entry_Mode=BREAKOUT."

  BUY_PULLBACK_WAIT:
    required_any:
      - {SS001_Grade: "A", Timing_Action: "BUY_PULLBACK_WAIT"}
      - {SS001_Grade: "B", Timing_Action: "BUY_PULLBACK_WAIT"}
    blocked_if_any: [heatBlock, isRiskOffRegime, dartRisk, liquidityFail]
    note: "눌림목 대기 — 진입 타이밍 준비 중. 지정가 주문 미리 설정 권장."

  WATCH_TIMING_SETUP:
    required_all:
      - SS001_Grade: ["A", "B"]
    timing_condition: "Timing_Action not in [BUY_STAGE1_READY, BUY_BREAKOUT_PILOT_ONLY, BUY_PULLBACK_WAIT]"
    note: "등급은 되지만 타이밍 미충족. Action_Reason에 구체적 미충족 이유 표기."

# ─────────────────────────────────────────────────────────────────────────────
# 매도 패턴 매트릭스
# ─────────────────────────────────────────────────────────────────────────────
sell_action_matrix:
  purpose: "매도 신호 발생 시 Final_Action 결정 규칙. spec/exit/stop_loss.yaml sell_signal_priority와 연동."

  SELL_READY:
    trigger: "calcSellDecision_()의 Sell_Validation = PASS"
    substates:
      EXIT_100:              "손절전량 — STOP_OR_TIME_EXIT_READY 또는 RW_Partial >= 4"
      REGIME_TRIM_50:        "레짐 50% 축소 — getDailyBrief 포트폴리오 경고로 이동 (방향 A: 개별 종목 신호 아님)"
      TRIM_70:               "RW청산 70% — RW_Partial >= 3 또는 Timing_Exit_Score >= 75"
      TRAILING_STOP_BREACH:  "트레일링이탈 70% — close <= trailing_stop_price 직접 체크"
      TRIM_50:               "RW부분 50% — RW_Partial >= 2 OR (RW_Partial >= 1 AND Timing_Exit_Score >= 50). RW_Partial=0 단독 기술지표로는 TRIM_50 불가."
      PROFIT_TRIM_50/35/25:  "익절 사다리 — Profit_Pct >= 50/30/20"
      TAKE_PROFIT_TIER1:     "TP1 익절 25% — Profit_Pct >= 10"
      TIME_EXIT_100:         "타임스탑 전량 — Days_To_Time_Stop <= 0 (spec priority 6)"
      TIME_TRIM_50:          "타임스탑 50% — Days_To_Time_Stop <= 7 (spec priority 6)"
    canonical_price_field: Sell_Limit_Price
    canonical_qty_field: Sell_Qty
    action_reason_template: "{label} {qty}주 @{price}원 [{reason}]"
    note: >
      복수 조건 동시 발동 시 SL003_PRIORITY_MATRIX 적용:
      Sell_Limit_Price = max(모든 발동 조건의 후보가격). priceSource=PRIORITY_MATRIX_MAX.

  EXIT_SIGNAL:
    trigger: "Sell_Validation != PASS AND (RW_Partial >= 3 OR Timing_Action=STOP_OR_TIME_EXIT_READY)"
    action_reason_template: "RW{n}({items}) {exitSignalDetail}"
    note: "보유수량 미확인 상태. 사용자가 HTS에서 보유수량 확인 후 주문."

  EXIT_REVIEW:
    trigger: "RW_Partial >= 2 OR Timing_Action=EXIT_REVIEW"
    action_reason_template: "RW{n}({items}) 검토"
    note: "매도 검토 단계. 다음 영업일 재확인 권장."

# ─────────────────────────────────────────────────────────────────────────────
# Action_Priority 우선순위 숫자 (calcFinalDecision_ 기준)
# ─────────────────────────────────────────────────────────────────────────────
action_priority_table:
  10: SELL_READY
  20: SELL_CHECK_QTY
  28: EXIT_SIGNAL
  32: EXIT_REVIEW
  50: NO_BUY_OVERHEATED
  60: BUY_STAGE1_READY
  70: BUY_BREAKOUT_PILOT_ONLY
  80: BUY_PULLBACK_WAIT
  90: WATCH_TIMING_SETUP
  99: HOLD
  note: "낮을수록 우선순위 높음. Final_Rank는 Priority_Score 기준 내림차순 정렬 후 부여."

# ─────────────────────────────────────────────────────────────────────────────
# 브리핑 출력 형식 (getDailyBrief — C-1 재구조화)
# ─────────────────────────────────────────────────────────────────────────────
brief_format:
  canonical_source: "Final_Action (not Allowed_Action)"
  sort_within_group: "Final_Rank 오름차순 (Priority_Score 기반)"
  sections_order:
    1: "SELL_READY — 즉시 HTS 주문 가능"
    2: "EXIT_SIGNAL / EXIT_REVIEW — 보유수량 확인 후 매도"
    3: "BUY — 진입 조건 충족 (BUY_STAGE1_READY / BUY_BREAKOUT_PILOT_ONLY / BUY_PULLBACK_WAIT)"
    4: "WATCH — 타이밍 대기 (WATCH_TIMING_SETUP)"
    5: "HOLD / BLOCK — Allowed_Action으로 세분 표시"
  dedup_rule: >
    같은 종목이 SELL_READY이면서 EXIT_SIGNAL도 발생할 수 있다.
    Final_Action=SELL_READY가 최우선 — SELL_READY 섹션에만 출력.
  action_reason_display: "각 종목 한 줄에 Action_Reason 출력 → '왜'를 즉시 파악 가능"
