Files
QuantEngineByItz/spec/00_execution_contract.yaml
T
kjh2064 a4de0505a0 WBS-8.7 1단계: spec-코드 동기화 확장 (12.5%→22.22%)
3개 contract 파일 추가 태깅:
- spec/00_execution_contract.yaml (execution_slippage + snapshot_admin)
- spec/08_scoring_rules.yaml (score_thresholds + qualitative_sell)
- spec/09_decision_flow.yaml (execution_decision + routing_decision)

결과: 36/162 파일 (22.22% coverage)
목표: 50% 이상 (점진적 확장)

CI gate: PASS
2026-06-22 23:30:19 +09:00

581 lines
34 KiB
YAML
Raw 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: "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로 해석하되, 신규 수정 시 새 경로로 교체한다."