ee3e799de1
주요 변경: - tools/build_rebalance_engine_v1.py: REBALANCE_ENGINE_V1 신규 * account_snapshot 직접 합산(_build_snap_position_map) → 소수주 분리 행 병합 * 레짐 소스 macro.REGIME_PRELIM 최우선 (GAS 와 동일) - src/gas_adapter_parts/gdf_06_rebalance.gs: runRebalanceSheet_() 신규 * Logger.log / getSpreadsheet_() 로 run_all 연동 수정 - src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs * _mergePositionRecord_(): 소수주 중복 행 합산 신규 * parseInt → parseFloat (qty, availQty) - src/gas_adapter_parts/gdf_01_price_metrics.gs * 미보유 종목 SELL_READY → WATCH_EXIT_SIGNAL - spec/41_release_dag.yaml: build_rebalance_sheet 노드 추가 (step_count 63) - spec/51_formula_lifecycle_registry.yaml: REBALANCE_ENGINE_V1 등록 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
174 lines
11 KiB
YAML
174 lines
11 KiB
YAML
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 출력 → '왜'를 즉시 파악 가능"
|