meta: title: "은퇴자산포트폴리오 — 최상위 실행 계약" parent_file: "RetirementAssetPortfolio.yaml" version: "2026-05-18-F8_p4_keyword_lock" language: "ko-KR" timezone: "Asia/Seoul" role: "canonical" has_code_implementation: true code_path: ["src/quant_engine/execution_slippage_store_v1.py", "tools/run_snapshot_admin_server_v1.py"] purpose: > 기존 llm_compact_execution_contract에서 제공하던 최상위 안전 계약을 모듈형 구조에 맞게 복원한 단일 권위 파일. 이 파일은 모든 spec보다 먼저 적용한다. source_of_truth: priority: "highest" rule: "이 파일의 master_prohibitions, hard_stops, capture_read_ledger가 모든 하위 전략·점수·수량·출력 규칙보다 우선한다." conflict_resolution: risk_vs_strategy: "위험 차단 규칙과 매수/증액 규칙이 충돌하면 위험 차단 규칙만 적용한다." data_vs_output: "데이터·수량·현금 검산이 실패하면 출력 형식이 완성되어도 주문은 금지한다." table_vs_text: "표/수치/공식과 서술형 설명이 충돌하면 표/수치/공식을 우선한다." tie_rule: "동급 규칙 충돌 시 보류하고 다음 확인 출처를 출력한다." proposal_policy: canonical: true purpose: "사용자 판단용 제안 레이어와 HTS 즉시 실행 레이어를 분리한다." rule: - "가격·수량 산출 입력이 충족되면 시장 개장 여부와 무관하게 제안표를 유지한다." - "실행 차단은 validation_status와 별도 실행가능여부 컬럼으로만 표현한다." - "사용자 판단용 제안표와 HTS 즉시 입력 주문표를 같은 표로 섞지 않는다." allow_proposal_when: - "holding_quantity 또는 final_qty 입력이 존재" - "prices_json 또는 동등한 하네스 가격 입력이 존재" - "sell_quantities_json 또는 buy_qty_inputs_json이 존재" block_execution_when: - "intraday_lock = true" - "snapshot_execution_gate != ALLOW_EXECUTION" - "validation_status != PASS" execution_status: proposal_only: "제안 가능 / 즉시 HTS 입력 금지" execution_wait: "제안 가능 / 추가 확인 후 실행" execution_ready: "제안 가능 / 즉시 실행 가능" proposal_price_selection: canonical: true rule: sell_or_trim: trigger: "final_action in [SELL_READY, SELL, TRIM, EXIT_100, EXIT_FULL] 또는 sell_quantities_json 존재" proposed_limit_price_krw: "prices_json.stop_price 우선" proposed_take_profit_price_krw: "prices_json.tp1_price가 유효하면 병기, 없으면 tp2_price" note: "방어적 제안가를 우선 표시한다." take_profit: trigger: "tp_trigger_gate=TRIGGERED 또는 tp1_state/tp2_state=PENDING and final_action includes TAKE_PROFIT" proposed_limit_price_krw: "prices_json.tp1_price 우선, 없으면 tp2_price" proposed_stop_price_krw: "prices_json.stop_price 병기" buy: trigger: "final_action includes BUY 또는 buy_qty_inputs_json.final_qty 존재" proposed_limit_price_krw: "order_blueprint_json.limit_price_krw 우선, 없으면 buy_qty_inputs_json.entry_price_hint" proposed_stop_price_krw: "prices_json.stop_price" proposed_take_profit_price_krw: "prices_json.tp1_price 우선" watch_or_hold: trigger: "final_action in [WATCH, HOLD]" proposed_limit_price_krw: "주문가가 아니라 참고 방어가로 prices_json.stop_price 사용" note: "WATCH/HOLD는 HTS 주문가가 아니라 판단 참고용 가격임을 명시" proposal_quantity_selection: canonical: true rule: sell_or_trim: trigger: "final_action in [SELL_READY, SELL, TRIM, EXIT_100, EXIT_FULL] 또는 sell_quantities_json 존재" proposed_quantity: "sell_quantities_json.sell_qty 우선" note: "보유수량 기반 매도 제안 수량" buy: trigger: "final_action includes BUY 또는 buy_qty_inputs_json.final_qty 존재" proposed_quantity: "buy_qty_inputs_json.final_qty 우선" note: "하네스 산출 신규 매수 수량" watch_or_hold: trigger: "final_action in [WATCH, HOLD]" proposed_quantity: "sell_quantities_json.sell_qty 존재 시 참고 수량으로 표시, 없으면 null" note: "즉시 주문 수량이 아니라 판단 참고 수량" proposal_stop_ladder_selection: canonical: true rule: stop1: price: "prices_json.stop_price" quantity: "core=보유/제안수량의 50%, satellite=보유/제안수량의 70%" rationale: "spec/exit/stop_loss.yaml core/satellite quantity_rule의 1차 손절 물량" stop2: price: "stop1과 동일한 prices_json.stop_price" quantity: "잔여 수량 전부" rationale: "종가 회복 실패 시 잔여 청산 규칙을 사용자 판단용 제안표에 명시" stop3: price: "profit_preservation_json.auto_trailing_stop 우선, 없으면 protected_stop_price" quantity: "tp_quantity_ladder_json.tp3_qty 우선, 없으면 잔여 러너 수량" trigger: "profit_lock_stage != NORMAL 또는 trailing stop 유효" rationale: "수익보전 구간의 러너 보호 스탑을 별도 표기" prohibition: - "stop2/stop3 가격을 차트 지지선·심리적 가격으로 임의 산출 금지" - "profit_preservation/trailing 근거가 없으면 stop3를 비워 둔다" prohibition: - "실행 차단을 이유로 제안 수량·단가 자체를 숨기지 않는다." - "제안표를 HTS 즉시 입력표처럼 오인되게 렌더링하지 않는다." canonical_terms: immediate_cash: "당일 출금 가능 현금" settlement_cash: "D+2 추정현금성자산" buy_power_cash: "즉시현금 + D+2 추정현금성자산 - 예약된 주문금액" cash_floor: "즉시현금 기준 방어선" total_heat: "Σ(entry_price - stop_price) × quantity / 총자산 × 100" source_failure: ["TRANSPORT_BLOCKED", "FETCH_FAILED", "PARSE_FAILED", "SCHEMA_MISMATCH", "ROW_NOT_FOUND", "FIELD_MISSING"] data_gap: ["DATA_MISSING", "PARTIAL", "NOT_APPLICABLE"] master_prohibitions: canonical: true P1_no_quantity_without_holdings: rule: "보유수량 미확인 상태에서 매도수량 숫자 기재 금지." fail_action: "NO_SELL_QUANTITY" P2_no_atr_extrapolation: rule: "ATR20 미확인 상태에서 정수 매수수량 산출 금지." fail_action: "NO_BUY_QUANTITY" P3_no_risk_block_override: rule: "목표수익률, 공격슬롯, 주도주 논리를 이유로 cash_floor, Total_Heat, hard stop, circuit breaker를 완화하거나 우회 금지." fail_action: "BUY_BLOCKED" P4_no_intraday_speculation: rule: > [Intraday_Analysis_Restriction] 모든 정규 분석과 주문 산출은 16:30 장마감 종가를 기준으로 한다. 제공된 캡처(account_snapshot)나 데이터의 시각이 15:30 이전(장중)일 경우, 종가 이탈이 확정되지 않았으므로 '신규 매수'와 '조건부 전량 매도(손절 포함)' 지시를 절대 금지한다. 장중에는 오직 'TRIM(부분 감축)'이나 '현금 확보' 등 보수적 방어 액션만 허용된다. fail_action: "INTRADAY_PROHIBITED" keyword_lock: trigger_condition: "account_snapshot.timestamp < 15:30 KST OR data_feed.timestamp < 15:30 KST" blocked_keywords: ["EXIT_100", "SELL_FULL", "전량매도", "전량 매도", "EXIT_FULL"] blocked_actions: ["hard_stop 사유 EXIT_100", "time_exit 사유 EXIT_100", "신규 BUY 주문"] allowed_actions_only: ["TRIM_25", "TRIM_33", "TRIM_50", "현금 확보", "CASH_RAISE"] auto_action: > timestamp < 15:30 KST 확인 즉시 해당 리포트의 모든 EXIT_100·SELL_FULL 주문행을 validation_status=INTRADAY_PROHIBITED로 강등하고 주문표에서 제거한다. hard_stop·time_exit 사유가 있어도 예외 없이 적용. 사유 컬럼에 "P4:장중캡처 → EXIT_100 차단, TRIM만 허용" 표기 필수. cash_floor_trim_auto: # [2026-05-19_HARNESS_AUDIT_V1] H4 rule_id: "P4_CASH_TRIM" trigger: "intraday_lock=true AND cash_floor_status IN [TRIM_REQUIRED, HARD_BLOCK]" purpose: > 장중 현금 부족 상황에서 종가 대기 없이 즉시 TRIM으로 현금 확보. LLM 임의 배정 금지 — 아래 공식으로만 수량 산출. formula: cash_shortfall_krw: "(target_cash_pct/100 - current_cash_pct/100) × total_asset [CASH_RATIOS_V1 기반]" trim_qty_per_stock: "min(floor(remaining_shortfall_krw / current_price), holding_quantity)" assignment_order: "sell_priority_engine 1순위부터 계단식. 잔여 부족 시 2순위로 이동." output_format: > TRIM {종목명} {qty}주 / 지정가 {현재가} [TICK_OK: {price}원] 사유: "P4+현금부족 → TRIM 자동배정. 신규BUY/EXIT_100 차단 유지." [CASH_RAISE_AUTO: {qty}주 → 예상확보 {proceeds}원 / 부족액 {shortfall}원 중 {recovered}원 해소] prohibition: - "서사 설명으로 trim_qty 임의 결정 금지" - "trim_qty 없이 '현금 확보 권장'만 출력 금지" - "1순위 소진 전 2순위 배정 금지" violation_example: wrong: "15:09 KST 캡처 기반으로 현대로템 280주 EXIT_100 생성 → P4 위반" correct: "15:09 KST 캡처 → EXIT_100 BLOCKED. 필요 시 TRIM_50(140주) 생성 후 15:30 이후 재분석 대기" P5_no_fractional_share: rule: "소수점 매수 금지. 모든 주문 수량은 정수." fail_action: "ROUND_DOWN_OR_NO_QUANTITY" P6_no_a_grade_without_data: rule: "핵심 데이터 완성도 매트릭스 없이 A등급·즉시매수·정수수량 산출 금지." fail_action: "MAX_GRADE_C_OR_INSUFFICIENT_DATA" P7_price_formula_id_required: rule: > 모든 가격(손절가·익절가·trailing_stop·보호스탑 등) 산출 시 spec/13_formula_registry.yaml에 등록된 공식 ID(산출공식_ID)를 반드시 명시. 미등록 레이블(예: "profit_lock_ratchet", "차트 지지선", "심리적 지지선", "임의 하향")로 가격을 생성하는 행위 절대 금지. 등록된 가격 공식 ID: STOP_PRICE_CORE_V1, TRAILING_STOP_PRICE_V1, TAKE_PROFIT_LADDER_V1, TAKE_PROFIT_LADDER_V2, PROFIT_LOCK_RATCHET_V1. fail_action: "PRICE_FORMULA_REQUIRED — 산출공식_ID 미기재 가격은 HTS 입력 불가" hard_stops: canonical: true rules: - id: "HS001_CAPTURE_LEDGER_REQUIRED" rule: "capture_read_ledger 표 없이 계좌·종목별 최종 주문수량 확정 금지." - id: "HS002_AUTO_INVEST_SCREEN_NOT_HOLDINGS" rule: "자동투자/약정 화면을 보유수량·평단·현금으로 사용 금지." - id: "HS003_DATA_MATRIX_REQUIRED" rule: "데이터 완성도 매트릭스 없이 매수·매도 결론 확정 금지." - id: "HS004_BUY_SET_REQUIRED" rule: "매수제안은 지정가·수량·손절가·손절수량·익절가·익절수량 세트가 모두 있어야 유효." - id: "HS005_FLOW_ROWS_20D_REQUIRED_FOR_A" rule: "Flow_Rows<20이면 20D 수급 기반 A등급·즉시매수 판단 금지." - id: "HS006_TOTAL_HEAT_REQUIRED" rule: "Total_Heat 미산출 상태에서 신규 매수 수량 산출 금지." - id: "HS007_HTS_SINGLE_PRICE_ONLY" rule: > HTS 조건부 주문은 단일 이탈 가격 1개만 허용. "XX원 이탈 또는 YY원 회복 실패 시", "두 조건 중 하나 도달 시", "A 조건과 B 조건 동시 충족 시" 등 다중·복합 조건 주문을 플레이북에 산출 금지. 복수 조건이 필요한 시나리오는 "[수동확인 필요: 다중조건 HTS 미지원]" 표기로만 제시. keyword_lock: "주문 조건 텍스트에 '또는', '동시 충족', '실패 시', '회복 실패', '돌파 실패' 포함 시 해당 행 INVALID_MULTI_CONDITION 처리" - id: "HS008_TICK_NORMALIZED_REQUIRED" rule: > 플레이북의 모든 지정가(매수·손절·익절·trailing_stop)는 TICK_NORMALIZER_V1을 거친 KRX 호가 단위 정규화 값이어야 한다. 소수점 포함 가격 또는 호가 단위 불일치 가격(예: 144,568원, 25,886원)은 HTS 입력 불가 오류로 판단하고 해당 행을 INVALID_TICK으로 표시한 뒤 정규화 후 재산출한다. tick_table_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.TICK_NORMALIZER_V1.tick_table" - id: "HS009_TP_VALIDITY_CHECK_REQUIRED" rule: > prices_json의 tp1_price·tp2_price가 null이거나 tp1_state·tp2_state가 TP1_ALREADY_TRIGGERED·TP2_ALREADY_TRIGGERED이면 해당 가격은 INVALID_TP_STALE로 처리한다. prices_lock=true이더라도 TP_VALIDITY_CHECK_V1이 우선하며, LLM이 대체 TP 가격을 임의 산출하는 것은 이 규칙으로 절대 금지한다. fail_action: "INVALID_TP_STALE — HTS 주문표 기재 금지. GAS 재실행 필요." formula_ref: "spec/13_formula_registry.yaml:TP_VALIDITY_CHECK_V1" - id: "HS010_WATCH_BLOCKED_NULL_REQUIRED" rule: > order_blueprint_json에서 validation_status != 'PASS'인 행(WATCH, BLOCKED, INSUFFICIENT_DATA 포함)의 stop_price_krw·stop_quantity·take_profit_price_krw· take_profit_quantity는 반드시 null이어야 한다. LLM이 참고용이라도 WATCH 행과 동일 주문표에 가격·수량 숫자를 기재하면 HS010 위반으로 해당 표 전체가 BLOCKED_ORDER_TABLE로 처리된다. fail_action: "BLOCKED_ORDER_TABLE — WATCH 감시원장을 별도 섹션으로 분리 필수." - id: "HS011_NO_LLM_FORMULA_DEFINITION" rule: > LLM은 spec/13_formula_registry.yaml에 등록된 공식 ID만 인용할 수 있다. 대화 중 새 알고리즘명(V3, V4, ENGINE, REBALANCE, ACTIONABLE_ 등)을 즉석 정의하고 이에 기반한 구체적 원화 가격 또는 정수 수량을 산출하는 것을 절대 금지한다. 하네스 미구현 영역은 "DATA_MISSING — 하네스 업데이트 필요"로만 표시하고 LLM 대체 계산 금지. fail_action: "INVALID_UNREGISTERED_FORMULA — HTS 입력 금지. 해당 숫자 전부 무효." tag_requirement: # [2026-05-19_HARNESS_AUDIT_V1] H3 rule_id: "H3_TICK_TAG" rule: > TICK_NORMALIZER_V1 통과 후 모든 지정가 옆에 태그 필수 부착. 정규화 성공 → "[TICK_OK: {price}원]" 정규화 필요 → "[TICK_INVALID: {raw}원 → {normalized}원 재산출]" missing_tag_action: > 태그 없는 가격은 자동 INVALID_TICK_UNTAGGED 처리. HTS 주문표 기재 금지. 검산_통과여부 = FAIL_NO_TICK_TAG. # ── [2026-05-18_CONFLICT_RESOLUTION_V1] 매도 신호/수량 분리 원칙 ────────────── # AGENTS.md Direction-A와 동일 규칙을 spec 레벨로 명시하여 grey zone 해소. # 충돌 상황: spec/14_raw_workbook_mapping.yaml에 Sell_Qty 필드가 존재하지만 # GAS(Google Apps Script)는 이 필드를 의도적으로 blank로 남긴다 — 오류가 아니다. signal_quantity_separation: canonical: true rule_id: "SQS001" gas_role: > GAS(gas_data_feed.gs)는 매도 신호 생성 전용 엔진이다. GAS 출력 = Sell_Signal(종류) + Sell_Price(지정가) + Sell_Ratio_Pct(비율%). GAS는 절대 매도수량(Sell_Qty 정수)을 산출하지 않는다. agent_role: > 에이전트(LLM)가 account_snapshot(HTS 이미지 캡처) 잔고를 읽어 최종 정수 매도수량을 산출한다. 공식: Sell_Qty = floor(Sell_Ratio_Pct × holding_quantity) holding_quantity는 반드시 capture_read_ledger 판독값 사용. workbook_sell_qty_field_intent: field: "Sell_Qty (spec/14_raw_workbook_mapping.yaml)" normal_value: "blank (GAS 미기재가 정상)" filled_when: "에이전트가 account_snapshot 판독 후 최종 수량 확인 시 기재 가능" prohibition: - "Sell_Qty=blank 또는 Sell_Qty=None을 GAS 오류로 처리 금지" - "Sell_Qty=blank를 이유로 사용자에게 수동 입력 요구 금지" - "GAS가 이미 Sell_Qty를 채웠다고 가정하고 account_snapshot 판독 생략 금지" conflict_resolution: > AGENTS.md와 이 규칙이 충돌하면 이 spec이 우선한다(source_of_truth.priority=highest). AGENTS.md는 보조 운영 지침이고 00_execution_contract는 canonical 실행 계약이다. # ── [2026-05-18_ROUTING_OPTIMIZATION_V1] stop_price 단일 조회 체인 ────────── # 문제: stop_price를 찾을 때 AI가 account_snapshot → deprecated positions 탭으로 # 루프하는 판단 지연이 발생. 아래 3단계 체인으로 라우팅을 단일화한다. stop_price_routing_chain: canonical: true rule_id: "SPRC001" purpose: > 보유 포지션의 stop_price 조회는 오직 아래 3단계 순서를 따른다. positions 탭 참조는 체인의 어느 단계에도 존재하지 않는다. chain: step_1_account_snapshot: source: "account_snapshot.stop_price (HTS 캡처 원장)" condition: "값이 존재하고 > 0 이며 < average_cost" result: "이 값을 stop_price로 확정. 추정·계산 없이 직접 사용." step_2_atr_calculation: source: "ATR 공식 추정" condition: "step_1이 없거나(blank/0) ATR20이 data_feed에 존재할 때" formula: "stop_price_est = max(average_cost * 0.92, average_cost - ATR20 * 1.5)" output_tag: "(ATR추정)" result: "추정값임을 명시하고 사용." step_3_fallback: source: "고정 % 폴백" condition: "step_1·step_2 모두 불가(ATR20도 DATA_MISSING)" formula: "stop_price_est = average_cost * 0.92" output_tag: "(DATA_MISSING 폴백)" result: "DATA_MISSING 표기 필수. HTS 입력 불가 표시." dead_end: positions_tab: > DEPRECATED. 이 체인의 어느 단계에서도 positions 탭을 조회하지 않는다. (spec/15_account_snapshot_contract.yaml:positions_tab 참조) prohibition: - "step_1 미확인 상태에서 step_2 추정값을 '확정' stop_price로 표시 금지" - "positions 탭에 stop_price를 입력하라고 사용자에게 요구 금지" - "account_snapshot에 stop_price가 없다는 이유로 분석 전체를 중단 금지" capture_read_ledger: canonical: true purpose: "계좌·보유·현금 판독의 단일 원장. 이 표 없이 최종 주문수량 확정 금지." columns: ["파일/화면", "계좌", "화면종류", "읽은값", "확신도", "주문표반영", "판독_상태"] status_values: CAPTURE_READ_OK: "캡처/원장에서 읽었고 주문 산출에 반영 가능" CAPTURE_PROVIDED_BUT_NOT_HOLDINGS: "캡처는 있으나 자동투자/약정 화면이라 잔고 산출 불가" CAPTURE_READ_FAILED: "캡처가 있으나 판독 실패 또는 구조화 미완료" NOT_PROVIDED: "해당 화면/원장 미제공" screen_type_rule: auto_investment_screen: "자동투자 설정·약정 이체일·월 적립금액·ISA 한도 화면. 보유수량·평단·현금 판독 금지." holdings_screen: "보유종목 목록·수량·평단·현금·평가손익 화면. 주문 산출에 직접 사용 가능." hard_stop: - "CAPTURE_READ_FAILED 항목이 있으면 해당 계좌 전체 주문수량 산출 보류" - "NOT_PROVIDED 계좌는 신규 매수·매도 주문수량 산출 금지" - "CAPTURE_PROVIDED_BUT_NOT_HOLDINGS 수치를 잔고·보유수량·현금으로 사용 금지" # ── [2026-05-18_CONFLICT_RESOLUTION_V1] T+1 시초가 갭 주문표 재유효성 검사 ── # 배경: 모든 분석은 전일 16:30 마감 데이터 기준이나, 주문은 익일 09:00 이후 집행된다. # 미 증시 급락·지정학 이벤트 등으로 갭 개장 시 전일 계산된 지정가·손절가는 이미 무의미. t1_open_gap_revalidation: canonical: true rule_id: "T1GAP001" purpose: > 익일 시초가가 전일 종가 대비 ±3% 이상 갭이 발생하면 전일 산출된 모든 가격·수량 기반 주문표를 자동 무효화(INVALID_STALE)하고 당일 실시간 데이터 기준 재산출을 의무화한다. trigger: formula: "gap_pct = (open_price_D1 - close_price_D0) / close_price_D0 * 100" threshold_gap_up: "gap_pct >= +3.0%" threshold_gap_down: "gap_pct <= -3.0%" actions: on_gap_up: order_table_status: "INVALID_STALE_GAP_UP" required_actions: - "전일 산출된 손절가·익절가·매수지정가 전량 무효 처리" - "당일 시초가·ATR20 기준 가격 재산출 후 새 주문표 생성" - "stop_loss.tiered_ladder tier_1 이미 돌파 여부 확인 (gap_up > tier_1_target → 즉시 익절 판단)" output_tag: "[T+1 GAP_UP 재산출 필요] — 전일 주문표 무효" on_gap_down: order_table_status: "INVALID_STALE_GAP_DOWN" required_actions: - "전일 손절가 이미 하회 여부 확인" - "stop_loss.gap_down 규칙 즉시 적용: 09:00~09:15 15~30분 저가·거래대금·회복 여부 기록" - "전일 매수 지정가 전량 무효. 당일 갭하락 반응 확인 후 재진입 여부 판단" output_tag: "[T+1 GAP_DOWN 재산출 필요] — 전일 주문표 무효" holding_price_validity: # [2026-05-18_ROUTING_OPTIMIZATION_V1] 보유주 가격 유효성 purpose: > 갭 발생 시 신규 주문 무효화뿐 아니라 보유주의 전일 산출 stop_price· trailing_stop 기준가도 무효화된다. 갭하락 후 전일 가격으로 손절을 집행하는 오류를 방지한다. rule: > t1_open_gap_revalidation 발동 시 모든 보유 포지션의 stop_price·trailing_stop_price·take_profit tier 가격은 STALE_PRICE 상태로 전환. 재산출 전 HTS 조건부 주문 집행 금지. existing_holding_gap_down_procedure: xref: "spec/exit/stop_loss.yaml:stop_loss.gap_down" summary: > 갭하락 보유주 → 09:00~09:15 15~30분 관찰 후 판단. 전일 stop_price 도달 여부와 무관하게 즉시 전량 시장가 매도 금지. high_beta_exception 조건(갭 -5%+거래대금 300%) 충족 시만 50% 선 축소 허용. on_normal_open: condition: "abs(gap_pct) < 3.0%" order_table_status: "VALID_WITH_REVIEW" note: "1~3% 갭은 유효. 단 ATR20 기준 손절가·익절가가 여전히 의미있는지 확인 후 집행." scope: applies_to: ["매수 지정가", "손절 지정가", "익절 지정가 (tier_1/tier_2)", "trailing_stop 기준가"] does_not_apply_to: ["섹터 진단·국면 판단 (가격 무관한 수급/거시 분석)"] gap_measurement_source: "data_feed.Open (당일 시초가) vs data_feed.PrevClose (전일 종가)" missing_policy: open_price_missing: "GAP_CHECK_SKIPPED — 갭 여부 확인 불가. 전일 주문표를 REVIEW_REQUIRED로 표시." prohibition: - "open_price 미확인 상태에서 전일 주문표를 유효한 것으로 간주하고 즉시 집행 금지" - "갭 발생 시 09:00~09:15 사이 전량 시장가 매도·매수 금지 (stop_loss.gap_down 준수)" - "갭 재산출 없이 '어제 계획대로 집행' 지시 금지" output_requirement: > 분석 보고서 상단 블록0(국면 요약) 또는 블록1(주문 검산) 직전에 [T+1 갭 점검] 행을 출력: 형식: "시초가 {open}원 / 전일종가 {prev_close}원 / 갭 {gap_pct:+.1f}% → 주문표 상태: {status}" order_validation_contract: output_rendering_gate: rule: "사람용 보고서도 주문 산출 전 반드시 capture_read_ledger와 단계 검산 결과를 먼저 표로 출력한다." sequence: step_pre: > # [2026-05-19_HARNESS_AUDIT_V1] H1 formula_audit_trail (QEH_AUDIT_BLOCK) — 모든 주문 출력 전 필수 선행. TOTAL_HEAT_V1 / CASH_RATIOS_V1 / SELL_PRIORITY_V1 검산 표 없으면 주문표 전체 BLOCKED (FAT001). step_0: "sell_priority_precheck (SELL/TRIM 후보 2개 이상 또는 현금 부족 시 선행 필수)" step_1: "capture_read_ledger" step_2: "data_completeness_matrix" step_3: "order_quantity_4stage_gate" step_4: "HTS 입력 가능 주문표 또는 산출금지 사유" prohibition: - "capture_read_ledger 없이 계좌 총자산·현금·보유수량·평단·매도수량 숫자를 본문 문장에 단독 기재 금지" - "4단계 검산 결과 없이 즉시 실행 주문표 출력 금지" - "검산 표 밖의 산문 요약으로 주문수량·현금·보유수량 근거를 대체 금지" - "sell_priority_decision_table 없이 복수 매도 후보 중 특정 종목을 1차 대상으로 확정 금지" stages: stage_0_sell_priority_precheck: # spec: spec/risk/portfolio_exposure.yaml:sell_priority_engine activation: - "SELL/TRIM/EXIT 후보가 2개 이상 동시 존재" - "현금 < 목표 (cash_floor.trim_required_when 충족)" - "REGIME_TRIM_50 발동" required_actions: - "GAS ?view=sell_priority 결과를 sell_priority_decision_table로 출력한다" - "ETF 중복노출 합산 확인: 반도체(SK하이닉스+KODEX반도체+KODEX AI반도체) 총노출 계산" - "Sell_Priority_Score 순위표에서 tier 1→2→3→4 순서 확인" table_output_required: > sell_priority_decision_table은 보고서에 독립된 섹션·표 형태로 먼저 출력해야 한다. 텍스트 요약("1순위: 삼성E&A, 2순위: ...")으로 대체하는 것은 이 단계를 충족하지 않는다. fail: > 이 단계 없이 개별 종목 SELL/TRIM 수량을 확정하는 행위는 validation_status=BLOCKED_SELL_PRIORITY_REQUIRED로 처리하고 주문표의 모든 SELL/TRIM 행을 보류한다. text_summary_substitution_ban: "sell_priority_decision_table을 텍스트 서술로 대체 금지. 표가 없으면 SELL 주문표 전체 BLOCKED." core_leader_protection: > SK하이닉스·삼성전자(직접 코어 주도주)는 sell_priority_engine tier=9(마지막 순위). 이 종목이 상위 순위에 나타나면 반드시 tier 1~4 후보가 모두 소진된 이후임을 확인한다. 상승추세 중 core leader를 먼저 매도하려면 hard_stop 또는 명확한 thesis 훼손 근거 필수. stage_1_capture_ledger: check: "capture_read_ledger 모든 계좌 분류 완료" fail: "CAPTURE_READ_FAILED 또는 NOT_PROVIDED 계좌는 주문수량 보류" stage_2_cash_check: check: "매수 주문금액 합산 <= 주문가능현금 확인값" fail: "현금 확인값 없으면 매수금액 산출 금지" stage_3_holding_check: check: "매도수량 <= 확인된 보유수량" fail: > 보유수량 미확인이면 매도수량 미산출. HTS 캡처(보유종목 화면) 미제공 시 수량 칸을 '미산출' 또는 '캡처확인후기재'로만 표시. 보유수량을 기억·추정·이전 대화에서 재사용하는 행위 금지. stage_4_open_order_check: check: "동일 계좌·종목 미체결 주문 여부 확인" fail: "중복주문 방지 검산 불가 표시 후 수동 확인" final_order_table_columns: - "계좌" - "종목명" - "현재보유수량" - "평단" - "현재가" - "주문구분" - "지정가" - "수량" - "손절가" - "손절수량" - "익절가" - "익절수량" - "주문금액" - "tick_status" # [TICK_OK: {price}원] 또는 [TICK_INVALID → 재산출] — H3/HS008 의무 - "검산_통과여부" # ── [2026-05-19_HARNESS_AUDIT_V1] H1 — 공식 검산 표 강제화 ───────────────── formula_audit_trail: canonical: true rule_id: "FAT001" version: "2026-05-19_HARNESS_AUDIT_V1" purpose: > 모든 분석 보고서 최상단에 주문표보다 먼저 출력하는 공식 검산 원장. LLM의 '대략' 계산 습성을 차단하고 공식 ID 기반 결정론적 산출을 보장한다. QEH_AUDIT_BLOCK이 없으면 주문표 전체 BLOCKED. required_output_block: id: "QEH_AUDIT_BLOCK" columns: ["공식_ID", "입력값_요약", "결과값", "발동게이트"] mandatory_formulas: - id: "TOTAL_HEAT_V1" gate_check: "< 7% → ALLOW / 7~10% → HALVE / >= 10% → BLOCK_NEW_BUY" missing_action: "BLOCK_NEW_BUY 자동 발동 + DATA_MISSING 표기" - id: "CASH_RATIOS_V1" gate_check: "current_cash_pct vs target_cash_pct → PASS / CASH_RAISE_REQUIRED(-Xp)" missing_action: "현금 판정 불가 → 매수 보류" - id: "SELL_PRIORITY_V1" condition: "SELL/TRIM 후보 존재 또는 cash_floor_status != PASS 시" gate_check: "1순위 종목 + Score + tier 표시" optional_formulas_when_applicable: - "POSITION_SIZE_V1 — 매수 수량 산출 시" - "STOP_PRICE_CORE_V1 — 손절가 산출 시" - "TAKE_PROFIT_LADDER_V2 — 익절 사다리 산출 시" - "MARKET_RISK_SCORE_V1 — 국면 진단 시" - "RISK_BUDGET_CASCADE_V1 — 리스크 예산 계산 시" - "TICK_NORMALIZER_V1 — 모든 지정가 출력 전" enforcement: - "QEH_AUDIT_BLOCK 없이 주문표 출력 → INVALID_MISSING_AUDIT. 전체 BLOCKED." - "TOTAL_HEAT_V1 결과 없이 신규 BUY 주문 → BLOCKED (HS006 연동)" - "CASH_RATIOS_V1 결과 없이 현금 판정 → BLOCKED (P3 연동)" - "공식 ID 명시 없는 가격·수량 → PRICE_FORMULA_REQUIRED (P7 연동)" - "표에 기재된 공식 ID와 실제 사용 공식 불일치 → CRITICAL_FORMULA_MISMATCH" llm_role: "공식 결과를 표에 복사·기재. 재계산·재해석·임의 조정 금지." # ── [2026-05-20_HARNESS_V5] LLM 계산 화이트리스트 ────────────────────────── llm_computation_whitelist: canonical: true rule_id: "LCW001" version: "2026-05-20_HARNESS_V5" purpose: > LLM이 수행할 수 있는 계산과 절대 수행할 수 없는 계산을 명시한다. 하네스(GAS 확정 값)가 존재하는 영역에서 LLM의 재계산·재해석을 원천 차단한다. 이 규칙은 P7, HS011, FAT001보다 상위에서 계산 행위 자체를 규율한다. allowed: text_and_narrative: - "공식 결과값에 대한 텍스트 요약 및 해석 (숫자 재산출 없이)" - "하네스 출력 JSON 값을 인용·복사해 보고서에 표시" - "spec/13_formula_registry.yaml에 등록된 공식 ID 인용 및 적용 조건 서술" - "게이트 상태 레이블(PASS/BLOCKED/WATCH 등) 인용 및 조치 권고" - "여러 게이트 결과를 조합해 종합 판단 서술 (개별 숫자 재산출 없이)" simple_lookups: - "가격·수량 비교 (단순 대소 비교: close >= tp1_price 등)" - "비율 단순 적용: floor(Sell_Ratio_Pct × holding_quantity) — holding_quantity는 반드시 캡처 판독값" - "정수 반올림 처리: ROUND_DOWN for share quantities" forbidden: price_calculations: rule: "모든 가격은 GAS 확정값 또는 등록된 공식 ID 기반. LLM 임의 계산 절대 금지." examples: - "stop_price, trailing_stop_price 직접 계산" - "take_profit tier 가격 직접 계산" - "ATR 기반 가격 추정 (GAS ATR20 미제공 시 DATA_MISSING으로만 표시)" - "rebound_trigger_price = prevClose + 0.5×ATR20 직접 계산" fail_action: "FORBIDDEN_LLM_PRICE_CALC — 해당 가격 무효. HTS 입력 불가." quantity_calculations: rule: "포지션 사이즈, 매수 수량, 분할 트랜치 수량은 GAS POSITION_SIZE_V1 결과만 사용." examples: - "ATR 기반 포지션 사이즈 직접 계산" - "리스크 예산 기반 수량 직접 계산" - "smart_cash_raise_qty 직접 계산" - "트랜치 수량 임의 산출" fail_action: "FORBIDDEN_LLM_QTY_CALC — 해당 수량 무효. GAS 재실행 필요." cash_and_ratio_calculations: rule: "현금 비율, Heat 수치, 포트폴리오 가중치는 GAS 확정값만 사용." examples: - "total_heat_pct 직접 계산" - "cash_shortfall_krw 직접 계산" - "position weight % 직접 계산" - "settlement_cash_d2_krw 추정" fail_action: "FORBIDDEN_LLM_CASH_CALC — 해당 수치 무효. GAS 재실행 후 재인용." harness_gate_overrides: rule: "GAS 확정 게이트 상태를 LLM이 재판단·완화·우회 절대 금지." examples: - "BLOCKED_LATE_CHASE → 서사로 BUY 허용 유도" - "WHIPSAW_SUSPECTED → 매도 진행 서술" - "ROUTE_D 이외 경로에서 전량매도 권고" - "CRITICAL health_label을 CAUTION으로 임의 완화" fail_action: "FORBIDDEN_GATE_OVERRIDE — 해당 분석 전체 BLOCKED. 재실행 필요." score_recalculation: rule: "모든 스코어(breakout_quality_score, anti_whipsaw_score, t1_forced_sell_risk_score 등)는 GAS 계산 결과만 인용." examples: - "breakout_quality_score 직접 계산" - "distribution_risk_score 재계산" - "late_chase_risk_score 조정" - "portfolio_health_score 재산출" fail_action: "FORBIDDEN_SCORE_RECALC — 스코어 무효. 공식 ID 인용 또는 DATA_MISSING 표기." enforcement: - "FORBIDDEN 항목 위반 발견 시 해당 숫자·결론 전체 BLOCKED으로 처리" - "LCW001 위반이 주문표에 반영되면 주문표 전체 INVALID_LCW_VIOLATION" - "위반된 계산 대신 'GAS 재실행 필요' 또는 'DATA_MISSING — 하네스 업데이트 필요' 표기" cross_refs: - "master_prohibitions.P7_price_formula_id_required" - "hard_stops.HS011_NO_LLM_FORMULA_DEFINITION" - "formula_audit_trail.FAT001" - "spec/13b_harness_formulas.yaml:ANTI_WHIPSAW_HOLD_GATE_V1" - "spec/13b_harness_formulas.yaml:SMART_CASH_RAISE_V2" - "spec/13_formula_registry.yaml:BREAKOUT_QUALITY_GATE_V2" compatibility_aliases: old_paths: "llm_compact_execution_contract.non_negotiable_tables.capture_read_ledger.screen_type_rule": "spec/00_execution_contract.yaml:capture_read_ledger.screen_type_rule" "llm_compact_execution_contract.master_prohibitions": "spec/00_execution_contract.yaml:master_prohibitions" "llm_compact_execution_contract.hard_stops": "spec/00_execution_contract.yaml:hard_stops" rule: "기존 경로가 문서에 남아 있으면 위 alias로 해석하되, 신규 수정 시 새 경로로 교체한다."