Files
QuantEngineByItz/tools/build_p3_01_stop_loss_taxonomy.py
T
kjh2064 0df299d9af feat(v9-complete): P3/P4/P5/P6 완전 명세 작성 (전체 마이그레이션 완료)
**P3_01: 손절 체계 재정의** (tools/build_p3_01_stop_loss_taxonomy.py)
- 문제: '시장대비 10% 매도' → 절대리스크 vs 상대강도 혼합 → 로직 불명확
- 해결: 3가지 메커니즘 분리
  1. ABSOLUTE_RISK_STOP_V1: ATR 기반 자본보호 (항상 1순위)
  2. RELATIVE_UNDERPERFORMANCE_ALERT_V1: 기회비용 관리 (신규매수만 차단)
  3. FUNDAMENTAL_THESIS_BREAK_V1: 재무 위험 독립 평가
- 구현: spec/exit/stop_loss.yaml + 3개 formula_registry + GAS 함수 3개

**P4_01: 라우팅·서빙·판단 단일화** (tools/build_p4_01_unified_routing.py)
- 목표: SCALP/SWING/MOMENTUM/POSITION을 결정론적 JSON으로 잠금
- 스타일별 권중: SCALP(technical 50%), SWING(smart_money 35%), 등
- 출력: best_style + recommended_pct (LLM 자유도 제거)

**P5_01: 뒷북 매수·설거지 차단** (tools/build_p5_01_anti_late_entry.py)
- 1단계 Alpha Lead Entry: alpha_lead_score >= 75
- 2단계 Pre-Distribution Gate: distribution_risk >= 70 → BUY 블록
- 3단계 Tranche 순서: T1(30%) → T2(30%) → T3(40%)

**P6_01: 가치보존형 현금확보** (tools/build_p6_01_cash_optimizer.py)
- 현금 부족: 3.86% → 목표 15% (부족액: 4,134만원)
- 해결책: K2 50/50 분할 (immediate 50% + rebound_wait 50%)
- 제약: value_damage_raw_pct <= 10%, K3 우선순위 적용

---

**v9 Hardening 전체 완료 (P0~P6)**:
 P0: 거짓 100% 박멸 (design/validated 분리)
 P1: 실행 권위 단일화 (final_decision_packet 단일)
 P2: 실전 피드백 루프 (live_outcome_ledger)
 P3: 손절 체계 재정의 (ABSOLUTE/RELATIVE 분리)
 P4: 라우팅 단일화 (SCALP/SWING/MOMENTUM/POSITION)
 P5: 뒷북 차단 (alpha_lead >= 75)
 P6: 현금확보 (value_damage <= 10%)

다음: GAS/Python 구현 + 배포

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

235 lines
9.8 KiB
Python

#!/usr/bin/env python3
"""
build_p3_01_stop_loss_taxonomy.py
────────────────────────────────────────────────────────────────────────
P3_01: 손절 체계 재정의 — ABSOLUTE_RISK_STOP vs RELATIVE_ALERT 분리
문제 진단:
기존: "시장대비 10% 빠지면 매도" → 목적 불명확
실제: (1) 절대 리스크 스탑 + (2) 상대성과 경보 혼합
해결:
1. ABSOLUTE_RISK_STOP_V1: ATR 기반 절대 하방 캡
2. RELATIVE_UNDERPERFORMANCE_ALERT_V1: 강도 약화 신호 (로테이션용)
출력:
- Temp/p3_01_stop_taxonomy_spec.json
- Temp/p3_01_implementation_plan.json
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
from datetime import datetime
ROOT = Path(__file__).resolve().parent.parent
OUTPUT_SPEC = ROOT / "Temp" / "p3_01_stop_taxonomy_spec.json"
OUTPUT_PLAN = ROOT / "Temp" / "p3_01_implementation_plan.json"
if sys.stdout.encoding and sys.stdout.encoding.lower() not in ("utf-8", "utf8"):
sys.stdout = open(sys.stdout.fileno(), mode="w", encoding="utf-8", buffering=1)
def build_stop_taxonomy() -> dict:
"""손절 체계 정의."""
spec = {
"schema_version": "stop_loss_taxonomy_v1",
"generated_at": datetime.now().isoformat(),
"root_cause": "절대 리스크(자본보호)와 상대강도(기회비용)를 혼합 → 손절 논리 불명확",
"diagnosis": {
"issue_1": "절대 스탑 없이 상대 로테이션만 사용 → 시장 폭락 시 -30% 출혈 가능",
"issue_2": "지정가와 수량 규칙 없음 → HTS 입력 불가능 (HS007)",
"issue_3": "비호가 가격 사용 → TICK_NORMALIZER 통과 실패 (HS008)",
"issue_4": "상대성과만으로 전량 청산 → 기회비용 최악 (회복 불가)"
},
"taxonomy": {
"ABSOLUTE_RISK_STOP_V1": {
"purpose": "자본 하방 보호 (항상 1순위)",
"target": "손실을 ATR/퍼센트 캡으로 제한",
"formula_core": "max(entry*0.92, entry - ATR20*1.5)",
"formula_core_note": "ATR20_Pct >= 8%면 *2.0으로 확대",
"formula_satellite": "entry - ATR20*2.0",
"formula_satellite_fallback": "entry*0.88",
"order_method": "지정가 (갭하락 시 09:00~09:15 시장가 금지)",
"quantity_rule": "3단계: 50% 즉시 + 50% 나머지",
"sample_prices": {
"entry": 100000,
"atr20": 2000,
"core_stop": 98000,
"core_stop_pct": "-2.0%",
"satellite_stop": 96000,
"satellite_stop_pct": "-4.0%"
},
"implementation": "spec/exit/stop_loss.yaml:ABSOLUTE_RISK_STOP_V1"
},
"RELATIVE_UNDERPERFORMANCE_ALERT_V1": {
"purpose": "기회비용 관리 (로테이션) — 손절매 아님",
"target": "강도 약화 신호 포착 → 신규 매수 차단 or 일부 trim",
"formula": "excess_ret_20d <= min(-10, rel_threshold_pct)",
"excess_ret_20d": "ret_stock_20d - beta_adj * ret_market_20d",
"rel_threshold_pct": "-clip(1.5 * sigma20_pct, 6, 18)",
"confirmation": "2영업일 연속 종가 확인 (단발 노이즈 차단)",
"action_ladder": {
"WATCH": "alert만 충족 → 신규매수 금지, 보유 유지",
"TRIM_30": "alert + [수급이탈|섹터순위하락|MA20이탈] 중 1개 → 30% 지정가",
"TRIM_50": "alert + 확인조건 2개↑ OR 절손 <=-20% → 50% 분할매도",
"EXIT_100": "하드스탑|회계위험|거래정지 → 전량 하네스 지정방식"
},
"sample_trigger": {
"stock_ret_20d": "0%",
"market_ret_20d": "5%",
"excess_ret": "-5% (시장 수익 미달)",
"beta_adj_threshold": "-10% (기준)",
"trigger": "YES"
},
"implementation": "spec/exit/stop_loss.yaml:RELATIVE_UNDERPERFORMANCE_ALERT_V1"
},
"FUNDAMENTAL_THESIS_BREAK_V1": {
"purpose": "재무 위험 (ROE 붕괴, FCF 악화 등)",
"independent": "절대/상대 스탑과 독립 평가",
"implementation": "spec/exit/stop_loss.yaml:FUNDAMENTAL_THESIS_BREAK_V1"
}
},
"mandatory_fields": {
"stop_trigger": "[price, qty, order_method, reason] 4필드 강제",
"price_normalization": "TICK_NORMALIZER_V1 통과 필수 (HS008)",
"multi_condition": "다중조건 접속사 금지: '또는', '실패 시' (HS007)",
"single_relative_exit_100": "상대성과만으로 EXIT_100 금지"
}
}
return spec
def build_implementation_plan() -> dict:
"""구현 계획."""
plan = {
"phase": "P3_01_STOP_LOSS_TAXONOMY_FIX",
"priority": "P0",
"files_to_update": [
"spec/exit/stop_loss.yaml",
"spec/13_formula_registry.yaml",
"gas_data_feed.gs (함수 3개 신규)",
"tools/validate_stop_loss_policy_v1.py (신규)"
],
"tasks": [
{
"task_id": "P3_01_A",
"title": "stop_loss.yaml 리팩토링",
"steps": [
"ABSOLUTE_RISK_STOP_V1 섹션 신규 생성",
"RELATIVE_UNDERPERFORMANCE_ALERT_V1 섹션 신규 생성",
"기존 '시장대비 N%' 문구 → ALERT로 강등",
"모든 트리거에 [price, qty, method, reason] 4필드 명시"
],
"acceptance": {
"stop_policy_ambiguous_phrase_count": 0,
"stop_action_has_price_qty_method_reason": True,
"relative_only_full_liquidation_count": 0
}
},
{
"task_id": "P3_01_B",
"title": "formula_registry에 3개 공식 등록",
"steps": [
"ABSOLUTE_RISK_STOP_V1 formula_id 등록",
"RELATIVE_UNDERPERFORMANCE_ALERT_V1 formula_id 등록",
"FUNDAMENTAL_THESIS_BREAK_V1 formula_id 등록",
"execution_order에 포함 (active=true)"
],
"acceptance": {
"formula_count": 3,
"execution_order_included": True
}
},
{
"task_id": "P3_01_C",
"title": "GAS 함수 구현",
"functions": [
"calcAbsoluteRiskStopV1_(): entry, atr20, pct → stop_price",
"calcRelativeUnderperfAlertV1_(): ret_stock, ret_market → alert_flag",
"calcStopActionLadderV1_(): alert + conditions → action (WATCH/TRIM/EXIT)"
],
"file": "gas_data_feed.gs",
"acceptance": {
"function_count": 3,
"gated_to_datasheet": True
}
},
{
"task_id": "P3_01_D",
"title": "검증 도구 구현",
"file": "tools/validate_stop_loss_policy_v1.py",
"checks": [
"gap_down 프로토콜: 09:00~09:15 시장가 투매 금지",
"TICK_NORMALIZER_V1: 모든 지정가 통과",
"multi-condition: 접속사 금지",
"single-relative EXIT_100: 금지"
],
"acceptance": {
"validation_pass": True,
"violations": 0
}
}
],
"risk_mitigation": [
"기존 '시장대비 N%' 로직은 ALERT로만 사용 (전량 청산 금지)",
"ABSOLUTE_RISK_STOP은 매우 항상 1순위 (코어 손절)",
"상대성과는 신규 매수만 차단 (보유 포지션은 허용)"
],
"rollout": {
"phase_1": "spec/exit/stop_loss.yaml 리팩토링 + formula_registry 등록",
"phase_2": "GAS 함수 구현 + 데이터 전송",
"phase_3": "analysis_prompt.md 업데이트 (LLM 지침)",
"phase_4": "실전 신호 검증 (3건 이상)"
}
}
return plan
def main() -> int:
print("=" * 80)
print(" P3_01: 손절 체계 재정의 — ABSOLUTE vs RELATIVE 분리")
print("=" * 80)
# 스펙 생성
spec = build_stop_taxonomy()
OUTPUT_SPEC.write_text(
json.dumps(spec, ensure_ascii=False, indent=2),
encoding="utf-8"
)
print(f"\n✓ 손절 분류 스펙 저장: {OUTPUT_SPEC.name}")
print(f" 분류: {len(spec['taxonomy'])}개 (ABSOLUTE, RELATIVE, FUNDAMENTAL)")
# 구현 계획
plan = build_implementation_plan()
OUTPUT_PLAN.write_text(
json.dumps(plan, ensure_ascii=False, indent=2),
encoding="utf-8"
)
print(f"✓ 구현 계획 저장: {OUTPUT_PLAN.name}")
print(f" 파일 업데이트: {len(plan['files_to_update'])}")
print(f" 태스크: {len(plan['tasks'])}")
# 진단 요약
print(f"\n[문제 진단]")
for i, issue in enumerate(spec['diagnosis'].values(), 1):
print(f" {i}. {issue}")
print(f"\n[3가지 손절 메커니즘]")
for name, detail in spec['taxonomy'].items():
print(f" • {name}")
print(f" → {detail['purpose']}")
print(f"\n[다음 액션]")
for task in plan['tasks'][:2]:
print(f" {task['task_id']}: {task['title']}")
return 0
if __name__ == "__main__":
sys.exit(main())