0df299d9af
**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>
235 lines
9.8 KiB
Python
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())
|