Files
kjh2064 0a51702a9a feat(v9-hardening): Complete P3~P6 specs, GAS functions, and MudBlazor UI
Phase 2 implementation complete:

P3 - 손절 체계 재정의 (Stop Loss Taxonomy):
  - spec/exit/stop_loss.yaml: P3 섹션 추가
  - calcAbsoluteRiskStopV1_: 절대 손실 금지선 (entry * 0.92 vs ATR * 1.5)
  - calcRelativeUnderperfAlertV1_: 상대 성과 추적 (WATCH/TRIM_30/TRIM_50/EXIT_100)
  - calcStopActionLadderV1_: 사다리식 액션 결정

P4 - 라우팅 단일화 (Unified Routing):
  - spec/xx_routing_contract.yaml: 4가지 스타일 가중치 정의
  - buildRoutePacket_: SCALP/SWING/MOMENTUM/POSITION 점수 + best_style 결정

P5 - 뒷북 차단 (Anti-Late Entry):
  - spec/exit/pre_distribution_gate.yaml: 배분 위험 조기 감지
  - calcAlphaLeadV1_: Alpha Lead Entry Gate (alpha_lead_score >= 75)
  - calcDistributionRiskV1_: Pre-Distribution Early Warning (risk >= 70)

P6 - 현금확보 (Cash Recovery):
  - spec/exit/cash_recovery.yaml: K2 50/50 분할 + value_damage <= 10%
  - calcCashRecoveryOptimizerV1_: 현금 최적화 (부족액 4,134만원)

UI/UX 개선 (MudBlazor 6.10.0):
  - Dashboard.razor: 단순 버전 (컴파일 에러 제거)
  - MainLayout.razor: Typo enum 수정 (H5→h5, H6→h6)
  - NavMenu.razor: Icons.Material.Filled.Portfolio → Inventory2

릴리스 빌드:
  - dotnet publish -c Release 성공
  - publish 폴더 24MB (배포 준비 완료)

실전 운영 계획:
  - spec/realtime/live_outcome_ledger_plan.yaml: 30건 신호 샘플링 계획
  - honest_proof_score: 56.57 → 95.0 개선 경로 정의
  - 예상 기간: 2026-06-25 ~ 2026-08-10 (약 6주)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-25 17:56:13 +09:00

429 lines
27 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
meta:
title: "은퇴자산포트폴리오 — 손절 정책"
parent_file: "spec/06_exit_policy.yaml"
version: "2026-05-15-F12_split"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "canonical"
migration_status: "canonical_split_active"
# ─────────────────────────────────────────────────────────────────────────────
# 매도 신호 전체 우선순위 계층 (P1-B / 2026-05-17 추가)
# calcSellDecision_()이 이 순서로 평가한다. 상위 조건이 성립하면 하위 조건 무시.
# ─────────────────────────────────────────────────────────────────────────────
sell_signal_priority:
purpose: >
여러 매도 신호가 동시에 발생할 때 어느 신호를 최우선으로 처리할지 결정한다.
신호 충돌 시 "undefined behavior" 방지가 목적.
levels:
1_hard_stop:
condition: "timingAction == STOP_OR_TIME_EXIT_READY OR rw_partial >= 4"
action: "EXIT_100 — 전량 즉시 청산"
gas_field: "Sell_Action=EXIT_100, Sell_Reason=STOP_OR_TIME_EXIT_READY|RW_EXIT_STRONG"
2_risk_off_regime:
condition: "REGIME_PRELIM == RISK_OFF OR RISK_OFF_CANDIDATE (포지션 보유 시)"
action: "REGIME_TRIM_50 — 50% 단계 축소"
gas_field: "Sell_Action=REGIME_TRIM_50, Sell_Reason=REGIME_RISK_OFF|REGIME_RISK_OFF_CANDIDATE"
note: "hard_stop이 없는 경우에만 적용. 전량 청산이 아닌 단계적 축소."
# ── REGIME_TRIM_50 발동 시 sell_priority_engine 의무 실행 ────────────────
# spec: spec/risk/portfolio_exposure.yaml:sell_priority_engine
# REGIME_TRIM_50은 '포트폴리오 레벨' 신호 — 어떤 종목에 적용할지는 sell_priority_engine이 결정한다.
# 개별 종목 내부 신호(level 3~7)로 대상 종목을 임의 선택하는 것은 이 신호의 용도를 오용하는 것.
mandatory_next_step:
rule: >
REGIME_TRIM_50 발동 즉시 sell_priority_engine을 실행하고
sell_priority_decision_table을 출력한 후 상위 tier 종목에만 TRIM 적용.
sell_priority_order: "①하드스탑 → ②매도신호 → ③중복ETF → ④손실위성 → ⑥익절후보 → ⑨코어주도주(마지막)"
gas_endpoint: "?view=sell_priority (runSellPriority() 함수)"
prohibition:
- "sell_priority_decision_table 없이 TRIM 대상 종목을 직접 지정 금지"
- "상승추세 중인 반도체 주도주(SK하이닉스·삼성전자)를 REGIME_TRIM_50 1차 대상으로 선정 금지"
- "ETF 중복노출 종목이 있는데 코어주도주를 먼저 TRIM하는 행위 금지"
2b_rw2b_fast_track: # [2026-05-18_ADVANCED_EXIT_V2] 5D 조기약세 fast track
condition: "RW2b_5d_rapid_weakness == 1 AND rw_partial_excluding_rw2b >= 1"
action: "TRIM_50 — 50% 선제 부분 청산 (fast_track)"
gas_field: "Sell_Action=TRIM_50, Sell_Reason=RW2B_FAST_TRACK"
priority_note: "2번(RISK_OFF)과 3번(rw_partial>=3) 사이 삽입. RISK_OFF 없을 때만 발동."
3_rw_signals:
condition: "rw_partial >= 3 OR timingExitScore >= 75"
action: "TRIM_70 — 70% 부분 청산"
gas_field: "Sell_Action=TRIM_70, Sell_Reason=RW_EXIT|TIMING_EXIT_SCORE"
4_trailing_stop:
condition: "trailingStopPrice 이탈 OR (rw_partial >= 2) OR (rw_partial >= 1 AND timingExitScore >= 50)"
action: "TRIM_50 — 50% 부분 청산. rw_partial=0 + 기술지표만으로는 TRIM_50 불가 → EXIT_REVIEW 강등."
gas_field: "Sell_Action=TRIM_50"
5_take_profit_ladder:
condition: "Profit_Pct >= 10 (TP1/TP2 도달)"
action: "TAKE_PROFIT_TIER1 — 25% 익절"
gas_field: "Sell_Action=TAKE_PROFIT_TIER1|PROFIT_TRIM_*"
6_time_stop:
condition: "Days_To_Time_Stop <= 0"
action: "TIME_EXIT_100 — time stop 만료 전량 청산"
gas_field: "Sell_Action=TIME_EXIT_100, Sell_Reason=TIME_STOP_EXPIRED"
7_scheduled_review:
condition: "분기 정기점검 (수동 검토)"
action: "사람이 결정. 자동 신호 없음."
prohibition:
- "우선순위 높은 신호 무시하고 낮은 신호로 재해석 금지"
- "신호 충돌 시 '관망'으로 모호하게 처리 금지 — 항상 최고 우선순위 신호 적용"
# ─────────────────────────────────────────────────────────────────────────────
# timingExitScore 산출 공식 (calcTimingDecision_ 내 exitScore) — 2026-05-17 v2
# ─────────────────────────────────────────────────────────────────────────────
timing_exit_score_formula:
version: "v2-2026-05-17"
purpose: >
매도 타이밍 강도 점수. 0~100 범위. thresholds: 50=EXIT_REVIEW, 75=STOP_OR_TIME_EXIT_READY.
v1 대비 변경: RW 가중치 상향(20→25), 기술지표 가중치 하향(18→10).
이유: 기술지표(RSI, 이격도, MA20) 단독 신호의 오신호율(30~40%)을 억제하고
수급 기반 RW(상대약세)의 실증적 신뢰도를 반영.
components:
rw_partial:
weight: 25 # 구: 20. 수급 기반 — 외국인·기관 이탈 확인, 신뢰도 높음
max: 100 # rwPartial × 25, cap=100
example: "RW=2 → 50pt (EXIT_REVIEW 단독 도달)"
exit_signal_tokens:
weight: 10 # 구: 18. 기술지표(VOL_EXHAUSTION/MA20_BREAK/DISPARITY_TOP/RSI_OVERBOUGHT) — 노이즈 多
note: "RW=0 + 기술신호 4개 = 40pt → 임계값 미달. RW=1 이상 있어야 TRIM_50 발동."
time_stop_near:
condition: "daysToTimeStop >= 0 AND <= 7"
points: 20 # 구: 25
profit_protect_zone:
condition: "profitPct >= 10"
points: 15 # 구: 10. 실현 이익 보호 중요도 상향.
thresholds:
EXIT_REVIEW: 50
STOP_OR_TIME_EXIT_READY: 75
sell_price_formula:
v2: "close - ATR20 × 0.3 (변동성 비례 보호 하한). ATR 없으면 close × 0.995 폴백."
v1_deprecated: "close × 0.998 (0.2% — 변동성 무시, 사실상 시가 매도)"
trailing_stop_breach: "trailingStop 가격 직접 사용. min(trailingStop, close×0.998) 적용 금지."
# ─────────────────────────────────────────────────────────────────────────────
# [P3: 손절 체계 재정의] ABSOLUTE_RISK_STOP_V1, RELATIVE_UNDERPERFORMANCE_ALERT_V1
# ─────────────────────────────────────────────────────────────────────────────
p3_absolute_risk_stop_v1:
name: "절대 손실 금지선 (P3)"
formula: "max(entry_price * 0.92, entry_price - ATR20 * 1.5)"
purpose: "진입가 대비 절대 손실 8% 또는 변동성 1.5배 중 높은쪽"
quantity_strategy:
immediate: "50% 즉시 매도"
rebound_wait: "50% 반등 대기"
rebound_trigger: "prevClose + 0.5 * ATR20"
order_method: "지정가 주문"
enforcement: "자동 실행, LLM 자유도 없음"
gas_function: "calcAbsoluteRiskStopV1_(entry_price, atr20) → stop_price"
p3_relative_underperformance_alert_v1:
name: "상대 성과 추적 (P3)"
condition: "excess_return_20d <= min(-10%, relative_threshold)"
action_ladder:
step_1: "WATCH: 모니터링 시작 (상대 underperformance -10%~-15%)"
step_2: "TRIM_30: 30% 감소 (상대 underperformance -15%~-20%)"
step_3: "TRIM_50: 추가 20% 감소 총 50% (상대 underperformance -20%~-25%)"
step_4: "EXIT_100: 완전 청산 (상대 underperformance < -25% AND 절대손실 >= 8%)"
forbidden:
- "상대 성과만으로 EXIT_100 금지 (절대손실 8% 미만)"
- "기술지표만으로 TRIM_50 금지"
gas_function: "calcRelativeUnderperfAlertV1_(ret_stock_20d, ret_market_20d) → alert_state"
action_ladder_function: "calcStopActionLadderV1_(alert_state, underperf_pct) → action"
p3_fundamental_thesis_break_v1:
name: "기본 이론 파괴 감지 (P3)"
description: "기업 기본가치 붕괴 신호 (절대/상대와 독립 평가)"
signals:
- "EPS cut ≥ 10%"
- "분기별 매출 성장률 역신장"
- "경쟁사 점유율 급락"
- "법적/규제 문제 발생"
action: "검증 후 EXIT_100 (다른 제약 불적용)"
override_absolute_stop: true
override_relative_alert: true
enforcement: "수동 검증 + 자동 실행"
p3_formula_registry:
- name: "calcAbsoluteRiskStopV1"
inputs: ["entry_price", "atr20"]
output: "stop_price"
formula: "max(entry * 0.92, entry - atr20 * 1.5)"
unit: "KRW"
- name: "calcRelativeUnderperfAlertV1"
inputs: ["return_stock_20d", "return_market_20d"]
output: "alert_state"
states: ["WATCH", "TRIM_30", "TRIM_50", "EXIT_100"]
logic: "ladder transition based on excess_return"
- name: "calcStopActionLadderV1"
inputs: ["alert_state", "underperf_pct", "absolute_loss_pct"]
output: "action"
logic: "WATCH → TRIM_30 → TRIM_50 → EXIT_100 with absolute_loss check"
p3_validation:
- "gap_down 프로토콜: 매도불가 상황에서 WAIT_FOR_OPEN"
- "TICK_NORMALIZER 통과 확인"
- "포지션 크기 조정 후 재진입 재평가"
- "지정가 주문이 체결되지 않으면 시장가 전환"
stop_loss:
principle: "손절가·손절수량·잔여수량·재진입 조건을 함께 제시"
priority_matrix: # [proposal_75 / 2026-05-15] 복수 손절 조건 동시 발동 시 최종 HTS 지정가 결정
purpose: "동일 종목에 trailing_stop·MA20·relative_weakness_exit·time_stop 4개 조건이 동시 발동 시 최종 손절가 결정 규칙"
priority_order:
"1_time_stop": "보유기간 초과 → 매도 시간 강제. 가격보다 시간 우선."
"2_relative_weakness_exit": "RW >= 4 상대약세 → 70~100% 청산 목표가"
"3_trailing_stop": "고점 대비 trailing_stop% 하락가"
"4_MA20_break": "종가 MA20 하향 이탈가"
final_price_rule: "복수 조건 동시 충족 시 → max(time_stop_price, RW_exit_price, trailing_stop_price, MA20_price) 중 가장 높은(보수적인) 값을 최종 손절가로 설정"
quantity_rule: "최종 손절가 결정 후, 청산 수량은 상위 우선순위 조건의 청산 비율 적용 (time_stop: 100%, RW>=4: 70~100%, RW=3: 30~50%)"
output_requirement: "블록4 플레이북에 [손절조건] 열에 발동 조건명과 최종가 계산 근거 명시"
prohibition:
- "복수 조건 중 더 완화된(낮은) 손절가 선택 금지"
- "조건 간 충돌을 이유로 손절 전면 보류 금지"
core: # [P131] ATR 기준 우선. % 기준은 ATR 미확인 시 fallback
# [Q2 / 2026-05-15] "1.5~2.0배" 범위 표현이 HTS 입력 시 어느 값인지 모호해
# LLM이 2.0배 적용 또는 임의 중간값 산출하는 착오 방지. hts_entry_formula를 canonical로 고정.
primary_rule: "HTS 입력 canonical: max(진입가 × 0.92, 진입가 ATR20 × 1.5). 1.5배 고정."
extended_rule: "ATR20_Pct >= 8%(고변동성 종목)인 경우에만 ATR20 × 2.0배 허용. 이 경우 hts_entry_formula에서 1.5 → 2.0으로 교체."
atr_pct_definition: "ATR20_Pct = ATR20 / 현재가 × 100. 8% 기준은 일별 평균 변동폭이 주가의 8%를 초과함을 의미."
fallback_rule: "ATR20 미산출 시에만 매수가 -8% 단일 고정값 (DATA_MISSING 표시)"
hts_entry_formula: "max(진입가 × 0.92, 진입가 ATR20 × 1.5)"
quantity_rule: "트리거 발생 시 50% 손절, 종가 회복 실패 시 잔여 50%"
prohibition:
- "ATR20 미확인 시 -7~-10% 범위 임의 선택 금지. -8% 고정 또는 DATA_MISSING."
- "ATR 기준 손절가와 % 기준이 다르면 더 높은 값(보수적) 채택."
- "ATR20_Pct < 8%인 일반 종목에 2.0배 적용 금지. extended_rule 조건 미충족 시 1.5배만."
satellite: # [P131] 위성 ATR 기준
primary_rule: "20일 ATR 2.0배 이탈 기준"
fallback_rule: "ATR20 미산출 시 매수가 -12% 고정"
quantity_rule: "트리거 발생 시 70%, 종가 회복 실패 시 잔여 30%"
emergency: "60일선 이탈·실적쇼크·회계·거래정지 리스크는 전량 가능"
legacy_position_protocol: # [P147] 사후 뒷북 손절 방지 — 기존 포지션 처리 원칙
detection_rule: # [R5] 레거시 포지션 식별 기준
primary: "capture_read_ledger 판독 결과에서 해당 종목의 stop_price 필드가 0이거나 누락"
secondary: "performance_evidence 로그에 진입 당시 손절가 기록이 없는 포지션"
output_tag: "[레거시포지션] 표기 필수. 신규 ATR 손절가 자동 할당 금지."
condition: "진입 당시 손절가가 미설정된 기존 포지션"
prohibition: "사후 ATR 손절 기계 적용 금지. 20일선 이탈·수급 악화·time_stop 기준 중 하나로만 청산."
damaged_position:
condition: "현재 손실률 -10% 이상"
action: "시장가 투매 금지. 반등 매도(1차 30~50%) / time_stop 청산 / 논리 훼손 손절 중 택1."
xref: "stop_loss.gap_down.principle, stop_loss.time_stop"
gap_down:
principle: "손절가 아래 갭하락 시 09:00~09:15 전량 시장가 매도 금지. 첫 15~30분 저가·거래대금·회복 여부 기록."
sea_timing_integration: # [2026-05-19_ALPHA_SHIELD_APEX_V2] SEA001
rule: >
장중 갭하락 또는 급락 시 즉시 투매 금지. SEA_TIMING_V1 공식을 호출하여
현재가가 VWAP을 하향 돌파하거나 15분 RSI가 과매도(30 미만) 구간에서
반등하는 'Exit Window'를 찾아 분할 매도한다.
action: "VWAP 하향 돌파 확인 시 TRIM 실행. RSI < 30 구간에서는 반등 시까지 유예."
high_beta_exception: "고베타 위성은 -5% 이상 갭하락 + 거래대금 300% 이상 폭증 동시 확인 시 50% 우선 축소 가능."
flow: "외국인·기관 5D 동반 순매도 경고, 20D 수급 이탈 축소"
time_stop:
direction_absent_definition: "20일선 ±2% 박스권 + 거래대금 감소가 10거래일 이상 지속"
core: "60거래일 경과 시 thesis 재검증. 유지 근거 없으면 50% 이상 청산 검토."
satellite: "30거래일 방향성 없음이면 30% 축소 검토, 60거래일이면 잔여 청산 검토."
prohibition: "데이터로 확인된 thesis 개선 없이 time_stop 연기 금지."
reentry:
basic_condition: "20일선 회복 + 거래대금 증가 + 수급 회복 + 기대수익비 2:1 이상 회복"
cooling_off:
default: "최소 5거래일 — 일반 기술적 손절(손절가 하회, 수급 이상 없음)"
flow_exit: "10거래일 — 수급 이탈 손절(Flow_OK=N 판정). 재진입 시 Flow_OK=Y 재확인 필수."
system_risk_exit: "거래일 제한 없음 — unified_engine Tier 발동 후 손절. VIX < 25 AND KOSPI 20일선 회복 확인 시에만 재진입 허용."
double_stop: "60거래일 Watch-only — 동일 종목 30거래일 내 2회 손절"
cooling_priority_rule: "손절 원인이 복합적이면 더 긴 쿨다운 적용. system_risk_exit > double_stop > flow_exit > default 순."
prohibition: "손절 다음 날 반등 또는 손실만회 목적 재매수 금지."
output_examples: "_reference: output_format.unified_example_row_set # [R6] 통합 예시 집합 참조"
executable_rules:
field_dictionary_ref: "spec/12_field_dictionary.yaml:field_dictionary"
formula_refs:
stop_price_core: "spec/13_formula_registry.yaml:formula_registry.formulas.STOP_PRICE_CORE_V1"
trailing_stop_price: "spec/13_formula_registry.yaml:formula_registry.formulas.TRAILING_STOP_PRICE_V1"
rules:
- id: "SL001_CORE_STOP_PRICE"
applies_to: "core"
inputs: ["entry_price", "atr20", "current_price"]
formula_ref: "STOP_PRICE_CORE_V1"
output_fields: ["stop_price", "stop_quantity"]
quantity_rule: "floor(quantity * 0.50)"
on_missing: "NO_STOP_PRICE_OR_DATA_MISSING_FALLBACK"
- id: "SL002_SATELLITE_STOP_PRICE"
applies_to: "satellite"
inputs: ["entry_price", "atr20", "quantity"]
expression: "entry_price - atr20 * 2.0"
fallback_expression: "entry_price * 0.88"
output_fields: ["stop_price", "stop_quantity"]
quantity_rule: "floor(quantity * 0.70)"
on_missing: "fallback_expression with DATA_MISSING tag"
- id: "SL003_PRIORITY_MATRIX"
applies_to: "all_positions"
inputs: ["time_stop_price", "relative_weakness_exit_price", "trailing_stop_price", "ma20_break_price"]
expression: "max(available_trigger_prices)"
output_field: "final_stop_price"
quantity_source: "highest_priority_trigger"
on_missing: "ignore missing trigger price; if all missing then NO_STOP_PRICE"
- id: "SL004_REENTRY_COOLDOWN"
applies_to: "stopped_out_position"
inputs: ["exit_reason", "last_exit_date", "current_trade_date"]
rules:
- {if: "exit_reason == 'system_risk_exit'", min_wait_trading_days: 0, extra_condition: "VIX < 25 AND KOSPI close > KOSPI_MA20"}
- {if: "exit_reason == 'double_stop'", min_wait_trading_days: 60}
- {if: "exit_reason == 'flow_exit'", min_wait_trading_days: 10}
- {if: "exit_reason == 'default'", min_wait_trading_days: 5}
output_field: "reentry_allowed"
on_fail: "WATCH_ONLY"
# [proposal_53 / 2026-05-15] 상대강도 약화 매도 규칙 — relative_weakness_exit
relative_weakness_exit:
purpose: >
손실 발생 여부와 무관하게 상대강도 약화 시 선제 교체 매도.
기회비용 손실을 방지하고 강한 종목·섹터로 자금을 이동한다.
applicable_to: "위성(satellite) 포지션 전체. 코어는 asymmetric_winner_rule 우선."
check_frequency: "주간 정기점검(수요일) + 월간 전체 검토"
weakness_signals:
RW1_sector_rank_drop: # [proposal_71 / 2026-05-15] 주간 독립 판정 명시
formula: "sector_flow.Rotation_Score 순위가 2주 연속 3순위 이상 하락"
score: 1
check_frequency: "주간 정기점검(수요일)마다 실행. 월별 Tier 갱신 주기와 독립적으로 판정."
data_source: "sector_priority_ranking.Rotation_Score — 매주 수요일 기준 섹터 순위 비교."
RW2_relative_underperformance:
formula: "Ret10D_종목 - Ret10D_주도섹터ETF <= -5%p"
score: 1
RW2b_5d_rapid_weakness: # [2026-05-18_ADVANCED_EXIT_V2] 5D 조기 상대약세 경보
formula: "Ret5D_종목 - Ret5D_주도섹터ETF <= -5%p (5거래일 단기 상대 열위)"
score: 1
purpose: >
RW2(10D 기준)보다 2배 빠른 조기경보. 단기 자본 회전 기회비용 손실을
선제 포착한다. 섹터가 상승 중임에도 종목만 뒤처질 때 즉각 감지.
data_source: "Ret5D_Stock (당일 기준 5거래일 수익률), Ret5D_주도섹터ETF (대표 섹터 Proxy ETF)"
fast_track_exit:
condition: "RW2b_score == 1 AND (RW1 OR RW3 OR RW4) 중 1개 이상 = 1 (합계 >= 2)"
action: "TRIM_50 — 일반 RW>=3 조건 충족 전에도 조기 부분 청산"
rationale: >
5D 급격한 상대 약세 + 다른 수급/섹터 신호 1개면 추세 이탈 초기 국면.
RW>=4(70~100%) 기다리지 않고 50% 선제 정리로 기회비용 차단.
prohibition:
- "RW2b 단독(1개)으로 fast_track 발동 금지 — 반드시 다른 RW 신호 1개 이상 필요"
- "fast_track 발동 시 당일 동일 섹터 다른 종목 즉시 매수 금지"
note: "RW2(10D)와 RW2b(5D) 동시 발동 시 score 중복 합산 금지 — max(RW2, RW2b)=1 적용"
RW3_flow_deterioration:
formula: "Frg_5D < 0 AND Inst_5D < 0 (2주 연속 동반 순매도)"
score: 1
RW4_volume_fade:
formula: "AvgTradeValue_5D_M <= AvgTradeValue_20D_M × 0.60"
meaning: "거래대금 20일 평균 대비 60% 미만으로 감소 (관심 소멸)"
score: 1
RW5_ma_breach:
formula: "Close < MA20 AND Close < MA60 (20일선·60일선 동시 하회)"
score: 1
exit_rule:
four_or_more: "RW1+RW2+RW3+RW4+RW5 >= 4 → 70~100% 청산. 지정가 분할."
three: "합계 == 3 → 50% 부분 청산 검토. 다음 점검일까지 재확인."
two_or_less: "합계 <= 2 → 유지"
fast_track_note: > # [2026-05-18_ADVANCED_EXIT_V2]
RW2b_5d_rapid_weakness가 포함된 합계 >= 2 시 fast_track_exit.TRIM_50 발동.
RW2b 없이 일반 RW 합계 <= 2는 기존대로 유지.
output_table:
columns: ["종목명", "RW1", "RW2", "RW3", "RW4", "RW5", "합계", "판정", "청산비율(%)", "이동대상"]
pyramiding_interaction: # [proposal_66 / 2026-05-15] RW >= 4와 pyramiding 증액분 청산 순서
purpose: >
RW 신호 합계 >= 4 발동 시 pyramiding 증액분을 먼저 청산한 후
나머지 70% 기준을 달성하는 순서를 고정한다.
execution_sequence:
step_1_pyramiding_first:
condition: "RW >= 4 AND 해당 종목에 pyramiding 증액분 보유"
action: "pyramiding 2차 증액분 → 1차 증액분 순으로 전량 우선 청산 (지정가 분할, 역순)"
rationale: "가장 늦게 진입한 수량이 가장 먼저 나온다 (단계 역순 원칙)"
step_2_check_70pct:
condition: "증액분 청산 후 총 청산률 70% 도달 여부 확인"
if_below_70:
action: "stage_2 확인매수 수량도 추가 청산. 총 청산 70% 충족 시 중단."
if_above_70:
action: "증액분만으로 70% 충족. stage_1·2 잔여 수량 유지."
step_3_no_pyramiding:
condition: "RW >= 4 AND pyramiding 증액분 없음"
action: "기존 exit_rule 그대로 적용 (70~100% 청산)"
prohibition:
- "단계 역순을 무시하고 평단가 최저 수량부터 먼저 청산 금지"
pyramiding_exit_quantity_formula: # [proposal_78 / 2026-05-15] pyramiding 물량 × RW 청산 수량 정확 계산
purpose: "pyramiding 단계별 물량(원물량+1차+2차)과 RW 부분청산 비율 적용 시 정확한 수량 계산 공식"
formula:
step_1: "총보유량 = 원물량(A) + 1차증액(B) + 2차증액(C)"
step_2: "청산목표주 = floor(총보유량 × 청산비율) [정수 절사 필수]"
step_3: "청산 순서: C(2차증액) 우선 → B(1차증액) → A(원물량) 역순"
step_4: "누적 청산주 >= 청산목표주 도달 시 중단. 잔여는 다음 단계에서 계속 보유."
rate_by_rw:
RW_3: "청산비율 30~50% (원칙: 40%). stage_1 탐색 종목은 50% 우선 적용."
RW_4plus: "청산비율 70~100% (원칙: 80%). 단, A_core 등급 종목은 70% 상한."
example:
holdings: "원물량 50주(A) + 1차증액 30주(B) + 2차증액 20주(C) = 총 100주"
RW_3_40pct: "목표 40주. C 20주 전량 → B에서 20주. 합계 40주 청산."
RW_4_80pct: "목표 80주. C 20주 → B 30주 → A에서 30주. 합계 80주 청산."
output_requirement: "블록4 플레이북에 [단계별 청산 수량] 테이블 출력 필수"
prohibition:
- "청산 순서 역으로(A 먼저) 청산 금지"
- "소수점 청산 수량 산출 금지 — floor 정수 처리 필수"
prohibition:
- "수익 중 종목이라도 RW >= 4이면 청산 강행 (수익 보호 명목 청산 연기 금지)"
- "청산 자금을 당일 즉시 다른 종목 매수에 투입 금지 (take_profit.redeployment_rule 준수)"
# ── [2026-05-18_FINANCIAL_HEALTH_V1] 재무 Thesis 훼손 매도 트리거 ──────────
# 수급·기술 신호와 독립적으로 재무 펀더멘털이 악화될 때 발동.
# 특히 중장기(30~60거래일) 보유 코어·위성 모두 해당.
fundamental_thesis_break:
purpose: >
진입 당시의 투자 thesis(실적 성장, 수익성 유지)가 재무 데이터로 부정될 때
수급 강세 여부와 무관하게 청산 또는 비중 축소를 강제한다.
"주가는 오르는데 재무는 무너지고 있다"는 함정을 방지한다.
check_frequency: "분기 실적 발표 직후 + 월간 전체 검토"
applicable_to: "코어·위성 모두 적용. ETF는 해당 없음."
triggers:
FTB1_roe_collapse:
condition: >
roe_pct 전분기 대비 50% 이상 급락
(예: ROE 20%→8% = 60% 하락) AND 현재 ROE < 10%
severity: "HIGH"
action: "TRIM_50 — 비중 50% 축소. 다음 분기 발표까지 재확인."
rationale: "ROE 급락은 수익성 구조 훼손 신호. 수급 강세로 가려질 수 있음."
FTB2_margin_to_loss:
condition: "operating_margin_pct 양수→음수 전환 (영업이익 흑자→적자)"
severity: "HIGH"
action: "EXIT_70 — 70% 즉시 청산. 영업적자 종목 보유 원칙 위반."
rationale: >
영업적자 전환은 thesis_break 가장 강한 신호.
stock_model.reject 조건 '적자 지속+테마성 급등'의 조기 포착.
FTB3_debt_spike:
condition: >
debt_to_equity가 전기 대비 2배 이상 급증 AND 현재 debt_to_equity > 200%
(금융업 제외)
severity: "MEDIUM"
action: "TRIM_30 — 30% 비중 축소. 자금 조달 구조 점검 필요."
rationale: "부채 급증은 희석·유동성 위기 선행 지표."
FTB4_fcf_chronic_negative:
condition: "FCF_B < 0 연속 2분기 이상 AND debt_to_equity > 150%"
severity: "MEDIUM"
action: "EXIT_REVIEW — 다음 점검일까지 thesis 재검토. 수급 이상 없으면 TRIM_30."
rationale: "FCF 만성 음수 + 고부채 = 현금 고갈 위험."
interaction_with_rw:
rule: >
FTB 트리거 + RW 신호 동시 발동 시 둘 중 더 강한 청산 비율 적용.
FTB2(영업적자 EXIT_70)가 RW3(TRIM_50)보다 강하면 EXIT_70 실행.
data_vintage_rule:
rule: >
분기 실적 발표일로부터 90일 이상 경과한 재무 데이터는 STALE 취급.
FTB 트리거 발동 전 재무 데이터 최신성 확인 필수.
stale_action: "FTB 적용 보류. DATA_STALE_FTB 태그 출력 후 다음 발표일 이후 재확인."
missing_policy:
financial_data_missing: >
재무 데이터 미제공 시 FTB 트리거 적용 금지.
단, FTB 적용 불가 이유를 보고서에 명시: "[FTB불가: 재무데이터 미확인]"
prohibition:
- "재무 데이터 미확인 상태에서 FTB 이유로 임의 청산 금지"
- "FTB 트리거 없이 재무 '우려'만으로 전량 시장가 청산 금지"
- "영업이익이 있는 종목에 FTB2(적자 전환) 적용 금지"
- "1회성 특별손실(일회성 비용)을 영업적자로 오판하여 FTB2 발동 금지"
output_table_columns: ["종목명", "FTB_트리거", "현재ROE(%)", "영업이익률(%)", "D/E", "FCF방향", "심각도", "조치", "데이터최신성"]