meta: title: "은퇴자산포트폴리오 — 데이터 원천·완성도·검증 명세" parent_file: "RetirementAssetPortfolio.yaml" version: "2026-05-15-F1_modular" language: "ko-KR" timezone: "Asia/Seoul" purpose: "메인 manifest에서 로드되는 구조화 규칙 명세 파일." quant_feed_contract: primary_raw_json: file: "GatherTradingData.json" source_workbook: "GatherTradingData.xlsx" role: "provided_raw_analysis_data_json" priority: "highest_user_provided_market_data" schema_version: "2026-05-18-json-raw-data-v1" required_paths: - "data.data_feed" - "data.sector_flow" - "data.macro" - "data.event_risk" - "data.core_satellite" conversion_tool: "tools/convert_xlsx_to_json.py" usage_rule: - "종목 후보, 가격, 수급, 섹터 흐름, 매크로, 이벤트 위험은 이 JSON을 1차 raw 데이터로 사용한다." - "xlsx는 GAS/Google Sheets 원본·감사·JSON 재생성 소스이며 일반 LLM 분석에서 직접 파싱하지 않는다." - "공개 웹 조회는 JSON 누락·오래됨·충돌 검산용 보조 소스다." - "JSON 값과 웹 조회 값이 충돌하면 data_rule.conflict_action을 적용하고 평균값을 사용하지 않는다." validation_tool: "tools/validate_data_sample_json.py" mapping_file: "spec/14_raw_workbook_mapping.yaml" account_snapshot_contract: "spec/15_account_snapshot_contract.yaml" prohibition: - "JSON이 제공된 상태에서 웹 데이터만으로 종목 점수·수량을 확정하지 않는다." - "JSON에 없는 필드를 LLM이 임의 생성하지 않는다." - "JSON 검증 실패 상태에서 BUY·SELL 수량을 확정하지 않는다." source_priority: 0: "사용자 제공 raw 데이터: 'GatherTradingData.json'.data[core_satellite,event_risk,macro,sector_flow,data_feed]" 1: "AI 직접 공개 데이터 조회: Naver Finance, Yahoo Finance, KRX, OpenDART" 2: "사용자 제공 원자료: HTS, 증권사, FnGuide, KRX, 네이버금융 화면" 3: "부록 자동화 산출물: 공개 URL·기준시각·필드 의미 검증된 경우에만" account_holdings_snapshot: file: "사용자 캡처/원장 자료" usage: "계좌별 보유수량·평단·평가금액·현금 raw 원장. Quantity·Avg_Cost·Cash_Available 없이 정수 주문수량 산출 금지." prohibition: - "공개 데이터로 보유수량·평단·현금을 추정하지 않는다." - "ticker_master·data_feed.csv에 있다는 이유로 실제 보유 중이라고 단정하지 않는다." - "자동투자 화면 월 적립금액을 잔고·보유수량으로 환산하지 않는다." screen_type_separation: canonical_ref: "spec/00_execution_contract.yaml:capture_read_ledger.screen_type_rule" note: "[XREF / proposal_120] 이 섹션 대신 capture_read_ledger.screen_type_rule을 읽는다." image_extraction_rules: label_first_principle: "화면 내 숫자를 먼저 읽지 않는다. 숫자 왼쪽 또는 위쪽 라벨을 먼저 확인하고 계좌·항목을 특정한 뒤에만 값을 추출한다." exclude_screens: if_contains_any: ["투자내역 상세", "회차당 투자금액", "투자 회차", "모으기 신청일", "다음 투자(예정)일", "한도설정금액", "한도사용금액", "납입가능금액", "ISA 가입 정보", "isa 10년투자", "10년 연금저축", "매월 1일 투자"] action: "CAPTURE_PROVIDED_BUT_NOT_HOLDINGS로 분류하고 예수금 판독 제외" direct_cash_read: rule: "총금액 - 평가금액 계산 방식은 사용" required_confirmation: - "어떤 계좌(일반계좌/ISA/연금저축)의 예수금인지 라벨로 먼저 확인" - "'예수금' 또는 '주문가능금액' 라벨 확인 후 값 추출" - "원화(원) 단위 확인" validation: "예수금 >= 0 확인. 상식 범위 벗어나면 판독 재확인 요청." direct_public_lookup: steps: - "KRX: 상장상태·시장구분·가격·거래대금·시가총액·투자자별 거래" - "OpenDART: 공시 원문·실적·계약·증자·CB/BW·지분변동 촉매·리스크" - "Naver Finance: 가격 보조·거래대금·외국인·기관 5D/20D 순매매" - "Yahoo Finance: OHLC 보조·ATR20·20/60거래일 수익률" rule: "모든 숫자에 기준일·기준시각·출처·데이터태그 필수. [웹확인:출처명] 또는 [계산값] 태그." investor_flow_rules: source: "'GatherTradingData.json'.data.data_feed, 'GatherTradingData.json'.data.core_satellite, Naver Finance frgn 또는 KRX 투자자별 거래 직접 조회" accepted_fields: ["Frg_5D(sh)", "Inst_5D(sh)", "Frg_20D(sh)", "Inst_20D(sh)", "Ind_5D(sh)", "Flow_Rows", "Flow_Unit=shares", "Flow_OK"] individual_flow: field: "Ind_5D(sh) — 개인 5D 순매수(주식수)" purpose: "과열 경보 및 수급 우위 확신도 보조 확인. A등급 gating 조건에는 포함하지 않는다." scoring_rules: high_conviction: "Frg_5D↑ + Inst_5D↑ + Ind_5D↓ → 스마트머니 집중·개인 이탈. 수급 우위 확신도 상향." caution_signal: "Frg_5D↓ + Inst_5D↓ + Ind_5D↑ → 개인 쏠림 경계. 신규 진입 시 과열 위험 표시." late_cycle: "외인·기관·개인 모두 순매수 → 추세 후반부 가능성. 추격매수 주의." not_required: "Ind_5D(sh)가 DATA_MISSING이어도 Flow_OK 판정에는 영향 없음." caution: - "Flow_OK=Y가 아니면 외국인·기관 수급 점수 0점." - "Flow_Rows<20이면 20D 수급 판단 금지." - "주식 수 단위 수급을 금액 수급으로 임의 환산 금지." - "Frg_20D(M), Inst_20D(M)은 legacy 더미 컬럼이므로 사용 금지." - "Ind_5D(sh)는 보조 경보 필드다. 개인 순매수라는 이유만으로 A등급 하향 또는 매도 결정 금지." active_quality_gate: # [Q4 / 2026-05-15] C1/C2/C3 시간 기준 미명시로 LLM이 모두 5D로 오산출하거나 # C3의 5D 합산을 "당일" 수급으로 오독하는 할루시네이션 방지. formula: "flow_credit = C1 × 0.30 + C2 × 0.30 + C3 × 0.40" time_scope_summary: "C1·C2는 당일 단일 바 기준, C3는 최근 5거래일 누적 기준. 혼용 금지." components: C1_price_action: time_scope: "당일 단일 바 (종가 vs 시가, 또는 종가 vs 전일 종가)" definition: "당일 종가가 시가 이상(양봉) 또는 전일 대비 상승 시 1점, 미충족 시 0점" weight: 0.30 C2_volume_action: time_scope: "당일 거래량 vs 최근 5거래일 평균거래량 (AvgVolume_5D_shares 단위 — 주식 수. AvgTradeValue_5D_M 억원 단위 혼용 금지)" definition: "당일 거래량이 최근 5일 평균의 120% 이상 시 1점, 미충족 시 0점" weight: 0.30 C3_flow_action: time_scope: "최근 5거래일 누적 순매수 (Frg_5D_sh + Inst_5D_sh 합산. 주식 수 단위)" definition: "외국인+기관 합산 5D 순매수 양수 시 1점, 미충족 시 0점" weight: 0.40 tier_threshold: 대형: "5D평균거래대금 >= 1,000억 → 기관 합산 순매수 50K주/일 이상 확인 권고" 중형: "5D평균거래대금 100억~1,000억 → 10K주/일 이상" 소형ETF: "5D평균거래대금 < 100억 → 2K주/일 이상" pass_condition: # [P130] 예외 조건 구조화 — 문자열 매몰 제거 full_credit: threshold: "flow_credit >= 0.70" action: "수급 가점 100% 부여" partial_credit: threshold: "0.40 <= flow_credit < 0.70" action: "수급 가점 30%만 인정" override_to_reject: condition: "C1 == 0 AND C2 == 0" action: "reject 처리 (C3 단독 충족 = 물량 받기로 간주)" reject: threshold: "flow_credit < 0.40" also_reject_if: "C1 == 0 AND C2 == 0 (flow_credit 수치 무관)" action: "수급 점수 0점. kelly.brake_conditions 발동 검토." prohibition: - "주가 하락 중 기계적 순매수만 유입되는 '물량 받기' 종목을 A등급으로 추천 금지" - "C3만 충족(C1·C2 모두 0점)인 경우 flow_credit 수치에 관계없이 reject 처리" - "flow_credit 미산출 상태에서 수급 기반 A등급 부여 금지" data_completeness_gate: buy_ready: "identity·price_value·investor_flow(20D)·volatility_atr·momentum_value_surge 모두 OK, DATA_CONFLICT 없음 → A등급·즉시매수 검토" watch_only: "identity·price_value만 OK → C-관찰까지" field_status: OK: "원시값·기준일·출처·단위 모두 확인" PARTIAL: "일부 행·기간만 확인" DATA_MISSING: "Plan A/B/C까지 모두 실패" DATA_CONFLICT: "출처 간 수치 불일치" NOT_APPLICABLE: "ETF·신규상장 등 구조적 미해당" fallback_ladder: ATR20: "Naver 일별시세(sise_day.naver) 21거래일 OHLC로 계산. Naver 실패 시 KRX 또는 보조 Yahoo로 전환. ATR20_OK=Y는 21거래일 OHLC 원시행 확인 시에만." investor_flow: "Naver 실패 → KRX 투자자별 거래" trading_value: "KRX 거래대금 또는 Close×Volume 계산값" mandatory_collection_sequence: ["①종목코드·상장상태", "②가격·거래량·거래대금", "③5D 수급", "④20D 수급", "⑤21거래일 OHLC로 ATR20·MA20·Val_Surge 계산", "⑥52주 고저·목표가 참고", "⑦OpenDART 공시 촉매·리스크", "⑧필드 완성도 매트릭스 출력 후 등급·매매 판단"] prohibition: - "필드 완성도 매트릭스 없이 종목 등급 또는 매수·매도 결론 먼저 작성 금지." - "로컬 파일 부재를 분석 중단 사유로 사용 금지." - "자동화 실패와 공개 직접 조회 실패를 같은 의미로 사용 금지." data_maturity_truth_gate: objective: "데이터 형식 완성도와 실전 성숙도를 분리한다." required_outputs: - "data_integrity_score" - "data_maturity_score" - "pending_critical_category_count" - "pending_critical_categories" maturity_penalty_rule: "PENDING 카테고리는 분모 제외가 아니라 maturity penalty로 반영한다." report_rule: - "보고서 첫 섹션에 data_integrity_score와 data_maturity_score를 함께 표시한다." - "data_integrity_score=100이어도 pending_critical_category_count>0이면 PASS_100 문구를 쓰지 않는다." json_analysis_protocol: purpose: "GatherTradingData.json에서 시장 raw 분석 데이터를 빠르게 파싱해 data_completeness_matrix와 판단 입력으로 사용." python_parsing_baseline: shell_rule: "PowerShell에서는 Bash heredoc 금지. '@ ... @ | python -' 형식으로 실행." json_load_rule: "json.loads(Path('GatherTradingData.json').read_text(encoding='utf-8'))를 기본값으로 사용." required_top_level: ["metadata", "data"] required_schema_version: "2026-05-18-json-raw-data-v1" required_paths: ["data.data_feed", "data.sector_flow", "data.macro", "data.event_risk", "data.core_satellite"] code_column_rule: text_columns: ["Ticker", "ETF_Code", "Proxy_Ticker", "Base_Ticker", "Constituent_Code", "ETF_Ticker", "Symbol", "ticker"] normalization: "숫자로 읽힌 91160.0, 5930.0 등은 문자열화 후 6자리 zero-pad 적용." validation_commands: ["npm run validate-data-sample", "npm run validate-specs"] xlsx_refresh_rule: "xlsx 원본을 갱신했으면 npm run convert-data-json 실행 후 JSON을 다시 검증한다." xlsx_analysis_protocol: purpose: "xlsx는 HTS 잔고·거래내역 판독 또는 raw JSON 재생성 감사를 위한 보조 프로토콜이다. 시장 raw 일반 분석은 json_analysis_protocol을 우선한다." python_parsing_baseline: shell_rule: "PowerShell에서는 Bash heredoc 금지. '@ ... @ | python -' 형식으로 실행." openpyxl_read_rule: "값 점검은 openpyxl.load_workbook(path, data_only=True, read_only=True)를 기본값으로 사용." header_rows: gas_output_sheets: "대부분 row1=updated 메타, row2=헤더, row3부터 데이터." universe: "row1=헤더, row2부터 데이터." settings: "key-value 구조. 일반 row2 헤더 시트로 오인 금지." row_count_rule: "ws.max_row는 서식 범위 때문에 3000일 수 있으므로 데이터 행 수로 사용 금지. 첫 컬럼 또는 핵심 키 컬럼 non-empty 행만 센다." code_column_rule: text_columns: ["Ticker", "ETF_Code", "Proxy_Ticker", "Base_Ticker", "Constituent_Code", "ETF_Ticker", "Symbol"] normalization: "숫자로 읽힌 91160.0, 5930.0 등은 문자열화 후 6자리 zero-pad 적용." access_rule: "열 번호 하드코딩 금지. 헤더 행에서 header_map을 만든 뒤 컬럼명으로 접근." validation_commands: ["npm run validate-xlsx-source", "npm run convert-data-json", "npm run validate-data-sample", "npm run validate-specs"] temporary_sheet_rule: "cs_chunk_N은 core_satellite_status.Status=COMPLETE AND Coverage_Pct>=99.9일 때만 삭제 가능." sheet_diet_policy: canonical_required: ["data_feed", "sector_flow", "macro", "event_risk", "core_satellite"] retained_support: - "settings" - "account_snapshot" - "sector_universe" - "sector_flow_history" - "etf_nav_manual" - "universe" - "monthly_history" - "performance" deprecated_sheets: - "positions" - "chat_input" - "etf_raw" - "core_satellite_status" - "orbit_gap" - "asset_history" transient_delete_when_complete: ["cs_chunk_N"] rules: - "orbit_gap·asset_history는 monthly_history(16컬럼)로 통합됐다." - "etf_raw는 GAS in-memory map 전환으로 시트 쓰기 중단." - "core_satellite_status는 ScriptProperties로 이전." - "cs_chunk_N은 core_satellite 병합 완료 후 삭제해 workbook 비대화를 방지한다." step_1_file_classification: types: holdings_xlsx: "보유종목·수량·평단·현금 포함 → CAPTURE_READ_OK" transaction_xlsx: "거래내역·체결가·수수료 포함 → CAPTURE_READ_OK (거래 검증용)" step_2_column_mapping: label_first_principle: "셀 값보다 헤더 행(보통 1~3행)을 먼저 읽어 컬럼 의미를 확정." required_columns_for_holdings: ["계좌명 또는 계좌번호", "종목명 또는 종목코드", "보유수량(주)", "평균단가(원/주)", "현재가(원/주)", "평가금액(원)", "예수금 또는 주문가능금액(원)"] unit_validation: quantity: "정수. 소수 발견 시 PARSE_FAILED." price: "원/주. 단위 확인 불가 시 DATA_CONFLICT." amount: "원. 총금액과 단가×수량 오차 1% 초과 시 DATA_CONFLICT." step_3_data_validation: cross_check: - "평가금액 = 보유수량 × 현재가. 오차 0.5% 초과 시 DATA_CONFLICT." - "예수금 = 총평가금액 - 보유종목 합산 평가금액 (단순 검증)." hard_stop: - "헤더 행 없거나 빈 파일 → PARSE_FAILED." - "수량 열 전체 공백 → unknown_xlsx로 강등." - "통화 단위가 달러($) → 환율 확인 전 금액 산출 금지." step_4_ledger_integration: rule: "유효성 검사 통과 후 capture_read_ledger에 기재." ledger_row_format: "[파일화면, 계좌, 화면종류, 읽은값, 확신도, 주문표반영, 판독상태]" step_5_prohibited: - "research_xlsx의 컨센서스·목표가만으로 보유수량·평단 대체 금지." - "xlsx의 수익률 컬럼을 실현수익률로 단정 금지 (미실현 포함 여부 불명)." - "여러 xlsx가 동일 종목 다른 수량 제공 시 → DATA_CONFLICT, 최신 파일 우선 후 사용자 확인." - "거래내역 xlsx를 잔고 xlsx로 오인 금지." step_6_supplementary_types: fnguide_consensus_xlsx: "컨센서스 참고용. 보유수량·주문수량 산출 금지." trading_history_xlsx: "실현손익 검증용. 잔고 보유수량 추정 사용 금지." ticker_master: status: "dynamic_reference_from_latest_account_snapshot" scope: "첨부 잔고·계좌 원장에서 확인된 보유 종목의 코드 매핑. 후보 유니버스나 고정 보유 목록 사용 금지." refresh_rule: "최신 계좌 자료에 없는 종목은 보유종목으로 출력 금지. 최신 자료에 새 종목은 KRX 코드 확인 후 임시 매핑." prohibited_use: ["있다는 이유로 보유 중 단정", "없다는 이유로 분석 제외", "위성 후보 추출 유니버스로 사용"] data_rule: canonical: true # [proposal_122 / 2026-05-15] 투자 판단 시 이 섹션의 rules가 최우선. data_snapshot_precedence·anti_hallucination보다 우선. supersedes: ["data_snapshot_precedence.conflict_rule", "anti_hallucination.precedence"] required_label: "모든 숫자에 기준시각·출처·데이터유형 표시" data_tags: ["[사용자입력]", "[웹확인:출처명]", "[판독값]", "[계산값]", "[데이터누락]"] priority: ["공식기관·거래소·중앙은행", "증권사·FnGuide", "네이버금융·포털"] strict_unit_enforcement: units: {shares: "주 (정수)", amount: "원 (정수)", ratio: "% (소수 2자리)", price: "원/주"} allowed_transform: - "amount = shares × price" - "ratio = amount / capital × 100" - "risk_amount = shares × (entry_price - stop_price)" - "qty = floor(risk_amount_budget / risk_per_share)" forbidden_transform: - "shares + amount → 이종단위 합산 금지" - "ratio + shares → 비율과 수량 혼합 금지" - "price - ratio → 가격과 비율 뺄셈 금지" hard_rule: - "단위 미확인 시 해당 연산 결과는 산출 불가 처리. 주문수량 0주." - "소수점 주 발생 시 floor() 적용. 반올림 금지." rule: "모든 연산 시 단위(원, 주, %)를 교차 검증하며, 이종 단위(예: 수량과 금액)의 산술 연산 시도는 즉시 차단하고 산출 불가 처리한다." raw_json_trade_value_rule: preferred: "AvgTradeValue_5D_KRW, AvgTradeValue_20D_KRW" legacy: "AvgTradeValue_5D_M, AvgTradeValue_20D_M" legacy_unit: "million KRW" transform: "legacy_value * 1000000" prohibition: "AvgTradeValue_*_M을 억원 단위로 해석 금지." rules: - "첨부 이미지·잔고표·거래내역의 확인값만 사용. 불명확 숫자는 판독불가." - "필수 입력 누락 시 정확한 수익률·계좌별 금액·매수수량·세후성과 산출 금지." - "미확인 수치는 핵심 근거 제외, 점수 산정 시 0점." - "기준시각이 다른 데이터는 직접 비교 금지." - "사용자가 공개 화면에서 복사한 수치는 종목코드·항목명·단위·기준시각이 함께 있을 때만 [사용자입력]으로 인정." freshness_gate: price_max_age_trading_days: 1 flow_max_age_trading_days: 2 consensus_max_age_calendar_days: 14 macro_max_age_calendar_days: 3 stale_action: {price: "정수수량 산출 금지", flow: "수급 점수 0점", consensus: "실적 점수 50%만 인정", macro: "market_context 기반 가중 판단 금지"} conflict_action: "DATA_CONFLICT 표시. A등급·즉시매수·정수수량 산출 금지." alignment_gate: purpose: "가격·수급·공시 기준시각이 혼재된 상태에서 매수 신호 산출을 차단한다." required_fields: ["as_of_date(price)", "as_of_date(flow)", "as_of_date(disclosure)"] alignment_rule: "주문 산출 시 price와 flow의 as_of_date 차이가 1거래일 초과이면 DATA_STALE 처리." DATA_STALE_action: - "정수 수량·손절가·익절가·기대수익비 산출 금지" - "관찰가(진입 후보가) 표기만 허용" - "다음 확인 출처와 갱신 예정 시각을 함께 표기" exception: "macro 데이터는 시장 국면 판단에만 사용. 개별 종목 주문 산출에는 price/flow alignment 기준만 적용." prohibition: "DATA_STALE 상태에서 최신 price와 구형 flow를 조합해 A등급 산출 금지." anti_hallucination: role: "supplementary" # [proposal_122 / 2026-05-15] 종목 진위 확인·수치 검증 용도로만 사용. 우선순위는 data_rule. krx_listing_integrity: required_before_recommendation: ["종목코드 또는 표준코드", "정식 종목명", "시장구분", "상장상태", "기준일", "데이터 출처"] hard_reject: ["KRX 공식 종목마스터 미확인 상품", "AI 생성 유사 종목명·존재하지 않는 테마형 ETF명", "코드 없는 ETF 추천"] historical_price_integrity: required: ["OHLC 원시데이터 또는 차트의 기준일·주기·수정주가 여부", "고가/종가 기준 명시", "액면분할·병합·권리락 확인"] rule: "두 출처의 고점이 다르면 DATA_CONFLICT 표시 후 수량 산출 보류. 추세 돌파 판단에는 종가 확인 필수." mathematical_cross_check: formula: "conflict_rate = abs(Source_A - Source_B) / max(abs(Source_A), abs(Source_B), 1)" field_thresholds: price: "0.005 (0.5%) — 호가 정밀도 기준. 초과 시 즉시 DATA_CONFLICT" volume: "0.10 (10%) — 집계 단위 차이 허용 범위" flow_5d: "0.15 (15%) — 기관/외국인 순매수 집계 오차 허용" atr20: "0.10 (10%) — ATR 계산 기준일 차이 허용" target_price: "0.20 (20%) — 컨센서스 목표가 분산 허용" action: pass: "임계치 이내 → source_priority 상위 소스 사용, 하위 소스는 보조 검산값으로 기록" fail: "임계치 초과 → DATA_CONFLICT, 해당 종목 신규 매수·매도 수량 산출 금지" fallback_rule: - "두 소스 충돌 시 평균값 사용 금지. source_priority 상위 소스만 사용." - "세 소스 이상 충돌 시 중앙값 대신 출처 신뢰도 우선순위 적용." - "신뢰도 동률이면 DATA_CONFLICT로 보류 후 사용자 재확인 요청." rule: "두 출처의 수치 오차율이 field_thresholds를 초과할 경우, DATA_CONFLICT 처리 및 수량 산출 보류. 기존 1% 단일 임계치는 field_thresholds로 대체." precedence: "→ data_rule.priority 참조 (data_rule이 canonical)" market_context: required_fields: macro: ["KOSPI", "KOSDAQ", "USD/KRW", "VIX", "미국 10년물", "미국 HY OAS", "국내 CP/CD 스프레드", "WTI/Brent"] macro_sheets_source: "macro 탭 → Category=Index(KOSPI·KOSDAQ·S&P500), Category=Risk(VIX), Category=FX(USD/KRW), Category=Bond(미국10년물), Category=Commodity(WTI)" leadership: ["주도섹터 1M/3M 상대수익률", "외국인·기관 5D/20D 수급", "거래대금", "52주 신고가"] leadership_sheets_source: "sector_flow 탭 → Sector_Ret5D/Sector_Ret20D(상대수익률), SmartMoney_5D_KRW/SmartMoney_20D_KRW(수급), Alert_Level·Sector_Score(리더십판단)" benchmark: ["KOSPI 시총 상위 비중", "삼성전자+SK하이닉스 KOSPI 비중", "포트폴리오 반도체 실질노출"] benchmark_sheets_source: "data_feed 탭 → 삼성전자(005930)·SK하이닉스(000660) Frg_5D/Inst_5D로 수급 확인 후 비중 판단" regime_response: Risk-On: "분할 집행 허용, 추격매수 금지" 실적장세: "컨센서스 상향·수급 확인 종목 우선" Risk-Off: "신규 위험자산 축소, cash_floor 상향" Credit-Stress: "위성·중복 ETF·수급 이탈 종목부터 축소" guardrails: - "벤치마크 비중 없으면 주도주 초과비중 판단은 DATA_MISSING." - "신용스프레드 데이터 없으면 credit stress 발동 금지." - "뉴스 단독으로 위성 전량청산 금지. 가격·수급·신용 중 2개 이상 확인 필요." liquidity_execution_audit: required_fields: ["최근 5D/20D 평균거래대금", "주문금액", "주문금액/5D평균거래대금(%)", "호가단위·지정가", "예상 세금·수수료", "예상 순현금"] pass_condition: normal_stock: "주문금액이 5D 평균거래대금의 0.25% 이하" etf: "주문금액이 5D 평균거래대금의 0.5% 이하이며 괴리율·스프레드 과도하지 않음" hard_gate: spread_unknown: "호가스프레드 미확인 시 신규매수 금지. 매도도 시장가 금지, 분할 지정가만 허용. 코어는 주문금액 50% 감액." spread_too_wide: "예상 스프레드+슬리피지가 목표 기대수익의 20% 초과 시 A등급 금지." volume_pressure: "주문수량이 20D 평균거래량의 1% 초과 시 최소 3회 분할 또는 보류." thin_book: "호가 공백 3틱 이상 또는 장중 거래대금 급감 시 시장가 금지." missing_policy: - "평균거래대금 없으면 주문금액 제시 가능하나 체결가능성은 PARTIAL 표기." - "예상금액은 지정가 하한 × 수량에서 세금·수수료 차감 전/후 분리."