feat: 리밸런싱 엔진 V1 + GAS 버그 수정 (2026-06-13)

주요 변경:
- 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>
This commit is contained in:
2026-06-13 13:20:14 +09:00
commit ee3e799de1
1474 changed files with 176087 additions and 0 deletions
@@ -0,0 +1,22 @@
formula_id: DYNAMIC_VALUE_PRESERVATION_SELL_V3
name: 동적 가치 보존 매도 (Dynamic Value Preservation Sell)
description: 현금 확보 시 기계적 매도를 지양하고 반등 수익을 극대화하는 세련된 매도 기법을 적용합니다.
rules:
- id: DVP001
condition: "rsi14 < 30 AND is_cash_shortfall == TRUE"
action: "SET_RATIO_10_90"
reason: "과매도 바닥 구간: 즉시매도 10%, 반등대기 90%"
- id: DVP002
condition: "rsi14 > 70 AND is_cash_shortfall == TRUE"
action: "SET_RATIO_80_20"
reason: "고점 과열 구간: 즉시매도 80%, 반등대기 20%"
- id: DVP003
condition: "rebound_wait_qty > 0"
action: "ACTIVATE_REBOUND_RATCHET"
reason: "반등 대기 물량에 트레일링 스탑 적용"
output:
schema: dynamic_preservation_json
fields:
- immediate_sell_ratio: NUMBER
- rebound_wait_ratio: NUMBER
- ratchet_trigger_price: NUMBER
+15
View File
@@ -0,0 +1,15 @@
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"
event_response:
rule:
- "이벤트 전: 신규 위성 축소와 미체결 주문 재확인 우선."
- "이벤트 후: 가격반응·거래대금·수급 확인 전 추격매수 금지."
- "실적 이벤트는 발표일·컨센서스 방향·D+1~D+3 가격/수급 반응만 표로 기록."
detail: "세부 이벤트 대응은 portfolio_exposure_framework.cash_floor.policy_event_week_escalation 우선."
+23
View File
@@ -0,0 +1,23 @@
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"
position_review_cycle:
weekly_scheduled:
wednesday_check:
required: ["보유 종목별 20일선·5일선 위치, 외국인·기관 5D 수급", "goal_orbit_check 현황", "cash_floor 충족 여부"]
output: "이상없음 / 조건부경고 / 즉각대응 중 하나를 기록. 아무것도 하지 않는 것도 결정으로 기록."
friday_check:
required: ["다음 주 FOMC·금통위·CPI·실적발표 일정", "주간 누적 외국인·기관 5D 수급 방향", "보유 위성 포지션별 time_stop 잔여 거래일"]
immediate_trigger:
conditions: ["보유 종목 장중 -5% 이상", "KOSPI/KOSDAQ 장중 -2% 이상 동반 급락", "VIX 20→25 돌파 또는 USD/KRW 10원 이상 장중 급등", "외국인·기관 5D 동반 순매도 전환"]
action: "즉시 stop_loss·correlation_shock·credit_stress 트리거 조건 재점검"
documentation:
- "점검일·종목·현황·조치내용·다음점검예정일을 performance_evidence 로그에 기록"
- "점검 기록 없이 10거래일 이상 경과하면 위성 포지션은 자동 C등급 강등"
- "자동 C등급 강등 근거 → recommendation_grade.auto_downgrade_rule (단방향 참조. 역참조 금지)"
+33
View File
@@ -0,0 +1,33 @@
meta:
title: "은퇴자산포트폴리오 — 선제적 매도 레이더 명세"
parent_file: "RetirementAssetPortfolio.yaml"
version: "2026-05-19-F1_proactive_exit"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "canonical"
purpose: >
주가 하락 이전의 수급 다이버전스, 오버행 압박, 섹터 로테이션 등을 감지하여
'어깨'에서 선제적으로 이익을 실현하거나 리스크를 축소하기 위한 분석 레이더.
proactive_exit_radar:
policy:
execution: "보유 포지션 진단 시 항상 실행"
cross_alert_rule: "두 개 이상의 레이더에서 ALERT 발생 시 CRITICAL_ALERT로 승격"
divergence_alert:
id: "W1"
formula_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.DIVERGENCE_SCORE_V1"
threshold: 0.70
overhang_warning:
id: "W2"
formula_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.OVERHANG_PRESSURE_V1"
threshold: 0.60
sector_rotation_radar:
id: "W3"
formula_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.SECTOR_ROTATION_RADAR_V1"
flow_acceleration_radar:
id: "W4"
formula_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.FLOW_ACCELERATION_V1"
+361
View File
@@ -0,0 +1,361 @@
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) 적용 금지."
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방향", "심각도", "조치", "데이터최신성"]
+345
View File
@@ -0,0 +1,345 @@
meta:
title: "은퇴자산포트폴리오 — 익절 정책"
parent_file: "spec/06_exit_policy.yaml"
version: "2026-05-16-F13_secular_leader"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "canonical"
migration_status: "canonical_split_active"
take_profit:
# [proposal_117 / 2026-05-15] 익절 시스템 단일 지정 — HTS 주문 입력 기준 명확화
canonical_exit_system: "profit_lock_ratchet + tiered_ladder"
canonical_rule: >
HTS 주문 입력 기준은 항상 profit_lock_ratchet(손절선 상향 래칫)과
tiered_ladder(분할 익절 3단계)의 조합이다.
trailing_stop은 tiered_ladder.tier_3의 트리거 참조로만 사용한다.
sector_rotation_exit·event_driven·overheated_market_exit는
해당 조건 발동 시에만 추가로 적용한다. 기본 상태에서 출력 금지.
trailing_bands는 CSCS 기반 처우 선택(sector_model.core_satellite_classification_score) 후
trailing_stop.core_large_cap(코어) 또는 trailing_stop.satellite_trigger(위성)로 자동 매핑된다.
decision_map:
purpose: "어느 익절 시스템을 언제 쓰는가 — 순서대로 확인"
step_1: "항상 profit_lock_ratchet 적용: 현재수익률 확인 → 래칫 손절선 업데이트"
step_2: "항상 tiered_ladder 적용: tier_1/2/3 가격·수량 산출 → 이것이 HTS 주문 기준"
step_3: "sector_rotation_exit: 섹터 경보 발동 시에만 추가 적용"
step_4: "event_driven: 실적발표 D-5 이내 시에만 추가 적용"
step_5: "overheated_market_exit: VIX<13 + KOSPI 신고가 + 개인 50% 이상 조건 발동 시에만"
default_output: "기본 보고서는 step_1 + step_2만 출력. step_3~5는 조건 미발동 시 출력 생략."
principle: "감정적 전량 매도 금지. 저항선·수익률·ATR 기반 분할 익절 우선."
integrated_exit_algorithm:
rule: "Tiered Ladder는 익절 물량(Size)의 단계적 분할을 관장하고, Profit Lock Ratchet은 잔여 물량의 하한가(Floor Price)를 기계적으로 상향 조정한다. 상호충돌 시 물량 매도는 Tiered Ladder 우선, 손절선 상향은 Ratchet 우선 적용."
requirements:
- "현재가·평단가·보유수량·기준시각 확인 종목에만 익절 수량 산출."
- "ATR 또는 저항선이 [데이터누락]이면 trailing_stop 가격 산출 금지. 관찰 조건으로만 표시."
core:
first_take_profit: "수익률 +15%만으로 기계적 익절 금지. 20D 수급 유지·거래대금 유지·5일선 위 주행이면 추세추종."
trailing: "수익권 또는 저항선 돌파 후 고점 대비 ATR 1.5~1.8배 하락 시 20~30% 부분 익절 검토."
leadership: "삼성전자·SK하이닉스는 비중상한·추세이탈·수급이탈·현금부족 중 2개 이상 충족 시에만 부분 익절 우선."
satellite:
first_take_profit:
primary_rule: "tiered_ladder.satellite.tier_1 기준 적용 (진입가 +10% 도달 시)"
defensive_partial_exit:
condition: |
당일 +7% 이상 + 거래대금 200% 이상 동반 AND 아래 이상 급등 의심 신호 중 1개 이상:
- 구체적 실적·수급 근거 없는 테마·루머 급등
- 외국인·기관 동반 순매수 아닌 개인 주도 급등 (Ind_5D 급증)
- 52주 신고가 돌파 후 당일 음봉 반전
action: "보유수량 25% 방어 익절 (단타 방지: 기존 50% 하향)"
prohibition:
- "실적 발표 후 정상 갭업 + 기관·외국인 수급 동반 상승은 이 규칙 적용 금지"
- "당일 가격 반응만으로 tiered_ladder 순서 건너뛰고 전량 익절 금지"
trailing_stop:
rule: "직전 고점 대비 20일 ATR 1.5배 하락 시 잔여 전량 또는 50% 이상 청산 검토. 고베타 주도주는 1.8배까지 허용."
core_large_cap: "진입 후 최고가 대비 -6% 또는 ATR20 1.5배 하락 중 더 넓은 값. 20~30% 익절."
high_beta_leader: "최고가 대비 -8% 또는 ATR20 1.8배 하락. 25~33% 익절."
satellite_trigger: "최고가 대비 -7% 또는 ATR20 1.5배. 50% 익절."
ratchet_rule: "신규 최고가 갱신 시 trailing 기준가만 상향. 잔여 손절선은 최소 본절 이상."
prohibition: ["수익률 +15% 도달만으로 전량 익절 금지", "ATR20 없으면 trailing 가격 산출 금지", "trailing 기준가를 심리로 하향 조정 금지"]
rebalancing_trim:
rule: "단일 종목 총자산 18% 초과 시 예외 종목 제외하고 초과분만 지정가 분할 매도."
exception: "삼성전자·SK하이닉스는 special_exception.kospi_semiconductor_leadership 우선."
asymmetric_winner_rule:
hold_if_all: ["20일선 위", "20D 수급 유지", "거래대금 급감 없음", "섹터 상대강도 상위권"]
trim_if_two_or_more: ["5일선 종가 이탈", "장대음봉+거래대금 200% 이상", "외국인·기관 5D 동반 순매도", "섹터 상대강도 급락"]
action: "1차 20~30%, 20일선 이탈 시 추가 30%, thesis_break 시 잔여 청산."
redeployment_rule:
cooling_period: {satellite_exit: "3~5거래일 대기 후 재투자 검토", core_partial_trim: "cash_floor 귀속 원칙. 재투자는 다음 수요일 정기점검에서 결정"}
prohibition: ["익절 당일 다른 종목 즉시 신규 매수 금지", "익절 자금을 고위험 위성에 집중 투입 금지"]
secular_leader_profit_lock: # [proposal_87 / 2026-05-16] 주도주 승자 포지션 이익 잠금 강화
purpose: >
SECULAR_LEADER_RISK_ON 국면에서 삼성전자·SK하이닉스의 수익 포지션에 한해
일반 위성 tiered_ladder의 조기 부분익절 대신 trailing_stop 상향 + 래칫 방식을 우선 적용하여
승자 포지션을 더 오래 유지한다. 기존 core.leadership 규칙의 강화 버전.
applicable_to: ["삼성전자", "SK하이닉스"]
activation_required_all:
- "market_regime_state == SECULAR_LEADER_RISK_ON"
- "보유수량 확인 완료 (account_snapshot 기준)"
- "Close > MA20"
- "20D 수급 유지 (C4: Flow_OK=Y AND Frg_5D>0 OR Inst_5D>0)"
rules:
plus_10:
condition: "보유 포지션 수익률 +10% 이상 종가 확인"
action: "tiered_ladder.tier_1 부분익절 보류. profit_lock_ratchet 본절(+0%) 상향 실행만."
rationale: "수급 훼손 없는 한 +10%에서의 매도는 주도주 상승 사이클을 조기 종료시킴."
exception: "anti_climax_buy_gate >= 3 발동 시 기존 tiered_ladder.tier_1 즉시 적용"
plus_20:
condition: "수익률 +20% 이상 종가 확인"
action: "손절선 진입가 +10%로 상향. 과열신호 2개 미만이면 20~30% 부분익절만 검토."
overheated_signals:
definition: "아래 중 2개 이상 시 과열 판정"
signals:
- "당일 거래대금 20일 평균의 300% 이상"
- "외국인+기관 동반 순매도 전환 (당일 기준)"
- "RSI 또는 이격도 과매수 극단 (별도 지표 확인 시에만)"
- "anti_climax_buy_gate >= 2"
prohibition: "과열신호 0~1개이면 이 단계 부분익절 금지. 래칫 상향만."
plus_30:
condition: "수익률 +30% 이상 종가 확인"
action: "trailing_stop을 최근 고점 대비 ATR20 × 1.5~2.0배로 설정. 30~40% 단계 익절."
note: "profit_lock_ratchet.ratchet_table +30% 래칫 동시 적용. 전량익절 금지."
deactivation_any:
- "anti_climax_buy_gate >= 3 발동"
- "5D 외국인·기관 동반 순매도 (foreign_5d_flow < 0 AND institution_5d_flow < 0)"
- "종가 MA20 이탈"
- "market_regime_state != SECULAR_LEADER_RISK_ON"
fallback: "비활성 시 take_profit.core.leadership 규칙으로 즉시 복귀. 이 규칙보다 완화된 방향 변경 금지."
prohibition:
- "보유수량 미확인 상태에서 이 규칙 기반 익절수량 산출 금지"
- "SECULAR_LEADER_RISK_ON 비활성 상태에서 이 규칙 단독 적용 금지"
- "비활성 후 매도 보류를 이유로 trailing_stop 하향 조정 금지"
profit_lock_ratchet:
principle: "수익 구간 진입 시 손절가는 기계적으로 상향한다. 감정적 하향 조정 절대 금지."
atr_break_even_trigger: > # [2026-05-18_ADVANCED_EXIT_V2] ATR 연동 본절 트리거
TAKE_PROFIT_LADDER_V2의 tier_1 가격(max(+10%, Entry+ATR20×1.5)) 달성 시
손절선을 즉시 평단가(본절)로 상향한다. 이 조건이 고정% +10% 조건보다 먼저
충족되는 경우(고변동성 종목)에도 동일하게 본절 상향을 실행한다.
atr_early_ratchet: # [2026-05-19_ALPHA_SHIELD_V1] X4: 1R 조기 본절 전환
trigger: "current_price >= entry_price + ATR20 * 1.0"
action: "stop_price = max(stop_price, entry_price)"
note: "ATR*1.5(tier_1 기준) 대기 없이 ATR*1.0 달성 즉시 본절 전환. 무위험 게임 조기 보장."
priority: "atr_early_ratchet이 +10% 고정 조건보다 선행 가능. 먼저 발동된 것 적용."
ratchet_table:
- 수익_구간: "+10% 이상 확인 (종가 기준)"
새_손절선: "진입가 +0% (본절)"
부분_익절: "없음. 손절선 상향만 실행."
HTS입력: "매수가 × 1.000 조건부 주문"
- 수익_구간: "+20% 이상 확인"
새_손절선: "진입가 +10%"
부분_익절: "20~30% 부분 익절 검토 (20D 수급 유지 확인 후)"
HTS입력: "매수가 × 1.100 조건부 주문"
- 수익_구간: "+30% 이상 확인"
새_손절선: "진입가 +20%"
부분_익절: "30~40% 추가 익절"
HTS입력: "매수가 × 1.200 조건부 주문"
- 수익_구간: "+50% 이상 확인"
새_손절선: "진입가 +35% 또는 ATR20 × 2.0배 하락 기준 중 높은 값"
부분_익절: "50% 이상 익절. 잔여분 trailing_stop 관리."
HTS입력: "trailing_stop 원화 가격 계산 후 HTS 입력"
atr_trailing_universal: # [2026-05-19_ALPHA_SHIELD_V1] X4: 전 수익 구간 트레일링 스탑
trigger: "current_price < (max_price_since_entry - ATR20 * 2.0)"
action: "TAKE_PROFIT_TRAIL -- 즉시 익절 실행 (TICK_NORMALIZER 적용)"
applicable: "수익 구간 불문. 기존 +50% 제한 해제. 어깨에서 파는 하네스 핵심."
note: "max_price_since_entry = 진입 후 최고 종가. 매 거래일 종가 기준 갱신."
priority: "hard_stop 다음, tiered_ladder tier 실행 이전 확인."
special_case:
core_leader: "삼성전자·SK하이닉스 ratchet 기준 +5%p 완화"
satellite: "위성 ratchet 기준 -5%p 강화"
hard_stop:
- "ATR20 미확인 시 HTS 조건부주문 가격 산출 금지"
- "수익 구간 진입 확인은 종가 기준. 장중 고점만으로 래칫 상향 금지."
- "래칫 손절선은 진입가보다 낮게 내리는 것 절대 금지."
output_columns: ["계좌", "종목명", "평단(원)", "현재가(원)", "현재수익률(%)", "현_손절선(원)", "래칫_신_손절선(원)", "부분익절수량(주)", "HTS조건부주문_입력가"]
tiered_ladder:
principle: "1회 전량 익절 금지. 수익 구간을 3단계로 나눠 기계적으로 익절. 마지막 잔여분은 trailing_stop 관리."
formula_version: "TAKE_PROFIT_LADDER_V2 (ATR R-Multiple 변동성 조정). ATR 미확인 시 V1(고정%) fallback."
core_ladder:
tier_1:
trigger: "진입가 대비 +15% 도달 또는 컨센서스 목표가의 90%"
action: "보유수량 25% 익절 (지정가 하한 계산)"
condition: "20D 수급 유지 중이면 스킵 가능. 단 손절선은 반드시 본절로 상향."
tier_2:
trigger: "진입가 대비 +25% 도달 또는 컨센서스 목표가 도달"
action: "남은 보유수량의 40% 익절"
condition: "외국인·기관 5D 수급 유지 + 거래대금 급감 없음 → 보유 연장 검토"
tier_3:
trigger: "최고가 기준 ATR20 × 1.5배 하락 또는 profit_lock_ratchet 손절 발동"
action: "잔여 전량 또는 50% 익절"
satellite_ladder:
tier_1:
trigger: "진입가 대비 +10% 도달 (또는 ATR20 × 1.5 중 높은 값. TAKE_PROFIT_LADDER_V2 기준)"
action: "보유수량 33% 익절 + 손절선 본절 상향 (중장기 추세 추종: 잔여 67% 보유)"
v1_legacy_note: "구버전 50% 익절은 단기 익절 편향 — V2에서 33%로 조정. TAKE_PROFIT_LADDER_V1은 ATR 미확인 fallback용."
tier_2:
trigger: "진입가 대비 +20% 도달"
action: "남은 수량 50% 추가 익절"
tier_3:
trigger: "trailing_stop 또는 time_stop 발동"
action: "잔여 전량 청산"
prohibition:
- "tier 순서 건너뛰고 전량 익절 금지"
- "ATR20 미확인 시 tier_3 기준가 산출 금지"
- "tier_1 익절 후 다른 종목 즉시 매수 금지 (redeployment_rule 준수)"
output_columns: ["계좌", "종목명", "평단(원)", "현재가(원)", "현재수익률(%)", "tier_1_가격(원)", "tier_1_수량(주)", "tier_2_가격(원)", "tier_2_수량(주)", "tier_3_기준가(원)", "잔여수량(주)"]
output_examples: "_reference: output_format.unified_example_row_set # [R6] 통합 예시 집합 참조"
executable_rules:
field_dictionary_ref: "spec/12_field_dictionary.yaml:field_dictionary"
formula_refs:
take_profit_ladder: "spec/13_formula_registry.yaml:formula_registry.formulas.TAKE_PROFIT_LADDER_V2 # ATR R-Multiple 기준. ATR 없으면 V1 fallback."
trailing_stop_price: "spec/13_formula_registry.yaml:formula_registry.formulas.TRAILING_STOP_PRICE_V1"
rules:
- id: "TP001_PROFIT_LOCK_RATCHET"
inputs: ["average_cost", "current_price", "quantity", "position_class"]
derived_field:
profit_pct: "(current_price - average_cost) / average_cost * 100"
output_fields: ["ratchet_stop_price", "partial_take_profit_quantity"]
tiers:
- {if: "profit_pct >= 50", stop_expression: "max(average_cost * 1.35, TRAILING_STOP_PRICE_V1)", partial_quantity_expression: "floor(quantity * 0.50)"}
- {if: "profit_pct >= 30", stop_expression: "average_cost * 1.20", partial_quantity_expression: "floor(quantity * 0.35)"}
- {if: "profit_pct >= 20", stop_expression: "average_cost * 1.10", partial_quantity_expression: "floor(quantity * 0.25)"}
- {if: "profit_pct >= 10", stop_expression: "average_cost * 1.00", partial_quantity_expression: 0}
missing_policy: "NO_RATCHET_OUTPUT"
- id: "TP002_TIERED_LADDER"
inputs: ["average_cost", "atr20", "quantity", "position_class"]
formula_ref: "TAKE_PROFIT_LADDER_V2 # ATR 있으면 V2, 없으면 V1 fallback"
output_fields: ["tier_1_price", "tier_1_quantity", "tier_2_price", "tier_2_quantity", "tier_3_reference", "remaining_quantity"]
missing_policy: "NO_TAKE_PROFIT_OUTPUT"
- id: "TP003_REDEPLOYMENT_COOLING"
inputs: ["exit_type", "exit_date", "current_trade_date"]
rules:
- {if: "exit_type == 'satellite_exit'", min_wait_trading_days: 3, max_wait_trading_days: 5}
- {if: "exit_type == 'core_partial_trim'", action: "cash_floor_reserved_until_next_wednesday_review"}
output_field: "redeployment_allowed"
on_fail: "cash_or_watch_only"
sector_rotation_exit:
data_required: ["섹터 1M 상대강도 순위 (전주 대비)", "해당 섹터 외국인·기관 5D 순매수 추세", "거래대금 전주 대비 변화율"]
trigger_conditions:
경보_1단계:
조건: "보유종목 섹터의 1M 상대강도 순위가 전주 대비 2순위 이상 하락"
조치: "profit_lock_ratchet 재확인. tier_1 미도달 종목은 tier_1 익절가 미리 입력."
경보_2단계:
조건: "1단계 경보 + 해당 섹터 외국인·기관 5D 동반 순매도 전환"
조치: "수익권 종목 30% 부분 익절. 섹터 내 중복 ETF 우선 청산."
청산_신호:
조건: "2단계 경보 + 섹터 거래대금 3D 연속 감소 + 섹터 ETF 가격 20일선 이탈"
조치: "해당 섹터 위성 포지션 전량 청산 검토. 코어 직접보유는 개별 추세 판단."
decoupling_exception:
rule: "섹터 이탈에도 해당 종목이 신고가·외국인 집중매수·거래대금 폭증 3개 동시 충족 시 예외 허용"
action: "예외 허용 시 trailing_stop을 ATR20 × 1.2배로 타이트하게 설정"
prohibition:
- "섹터 1M 상대강도 데이터 미확인 시 이 규칙 적용 금지. DATA_MISSING 표기."
- "섹터 이탈 신호만으로 코어 직접보유 전량 청산 금지."
output_columns: ["섹터명", "1M순위_전주", "1M순위_현재", "순위변화", "5D외국인", "5D기관", "거래대금_추세", "경보단계", "조치내용", "해당_보유종목"]
event_driven:
실적발표_전_관리:
D-5_부터_D-1:
조건: "현재 수익률 +10% 이상인 위성 포지션"
조치_plus10_20: "20% 선제 익절 + 손절선 본절 상향"
조치_plus20이상: "30% 선제 익절 + 손절선 진입가+10% 상향"
조치_손실구간: "추가 익절 없음. 발표 전 손절 여부만 판단."
실적발표_후_관리:
컨센서스_상회:
조건: "EPS/매출 컨센서스 5% 이상 초과 + 당일 양봉 + 거래대금 급증"
조치: "보유 유지 + trailing_stop 기준가 갱신 + tier_2 익절가 상향 재설정"
금지: "D+0 당일 추격 신규 매수 금지"
컨센서스_부합:
조건: "컨센서스 ±5% 이내"
조치: "보유 유지. D+1~D+3 수급 반응 확인 후 판단."
컨센서스_하회:
조건: "EPS/매출 컨센서스 5% 이상 하회 또는 가이던스 하향"
조치: "D+0~D+1 내 보유수량 50% 이상 익절(수익권) 또는 손절(손실권)"
D+1~D+3_반응없음:
조건: "상회 발표 후 3거래일 내 주가 반응 없음"
조치: "선제 익절 외 나머지 30% 추가 익절. 논리 재검토."
prohibition:
- "발표 당일 EPS 수치 확인 전 추격매수·추격매도 금지"
- "컨센서스 미확인 상태에서 실적 단독 수치만으로 A등급 승격 금지"
- "실적 쇼크 시 09:00~09:15 전량 시장가 매도 금지 (gap_down 룰 준용)"
overheated_market_exit:
vix_context: # [P135] regime_adaptive VIX<18 과의 관계 명시
regime_relation: "Risk-On 구간(VIX < 18) 내 극단 과열 하위 집합"
normal_risk_on: "VIX 13~18 구간은 regime_adaptive Risk-On 규칙만 적용 (overheated 미발동)"
overheated_zone: "VIX <= 13 시 trigger_required_all 조건 추가 확인"
behavior: "overheated_market_exit 발동 = Risk-On 규칙 + 추가 부분 익절 중첩 적용"
trigger_required_all:
- "VIX 13 이하 (3년 저점 수준)"
- "KOSPI 52주 신고가 갱신 중 + 개인 5D 순매수 비중 일평균 50% 이상"
- "보유 위성 포지션 수익률 평균 +15% 이상"
optional_강화신호_any_1:
- "KOSPI PER 역사적 상위 30% 이상"
- "신용잔고 1년 최고 수준"
action:
trigger_충족시: "tiered_ladder tier_1 즉시 실행. 위성 30% 부분 익절. 중복 ETF 50% 이상 익절."
강화신호_추가시: "위성 추가 20% 익절 (총 50% 축소). 신규 매수 전면 중단."
현금_목표: "tactical_cash_buffer 5% + cash_floor 12% = 총자산 17% 이상 확보"
재진입_기준:
- "VIX 18 이상 회귀 시 재진입 검토"
- "KOSPI 신고가 대비 -5% 이상 조정 후 20일선 회복 시"
prohibition:
- "VIX 미확인 시 이 규칙 적용 금지"
- "과열 신호만으로 삼성전자·SK하이닉스 직접보유 익절 금지"
- "선제 익절 후 동일 종목 5거래일 내 재매수 금지"
account_tax_optimization: # xref: 진입 단계 계좌 배치 원칙 → account_policy.cost_parity_rule 참조
원칙: "세후 수익이 낮은 계좌부터 익절 우선. 세금은 통제 가능한 비용."
계좌별_특성:
일반계좌: "매매차익 대주주 기준 미해당 시 비과세. 배당소득세 15.4%."
ISA: "손익통산 후 200만원(서민형 400만원) 비과세, 초과분 9.9% 분리과세. ISA 내 손실 먼저 정리 후 수익 실현."
연금저축: "계좌 내 ETF 교체는 비과세 리밸런싱. 실제 인출 시 연금소득세 3.3~5.5%. 중도인출 기타소득세 16.5% 주의."
익절_우선순위:
1순위: "ISA 내 손익통산 활용 (비과세 한도 내 수익 실현)"
2순위: "연금저축 내 ETF 교체 리밸런싱 (인출 없이)"
3순위: "일반계좌 매매차익 (비과세)"
4순위: "ISA 비과세 한도 초과분 (9.9% 분리과세)"
prohibition:
- "ISA 비과세 한도를 손실 종목 익절로 낭비하지 않는다"
- "연금저축 중도 인출 없이 계좌 내 리밸런싱을 익절로 계산하지 않는다"
time_based_realization:
# [Q3 / 2026-05-15] role.principles "위성 평균 보유 목표 20거래일 이상"과 D+20 청산 조건이
# 동일 일수를 사용해 "20일 보유가 손절 트리거"로 오독되는 논리 충돌 해소.
# satellite_time_return_gate는 성과 불만족 시 청산 검토이지, 20일 도달이 자동 청산 아님.
satellite_time_return_gate:
purpose: "성과 미달 시 자본 효율성 제고 목적의 청산 검토. 20거래일 보유 목표 자체가 손절 트리거가 아님."
D_plus_10: "+3% 이상 미달 → thesis 재검증"
D_plus_10_opportunity_cost: # [2026-05-18_ADVANCED_EXIT_V2] 기회비용 실행 트리거
purpose: >
D+10 리뷰를 '검토만' 하던 방식에서 조건부 실행 트리거로 강화.
섹터가 상승 중임에도 종목이 정체하면 자본 낭비 = 기회비용 손실.
condition_all_required:
- "보유 거래일 >= 10"
- "profit_pct < 1.0% (본절 ±1% 이내 정체)"
- "Ret5D_Sector_Proxy > 1.0% (섹터 Proxy ETF 5일 수익률 +1% 초과)"
action: "TRIM_30 — 30% 부분 청산 (기회비용 조기 회수)"
rationale: >
섹터가 오르는데 종목만 오르지 않으면 자본을 둔 의미가 없다.
빈번한 진출입 방지를 위해 50%가 아닌 30% 조기 정리.
잔여 70%는 thesis 유효 시 보유 연장 가능.
exemptions:
- "실적발표 D-10 이내: 적용 보류"
- "외국인·기관 20D 순매수 전환 직후 3거래일 이내: 적용 연기"
- "Ret5D_Sector_Proxy 미확인 시: DATA_MISSING — 적용 금지"
output_columns: ["종목명", "보유일수", "profit_pct(%)", "Ret5D_섹터(%)", "조건충족여부", "조치"]
alpha_lag_weed_out: "보유 10일 기준 KOSPI 대비 초과수익(Alpha)이 -5%p 이하일 경우, 수익/손실 무관 비중 30%를 축소하여 자본 효율성을 제고한다."
D_plus_20: "수익률 +7% 미달 AND KOSPI 대비 Alpha 음수 → 보유수량 30% 청산 검토. 수익률만 미달이고 Alpha 양수면 유지."
D_plus_30: "+10% 이상 미달 → 보유수량 50% 청산"
D_plus_60: "time_stop 기준 연동 → 잔여 청산 검토"
core_time_return_gate:
D_plus_30: "+5% 이상 미달 → KOSPI 대비 초과수익 확인 후 재검토"
D_plus_60: "+10% 이상 미달 → 보유수량 30% 청산 검토"
D_plus_90: "time_stop 연동 → 유지 근거 없으면 50% 청산"
benchmark_comparison: "종목 수익률이 동기간 KOSPI 대비 -5%p 이상 열위이면 재검토 트리거. 절대 수익 양수여도 해당."
exception:
- "실적발표 예정 D-10 이내: gate 적용 연기"
- "외국인·기관 20D 순매수 전환 직후: gate 완화 (3거래일 추가 관찰)"
prohibition:
- "KOSPI 수익률 미확인 시 벤치마크 대비 열위 판단 금지"
- "loss_recovery 목적으로 gate 기준 완화 금지"
output_columns: ["계좌", "종목명", "진입일", "보유일수", "진입가", "현재가", "현재수익률(%)", "기간_KOSPI수익률(%)", "초과수익(%)", "gate_상태", "조치"]
@@ -0,0 +1,53 @@
schema_version: 2026-06-03-value-preserving-cash-raise-optimizer-v9
formula_id: VALUE_PRESERVING_CASH_RAISE_V9
supersedes: VALUE_PRESERVING_CASH_RAISE_OPTIMIZER_V7
purpose: 현금확보와 가치보전 동시 관리 — BREACH_FULL_LIQUIDATION 금지, K2 50/50 강제.
required_fields:
- value_damage_pct_avg_raw
- value_damage_pct_avg
- execution_damage_for_gate
# ── 청산 정책 (liquidation_policy) ──────────────────────────────────────────
liquidation_policy:
formula_id: VALUE_PRESERVING_CASH_RAISE_V9
rationale: >
현재 현금확보가 한 종목을 통째로 던져 15.7% 가치를 깎고 반등 포착 확률 0%.
가치훼손 캡(10%)과 반등 포착(>=50%)을 게이트로 두고,
과매도 구간은 K2 50/50 분할로 강제한다.
rules:
- id: LP001_SINGLE_STOCK_CONCENTRATION
rule: "단일 종목 비중이 청산금액의 60%를 넘지 못하게 분산"
enforcement: HARD_BLOCK
- id: LP002_BREACH_FULL_LIQUIDATION_BAN
rule: "후보가 oversold(rsi14<30) 이거나 brt_verdict!=BROKEN 이면 BREACH_FULL_LIQUIDATION 금지"
exception: "emergency_full_sell=true 일 때만 전량 즉시 허용"
enforcement: HARD_BLOCK
- id: LP003_K2_50_50_SPLIT
rule: "LP002 조건 충족 시 K2 50/50: immediate=floor(qty/2), rebound_wait=나머지"
rebound_trigger: "prevClose + rebound_factor×ATR20"
rebound_factors:
EVENT_SHOCK: 0.7
RISK_OFF: 0.6
NEUTRAL: 0.5
RISK_ON: 0.3
enforcement: MANDATORY
- id: LP004_VALUE_DAMAGE_CAP
rule: "raw_value_damage_pct_avg <= 10.0"
cap_violated_action: "조합 재탐색. 캡 위반 시 HTS 주문 차단"
enforcement: HARD_BLOCK
- id: LP005_REBOUND_CAPTURE
rule: "rebound_capture_probability >= 0.50"
enforcement: GATE
emergency_override:
condition: "half_expected_krw × 2 < cash_shortfall_min_krw"
emergency_full_sell: true
note: "이 조건만이 전량 즉시 허용. 다른 이유로 전량 즉시 금지."
acceptance_criteria:
raw_value_damage_pct_avg: {op: "<=", target: 10.0, current: 15.7, blocking: true}
rebound_capture_probability: {op: ">=", target: 0.50, current: 0.0, blocking: true}
breach_full_liquidation_count: {op: "==", target: 0, note: "emergency_full_sell=true 제외"}
single_stock_concentration_pct: {op: "<=", target: 60.0}
output: Temp/smart_cash_recovery_v9.json
python_tool: "tools/build_value_preserving_cash_raise_optimizer_v7.py (v9로 출력)"
gs_coverage: "gas_apex_runtime_core.gs:calcValuePreservingCashRaiseV9_()"
validator: "tools/validate_value_preserving_cash_raise_optimizer_v7.py + validate_cash_raise_pareto_executor_v2.py"