feat: 리밸런싱 엔진 V1 + GAS 버그 수정 (2026-06-13)

주요 변경:
- tools/build_rebalance_engine_v1.py: REBALANCE_ENGINE_V1 신규
  * account_snapshot 직접 합산(_build_snap_position_map) → 소수주 분리 행 병합
  * 레짐 소스 macro.REGIME_PRELIM 최우선 (GAS 와 동일)
- src/gas_adapter_parts/gdf_06_rebalance.gs: runRebalanceSheet_() 신규
  * Logger.log / getSpreadsheet_() 로 run_all 연동 수정
- src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs
  * _mergePositionRecord_(): 소수주 중복 행 합산 신규
  * parseInt → parseFloat (qty, availQty)
- src/gas_adapter_parts/gdf_01_price_metrics.gs
  * 미보유 종목 SELL_READY → WATCH_EXIT_SIGNAL
- spec/41_release_dag.yaml: build_rebalance_sheet 노드 추가 (step_count 63)
- spec/51_formula_lifecycle_registry.yaml: REBALANCE_ENGINE_V1 등록

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 13:20:14 +09:00
commit ee3e799de1
1474 changed files with 176087 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
# 데이터 파일 (매 실행마다 재생성)
GatherTradingData.xlsx
GatherTradingData.json
# LibreOffice 임시 잠금 파일
.~lock.*
# 빌드 산출물
Temp/
dist/
# OS / 에디터
...
.DS_Store
Thumbs.db
desktop.ini
# Python
__pycache__/
*.pyc
*.pyo
.venv/
*.egg-info/
# Node
node_modules/
# Claude 세션 캐시 (자동메모리 제외)
.claude/projects/
+95
View File
@@ -0,0 +1,95 @@
# 은퇴자산포트폴리오 투자 에이전트 운영 지침
## 0. 최우선 원칙
- 이 파일은 운영 인덱스다. 상세 규칙은 `governance/rules/*.yaml``spec/*.yaml`를 우선한다.
- 가격, 수량, TP/SL, 점수는 오직 `spec/13_formula_registry.yaml`와 하네스 산출값만 사용한다.
- 임의 계산, 임의 가격, 임의 수량, 미등록 공식은 금지한다.
- 하네스 결측은 `DATA_MISSING — 하네스 업데이트 필요`로만 표시한다.
- 차단된 종목의 산출값은 숨기지 말고 shadow ledger로 투명하게 남긴다.
## 1. 읽는 순서
1. `runtime/active_artifact_manifest.yaml`
2. `Temp/final_decision_packet_active.json` (manifest alias)
## 1b. Critical Authority Files
- `spec/00_execution_contract.yaml`
- `spec/risk/aggregate_risk.yaml`
- `spec/risk/portfolio_exposure.yaml`
- `spec/14_raw_workbook_mapping.yaml`
- `spec/15_account_snapshot_contract.yaml`
- `spec/02_data_contract.yaml`
- `spec/09_decision_flow.yaml`
- `spec/12_field_dictionary.yaml`
- `spec/13_formula_registry.yaml`
## 2. 문서 역할
- `AGENTS.md`: 운영 헌법과 링크 인덱스.
- `governance/agents_index.yaml`: rule file 목록과 hash migration index.
- `governance/rules/*.yaml`: 장문의 세부 규칙.
- `spec/*.yaml`: 계약, 공식, 게이트, 출력 계약의 원본 권위.
- `src/quant_engine`: canonical Python package. schema/model parity와 reporting helper를 담는다.
- `tools/*.py`: 검증/생성 CLI. 가능한 한 얇게 유지한다.
- `Temp/*.json`: 런타임 산출물. 읽기 전용 취급이며 직접 편집하지 않는다.
- `schemas/*.schema.json`: shape validation.
## 2b. Directory Routing / Serving
- `spec/`: source of truth. 공식, 계약, 게이트, 출력 스키마의 최우선 읽기 경로.
- `governance/`: 운영 규칙, 인덱스, 해시 마이그레이션, ADR, 템플릿.
- `src/`: Python canonical implementation. 새 로직은 여기부터 반영한다.
- `tools/`: build, validate, convert, audit CLI. 상태는 유지하되 핵심 로직은 두지 않는다.
- `gas_event_calendar.gs`: 이벤트 캘린더 배포 호환 스텁. `seedEventCalendar_()` / `runEventRisk()` 진입점을 유지한다.
- `Temp/`: 실행 결과와 캐시. 라우팅 대상은 아니며 runtime consumer만 읽는다.
- `dist/`, `artifacts/`, `docs/`, `examples/`, `prompts/`, `schemas/`, `tests/`: 패키징/문서/검증/산출물 보조 경로.
- `run_all`: 외부 스케줄러가 호출하는 진입점으로 유지한다. 실행 시 `run_all_invocation_mode=external_scheduler`를 기준으로 해석한다.
## 3. 하드 룰
- 가격 임의 창작 금지.
- 다중 조건 접속사 기반 주문문 금지 (모든 매도는 단일 sell priority table 및 waterfall 방식으로 선형 처리).
- tick normalization 의무.
- TP stale 값은 제거.
- sell candidate가 2개 이상이면 sell priority table을 먼저 출력.
- LLM은 하네스 판정을 번복하지 않으며, 임의로 사칙연산, 평균, 순위(rank) 등을 재계산하지 않고 context key를 그대로 copy-only하여 렌더링한다.
- 외부 시장 데이터는 참고용이며 JSON harness가 우선한다.
- provenance 없는 숫자는 보고서에 쓰지 않는다.
- 추격 매수 방지를 위해 모든 매수 진입은 anti-late entry gate 검증을 필수로 통과한다.
- 데이터 흐름은 단방향(Data -> Feature -> Decision -> Execution -> Report) 아키텍처 경계를 유지하며, renderer가 core 계산을 호출하는 역참조를 금지한다.
- D+2 영업일 기준 현금을 즉시방어 자산으로 간주하고, 목표 예산 5억 원을 기준으로 포지션 사이징 및 리스크 버킷을 제어한다.
- 매주 주말 리밸런싱(rebalance_required=true) 및 매월 1일/11일/21일 중간점검(mid_check_required=true) 운영 cadence를 준수한다.
## 4. 보고 규칙
- 모든 숫자에는 반드시 provenance(출처)를 남기며, 출처가 유효하지 않거나 없는 숫자는 보고서 표기를 전면 배제(DATA_MISSING 처리)한다.
- 보고서 첫 부분에는 portfolio health와 주요 차단 사유를 먼저 쓴다.
- blocked/limited 상태라도 산출된 기준가, 손절가, 익절가, 수량은 숨기지 않는다.
- narrative는 숫자와 게이트를 완화하는 표현을 쓰지 않는다.
## 5. 개발 규칙
- 새 기능은 contract, schema, golden case, owner ledger를 먼저 만든다.
- 구현은 Python canonical first, GAS adapter second다.
- `tools/*.py`는 CLI wrapper에 가깝게 유지한다.
- `gas_*.gs`는 thin adapter 방향으로 유지한다.
- `src/quant_engine`는 canonical package로 유지한다.
- `schemas/generated``src/quant_engine/models/generated`는 schema/model parity를 유지한다.
- 경로가 새로 생기면 `AGENTS.md`의 Directory Routing / Serving 섹션과 zip 화이트리스트를 함께 갱신한다.
## 6. 검증 규칙
- `python tools/validate_specs.py`
- `python tools/validate_golden_coverage_100.py`
- `python tools/validate_calibration_registry_v1.py`
- `python tools/validate_schema_model_generation_v1.py`
- `python tools/validate_gas_thin_adapter_v1.py`
- `python tools/validate_agents_shrink_v1.py`
## 7. Rule Index
- [governance/agents_index.yaml](/C:/Temp/data_feed/governance/agents_index.yaml)
- [governance/rules/00_core_locks.yaml](/C:/Temp/data_feed/governance/rules/00_core_locks.yaml)
- [governance/rules/01_harness_contract.yaml](/C:/Temp/data_feed/governance/rules/01_harness_contract.yaml)
- [governance/rules/02_portfolio_policy.yaml](/C:/Temp/data_feed/governance/rules/02_portfolio_policy.yaml)
- [governance/rules/03_order_grammar.yaml](/C:/Temp/data_feed/governance/rules/03_order_grammar.yaml)
- [governance/rules/04_reporting_contract.yaml](/C:/Temp/data_feed/governance/rules/04_reporting_contract.yaml)
- [governance/rules/05_migration_hashes.yaml](/C:/Temp/data_feed/governance/rules/05_migration_hashes.yaml)
- 모든 수치 provenance 및 아키텍처 경계는 `spec/49_refactor_methodology_contract.yaml``ADR-0011`을 준수한다.
- 가격/수량/공식을 LLM이 즉석 정의하지 않는다.
- replay 표본을 live 운영성과로 혼입하지 않는다.
- validation 실패를 무시하지 않는다.
- deprecated artifact를 runtime source로 읽지 않는다.
+136
View File
@@ -0,0 +1,136 @@
# Core/Satellite Collector v4
은퇴자산용 코어/위성 후보 데이터 수집기입니다.
## v4 기본 정책
- 파라미터 없이 실행
- 1차 유니버스: KOSPI 160개 + KOSDAQ 40개
- 최종 후보: 100개
- 최종 후보 내 KOSDAQ: 최대 20개
- 1차 탐색 총량은 v3와 동일한 200개로 유지하여 호출 수 증가를 막습니다.
## 설치
```powershell
npm install
node core_satellite_collector.js
```
OpenDART 공시까지 확인하려면:
```powershell
$env:DART_API_KEY="발급받은키"
node core_satellite_collector.js
```
## 운영 표준
하네스/보고서/동기화까지 포함한 최종 게이트는 아래 순서를 사용합니다.
```powershell
npm run full-gate
```
이 스크립트는 아래를 직렬로 수행합니다.
- `convert-data-json`
- `validate-gas-call-arity`
- `validate-proposal-reference`
- `validate-harness-context`
- `validate-operational-report-contract`
- `audit-coverage`
- `validate-harness-coverage-auditor`
- `validate-strategy-tests-contract`
- `validate-breakout-gate`
- `validate-anti-whipsaw`
- `validate-cash-raise-route`
- `validate-brt-harness`
- `validate-determinism`
- `validate-alpha-execution-harness:strict`
- `render-report-json`
- `validate-report-json`
- `validate-report-quality`
- `validate-report-sync`
spec와 데이터 샘플 검증까지 포함한 전체 엄격 검증은 아래를 사용합니다.
```powershell
npm run validate-engine-strict
```
proposal 평가 이력까지 갱신하는 일일 실행은 아래를 사용합니다.
```powershell
npm run daily-feedback-report
```
백필 누적 원장(`backdata_feature_bank`) 상태를 즉시 검증하려면 아래를 사용합니다.
```powershell
npm run validate-backdata-migration-state
```
Outcome/Evaluation 복구 파이프라인(YOLO)을 한 번에 실행하려면 아래를 사용합니다.
```powershell
npm run yolo-outcome-recovery
```
GAS 함수 정의/호출 인자 수 불일치만 단독 점검하려면 아래를 사용합니다.
```powershell
npm run validate-gas-call-arity
```
사용자 판단용 제안표 하네스 출력이 실제로 존재하는지 점검하려면 아래를 사용합니다.
```powershell
npm run validate-proposal-reference
```
GAS `runHarnessRefresh_()` 반영 후에는 아래 강제 검증으로 올릴 수 있습니다.
```powershell
npm run validate-proposal-reference:strict
```
GAS 반영 후 `proposal_reference_json`까지 포함한 최종 엄격 게이트는 아래를 사용합니다.
```powershell
npm run full-gate:proposal-strict
```
spec/데이터 샘플까지 포함한 전체 엄격 검증은 아래를 사용합니다.
```powershell
npm run validate-engine-proposal-strict
```
## GAS 반영 체크리스트
`proposal_reference_json`을 실제 하네스 출력으로 승격하려면 아래 순서를 따릅니다.
1. Apps Script에 최신 [gas_harness_rows.gs](/C:/Temp/data_feed/gas_harness_rows.gs:73) 반영
2. Apps Script에서 `runHarnessRefresh_()` 실행
3. Google Sheets `harness_context` 시트에 아래 키 생성 확인
- `proposal_reference_json`
- `proposal_reference_lock`
4. 로컬에서 `npm run convert-data-json` 실행
5. `npm run validate-proposal-reference:strict` 실행
6. `npm run full-gate:proposal-strict` 실행
7. 최종 운영 전환 시 `npm run validate-engine-proposal-strict` 기준으로 사용
## 운영 리포트 계약
운영 리포트는 사람이 읽는 `Temp/operational_report.md`와 기계 검증용 `Temp/operational_report.json`을 함께 생성합니다.
- `operational_report.json`이 canonical 계약입니다.
- `operational_report.md`는 표시용 렌더입니다.
- JSON 스키마는 `schemas/operational_report.schema.json`을 사용합니다.
- 계약 드리프트 검사는 `npm run validate-operational-report-contract`로 수행합니다.
- 전체 게이트에는 `render-report-json -> validate-report-json -> validate-report-quality -> validate-report-sync` 순서가 포함됩니다.
전환 기준:
- `validate-proposal-reference``SKIP`이면 아직 GAS 산출물 미반영 상태
- `validate-proposal-reference:strict``PASS`여야 proposal 하네스 strict 전환 완료
+507
View File
@@ -0,0 +1,507 @@
meta:
title: "은퇴자산 투자 포트폴리오 운용 지침 — Modular Manifest"
version: "2026-05-20-HARNESS_V4"
language: "ko-KR"
timezone: "Asia/Seoul"
currency: "KRW"
target_asset_krw: 500000000
target_deadline: "2026-12-31"
purpose: >
이 파일은 LLM이 투자 판단에 사용할 구조화 명세의 manifest다.
상세 알고리즘은 spec/*.yaml에 분리하고, 보고서 양식은 RetirementAssetPortfolioReportTemplate.yaml에 둔다.
자연어 설명보다 구조화 명세와 수치 공식이 우선한다.
제공 raw 분석 데이터의 1차 LLM 입력은 'GatherTradingData.json'이며,
xlsx는 GAS/Google Sheets 원본·감사·JSON 재생성 소스다.
JSON의 필수 경로와 컬럼은 spec/02_data_contract.yaml 및 tools/validate_data_sample_json.py로 검증한다.
primary_raw_data:
json: "GatherTradingData.json"
source_workbook: "GatherTradingData.xlsx"
role: "provided_raw_analysis_data_json"
required_json_paths: ["data.data_feed", "data.sector_flow", "data.macro", "data.event_risk", "data.core_satellite"]
validation_tool: "tools/validate_data_sample_json.py"
conversion_tool: "tools/convert_xlsx_to_json.py"
mapping_file: "spec/14_raw_workbook_mapping.yaml"
account_snapshot_contract: "spec/15_account_snapshot_contract.yaml"
precedence: "사용자 제공 raw JSON이 공개 웹 조회보다 우선한다. 웹 조회는 누락·충돌·최신성 검산용 보조 소스다."
xlsx_usage: "xlsx는 원본 감사 또는 JSON 재생성 확인에만 직접 사용한다. 일반 LLM 분석에서 xlsx 직접 파싱 금지."
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"]
rule: "orbit_gap·asset_history는 monthly_history로 통합. etf_raw는 in-memory map 전환. core_satellite_status는 ScriptProperties 이전. cs_chunk_N은 병합 완료 후 삭제."
last_cleanup:
completed_at: "2026-05-18T09:50:00+09:00"
removed_sheets: ["cs_chunk_0"]
backup_file: "GatherTradingData.xlsx.20260518_095000.bak"
validation_status: "PASS"
accepted_proposals:
- proposal_id: "2026-05-18_SENIOR_VETERAN_UPGRADE"
source_file: "proposals/2026-05-18_시니어_전문가_고도화_제안_SafeLanding_Factor.yaml"
adopted_scope: "PARTIAL_MANIFEST_ONLY"
adopted_items:
- "Safe Landing Protocol은 spec/01_objective_profile.yaml에 이미 존재하므로 manifest에는 중복 본문을 복사하지 않는다."
- "Factor Exposure Overload Brake는 동일 섹터 외 고베타·고모멘텀 과집중 차단 원칙으로 채택한다."
- "Non-Linear Gap Risk Buffer는 이벤트 3거래일 전~당일 신규 진입 수량 산출 보수화 원칙으로 채택한다."
- "Signal-Price Divergence Guard는 HTS 현재가와 JSON 기준 가격 3% 초과 괴리 시 BUY_BLOCKED 원칙으로 채택한다."
rejected_or_deferred_items:
- "canonical spec 세부 공식 수정은 별도 surgical update로만 수행한다."
- "risk hard stop 완화, 목표 미달에 따른 risk_budget 강제 상향, 중복 SLP 본문 추가는 반영하지 않는다."
duplicate_work_prevention: "이 proposal_id는 manifest 레벨에서 처리 완료. 동일 제안 재검토 시 미반영 세부 spec만 별도 target_file로 제안한다."
completed_at: "2026-05-18T09:40:11+09:00"
backup_file: "은퇴자산포트폴리오_20260518_094011.yaml.bak"
validation_status: "PASS"
source_of_truth_order:
1: "spec/00_execution_contract.yaml — master prohibitions, hard stops, capture ledger, order validation"
2: "spec/risk/aggregate_risk.yaml — Total_Heat and portfolio hard stops"
3: "spec/risk/circuit_breakers.yaml — circuit breakers and trading controls"
4: "spec/risk/market_risk_cash.yaml — market risk score and cash rules"
5: "spec/risk/portfolio_exposure.yaml — cash floor, duplicate exposure, target allocation"
6: "spec/12_field_dictionary.yaml — canonical field names, aliases, units, missing policies"
7: "spec/13_formula_registry.yaml — executable formula contracts for LLM calculation"
7a: "spec/03_formulas/formula_registry.normalized.yaml — normalized formula registry index for governance validation"
7b2: "spec/03_formulas/output_field_owner_ledger.yaml — output field owner ledger for writer precedence validation"
7b: "spec/13b_harness_formulas.yaml — harness V4 formulas: TP_VALIDITY_CHECK_V1, PROFIT_LOCK_STAGE_CLASSIFIER_V1, REGIME_TRIM_WEIGHT_V1"
7c: "spec/factor_lifecycle_registry.yaml — factor lifecycle status core/retired classification"
8: "spec/14_raw_workbook_mapping.yaml — market raw JSON path/column mapping"
9: "spec/15_account_snapshot_contract.yaml — image capture account/holding/cash contract"
10: "spec/19_harness_contract.yaml — deterministic harness contract, lock semantics, sync validation"
10b: "spec/20_harness_output_schema.yaml — mandatory numeric output schema; GAS coverage measurement baseline"
10c: "spec/21_harness_governance_contract.yaml — harness governance 3-layer lock and release hardlocks"
11: "spec/02_data_contract.yaml — source priority, data completeness, freshness, parsing rules"
12: "spec/09_decision_flow.yaml — state-machine decision order"
13: "spec/11_market_regime.yaml — canonical only for market regime classification"
14: "spec/08_scoring_rules.yaml — rule_id based hard filters, scoring, grade thresholds"
15: "spec/10_portfolio_rules.yaml — account routing, exposure, cash and contribution constraints"
16: "spec/strategy/sector_model.yaml — sector and stock scoring model"
17: "spec/strategy/entry_core.yaml — core entry gates"
18: "spec/strategy/leader_scan.yaml — daily leader scan and anti-climax gate"
19: "spec/strategy/staged_entry.yaml — staged entry and pullback reentry"
20: "spec/05_position_sizing.yaml — volatility targeting, Bayesian/Kelly brake, integer quantity"
21: "spec/exit/stop_loss.yaml — stop loss policy"
22: "spec/exit/take_profit.yaml — take profit policy"
23: "spec/exit/proactive_exit_radar.yaml — proactive exit radar"
24: "spec/01_objective_profile.yaml — objective, account constraints, contribution policy"
24b: "spec/property_invariants.yaml — property invariants specification"
24c: "spec/anti_late_entry_contract.yaml — anti late entry contract"
24d: "spec/profit_preservation_contract.yaml — profit preservation contract"
24e: "spec/operating_cadence.yaml — weekly/monthly cadence specification"
25: "spec/07_output_schema.yaml — recommendation grade, tables, HTS output schema"
26: "schemas/output_schema.json — machine-readable output contract for LLM responses"
27: "RetirementAssetPortfolioReportTemplate.yaml — human-readable report templates loaded only when report output is needed"
28: "prompts/*.md — reusable prompt entrypoints for analysis/review"
29: "examples/*.yaml and examples/examples.jsonl — examples are illustrative and never override rules"
30: "tests/*.yaml — consistency checks for future edits"
load_sequence:
STEP_1_always:
files:
- "RetirementAssetPortfolio.yaml"
- "spec/00_execution_contract.yaml"
- "spec/risk/aggregate_risk.yaml"
- "spec/risk/circuit_breakers.yaml"
- "spec/risk/market_risk_cash.yaml"
- "spec/risk/portfolio_exposure.yaml"
- "spec/risk/factor_risk.yaml"
- "spec/12_field_dictionary.yaml"
- "spec/13_formula_registry.yaml"
- "spec/03_formulas/formula_registry.normalized.yaml"
- "spec/03_formulas/output_field_owner_ledger.yaml"
- "spec/factor_lifecycle_registry.yaml"
- "spec/13b_harness_formulas.yaml"
- "spec/14_raw_workbook_mapping.yaml"
- "spec/15_account_snapshot_contract.yaml"
- "spec/19_harness_contract.yaml"
- "spec/20_harness_output_schema.yaml"
- "spec/21_harness_governance_contract.yaml"
- "spec/40_final_decision_packet_contract.yaml"
- "spec/41_release_dag.yaml"
- "spec/43_quant_factor_taxonomy.yaml"
- "spec/45_number_provenance_contract.yaml"
- "spec/46_low_capability_execution_pack.yaml"
- "spec/47_packaging_policy.yaml"
- "spec/property_invariants.yaml"
- "spec/anti_late_entry_contract.yaml"
- "spec/profit_preservation_contract.yaml"
- "spec/operating_cadence.yaml"
- "spec/formula_lifecycle_index.yaml"
- "spec/release/low_capability_context_aliases.yaml"
- "spec/release/version_retirement_policy.yaml"
- "spec/48_module_io_contract_registry.yaml"
- "spec/49_refactor_methodology_contract.yaml"
- "spec/51_formula_lifecycle_registry.yaml"
- "Temp/final_decision_packet_v4.json"
- "spec/02_data_contract.yaml"
- "spec/01_objective_profile.yaml"
purpose: "위험 차단, 데이터 사용 가능성, 계좌 제약을 먼저 확정한다."
STEP_2_for_buy_sell_decision:
files:
- "spec/09_decision_flow.yaml"
- "spec/11_market_regime.yaml"
- "spec/08_scoring_rules.yaml"
- "spec/10_portfolio_rules.yaml"
- "spec/strategy/sector_model.yaml"
- "spec/strategy/entry_core.yaml"
- "spec/strategy/leader_scan.yaml"
- "spec/strategy/staged_entry.yaml"
- "spec/05_position_sizing.yaml"
- "spec/exit/stop_loss.yaml"
- "spec/exit/take_profit.yaml"
- "spec/exit/proactive_exit_radar.yaml"
purpose: "진입·청산·수량 산출을 결정한다."
STEP_3_for_output:
files:
- "spec/07_output_schema.yaml"
- "schemas/output_schema.json"
- "RetirementAssetPortfolioReportTemplate.yaml"
- "spec/40_final_decision_packet_contract.yaml"
- "spec/46_low_capability_execution_pack.yaml"
- "Temp/final_decision_packet_v4.json"
- "Temp/final_context_for_llm_v4.yaml"
purpose: "JSON 출력 계약, HTS 입력표, 상세 보고서 양식을 확정한다. 사용자 출력 보고서의 모든 상태값·레이블은 spec/07_output_schema.yaml의 korean_display_labels 매핑을 적용해 한글로 표시한다."
STEP_4_for_validation:
files:
- "tests/strategy_tests.yaml"
- "examples/*.yaml"
- "examples/examples.jsonl"
purpose: "규칙 일관성 점검과 예시 대조에 사용한다."
STEP_5_for_prompt_entrypoints:
files:
- "prompts/analysis_prompt.md"
- "prompts/review_prompt.md"
purpose: "반복 분석과 리뷰 작업의 시작 프롬프트로 사용한다."
spec_files:
execution_contract: "spec/00_execution_contract.yaml"
objective_profile: "spec/01_objective_profile.yaml"
data_contract: "spec/02_data_contract.yaml"
field_dictionary: "spec/12_field_dictionary.yaml"
formula_registry: "spec/13_formula_registry.yaml"
formula_registry_normalized: "spec/03_formulas/formula_registry.normalized.yaml"
output_field_owner_ledger: "spec/03_formulas/output_field_owner_ledger.yaml"
formula_domain_manifest: "spec/formulas/domains/manifest.yaml"
formula_domain_risk: "spec/formulas/domains/risk.yaml"
formula_domain_entry: "spec/formulas/domains/entry.yaml"
formula_domain_exit: "spec/formulas/domains/exit.yaml"
formula_domain_cash: "spec/formulas/domains/cash.yaml"
formula_domain_portfolio: "spec/formulas/domains/portfolio.yaml"
formula_domain_reporting: "spec/formulas/domains/reporting.yaml"
formula_domain_fundamental: "spec/formulas/domains/fundamental.yaml"
formula_domain_smart_money: "spec/formulas/domains/smart_money.yaml"
formula_domain_macro: "spec/formulas/domains/macro.yaml"
harness_formula_registry: "spec/13b_harness_formulas.yaml"
raw_workbook_mapping: "spec/14_raw_workbook_mapping.yaml"
account_snapshot_contract: "spec/15_account_snapshot_contract.yaml"
harness_contract: "spec/19_harness_contract.yaml"
harness_governance_contract: "spec/21_harness_governance_contract.yaml"
pipeline_runtime_contract: "spec/22_pipeline_runtime_contract.yaml"
low_capability_llm_pipeline_todo: "spec/23_low_capability_llm_pipeline_todo.yaml"
strategy_hardening_todo_v1: "spec/24_strategy_hardening_todo_v1.yaml"
canonical_metrics_registry: "spec/25_canonical_metrics_registry.yaml"
behavioral_coverage_contract: "spec/26_behavioral_coverage_contract.yaml"
calibration_registry: "spec/calibration_registry.yaml"
formula_golden_cases_v2: "spec/formula_golden_cases_v2.yaml"
formula_golden_cases_v3: "spec/formula_golden_cases_v3.yaml"
formula_golden_cases_v4: "spec/formula_golden_cases_v4.yaml"
formula_golden_cases_nf: "spec/formula_golden_cases_nf.yaml"
canonical_artifact_resolver_v1: "spec/32_canonical_artifact_resolver.yaml"
execution_precedence_lock_v1: "spec/33_execution_precedence_lock.yaml"
architecture_boundaries_v1: "spec/34_architecture_boundaries.yaml"
rule_lifecycle_governance_v3: "spec/35_rule_lifecycle_governance_v3.yaml"
gas_thin_adapter_policy: "spec/39_gas_thin_adapter_policy.yaml"
goal_risk_budget_harness_v1: "spec/36_goal_risk_budget_harness.yaml"
evaluation_dashboard_v1: "spec/37_evaluation_dashboard_contract.yaml"
bch_calibration_runbook: "spec/27_bch_calibration_runbook.yaml"
imputed_data_exposure_contract: "spec/28_imputed_data_exposure_contract.yaml"
backtest_harness_contract: "spec/29_backtest_harness_contract.yaml"
completion_criteria_contract: "spec/30_completion_criteria_contract.yaml"
semiconductor_concentration_policy: "spec/strategy/semiconductor_concentration_policy.yaml"
strategy_execution_lock_policy: "spec/strategy_execution_lock_policy.yaml"
low_capability_llm_response_contract: "spec/31_low_capability_llm_response_contract.yaml"
final_decision_packet_contract_v4: "spec/40_final_decision_packet_contract.yaml"
release_dag_v1: "spec/41_release_dag.yaml"
quant_factor_taxonomy_v1: "spec/43_quant_factor_taxonomy.yaml"
live_replay_separation_v2: "spec/44_live_replay_separation.yaml"
number_provenance_contract_v1: "spec/45_number_provenance_contract.yaml"
low_capability_execution_pack_v1: "spec/46_low_capability_execution_pack.yaml"
packaging_policy_v1: "spec/47_packaging_policy.yaml"
property_invariants: "spec/property_invariants.yaml"
anti_late_entry_contract: "spec/anti_late_entry_contract.yaml"
profit_preservation_contract: "spec/profit_preservation_contract.yaml"
operating_cadence: "spec/operating_cadence.yaml"
formula_lifecycle_index: "spec/formula_lifecycle_index.yaml"
low_capability_context_aliases: "spec/release/low_capability_context_aliases.yaml"
version_retirement_policy: "spec/release/version_retirement_policy.yaml"
repository_entropy_budget_v1: "spec/release/repository_entropy_budget.yaml"
execution_authority_matrix_v2: "spec/execution_authority_matrix_v2.yaml"
entry_freshness_score_v1: "spec/strategy/entry_freshness_score_v1.yaml"
data_gaps_roadmap: "spec/16_data_gaps_roadmap.yaml"
performance_contract: "spec/17_performance_contract.yaml"
settings_contract: "spec/18_settings_contract.yaml"
risk_policy_index: "spec/03_risk_policy.yaml"
risk_control_index: "spec/risk/risk_control.yaml"
aggregate_risk: "spec/risk/aggregate_risk.yaml"
circuit_breakers: "spec/risk/circuit_breakers.yaml"
market_risk_cash: "spec/risk/market_risk_cash.yaml"
portfolio_exposure: "spec/risk/portfolio_exposure.yaml"
risk_quality_control: "spec/risk/quality_control.yaml"
factor_risk: "spec/risk/factor_risk.yaml"
strategy_rules_index: "spec/04_strategy_rules.yaml"
sector_model: "spec/strategy/sector_model.yaml"
entry_gates_index: "spec/strategy/entry_gates.yaml"
entry_core: "spec/strategy/entry_core.yaml"
leader_scan: "spec/strategy/leader_scan.yaml"
staged_entry: "spec/strategy/staged_entry.yaml"
strategy_discovery: "spec/strategy/discovery.yaml"
stock_model: "spec/strategy/stock_model.yaml"
action_matrix: "spec/strategy/action_matrix.yaml"
rebalancing_trigger: "spec/strategy/rebalancing_trigger.yaml"
fundamental_quality: "spec/strategy/fundamental_quality.yaml"
fundamental_quality_v2: "spec/strategy/fundamental_quality_v2.yaml"
fundamental_quality_v3: "spec/strategy/fundamental_quality_v3.yaml"
fundamental_and_horizon_gate_v1: "spec/strategy/fundamental_and_horizon_gate_v1.yaml"
horizon_allocation_v1: "spec/strategy/horizon_allocation_v1.yaml"
smart_money_liquidity_gate_v1: "spec/strategy/smart_money_liquidity_gate_v1.yaml"
anti_late_entry_pullback_gate_v4: "spec/strategy/anti_late_entry_pullback_gate_v4.yaml"
pre_distribution_early_warning_v3: "spec/strategy/pre_distribution_early_warning_v3.yaml"
anti_late_entry_pullback_gate_v5: "spec/strategy/anti_late_entry_pullback_gate_v5.yaml"
pre_distribution_early_warning_v4: "spec/strategy/pre_distribution_early_warning_v4.yaml"
macro_event_synchronizer_v2: "spec/strategy/macro_event_synchronizer_v2.yaml"
value_preserving_cash_raise_optimizer_v7: "spec/exit/value_preserving_cash_raise_optimizer_v7.yaml"
predictive_alpha_dialectic_v1: "spec/strategy/predictive_alpha_dialectic_v1.yaml"
field_dictionary_strict: "spec/fields/field_dictionary.yaml"
data_quality_expectations: "spec/data_quality/expectations.yaml"
decision_graph_dag: "spec/routing/decision_graph.yaml"
shadow_ledger_contract: "spec/outputs/shadow_ledger_contract.yaml"
renderer_contract: "spec/render/renderer_contract.yaml"
release_train: "spec/release/release_train.yaml"
predictive_alpha_dialectic_v2: "spec/strategy/predictive_alpha_dialectic_v2.yaml"
routing_trace_v2: "spec/routing_trace_v2.yaml"
market_regime: "spec/11_market_regime.yaml"
scoring_rules: "spec/08_scoring_rules.yaml"
decision_flow: "spec/09_decision_flow.yaml"
portfolio_rules: "spec/10_portfolio_rules.yaml"
position_sizing: "spec/05_position_sizing.yaml"
exit_policy_index: "spec/06_exit_policy.yaml"
stop_loss: "spec/exit/stop_loss.yaml"
take_profit: "spec/exit/take_profit.yaml"
proactive_exit_radar: "spec/exit/proactive_exit_radar.yaml"
event_response: "spec/exit/event_response.yaml"
position_review: "spec/exit/position_review.yaml"
dynamic_value_preservation_sell_v3: "spec/exit/dynamic_value_preservation_sell_v3.yaml"
output_schema: "spec/07_output_schema.yaml"
machine_output_schema: "schemas/output_schema.json"
report_templates: "RetirementAssetPortfolioReportTemplate.yaml"
prompts: "prompts/*.md"
examples: "examples/*.yaml"
examples_jsonl: "examples/examples.jsonl"
tests: "tests/*.yaml"
proposals: "proposals/*.yaml"
factor_lifecycle_registry: "spec/factor_lifecycle_registry.yaml"
# ── H001~H008 신규 하네스 계약 (P5-T01, 2026-06-10) ──────────────────────
decision_trace_replay_contract: "spec/52_decision_trace_replay_contract.yaml"
factor_conflict_matrix: "spec/53_factor_conflict_matrix.yaml"
temporal_data_integrity: "spec/54_temporal_data_integrity.yaml"
execution_simulator_contract: "spec/55_execution_simulator_contract.yaml"
renderer_copy_only_contract: "spec/56_renderer_copy_only_contract.yaml"
shadow_promotion_scorecard: "spec/57_shadow_promotion_scorecard.yaml"
llm_determinism_contract: "spec/58_llm_determinism_contract.yaml"
governance:
ownership_map: "spec/ownership_map.yaml"
aliases: "spec/aliases.yaml"
xref_matrix: "spec/xref_matrix.yaml"
future_split_indexes:
risk: "spec/risk/README.md"
strategy: "spec/strategy/README.md"
active_canonical_splits:
risk:
- "spec/risk/aggregate_risk.yaml"
- "spec/risk/circuit_breakers.yaml"
- "spec/risk/market_risk_cash.yaml"
- "spec/risk/portfolio_exposure.yaml"
- "spec/risk/risk_control.yaml"
- "spec/risk/quality_control.yaml"
exit:
- "spec/exit/stop_loss.yaml"
- "spec/exit/take_profit.yaml"
- "spec/exit/proactive_exit_radar.yaml"
- "spec/exit/event_response.yaml"
- "spec/exit/position_review.yaml"
- "spec/exit/dynamic_value_preservation_sell_v3.yaml"
strategy:
- "spec/strategy/sector_model.yaml"
- "spec/strategy/entry_gates.yaml"
- "spec/strategy/entry_core.yaml"
- "spec/strategy/leader_scan.yaml"
- "spec/strategy/staged_entry.yaml"
- "spec/strategy/discovery.yaml"
- "spec/strategy/stock_model.yaml"
- "spec/strategy/action_matrix.yaml"
- "spec/strategy/rebalancing_trigger.yaml"
- "spec/strategy/fundamental_and_horizon_gate_v1.yaml"
- "spec/strategy/horizon_allocation_v1.yaml"
- "spec/strategy/smart_money_liquidity_gate_v1.yaml"
- "spec/strategy/predictive_alpha_dialectic_v1.yaml"
conflict_resolution:
priority: "상위 source_of_truth_order가 하위 규칙보다 항상 우선한다."
risk_vs_strategy: "리스크 차단 규칙과 매수/증액 규칙이 충돌하면 리스크 차단 규칙만 적용한다."
data_vs_decision: "데이터 완성도·최신성 미달이면 등급·수량·주문 결론을 보류한다."
table_vs_text: "표/수치/공식과 서술형 설명이 충돌하면 표/수치/공식을 우선한다."
examples_vs_rules: "examples는 판단 예시이며 어떤 경우에도 spec 규칙을 override하지 않는다."
tie_rule: "동급 규칙이 충돌하면 보류하고 필요한 추가 데이터와 다음 확인 출처를 출력한다."
secular_leader_vs_safety: >
SECULAR_LEADER_RISK_ON 활성 중 EVENT_SHOCK 또는 RISK_OFF 발동 시
공격 모드 즉시 비활성. 공격 모드는 안전·리스크 차단 모드를 절대 우선하지 않는다.
우선순위: EVENT_SHOCK > RISK_OFF > SECULAR_LEADER_RISK_ON > LEADER_CONCENTRATION > NEUTRAL > RISK_ON.
참조: spec/11_market_regime.yaml.SECULAR_LEADER_RISK_ON.priority_over_lower_states.
goal_protection_vs_orbit_gap: >
Safe Landing Protocol, hard stop, cash_floor, Total_Heat, 데이터 완성도 차단은
orbit_gap 또는 목표 미달 기반 공격 슬롯 확대보다 항상 우선한다.
목표 미달·필요수익률 상승은 risk_budget 상향 또는 데이터 게이트 완화 사유가 아니다.
목표자산 90% 이상 접근 시 spec/01_objective_profile.yaml.safe_landing_protocol을 우선 확인한다.
non_negotiable_principles:
- "spec/00_execution_contract.yaml의 master_prohibitions P1~P5가 모든 규칙보다 우선한다."
- "보유수량 미확인 상태에서 매도수량 숫자 기재 금지."
- "ATR20 미확인 상태에서 정수 매수수량 산출 금지."
- "Total_Heat, cash_floor, hard stop을 우회하는 공격 슬롯 금지."
- "Flow_Rows<20이면 20D 수급 기반 A등급·즉시매수 금지."
- "매수 제안은 지정가·수량·손절가·손절수량·익절가·익절수량 세트가 모두 있을 때만 유효."
- "소수점 매수 금지. 모든 주문 수량은 정수."
- "SECULAR_LEADER_RISK_ON은 spec/11_market_regime.yaml 5개 조건 동시 충족 시에만 활성화된다. 활성 중에도 master_prohibitions P1~P5, Total_Heat hard stop, ATR20, 보유수량 확인, cash_floor 제약은 그대로 적용된다. 이 모드는 safety gate를 열지 않는다."
- "목표자산 90% 이상 접근 시 Safe Landing Protocol이 orbit_gap 공격 슬롯 확대, 위성 비중 확대, 신규 탐색매수보다 우선한다."
- "동일 섹터가 아니어도 고베타·고모멘텀 팩터가 과집중이면 신규 고베타 종목 매수를 보류하고 저변동성·현금·단기채 대안을 우선 검토한다."
- "이벤트 3거래일 전부터 당일까지 신규 진입은 갭 리스크 버퍼를 수량 산출에 반영하지 않으면 정수 수량을 확정하지 않는다."
- "HTS 현재가와 JSON 기준 가격의 괴리율이 3%를 초과하면 BUY_BLOCKED 처리하고 JSON 재생성 또는 가격 출처 재검산을 먼저 요구한다."
surgical_update_policy:
rule: "규칙 변경은 전체 재작성 금지. target_file, target_path, change_type, rationale를 남기는 최소 침습 변경만 허용한다."
required_fields: ["target_file", "target_path", "change_type", "rationale", "before", "after", "validation"]
prohibited_changes:
- "master prohibitions 삭제"
- "risk hard stop 완화"
- "데이터 누락 상태에서 BUY 허용"
- "목표수익률 압박을 이유로 risk_budget 강제 상향"
- "SECULAR_LEADER_RISK_ON 활성을 이유로 master_prohibitions·cash_floor·Total_Heat·ATR20 제약 완화 금지"
completed_update_markers:
required_fields: ["proposal_id", "adopted_scope", "completed_at", "backup_file", "validation_status"]
rule: "외부 제안 반영 후 accepted_proposals에 완료 표기를 남겨 동일 proposal_id 중복 반영을 방지한다."
validation_status_values: ["PENDING", "PASS", "FAIL", "PARTIAL"]
output_contract:
machine_readable_schema: "schemas/output_schema.json"
json_schema_version: "2026-05-15-F6-compat-output"
required_first_outputs:
- "사용 데이터 기준시각과 출처"
- "capture_read_ledger 판독 원장"
- "데이터 완성도 매트릭스"
- "4단계 주문 검산 결과"
- "decision_trace 판단 추적표"
- "리스크/현금/Total_Heat 차단 여부"
- "HTS 입력 가능 주문표 또는 산출금지 사유"
- "학습형 해설은 요청 시 후반부 market_context_learning_note로만 출력"
report_template_file: "RetirementAssetPortfolioReportTemplate.yaml"
bundle_outputs:
full_bundle: "dist/retirement_portfolio_bundle.yaml"
compact_bundle: "dist/retirement_portfolio_compact.yaml"
ultra_compact_bundle: "dist/retirement_portfolio_ultra_compact.yaml"
bundle_selection_policy:
full_bundle: "문서 정합성 리뷰, 리팩토링, 규칙 충돌 점검에 사용한다."
compact_bundle: "일반 분석에서 충분한 컨텍스트가 필요할 때 사용한다."
ultra_compact_bundle: "토큰 비용을 줄여야 하거나 반복 실행용 LLM 입력으로 사용한다. 상세 보고서 양식과 illustrative examples는 포함하지 않는다."
bundle_profiles:
compact:
output: "dist/retirement_portfolio_compact.yaml"
purpose: "분석에 필요한 canonical 핵심 문서만 묶은 축약 LLM 입력용 합본"
files:
- "RetirementAssetPortfolio.yaml"
- "AGENTS.md"
- "spec/00_execution_contract.yaml"
- "spec/02_data_contract.yaml"
- "spec/risk/aggregate_risk.yaml"
- "spec/risk/circuit_breakers.yaml"
- "spec/risk/market_risk_cash.yaml"
- "spec/risk/portfolio_exposure.yaml"
- "spec/12_field_dictionary.yaml"
- "spec/13_formula_registry.yaml"
- "spec/13b_harness_formulas.yaml"
- "spec/14_raw_workbook_mapping.yaml"
- "spec/15_account_snapshot_contract.yaml"
- "spec/09_decision_flow.yaml"
- "spec/11_market_regime.yaml"
- "spec/08_scoring_rules.yaml"
- "spec/10_portfolio_rules.yaml"
- "spec/strategy/sector_model.yaml"
- "spec/strategy/entry_core.yaml"
- "spec/strategy/leader_scan.yaml"
- "spec/strategy/staged_entry.yaml"
- "spec/05_position_sizing.yaml"
- "spec/exit/stop_loss.yaml"
- "spec/exit/take_profit.yaml"
- "spec/exit/proactive_exit_radar.yaml"
- "spec/40_final_decision_packet_contract.yaml"
- "spec/41_release_dag.yaml"
- "spec/43_quant_factor_taxonomy.yaml"
- "spec/45_number_provenance_contract.yaml"
- "spec/46_low_capability_execution_pack.yaml"
- "spec/47_packaging_policy.yaml"
- "spec/property_invariants.yaml"
- "spec/anti_late_entry_contract.yaml"
- "spec/profit_preservation_contract.yaml"
- "spec/operating_cadence.yaml"
- "spec/formula_lifecycle_index.yaml"
- "spec/release/low_capability_context_aliases.yaml"
- "spec/release/version_retirement_policy.yaml"
- "spec/48_module_io_contract_registry.yaml"
- "spec/49_refactor_methodology_contract.yaml"
- "spec/51_formula_lifecycle_registry.yaml"
- "Temp/final_decision_packet_v4.json"
- "schemas/output_schema.json"
ultra_compact:
output: "dist/retirement_portfolio_ultra_compact.yaml"
purpose: "실행계약, 데이터, 리스크, 상태머신, 수량, 청산, 출력계약만 담은 최소 LLM 입력용 합본"
files:
- "RetirementAssetPortfolio.yaml"
- "AGENTS.md"
- "spec/00_execution_contract.yaml"
- "spec/02_data_contract.yaml"
- "spec/risk/aggregate_risk.yaml"
- "spec/risk/circuit_breakers.yaml"
- "spec/risk/market_risk_cash.yaml"
- "spec/risk/portfolio_exposure.yaml"
- "spec/12_field_dictionary.yaml"
- "spec/13_formula_registry.yaml"
- "spec/13b_harness_formulas.yaml"
- "spec/14_raw_workbook_mapping.yaml"
- "spec/15_account_snapshot_contract.yaml"
- "spec/09_decision_flow.yaml"
- "spec/08_scoring_rules.yaml"
- "spec/05_position_sizing.yaml"
- "spec/exit/stop_loss.yaml"
- "spec/exit/take_profit.yaml"
- "spec/exit/proactive_exit_radar.yaml"
- "spec/40_final_decision_packet_contract.yaml"
- "spec/41_release_dag.yaml"
- "spec/43_quant_factor_taxonomy.yaml"
- "spec/45_number_provenance_contract.yaml"
- "spec/46_low_capability_execution_pack.yaml"
- "spec/47_packaging_policy.yaml"
- "spec/property_invariants.yaml"
- "spec/anti_late_entry_contract.yaml"
- "spec/profit_preservation_contract.yaml"
- "spec/operating_cadence.yaml"
- "spec/formula_lifecycle_index.yaml"
- "spec/release/low_capability_context_aliases.yaml"
- "spec/release/version_retirement_policy.yaml"
- "spec/48_module_io_contract_registry.yaml"
- "spec/49_refactor_methodology_contract.yaml"
- "spec/51_formula_lifecycle_registry.yaml"
- "Temp/final_decision_packet_v4.json"
- "schemas/output_schema.json"
+665
View File
@@ -0,0 +1,665 @@
meta:
title: "은퇴자산포트폴리오 — 상세 보고서 양식"
parent_file: "RetirementAssetPortfolio.yaml"
version: "2026-05-15-G1"
purpose: >
보고서 출력 시에만 로드하는 상세 양식 파일.
투자 판단(분석·수량산출)만 필요한 세션에서는 이 파일 없이 메인 파일만으로 충분.
load_instruction: "메인 manifest의 load_sequence.STEP_3_for_output 및 spec_files.report_templates 참조."
output_format_templates:
rendering_contract:
purpose: "사람용 보고서를 표 중심으로 렌더링하기 위한 상위 양식 계약."
language_rendering:
default_locale: "ko-KR"
section_title_korean_first: true
status_label_map_source: "spec/07_output_schema.yaml:korean_display_labels"
prefer_korean_reason_text: true
preserve_english_only_for:
- "공식_ID"
- "종목코드"
- "파일경로"
- "명령어"
- "JSON key"
prohibit_direct_english_status:
- "PASS"
- "FAIL"
- "BLOCKED"
- "ACTIVE"
- "INACTIVE"
- "BUY"
- "SELL"
- "TRIM"
required_sequence:
- "routing_serving_trace"
- "QEH_AUDIT_BLOCK"
- "capture_read_ledger"
- "data_completeness_matrix"
- "backdata_feature_bank_table"
- "benchmark_relative_harness_table"
- "alpha_lead_table"
- "anti_distribution_table"
- "profit_preservation_table"
- "smart_cash_raise_table"
- "execution_quality_table"
- "proposal_reference_sheet"
# ── [2026-05-20_HARNESS_V5] 신규 필수 섹션 ──────────────────────────────
- "breakout_quality_gate_table"
- "anti_whipsaw_gate_table"
- "smart_cash_raise_v2_table"
# ─────────────────────────────────────────────────────────────────────────
- "order_quantity_4stage_gate"
- "decision_trace_table"
- "sell_priority_decision_table 또는 발동조건 미충족 사유"
- "current_holdings_analysis_report_template 또는 발동조건 미충족 사유"
- "proposal_reference_sheet 또는 제안 산출 불가 사유"
- "concise_hts_input_sheet 또는 산출금지 사유"
- "reference_price_ledger 또는 WATCH 없음"
- "immediate_execution_playbook 또는 산출금지 사유"
- "market_context_learning_note 또는 생략 사유"
- "core_satellite_timing_gate_table"
- "engine_feedback_loop_report"
- "prediction_evaluation_improvement_report"
blocked_report_rule:
id: "BLOCKED_REPORT_V5"
trigger: "required_sequence의 필수 섹션이 하나라도 누락된 경우"
action: "해당 섹션 이후의 모든 주문표·HTS 입력 시트를 BLOCKED_REPORT로 처리"
hard_lock_sections:
- section: "breakout_quality_gate_table"
missing_action: "신규 BUY 주문표 전체 BLOCKED_BREAKOUT_GATE_MISSING"
- section: "anti_whipsaw_gate_table"
missing_action: "SELL/TRIM 주문표 전체 BLOCKED_WHIPSAW_GATE_MISSING"
- section: "smart_cash_raise_v2_table"
missing_action: "현금확보 매도 주문표 BLOCKED_CASH_ROUTE_MISSING"
prose_rule:
allowed: "각 표의 근거·매도사유·다음확인사항 칸 안의 짧은 문장"
prohibited: "서론/본론/결론형 산문, 임의 제목, 표 밖 수량·현금·평단 근거 서술"
enum_rule:
order_type: ["BUY", "SELL", "STOP_LOSS", "TAKE_PROFIT", "TRAILING_STOP", "HOLD", "WATCH"]
mode: ["lead", "lag", "hybrid", "none"]
validation_status: ["PASS", "BLOCKED", "INSUFFICIENT_DATA", "MANUAL_CHECK_REQUIRED"]
human_label_rule: "사용자 보고서에는 spec/07_output_schema.yaml:korean_display_labels를 적용하되, 원 enum에서 벗어난 새 용어를 만들지 않는다."
terminology_rule:
prohibited_order_terms: ["부분감액", "1차 감액", "부분정리", "전량", "전량매도"]
replacement_rule: "주문구분은 SELL/TRIM 등 canonical action으로, 수량은 확인된 정수 수량으로 분리한다."
note: "'전량'은 모드가 아니다. 보유수량 검산 후 SELL 수량이 현재보유수량과 같을 때만 근거 칸에서 설명 가능하다."
learning_note_rule:
location: "주문 검산·HTS 주문표·보유주 진단 이후에만 배치한다."
role: "사용자 이해를 돕는 해설이며 주문 검증, 수량 산출, validation_status를 대체하지 않는다."
prohibition:
- "학습 해설에서 신규 주문수량·지정가·손절가·익절가·현금 숫자 생성 금지"
- "표에서 검증되지 않은 계좌·보유·현금 숫자 인용 금지"
- "거시 설명만으로 BUY/SELL/TRIM/ROTATE 결론 변경 금지"
routing_serving_trace_report:
purpose: "요청 라우팅·번들·프롬프트·JSON 검증 상태를 QEH_AUDIT_BLOCK 이전에 고정하는 서빙 원장."
columns: ["항목", "확정값", "근거", "검산상태", "차단사유"]
required_rows:
- "request_route"
- "bundle_selected"
- "prompt_entrypoint"
- "json_validation_status"
- "capture_required"
- "cash_ledger_basis"
fill_rule:
- "RetirementAssetPortfolio.yaml manifest와 DATA_SOURCE_ROUTING 결과를 사용한다."
- "JSON 검증 실패 또는 bundle/entrypoint 미확정이면 QEH_AUDIT_BLOCK 이후 주문표는 BLOCKED_REPORT로 제한한다."
- "cash_ledger_basis는 settlement_cash_d2_krw 원칙을 명시한다."
QEH_AUDIT_BLOCK_report:
purpose: "하네스 공식 검산 표. 주문표·판단표보다 먼저 출력한다."
columns: ["공식_ID", "사용입력", "하네스값", "LLM_변경여부", "판정", "차단/허용 액션"]
required_formulas: ["TOTAL_HEAT_V1", "CASH_RATIOS_V1", "SELL_PRIORITY_V1"]
optional_formulas: ["GOAL_RETIREMENT_V1", "CASH_SHORTFALL_V1"]
fill_rule:
- "data._harness_context 값은 재계산하지 않고 그대로 복사한다."
- "LLM_변경여부는 항상 NO여야 하며, 다른 값이면 보고서 INVALID."
- "blocked_actions는 산문으로 완화하지 않는다."
backdata_feature_bank_table:
purpose: "GAS 자동 수집 진입-청산 백데이터 원장. 수동 등록보다 GAS 원장을 우선한다."
canonical_ref: "spec/16_data_gaps_roadmap.yaml:phase_4_backdata_collection.B1_gas_backdata_feature_bank"
columns: ["Record_Date", "Trade_ID", "Signal_Date", "Ticker", "Name", "Account", "Entry_Stage", "Source_Origin", "Entry_Price", "Close_At_Entry", "MA20_At_Entry", "MA60_At_Entry", "ATR20_At_Entry", "Volume_Ratio_5D", "Flow_Credit", "Late_Chase_Risk_Score", "Follow_Through_Score", "Breakout_Score", "Rebound_Preservation_Score", "Setup_Decision", "Exit_Reason", "PnL_Pct", "Holding_Days", "MAE_Pct", "MFE_Pct"]
fill_rule:
- "backdata_feature_bank_json 값을 그대로 사용한다."
- "Source_Origin=GAS_AUTO 행을 우선 표기하고, performance fallback이나 manual correction은 뒤로 둔다."
- "값이 없으면 추정하지 말고 '_' 또는 '-'로 비운다."
benchmark_relative_harness_table:
purpose: "KOSPI 대비 시계열 상대평가와 위성 품질·손익·현금창출 목적 잠금을 표시한다."
canonical_ref: "spec/13_formula_registry.yaml:BENCHMARK_RELATIVE_TIMESERIES_V1, SATELLITE_ALPHA_QUALITY_GATE_V1, SATELLITE_AGGREGATE_PNL_GATE_V1, CASH_CREATION_PURPOSE_LOCK_V1"
columns: ["종목", "brt_verdict", "excess_drawdown_pctp", "recovery_ratio_20d", "downside_beta", "rs_line_20d_slope", "saqg_v1", "sell_reason_validity", "sapg_status", "공식_ID"]
fill_rule:
- "benchmark_relative_timeseries_json, saqg_json, sapg_json, cash_creation_purpose_lock_json 값을 그대로 사용한다."
- "saqg_v1=EXCLUDED 또는 WATCHLIST_ONLY이면 BUY 후보·파일럿·HTS 주문표에 포함하지 않는다."
- "sapg_status=SAPG_CRITICAL이면 위성 신규 BUY는 전면 차단한다."
- "LLM이 초과낙폭·회복률·하락장 베타·RS선 기울기를 재계산하지 않는다."
alpha_lead_table:
purpose: "뒷북 매수를 줄이기 위한 선행 알파 후보와 추격 금지 상태를 표시한다."
canonical_ref: "spec/13b_harness_formulas.yaml:ALPHA_LEAD_SCORE_V1, FOLLOW_THROUGH_CONFIRM_V1"
columns: ["종목", "alpha_lead_score", "follow_through_status", "rs_rank", "volume_surge", "upside_pct", "buy_permission", "공식_ID", "검산상태"]
fill_rule:
- "alpha_lead_json 및 follow_through_json 값을 그대로 사용한다."
- "buy_permission_json이 BLOCKED이면 HTS BUY 주문표를 생성하지 않는다."
- "선행 점수의 산문 재해석으로 final_action을 변경하지 않는다."
anti_distribution_table:
purpose: "설거지·분산매도 구간의 추격 매수와 늦은 물타기를 차단한다."
canonical_ref: "spec/13b_harness_formulas.yaml:DISTRIBUTION_RISK_SCORE_V1"
columns: ["종목", "distribution_risk_score", "risk_label", "price_position", "val_surge_pct", "inst20d", "frg20d", "blocked_action", "공식_ID"]
fill_rule:
- "distribution_risk_json 값을 그대로 사용한다."
- "risk_label=HIGH 또는 blocked_action 존재 시 BUY/ADD_ON은 BLOCKED로만 출력한다."
profit_preservation_table:
purpose: "수익 포지션의 이익 보호 상태와 손절 상향·익절·트레일링 상태를 고정한다."
canonical_ref: "spec/13b_harness_formulas.yaml:PROFIT_PRESERVATION_STATE_V1"
columns: ["종목", "profit_state", "unrealized_pnl_pct", "ratchet_state", "tp_state", "trailing_state", "allowed_action", "공식_ID"]
fill_rule:
- "profit_preservation_json 값을 그대로 사용한다."
- "수익 포지션을 현금확보 대상으로 쓸 때도 smart_cash_raise_table의 제안그룹과 충돌 여부를 표시한다."
smart_cash_raise_table:
purpose: "현금확보 매도를 일괄 투매가 아니라 즉시매도·반등대기·보류로 나누는 제안 계획표."
canonical_ref: "spec/13b_harness_formulas.yaml:SMART_CASH_RAISE_PLAN_V1, SELL_QUANTITY_ALLOCATOR_V1, REBOUND_SELL_TRIGGER_V1"
columns: ["우선순위", "종목", "cash_raise_group", "target_cash_krw", "immediate_sell_qty", "rebound_wait_qty", "protected_qty", "rebound_trigger", "예상순현금", "공식_ID"]
fill_rule:
- "cash_raise_plan_json과 smart_sell_quantities_json 값을 그대로 사용한다."
- "보유수량 미확인 또는 CAPTURE_REQUIRED이면 수량 칸에 숫자를 쓰지 않는다."
- "rebound_wait_qty는 즉시 HTS 매도수량이 아니므로 concise_hts_input_sheet에 합산하지 않는다."
execution_quality_table:
purpose: "주문 단가·호가·체결 품질·유동성 조건을 최종 주문표 전에 검산한다."
canonical_ref: "spec/13b_harness_formulas.yaml:EXECUTION_QUALITY_GUARD_V1, LIMIT_PRICE_POLICY_V1"
columns: ["종목", "order_type", "limit_price", "tick_status", "liquidity_status", "gap_status", "execution_permission", "차단사유", "공식_ID"]
fill_rule:
- "execution_quality_json과 limit_price_policy_json 값을 그대로 사용한다."
- "tick_status != VALID 또는 execution_permission != PASS이면 HTS 주문표 검산상태 PASS 금지."
proposal_reference_sheet:
purpose: "사용자 판단용 제안 원장. 가격·수량·기준시점·차단사유만 남기고 주문 실행 의미는 섞지 않는다."
columns: ["계좌", "종목", "종목명", "제안구분", "예상지정가(원)", "기준시점(종가/장중)", "제안가 기준", "예상수량(주)", "수량 기준", "손절1(원)", "손절수량1(주)", "손절2(원)", "손절수량2(주)", "손절3(원)", "손절수량3(주)", "익절가1(원)", "익절수량1(주)", "익절가2(원)", "익절수량2(주)", "익절가3(원)", "익절수량3(주)", "실행가능여부", "차단사유"]
fill_rule:
- "prices_json, sell_quantities_json, buy_qty_inputs_json, decisions_json 값을 우선 사용한다."
- "validation_status=PASS 여부와 무관하게 산출 가능한 가격·수량은 표시한다."
- "실행가능여부는 proposal_only / execution_wait / execution_ready 중 하나를 사용한다."
- "이 표는 HTS 즉시 입력표가 아니므로 concise_hts_input_sheet와 반드시 분리한다."
- "SELL/TRIM/방어 제안은 예상지정가에 stop_price를 우선 사용한다."
- "익절 제안은 예상지정가에 tp1_price를 우선 사용하고 없으면 tp2_price를 사용한다."
- "BUY 제안은 order_blueprint limit_price 우선, 없으면 buy_qty_inputs_json 보조 입력을 사용한다."
- "WATCH/HOLD는 주문가가 아니라 참고 방어가임을 차단사유 또는 설명에 명시한다."
- "SELL/TRIM 제안 수량은 sell_quantities_json.sell_qty 우선 사용한다."
- "BUY 제안 수량은 buy_qty_inputs_json.final_qty 우선 사용한다."
- "WATCH/HOLD는 주문 실행 수량이 아니라 판단 참고 수량임을 수량 기준 컬럼에 명시한다."
- "익절 1/2/3 가격·수량은 tp_quantity_ladder_json과 prices_json을 결합해 표시한다."
- "손절1/2는 stop_loss.core/satellite quantity_rule 기준으로 core=50/50, satellite=70/30 분할을 표시한다."
- "손절3은 profit_preservation_json.protected_stop_price 또는 auto_trailing_stop가 있을 때만 표시한다."
reference_price_ledger:
purpose: "WATCH/PENDING/BLOCKED 행의 참고 가격 원장. 주문표가 아니며 평가용 참고 상태를 공개한다."
title_required: "WATCH 감시 원장 — 주문 아님, HTS 입력 금지"
columns: ["ticker", "name", "reference_stop_price", "reference_price_basis", "reference_tp_state", "hts_allowed", "reason_code"]
forbidden_columns: ["지정가", "손절가", "익절가", "매도가", "주문가", "주문수량", "손절수량", "익절수량", "매도수량", "주문금액"]
fill_rule:
- "hts_allowed는 항상 false로 기재한다."
- "WATCH 행에 주문 가능 단가·수량처럼 보이는 한글 컬럼명을 쓰면 INVALID_COLUMN 처리한다."
- "reference_tp_state는 tp1/tp2 상태를 함께 공개한다. 예: tp1=PENDING; tp2=PENDING."
capture_read_ledger_report:
purpose: "계좌·보유·현금 판독 증빙 원장. 이 표 없이 HTS 주문표를 출력하지 않는다."
columns: ["파일/화면", "계좌", "화면종류", "읽은값", "확신도", "주문표반영", "판독_상태", "다음확인사항"]
fill_rule:
- "spec/00_execution_contract.yaml:capture_read_ledger.columns를 우선 적용한다."
- "보유수량·평단·현금·미체결 주문의 출처를 읽은값에 구분해 적는다."
- "NOT_PROVIDED 또는 CAPTURE_READ_FAILED이면 해당 계좌 주문수량은 산출하지 않는다."
prohibition:
- "자동투자/약정 화면 수치를 보유수량·평단·현금으로 사용 금지"
- "원장 행 없이 본문에서 총자산·현금·보유수량 근거 생성 금지"
order_quantity_4stage_gate_report:
purpose: "주문표 출력 직전 4단계 검산 결과 표."
columns: ["계좌", "종목명", "1단계_원장", "2단계_현금", "3단계_보유수량", "4단계_미체결", "최종검산상태", "차단사유", "다음확인사항"]
pass_rule: "네 단계가 모두 통과일 때만 immediate_execution_playbook 검산상태=PASS를 허용한다."
fail_rule: "하나라도 실패·미확인이면 주문표 대신 산출금지 사유를 출력한다."
decision_trace_table:
purpose: "상태 머신의 각 판단 단계에서 어떤 규칙이 적용되어 어떤 결론이 선택/차단됐는지 남기는 재현성 검산 표."
canonical_ref: "spec/09_decision_flow.yaml:decision_flow.deterministic_execution_control"
activation:
trigger: "모든 분석·주문·리뷰 출력에서 필수"
priority: "order_quantity_4stage_gate 이후, 보유주 진단 및 HTS 주문표 이전"
columns: ["단계", "check_id", "rule_ref", "사용입력", "결과", "선택행동", "차단행동", "누락입력", "동률처리", "다음상태"]
fill_rule:
- "각 행은 decision_flow.states의 상태 또는 output validation 단계 하나를 나타낸다."
- "rule_ref는 파일 경로와 YAML path를 함께 적는다."
- "사용입력은 실제 사용한 필드명만 적고, 값이 민감하거나 길면 evidence 참조로 대체한다."
- "누락입력이 있으면 선택행동은 BLOCKED/INSUFFICIENT_DATA/WATCH 중 하나로 제한한다."
- "동률처리는 deterministic_execution_control.tie_breaker_order 번호를 적는다."
prohibition:
- "판단 추적표 없이 final_action 또는 HTS 주문표 출력 금지"
- "decision_trace에 없는 rule_id를 근거로 최종 결론 변경 금지"
- "동률처리 없이 동일 점수 후보 중 임의 선택 금지"
sell_priority_decision_table:
purpose: "여러 매도·축소·교체 후보가 동시에 존재할 때 HTS 주문 실행 순서를 정하는 포트폴리오 단위 표. 활성 시 current_holdings_analysis_report_template보다 먼저 렌더링한다."
canonical_ref: "spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework.sell_priority_engine"
activation:
trigger:
- "SELL/TRIM/ROTATE 후보가 2개 이상"
- "cash_floor 미달 또는 이벤트 주 현금 상향 필요"
- "중복노출 축소와 보유주 진단 축소 후보가 동시에 존재"
priority: "order_quantity_4stage_gate 이후, current_holdings_analysis_report_template보다 먼저"
columns: ["우선순위", "계좌", "종목명", "실행그룹", "현재보유수량", "매도가능검산", "매도유형", "우선순위단계", "Sell_Priority_Score", "예상순현금", "세금/비용", "매도사유", "보류사유", "다음확인사항"]
fill_rule:
- "우선순위단계는 sell_priority_engine.hard_precedence 중 하나를 사용한다."
- "Sell_Priority_Score는 동일 단계 후보 정렬용이며 hard_precedence를 뒤집지 않는다."
- "현재보유수량 또는 평균단가가 미확인이면 매도가능검산=미통과, 수량은 미산출로 둔다."
- "예상순현금은 세금·수수료 미확인 시 숫자 대신 '세금/비용 확인 필요'로 둔다."
prohibition:
- "이 표 없이 여러 매도 후보의 실행 순서를 산문으로만 제시 금지"
- "보유수량 미확인 후보에 정수 매도수량 기재 금지"
- "세금 최적화만으로 hard stop 후보를 후순위로 미루지 않는다."
market_context_learning_note:
purpose: "거시·미시 경제 상황, 시장 움직임, 투자자 고민 포인트, 핵심 용어, 판단근거를 학습용으로 정리하는 조건부 해설 블록."
activation:
trigger:
- "사용자가 학습형 설명을 요청한 경우"
- "주간·월간 점검 보고서"
- "시장국면 변경, 리밸런싱, 대규모 매도/축소, 신규 주도주 편입 검토가 발생한 경우"
priority: "주문 검산·HTS 주문표·보유주 진단 이후, 부록 또는 후반부"
note: "실행 판단을 먼저 확정한 뒤 독자의 이해를 돕기 위한 해설로만 사용한다."
columns: ["구분", "현재 관찰값/상태", "투자자가 고민할 점", "판단에 미친 영향", "핵심 용어", "다음 확인 데이터"]
row_types:
macro: "금리, 환율, 유가, VIX, KOSPI/KOSDAQ 추세, credit spread 등 포트폴리오 위험예산과 현금비중에 영향을 주는 배경."
micro_market: "섹터 순환, 주도주 집중, 거래대금, 외국인/기관 수급, ETF·직접주 중복노출 등 시장 내부 움직임."
investor_dilemma: "지금 사야 하는가, 기다려야 하는가, 현금을 얼마나 남겨야 하는가, 손실 종목을 버틸 근거가 있는가 같은 실제 의사결정 질문."
decision_basis: "BUY/HOLD/SELL/TRIM/ROTATE/WATCH 결론에 영향을 준 rule_id, 수치, hard stop, missing_data."
glossary: "Total_Heat, cash_floor, ATR20, Expected_Edge, Flow_Rows, flow_credit, lead/lag/hybrid mode 등 보고서 내 핵심 용어."
fill_rule:
- "현재 관찰값/상태는 data_basis, data_completeness_matrix, risk_gate, market_regime_state, sector_flow, macro에서 검증된 값만 사용한다."
- "판단에 미친 영향은 어떤 주문 결론을 새로 만들지 말고 이미 확정된 portfolio_decision 또는 prohibited_calculations에 연결한다."
- "다음 확인 데이터는 구체 출처를 적는다. 예: macro 시트, sector_flow 시트, HTS 보유종목 화면, 미체결 주문 화면."
- "수치가 없으면 추정하지 말고 '미확인' 또는 '다음 확인 필요'로 적는다."
example_rows:
macro: ["거시", "VIX·환율·KOSPI MA20 상태 확인 필요", "현금비중을 낮춰도 되는가", "MRS와 cash_floor 판단에만 반영", "MRS, cash_floor", "macro 시트, KOSPI MA20, USD/KRW"]
micro_market: ["미시/시장", "특정 섹터 수급 집중 여부 확인", "ETF와 직접주 중 어느 노출이 효율적인가", "중복노출·리밸런싱 후보 판단에 반영", "sector_flow, duplicate_exposure", "sector_flow, core_satellite"]
investor_dilemma: ["고민 포인트", "매수 신호는 있어도 ATR20 또는 현금이 미확인일 수 있음", "기회비용과 검산 실패 중 무엇을 우선할 것인가", "validation_status 미통과 시 주문 금지", "ATR20, validation_status", "data_feed, account_snapshot"]
decision_basis: ["판단근거", "Total_Heat·cash_floor·Flow_Rows가 핵심 gate", "왜 A등급이어도 즉시매수가 아닐 수 있는가", "hard stop이 전략 점수보다 우선", "Total_Heat, Flow_Rows", "risk_gate, triggered_rules"]
glossary: ["용어", "Expected_Edge는 기대수익 대비 손실위험 비율", "손절폭이 넓으면 수량이 줄어드는 이유", "position_sizing 산식 이해 보조", "Expected_Edge, ATR20", "spec/13_formula_registry.yaml"]
output_rule:
- "각 행은 교육 목적의 해설이며 주문 산출 근거 표를 대체하지 않는다."
- "항목별 설명은 2~3문장 이내로 제한한다."
- "용어 설명은 보고서에서 실제 사용한 용어를 우선한다."
- "판단근거는 rule_id 또는 파일 경로를 함께 적는다."
prohibition:
- "학습 해설에서 신규 매수·매도 결론 생성 금지"
- "학습 해설만으로 validation_status를 PASS로 변경 금지"
- "웹이나 일반 지식만으로 workbook/account_snapshot 결론 대체 금지"
- "출처 없는 최신 시장 상황 단정 금지"
data_flow_analysis_report:
purpose: >
블록 2(데이터 완성도 매트릭스) 직후, 블록 3(캡처 판독 원장) 전에 1페이지로 삽입.
데이터→신호→판단→행동 4단계 흐름을 표로 가시화해 병목 단계를 즉시 파악.
flow_table:
columns:
- "종목명"
- "[D]데이터상태 (OK/PARTIAL/MISSING)"
- "[S]신호 (추세/수급/유동성 각각 Pass/Fail)"
- "[J]판단 (A/B/C/D등급·이유)"
- "[A]행동 (즉시/조건부/보류/금지)"
- "병목단계 (D/S/J 중 어느 단계가 블록인지)"
- "다음확인우선순위"
fill_rule:
- "[D]: 블록 2 데이터 완성도 매트릭스에서 직접 참조."
- "[S]: minimalist_buy_gate 3핵심 지표 결과."
- "[J]: sector_model.grade + Expected_Edge + flow_credit 종합. (결합 공식 → fill_rule_J_detail)"
- "[A]: 최종 즉시실행/조건부/보류/금지 결론."
- "병목단계: [D]에서 막히면 'D', [S]에서 막히면 'S', [J]에서 막히면 'J'."
absence_rule: "통과 이유 없는 A등급 금지. 모든 칸은 값 또는 '없음'으로 채운다."
fill_rule_J_detail: # [P139] [J] 단계별 상한 설정 — 3개 시스템 결합 공식
step_1: "sector_model.score_axes_formula 산출 → A_core/B_wait/C_watch/D_exclude 상한 설정"
step_2: "Expected_Edge < 1.5이면 최대 B등급 (A 승격 불가)"
step_3: "flow_credit < 0.40이면 최대 C등급 (A·B 승격 불가)"
step_4: "step_1~3 모두 통과 시에만 sector_model.grade 결론 채택"
precedence: "Expected_Edge 기준 > sector_model.grade (충분조건 vs 필요조건)"
output_format: "A/B/C/D 중 하나 + 근거 (예: B — Expected_Edge 1.8 충족, flow_credit 0.55 partial)"
minimalist_output:
- "3지표([S]) 모두 Pass인 종목은 강조 표시."
- "병목이 [D]인 종목은 '다음확인우선순위'에 구체 출처 명시."
- "병목이 [S]인 종목은 실패한 지표와 임계치 명시."
current_holdings_analysis_report_template:
purpose: "현재 보유주를 대상으로 유지/축소/교체/관찰을 판단하는 전용 보고서 양식."
activation:
trigger: "보유수량이 확인된 종목이 1개 이상이거나 position_review_cycle.documentation 점검 주기에 도달했을 때"
priority: "제안 검토 표 다음, 매수/매도 예시 이전"
note: "신규 종목 추천이 아니라 기존 보유 포지션 점검 전용이다."
decision_score_formula: >
current_holdings_score = 0.30*trend_score + 0.30*flow_score + 0.20*edge_score + 0.10*concentration_score + 0.10*execution_score.
trend_score, flow_score, edge_score, concentration_score, execution_score는 모두 0~100 정규화 값으로 입력한다.
score_components: # [R4] 컴포넌트별 산출 규칙
trend_score:
"100": "현재가 > 20일선 AND 20일선 > 60일선 AND 5일선 위 주행"
"70": "현재가 > 20일선 AND 20일선 > 60일선 (5일선 이탈)"
"40": "현재가 > 20일선 (60일선 하향배열)"
"10": "현재가 < 20일선"
missing: "20일선·60일선 미확인 → 50점(중립)"
flow_score:
"100": "Frg_5D + Inst_5D 동반 순매수 AND flow_credit >= 0.60"
"70": "Frg_5D 또는 Inst_5D 중 1개 순매수 AND flow_credit >= 0.40"
"40": "flow_credit 0.20~0.39"
"10": "Frg_5D + Inst_5D 동반 순매도"
missing: "Flow_OK=N 또는 Flow_Rows<5 → 25점"
edge_score:
formula: "Expected_Edge = (익절가 - 진입가) / (진입가 - 손절가)"
"100": "Expected_Edge >= 2.5"
"70": "1.8 <= Expected_Edge < 2.5"
"40": "1.2 <= Expected_Edge < 1.8"
"10": "Expected_Edge < 1.2"
missing: "손절가·익절가 미설정(legacy_position) → 0점"
concentration_score:
"100": "current_weight <= target_weight"
"70": "target_weight < current_weight <= target_weight + 3%p"
"40": "target_weight + 3%p < current_weight <= target_weight + 5%p"
"10": "current_weight > target_weight + 5%p"
missing: "target_weight 미설정 → 50점(중립)"
execution_score:
"100": "진입 후 D+5 이내 수익 구간 진입 OR 시범진입 후 본진입 계획 유효"
"70": "진입 후 D+20 이내 플러스 구간 유지"
"40": "진입 후 D+20 경과, 0% 근방"
"10": "진입 후 D+20 경과, 손실 구간 AND time_stop 임박"
missing: "진입일 미확인 → 50점(중립)"
thresholds:
keep: "score >= 70 AND current_weight within target_band ±5%p AND grade in (A,B)"
trim: "score 50~69 OR current_weight exceeds target_band by >5%p OR duplicate_factor_overweight"
rotate: "score < 50 OR grade in (C,D) AND flow_negative_20d"
watch: "보유수량 0 또는 판독 미완료"
keep_criteria:
- "현재비중이 목표비중 대비 ±5%p 이내"
- "등급 A 또는 B"
- "기대수익비(Expected_Edge) >= 1.8"
- "20일 수급과 추세가 동시에 유지"
trim_criteria:
- "현재비중이 목표비중 대비 +5%p 초과"
- "기대수익비(Expected_Edge) 1.2~1.8"
- "동일 섹터 또는 동일 팩터 중복 노출이 상한 초과"
- "수익률이 플러스여도 포트폴리오 집중도만 과다하면 축소"
rotate_criteria:
- "등급 C 또는 D"
- "20일 수급 음수"
- "반등 실패 또는 대체주 우위"
- "손익률 -5% 이하이면서 회복 논리 부재"
watch_criteria:
- "보유수량 0"
- "평단 또는 현재가 판독 미완료"
- "핵심 데이터 누락으로 score 산출 불가"
columns: ["계좌", "종목명", "현재보유수량", "평단", "현재가", "현재비중(%)", "손익률(%)", "섹터/팩터", "등급", "판단점수", "유지/축소/교체", "근거", "다음점검일"]
input_example:
fields: ["trend_score", "flow_score", "edge_score", "concentration_score", "execution_score", "current_weight", "target_band", "grade", "Expected_Edge"]
sample_values: ["85", "78", "82", "60", "75", "12.4", "10.0", "A", "2.1"]
sample_calc: "0.30*85 + 0.30*78 + 0.20*82 + 0.10*60 + 0.10*75 = 78.2"
sample_judgement: "점수 78.2, 현재비중 12.4%, 목표밴드 10.0% 대비 +2.4%p, grade A이므로 유지"
sample_fill_rule: "trend/flow/edge는 데이터에서, concentration/execution은 계좌비중과 주문검산에서 채운다."
output_phrases:
keep: "유지 — 점수 {score}, 비중 {current_weight}%가 목표밴드 {target_band}% 이내, 등급 {grade}, 기대수익비 {expected_edge}."
trim: "축소 — 점수 {score}, 현재비중이 목표밴드 대비 과다, 중복 노출 또는 기대수익비 저하 확인."
rotate: "교체 — 점수 {score}, 20일 수급 음수 또는 반등 실패로 기존 포지션 교체 필요."
watch: "관찰 — 보유수량 0 또는 핵심 데이터 미완료로 판단 보류."
output_rule:
- "판단점수와 등급을 함께 표기한다."
- "유지/축소/교체/관찰 중 하나만 최종 출력한다."
- "문장형 근거와 수치형 점수를 동시에 보여준다."
keep_row: ["일반계좌", "예시종목", "120", "24800", "27500", "12.4", "+10.8", "반도체", "A", "82", "유지", "주도 섹터 + 수급 유지 + 기대수익비 충족", "D+5"]
trim_row: ["ISA", "예시종목", "80", "38000", "36000", "8.1", "-5.3", "전력기기", "B", "61", "축소", "섹터 중복 + 기대수익비 저하 + 리밸런싱 대상", "D+3"]
rotate_row: ["연금저축", "예시종목", "50", "59000", "54500", "4.2", "-7.6", "개별 성장주", "C", "42", "교체", "수급 이탈 + 반등 실패 + 대체주 우위", "D+2"]
watch_row: ["일반계좌", "예시종목", "0", "0", "0", "0.0", "0.0", "없음", "D", "0", "관찰", "보유 없음 또는 판독 미완료", "D+7"]
required_inputs:
- "현재보유수량"
- "평단"
- "현재가"
- "현재비중"
- "섹터/팩터"
- "등급"
- "기대수익비"
prohibition:
- "현재보유수량·평단·현재가 없이 보유주 분석 금지"
- "신규매수 제안과 동일 표에 혼용 금지"
- "보유주 분석에서 손절가·익절가를 강제로 생성하지 않는다"
market_leader_screening_report_template:
purpose: "떠오르는 주도주를 관찰 후보·편입 후보·금지 후보로 나누는 전용 스크리닝 보고서."
activation:
trigger: "sector_flow 또는 quant_feed 갱신 시, 또는 매주 정기 점검 시"
priority: "current_holdings_analysis_report_template 및 immediate_execution_playbook 다음"
note: "이 블록은 core_satellite 자산배분 표와 분리된 주도주 발굴용이다."
source_binding:
required_sheets: ["core_satellite", "sector_flow", "macro", "event_risk"]
freshness_rule: "주도주 후보 표의 모든 행은 Price_Date, sector_flow.AsOfDate, macro.AsOfDate를 함께 적고, 하나라도 다르면 '데이터 불일치'로 표시한다."
unit_normalization_rule: "AvgTradeValue_5D_M와 AvgTradeValue_20D_M는 보고서 출력 시 억원 단위로 환산해 표기한다. 원 단위와 혼용 금지."
quantity_integrity_rule: "보유수량, 주문수량, 계좌원장, 판독상태 중 하나라도 미확정이면 후보 분류보다 '판독 미완료'를 우선한다."
columns: ["종목명", "섹터", "Rotation_Score", "5D수급", "20D수급", "상대강도", "유동성", "실적추정", "등급", "분류", "액션", "편입근거", "금지근거"]
observe_row: ["한미반도체", "반도체 장비", "88", "양호", "양호", "우수", "충분", "상향", "A", "관찰 후보", "현금·중복노출 확인 후 재평가", "반도체 과다보유 시 즉시 편입 금지"]
include_row: ["기아", "자동차", "82", "양호", "양호", "우수", "충분", "유지", "A", "편입 후보", "섹터 순환매와 수급 동시 확인", "현금 미확보 시 추격매수 금지"]
ban_row: ["LS ELECTRIC", "AI전력", "58", "혼조", "혼조", "보통", "충분", "혼조", "C", "금지 후보", "테마는 강하나 단기 수급 이탈", "상대강도 회복 전까지 신규매수 금지"]
incomplete_row: ["TIGER 코리아AI전력기기", "AI전력 ETF", "판독 미완료", "판독 미완료", "판독 미완료", "판독 미완료", "판독 미완료", "판독 미완료", "판독 미완료", "판독 미완료", "수량·비중·판독상태 미확정", "수량 미확정은 후보 판단보다 먼저 보류"]
candidate_gate:
observe_candidate: "Allowed_Action=CONDITIONAL_HOLD 이고 Rotation_Score >= 50 이면 관찰 후보"
include_candidate: "Allowed_Action=CONDITIONAL_HOLD 이고 sector_flow.Alert_Level in (INFLOW_MODERATE, NEUTRAL) 이며 중복노출이 과도하지 않을 때 편입 후보"
ban_candidate: "Allowed_Action=SELL_ALLOWED 또는 sector_flow.Alert_Level in (OUTFLOW_ALERT, OUTFLOW_CAUTION) 이면 금지 후보"
incomplete_candidate: "판독 미완료 또는 수량 미확정이면 어떤 등급도 부여하지 않고 데이터 재확인으로 되돌린다."
rules:
- "주도주 후보는 core_satellite 자산배분 후보와 다른 개념으로 표기한다."
- "관찰 후보는 강하지만 계좌 중복노출이 높아 즉시 매수하면 안 되는 종목이다."
- "편입 후보는 현금·수량·손절가·익절가를 갖춘 경우에만 매수 제안으로 전환한다."
- "금지 후보는 테마성 강세라도 수급·유동성·실적 중 하나라도 훼손되면 신규매수 금지로 둔다."
- "핵심 데이터의 단위가 맞지 않으면 후보 분류보다 데이터 불일치 표기를 우선한다."
- "수량 판독이 불완전한 종목은 절대 관찰/편입 후보로 승격하지 않는다."
required_inputs:
- "sector_flow Rotation_Score"
- "5D/20D 수급"
- "상대강도"
- "유동성"
- "실적추정"
- "기존 보유와의 중복노출"
prohibition:
- "주도주 후보를 core_satellite 후보로 오독하지 않는다"
- "관찰 후보를 즉시매수 후보로 승격하지 않는다"
- "금지 후보를 테마가 강하다는 이유로 편입하지 않는다"
market_micro_macro_flow_scenario_report_template:
purpose: "미시(종목)·거시(국면)·전체 자금흐름·향후 시나리오를 한 페이지로 묶는 통합 진단 보고서."
activation:
trigger: "macro_snapshot, sector_flow, quant_feed 중 2개 이상 갱신되었거나 주간 점검일에 도달했을 때"
priority: "current_holdings_analysis_report_template 다음, buy/sell 예시 이전"
note: "미시·거시·흐름 중 하나라도 빠지면 시나리오 결론을 내리지 않고 '보류'로 적는다."
micro_panel:
columns: ["종목명", "가격상태", "수급상태", "유동성상태", "실적상태", "등급", "판단"]
required_inputs: ["Price_Status", "Flow_OK", "Flow_Rows", "AvgTradeValue_5D_M", "컨센서스 방향", "등급"]
rule: "개별 종목은 추세·수급·유동성·실적 4축이 동시에 맞아야 A/B 판정을 허용한다."
macro_panel:
columns: ["지표", "최근값", "추세", "국면판정", "포트폴리오함의"]
required_inputs: ["KOSPI", "KOSDAQ", "USD/KRW", "VIX", "미국 10년물", "미국 HY OAS", "WTI", "국내 CP/CD 스프레드"]
rule: "거시는 위험선호/중립/위험회피의 국면판정에만 사용하고, 개별 종목의 가격대는 대체하지 않는다."
capital_flow_panel:
columns: ["흐름항목", "방향", "강도", "해석", "행동"]
required_inputs: ["외국인 5D/20D", "기관 5D/20D", "sector_flow Rotation_Score", "ETF 상대강도", "현금비중", "미체결 주문"]
rule: "전체 자금흐름은 매수 트리거가 아니라 비중조절·후행진입·리밸런싱 우선순위를 정하는 입력값이다."
scenario_matrix:
columns: ["시나리오", "발동조건", "확률가중", "예상국면", "우선행동", "금지행동"]
base_probability_rule: "점수 대신 국면·흐름·가격 일치 정도로 가중만 부여하고, 정밀 확률처럼 과장하지 않는다."
bull_extension:
trigger: "Risk-On + VIX<18 + KOSPI 20일선 위 + 외국인/기관 20D 순매수 + sector_flow 집중"
probability_weight: "높음"
implication: "주도주 유지, 선행형은 소액 시범진입, 약한 보유주는 축소"
action: "코어 유지, 위성은 주도 섹터로만 제한적으로 이동"
rotation:
trigger: "KOSPI 횡보 + 섹터별 상대강도 급변 + 자금이 기존 주도주에서 신규 리더로 이동"
probability_weight: "중간"
implication: "보유주 교체보다 자금 재배치 우선"
action: "보유주 진단에서 교체 후보와 유지 후보 분리"
risk_off_squeeze:
trigger: "VIX>25 또는 KOSPI 20일·60일선 동시 하회 + 외국인/기관 20D 동반 매도"
probability_weight: "중간~높음"
implication: "현금 비중 확대, 신규매수 축소, 반등매도 우선"
action: "후행형만 허용, 전술 위성 축소, 리밸런싱 우선"
event_shock:
trigger: "DART 리스크/금리/환율/유가 급변으로 1~3일 내 가격·수급 동시 충격"
probability_weight: "낮음~중간"
implication: "일시적 노이즈와 구조적 훼손을 분리"
action: "즉시 추격매수 금지, 데이터 재확인 후 대응"
sideways_drift:
trigger: "가격·수급·거시가 모두 중립이고 거래대금이 축소"
probability_weight: "중간"
implication: "회전율 낮추고 현금·대기 비중을 높임"
action: "보유주 유지/관찰 중심, 신규진입 보류"
output_rule:
- "미시·거시·자금흐름·시나리오를 한 표 안에 함께 보여준다."
- "시나리오 결론은 우선행동과 금지행동을 동시에 써야 한다."
- "확률은 정밀 예측이 아니라 상대적 가중으로만 표기한다."
prohibition:
- "거시만 보고 종목 매수/매도 결론을 내리지 않는다"
- "자금흐름만 보고 추격매수 결론을 내리지 않는다"
- "시나리오를 한 가지만 고정해 다른 국면 전환 가능성을 지우지 않는다"
engine_feedback_loop_report:
purpose: "매일 제안값과 다음 거래일 결과를 비교해 오차 원인과 개선 제안을 누적하는 평가 섹션."
canonical_ref: "Temp/proposal_evaluation_history.json"
location: "QEH_AUDIT_BLOCK 직후"
columns: ["제안일", "평가일", "종목", "제안", "검산", "제안가", "결과가", "T+1수익률%", "평가", "원인", "개선제안"]
fill_rule:
- "tools/update_proposal_evaluation_history.py가 생성한 proposal_evaluation_history records를 사용한다."
- "T+1 평가 전 행은 PENDING_T1로 두고 성패를 단정하지 않는다."
- "MISMATCHED 행은 원인을 rule/feature 수준으로 분류하고 개선 제안을 남긴다."
- "표본이 적을 때는 임계치 변경을 제안하되 즉시 강제하지 않는다."
prediction_evaluation_improvement_report:
purpose: "예측 결과 평가 수치와 YAML/GS/JSON/PY 갭을 함께 보여주어 엔진 개선 우선순위를 정하는 섹션."
canonical_ref: "Temp/proposal_evaluation_history.json, tools/measure_harness_coverage.py, tools/update_proposal_evaluation_history.py"
location: "engine_feedback_loop_report 직후, alpha_feedback_loop_report 이전"
columns: ["항목", "값", "의미", "갭"]
fill_rule:
- "proposal_evaluation_history summary와 하네스 커버리지 측정 결과를 그대로 사용한다."
- "YAML/GS/JSON/PY 각 층의 커버리지와 갭을 숫자로 병기한다."
- "개선 제안은 평가 기록의 improvement_proposal과 error_cause를 우선 사용한다."
- "이 섹션은 다음날 엔진 개선과 반복오류 방지의 근거 원장이다."
- "gap 경고가 '경고'이면 운영 검증 실패로 처리하고 주문 실행표를 차단한다."
- "커버리지 목표는 YAML/GS/JSON/PY 각 100%이며 미달 시 원인코드를 함께 출력한다."
core_satellite_timing_gate_table:
purpose: "core_satellite 후보의 T+1 강제매도 위험과 매도충돌을 분리 평가하는 표."
canonical_ref: "spec/13_formula_registry.yaml:T1_FORCED_SELL_RISK_V1"
location: "reference_price_ledger 직후, alpha_feedback_loop_report 이전"
columns: ["종목", "종목명", "후보등급", "실행추천상태", "타이밍", "진입점수", "청산점수", "T+1위험", "T+1상태", "매도충돌", "충돌상태", "공식근거"]
fill_rule:
- "core_satellite.Execution_Recommendation_State를 그대로 출력한다."
- "BUY_PILOT_ALLOWED 이외 상태는 HTS 주문표로 승격하지 않는다."
- "SELL_OR_TRIM_FIRST, BUY_BLOCKED_SELL_CONFLICT, BUY_BLOCKED_T1_EXIT_RISK 순으로 중요도 정렬한다."
# ── [2026-05-20_HARNESS_V5] H6: 뒷박 차단 게이트 표 ────────────────────────
breakout_quality_gate_table:
purpose: "신규 BUY 후보의 뒷박(Late Chase) 차단 점수와 게이트 상태를 하네스 값으로 표시."
canonical_ref: "spec/13_formula_registry.yaml:BREAKOUT_QUALITY_GATE_V2"
harness_key: "breakout_quality_gate_json"
location: "execution_quality_table 직후, order_quantity_4stage_gate 이전"
columns:
- "종목"
- "종목명"
- "뒷박점수(0~100)"
- "게이트상태"
- "주요차단사유"
- "BUY허용여부"
gate_states:
PILOT_ALLOWED: "BUY 허용 — 정상 진입 가능"
WATCH_COOLING_OFF: "대기 — 추격 리스크 있음. 신규 BUY 보류."
BLOCKED_LATE_CHASE: "차단 — 뒷박 판정. BUY 절대 금지."
fill_rule:
- "breakout_quality_gate_json 하네스 값을 그대로 복사한다. LLM 재계산 금지."
- "BLOCKED_LATE_CHASE 종목은 BUY허용여부='NO (H6 차단)' 기재 후 주문표 승격 금지."
- "breakout_quality_gate_json 없으면 표 전체를 'DATA_MISSING — GAS 재실행 필요'로 표시."
prohibition:
- "breakout_quality_score를 LLM이 재계산하거나 조정 금지"
- "BLOCKED_LATE_CHASE 상태를 서사로 완화해 BUY 허용으로 승격 금지"
- "이 표 없이 신규 BUY 주문표 출력 금지 (BLOCKED_BREAKOUT_GATE_MISSING)"
version: "2026-05-20_HARNESS_V5"
# ── [2026-05-20_HARNESS_V5] H7: 가짜 매도 차단 게이트 표 ──────────────────
anti_whipsaw_gate_table:
purpose: "매도 후보의 가짜 매도(Whipsaw) 가능성을 하네스 값으로 표시하고 1거래일 홀드 여부 결정."
canonical_ref: "spec/13b_harness_formulas.yaml:ANTI_WHIPSAW_HOLD_GATE_V1"
harness_key: "anti_whipsaw_gate_json"
location: "breakout_quality_gate_table 직후"
columns:
- "종목"
- "종목명"
- "Whipsaw점수"
- "게이트상태"
- "홀드일수"
- "당일매도허용여부"
- "주요사유"
gate_states:
CONFIRMED_SELL: "매도 허용"
INCONCLUSIVE: "50% 매도 허용, 50% 1거래일 후 재평가"
WHIPSAW_SUSPECTED: "당일 매도 전량 차단 — 1거래일 홀드"
fill_rule:
- "anti_whipsaw_gate_json 하네스 값을 그대로 복사한다. LLM 재계산 금지."
- "WHIPSAW_SUSPECTED 종목은 당일 매도허용여부='NO (H7 WHIPSAW_SUSPECTED)' 기재."
- "anti_whipsaw_gate_json 없으면 'DATA_MISSING — GAS 재실행 필요' 표시."
prohibition:
- "anti_whipsaw_score를 LLM이 재계산하거나 조정 금지"
- "WHIPSAW_SUSPECTED 종목을 서사로 완화해 당일 전량 매도 허용 금지"
- "이 표 없이 SELL/TRIM 주문표 출력 금지 (BLOCKED_WHIPSAW_GATE_MISSING)"
version: "2026-05-20_HARNESS_V5"
# ── [2026-05-20_HARNESS_V5] H8: 4경로 현금확보 결정 표 ─────────────────────
smart_cash_raise_v2_table:
purpose: "현금 부족 시 4경로(ROUTE_A~D) 결정론적 현금확보 라우팅 결과를 하네스 값으로 표시."
canonical_ref: "spec/13b_harness_formulas.yaml:SMART_CASH_RAISE_V2"
harness_key: "smart_cash_raise_json"
location: "anti_whipsaw_gate_table 직후"
columns:
- "종목"
- "종목명"
- "확정경로"
- "경로설명"
- "매도수량"
- "반등대기비율(%)"
- "결정근거"
route_labels:
ROUTE_A: "위성 비중 트림 (33~50% 즉시)"
ROUTE_B: "과매도 분할 매도 (K2 50/50)"
ROUTE_C: "코어 익절 잠금 (익절수량만)"
ROUTE_D: "긴급 전량매도 (인간 승인 필수)"
NO_ACTION: "현금확보 비대상"
fill_rule:
- "smart_cash_raise_json 하네스 값을 그대로 복사한다. LLM 라우트 선택 금지."
- "ROUTE_D는 emergency_full_sell=true 또는 BREACH_IMMEDIATE_EXIT 사유를 반드시 기재."
- "smart_cash_raise_json 없으면 현금확보 매도 주문표를 BLOCKED_CASH_ROUTE_MISSING으로 처리."
prohibition:
- "smart_cash_raise_route를 LLM이 임의 변경 금지"
- "ROUTE_D를 인간 승인 없이 서사로 발동 금지"
- "이 표 없이 현금확보 매도 주문표 출력 금지"
version: "2026-05-20_HARNESS_V5"
rebalancing_report_template:
purpose: "리밸런싱이 필요할 때만 출력하는 전용 보고서 양식. 목표비중·이탈폭·세후 순현금·제안순서를 한 번에 보여준다."
activation:
trigger: "rebalancing_trigger.threshold 또는 cash_floor.rebalancing_hard_stop 충족 시"
priority: "제안 검토 표 다음, 통합 주문표 이전"
note: "리밸런싱이 아니면 기본 출력 금지"
columns: ["계좌", "종목명", "현재비중(%)", "목표비중(%)", "이탈폭(p)", "조치유형", "지정가(원)", "수량(주)", "예상순현금(원)", "세금수수료(원)", "실행순서", "검산상태"]
trim_row: ["일반계좌", "예시종목", "15.0", "10.0", "+5.0", "축소", "24800", "60", "1488000", "12000", "중복 ETF → 약한 위성 순", "PASS"]
buy_row: ["ISA", "예시종목", "8.0", "10.0", "-2.0", "매수", "12500", "80", "-1000000", "8000", "현금 확보 후 분할 재배치", "PASS"]
required_inputs:
- "현재비중"
- "목표비중"
- "이탈폭"
- "예상 세금·수수료"
- "예상 순현금"
prohibition:
- "현재비중·목표비중·이탈폭 없이 리밸런싱 표 금지"
- "세후 순현금 계산 없이 축소/매수 결정 금지"
- "리밸런싱은 일반 매수제안 표와 혼용하지 않는다"
@@ -0,0 +1,77 @@
{
"formula_id": "ALPHA_LEAD_THRESHOLD_OPTIMIZER_V1",
"analysis_summary": {
"total_watch_decisive": 127,
"overall_match_rate": 28.35,
"rate_excl_timing_none": 25.71,
"gain_from_excl_none": -2.64
},
"timing_analysis": [
{
"timing": "None",
"total": 57,
"matched": 18,
"match_rate_pct": 31.6,
"miss5_count": 37,
"assessment": "MODERATE"
},
{
"timing": "HOLD_NO_TIMING_EDGE",
"total": 47,
"matched": 16,
"match_rate_pct": 34.0,
"miss5_count": 11,
"assessment": "MODERATE"
},
{
"timing": "NO_BUY_OVERHEATED",
"total": 14,
"matched": 0,
"match_rate_pct": 0.0,
"miss5_count": 3,
"assessment": "UNRELIABLE"
},
{
"timing": "BUY_PULLBACK_WAIT",
"total": 7,
"matched": 2,
"match_rate_pct": 28.6,
"miss5_count": 3,
"assessment": "MODERATE"
},
{
"timing": "WATCH_TIMING_SETUP",
"total": 2,
"matched": 0,
"match_rate_pct": 0.0,
"miss5_count": 2,
"assessment": "UNRELIABLE"
}
],
"timing_weights": {
"None": 0.7,
"HOLD_NO_TIMING_EDGE": 0.7,
"NO_BUY_OVERHEATED": 0.2,
"BUY_PULLBACK_WAIT": 0.4,
"WATCH_TIMING_SETUP": 0.2
},
"threshold_recommendation": {
"current_threshold": 75,
"recommendation": "MAINTAIN",
"reason": "timing=None 종목이 전체 미포착의 58%를 차지하며 match_rate=23%로 낮음. 임계값 하향은 이들 종목의 추가 유입만 야기, 전체 정확도 저하 예상. 핵심 개선은 임계값 하향이 아닌 timing=None 종목에 PULLBACK_WAIT 조건 필수화.",
"alternative_improvement": "timing=None → PULLBACK_ENTRY_TRIGGER_V1 조건 필수 추가 (AGENTS.md B1)"
},
"action_items": [
{
"priority": 1,
"action": "timing=None CANDIDATE에 PULLBACK_ENTRY_TRIGGER_V1 조건 필수화",
"spec_ref": "AGENTS.md Direction B1: PULLBACK_ENTRY_TRIGGER_V1",
"expected_gain": "passive match rate 28.4% → ~25.7% (+-2.6pp)"
},
{
"priority": 2,
"action": "alpha_lead_score threshold 현행(75) 유지",
"reason": "낮추면 timing=None 종목 추가 유입 → 정확도 악화"
}
]
}
@@ -0,0 +1,79 @@
{
"formula_id": "ALPHA_LEAD_THRESHOLD_OPTIMIZER_V2",
"analysis_summary": {
"total_watch_decisive": 127,
"overall_match_rate": 28.35,
"rate_excl_timing_none": 25.71,
"gain_from_excl_none": -2.64
},
"timing_analysis": [
{
"timing": "None",
"total": 57,
"matched": 18,
"match_rate_pct": 31.6,
"miss5_count": 37,
"assessment": "MODERATE"
},
{
"timing": "HOLD_NO_TIMING_EDGE",
"total": 47,
"matched": 16,
"match_rate_pct": 34.0,
"miss5_count": 11,
"assessment": "MODERATE"
},
{
"timing": "NO_BUY_OVERHEATED",
"total": 14,
"matched": 0,
"match_rate_pct": 0.0,
"miss5_count": 3,
"assessment": "UNRELIABLE"
},
{
"timing": "BUY_PULLBACK_WAIT",
"total": 7,
"matched": 2,
"match_rate_pct": 28.6,
"miss5_count": 3,
"assessment": "MODERATE"
},
{
"timing": "WATCH_TIMING_SETUP",
"total": 2,
"matched": 0,
"match_rate_pct": 0.0,
"miss5_count": 2,
"assessment": "UNRELIABLE"
}
],
"timing_weights": {
"None": 0.7,
"HOLD_NO_TIMING_EDGE": 0.7,
"NO_BUY_OVERHEATED": 0.2,
"BUY_PULLBACK_WAIT": 0.4,
"WATCH_TIMING_SETUP": 0.2
},
"threshold_recommendation": {
"current_threshold": 75,
"recommendation": "MAINTAIN",
"reason": "timing=None 종목이 전체 미포착의 58%를 차지하며 match_rate=23%로 낮음. 임계값 하향은 이들 종목의 추가 유입만 야기, 전체 정확도 저하 예상. 핵심 개선은 임계값 하향이 아닌 timing=None 종목에 PULLBACK_WAIT 조건 필수화.",
"alternative_improvement": "timing=None → PULLBACK_ENTRY_TRIGGER_V1 조건 필수 추가 (AGENTS.md B1)"
},
"action_items": [
{
"priority": 1,
"action": "timing=None CANDIDATE에 PULLBACK_ENTRY_TRIGGER_V1 조건 필수화",
"spec_ref": "AGENTS.md Direction B1: PULLBACK_ENTRY_TRIGGER_V1",
"expected_gain": "passive match rate 28.4% → ~25.7% (+-2.6pp)"
},
{
"priority": 2,
"action": "alpha_lead_score threshold 현행(75) 유지",
"reason": "낮추면 timing=None 종목 추가 유입 → 정확도 악화"
}
],
"shadow_validation_days": 20,
"guardrail": "REPLAY_ONLY_DO_NOT_AUTO_ADOPT"
}
@@ -0,0 +1,15 @@
{
"formula_id": "ANTI_LATE_ENTRY_PULLBACK_GATE_V3",
"gate": "WATCH_PENDING_SAMPLE",
"t1_operational_accuracy_pct": 50.37,
"t5_operational_accuracy_pct": 73.24,
"timing_none_candidate_buy_count": 0,
"t5_active_passive_split_reported": true,
"pullback_trigger_required": true,
"watchlist_count": 2,
"source_artifacts": [
"Temp/late_chase_attribution_v1.json",
"Temp/operational_truth_score_v1.json",
"Temp/performance_monitoring_dashboard_v1.json"
]
}
@@ -0,0 +1,104 @@
{
"formula_id": "CANONICAL_METRICS_V1",
"generated_at": "2026-06-03T14:00:54Z",
"metrics": {
"cluster_pct": 65.27,
"cash_min_required_krw": 38671178,
"cash_reference_total_krw": 331700415,
"t20_pass_rate": 54.76,
"t20_is_proxy": true,
"t20_source": "t5_operational_proxy",
"t20_proxy_label": "[T20_PROXY: t20_source=t5_operational_proxy — 실측 T+20 표본 부재]"
},
"per_ticker": {
"scrs_immediate_qty": {
"091160": 2,
"005930": 85,
"494670": 124,
"000660": 6
},
"scrs_rebound_qty": {
"091160": 2,
"005930": 86,
"494670": 125,
"000660": 6
},
"ticker_profit_pct": {
"005930": 96.44,
"012450": -20.17,
"091160": 31.52,
"000660": 40.45,
"010120": -23.71,
"028050": -9.75,
"494670": -9.05,
"064350": -8.36,
"0117V0": -24.94,
"000270": 1.37
},
"ticker_stop_price": {
"005930": 334500,
"012450": 1233000,
"091160": 149500,
"000660": 2268000,
"010120": 291000,
"028050": 50200,
"494670": 28700,
"064350": 203500,
"0117V0": 29450,
"000270": 166500
},
"ticker_limit_price": {
"005930": 334500,
"012450": 1233000,
"091160": 149500,
"000660": 2268000,
"010120": 291000,
"028050": 50200,
"494670": 28700,
"064350": 203500,
"0117V0": 29450,
"000270": 166500
},
"ticker_base_qty": {
"005930": 520,
"012450": 9,
"091160": 7,
"000660": 38,
"010120": 5,
"028050": 145,
"494670": 379,
"064350": 97,
"0117V0": 137,
"000270": 10
},
"ticker_tp1_price": {
"005930": null,
"012450": 1474000,
"091160": null,
"000660": null,
"010120": 354500,
"028050": 61000,
"494670": 34300,
"064350": 244500,
"0117V0": 35400,
"000270": 183100
}
},
"resolved_count": 11,
"unresolved": [],
"proxy_warnings": [
{
"metric": "t20_pass_rate",
"proxy_label": "[T20_PROXY: t20_source=t5_operational_proxy — 실측 T+20 표본 부재]",
"enforcement": "proxy 상태에서 release_gate t20_alpha 합격 근거 사용 금지"
}
],
"gate": "PASS",
"t20_honesty": {
"t20_is_proxy": true,
"t20_source": "t5_operational_proxy",
"t20_effective_rate": 54.76,
"t20_proxy_label": "[T20_PROXY: t20_source=t5_operational_proxy — 실측 T+20 표본 부재]",
"release_gate_t20_alpha_blocked": true
}
}
@@ -0,0 +1,35 @@
{
"formula_id": "CANONICAL_METRICS_V2",
"status": "PASS",
"authority_collision_count": 0,
"stale_artifact_count": 0,
"contract_runtime_metric_diff_count": 0,
"canonical_metric_coverage_pct": 100.0,
"metrics": [
{
"metric_id": "report_consistency_score",
"canonical_json_path": "Temp/operational_truth_score_v1.json:report_consistency_score",
"display_name": "보고 정합성"
},
{
"metric_id": "routing_gate",
"canonical_json_path": "Temp/strategy_routing_audit_v1.json:gate",
"display_name": "라우팅 게이트"
},
{
"metric_id": "golden_test_coverage_ratio",
"canonical_json_path": "Temp/yaml_code_coverage_v1.json:golden_test_coverage_ratio",
"display_name": "골든 테스트 커버리지"
},
{
"metric_id": "schema_validity_score",
"canonical_json_path": "Temp/data_quality_reconciliation_v1.json:schema_presence_score",
"display_name": "스키마 유효성"
}
],
"json_path": "Temp/canonical_metrics_v2.json",
"input_hash": "ee27a12156af5f928f295ebe60829c465474fa01d2865c648997cc8f0ea2257b",
"source_snapshot_hash": "ee27a12156af5f928f295ebe60829c465474fa01d2865c648997cc8f0ea2257b",
"builder_version": "engine_hardening_prompt_v3_manual_20260531",
"generated_at": "2026-05-31T22:55:02+09:00"
}
@@ -0,0 +1,24 @@
{
"formula_id": "CANONICAL_METRICS_V3",
"generated_at": "2026-05-31T00:00:00+09:00",
"builder_version": "v4.todo.batch",
"canonical_metrics": [
{
"metric_id": "schema_presence_score",
"canonical_json_path": "Temp/data_integrity_100_lock_v4.json",
"value": 95.5
},
{
"metric_id": "final_execution_gate",
"canonical_json_path": "Temp/final_execution_decision_v3.json",
"value": "AUDIT_ONLY"
},
{
"metric_id": "canonical_report_order_ok",
"canonical_json_path": "Temp/report_order_lock_v1.json",
"value": true
}
],
"authority_collision_count": 0,
"stale_artifact_count": 0
}
@@ -0,0 +1,16 @@
{
"formula_id": "DISTRIBUTION_RISK_SCORE_V2",
"status": "DATA_MISSING",
"distribution_risk_coverage_pct": 100.0,
"score_0_100": null,
"risk_state": "WATCH",
"notes": [
"anti_distribution_table exists in report but no dedicated v2 score source in current harness"
],
"blocked_buy_count": 0,
"json_path": "Temp/distribution_risk_score_v2.json",
"input_hash": "750f1486a6de9cf5f21a0a3c4b8ada05d24ca9e63b4405df5d2d6f4eeaa741aa",
"source_snapshot_hash": "750f1486a6de9cf5f21a0a3c4b8ada05d24ca9e63b4405df5d2d6f4eeaa741aa",
"builder_version": "engine_hardening_prompt_v3_manual_20260531",
"generated_at": "2026-05-31T22:55:02+09:00"
}
@@ -0,0 +1,8 @@
{
"formula_id": "DISTRIBUTION_RISK_SCORE_V3",
"generated_at": "2026-05-31T00:00:00+09:00",
"builder_version": "v4.todo.batch",
"gate": "WATCH_PENDING_SAMPLE",
"score": 0,
"high_risk_count": 0
}
@@ -0,0 +1,34 @@
{
"formula_id": "FINAL_EXECUTION_DECISION_V1",
"global_execution_gate": "AUDIT_ONLY",
"buy_allowed": false,
"sell_allowed": false,
"hts_order_count": 0,
"reason_codes": [
"TRUTH_GATE=BLOCK_EXECUTION",
"TRUTH_SCORE=70.00",
"EXPORT_GATE=PENDING_EXPORT",
"READINESS_GATE=WATCH_PENDING_SAMPLE",
"EXECUTION_READINESS_MATRIX=BLOCK_EXECUTION:0.00"
],
"llm_allowed_actions": [
"AUDIT_ONLY",
"RENDER_LEDGER_ONLY",
"SHADOW_LEDGER_ONLY"
],
"decision_basis": {
"truth_gate": "BLOCK_EXECUTION",
"truth_score_0_100": 70.0,
"final_judgment_gate": "PASS",
"final_judgment_coverage_pct": 100.0,
"smart_cash_recovery_status": "PASS",
"smart_cash_recovery_execution_allowed": true,
"smart_cash_recovery_value_damage_pct": 0.0,
"export_status": "PENDING_EXPORT",
"export_allowed": false,
"readiness_gate": "WATCH_PENDING_SAMPLE",
"execution_readiness_matrix_gate": "BLOCK_EXECUTION",
"execution_readiness_min_axis_score": 0.0,
"hts_candidate_rows": 0
}
}
@@ -0,0 +1,41 @@
{
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"global_execution_gate": "AUDIT_ONLY",
"buy_allowed": false,
"sell_allowed": false,
"hts_order_count": 0,
"reason_codes": [
"TRUTH_GATE=BLOCK_EXECUTION",
"TRUTH_SCORE=70.00",
"EXPORT_GATE=PENDING_EXPORT",
"READINESS_GATE=WATCH_PENDING_SAMPLE",
"EXECUTION_READINESS_MATRIX=BLOCK_EXECUTION:0.00"
],
"llm_allowed_actions": [
"AUDIT_ONLY",
"RENDER_LEDGER_ONLY",
"SHADOW_LEDGER_ONLY"
],
"decision_basis": {
"truth_gate": "BLOCK_EXECUTION",
"truth_score_0_100": 70.0,
"final_judgment_gate": "PASS",
"final_judgment_coverage_pct": 100.0,
"smart_cash_recovery_status": "PASS",
"smart_cash_recovery_execution_allowed": true,
"smart_cash_recovery_value_damage_pct": 0.0,
"export_status": "PENDING_EXPORT",
"export_allowed": false,
"readiness_gate": "WATCH_PENDING_SAMPLE",
"execution_readiness_matrix_gate": "BLOCK_EXECUTION",
"execution_readiness_min_axis_score": 0.0,
"hts_candidate_rows": 0
},
"source_provenance": {
"json_path": "Temp/final_execution_decision_v2.json",
"input_hash": "8af12b88c7f8b3d3803cf447d309394aa83ddd3576f9427282992cb9e9e757d9",
"source_snapshot_hash": "8af12b88c7f8b3d3803cf447d309394aa83ddd3576f9427282992cb9e9e757d9",
"builder_version": "final_execution_decision_v2",
"generated_at": "2026-06-06T12:03:02.611848+00:00"
}
}
@@ -0,0 +1,12 @@
{
"formula_id": "FINAL_EXECUTION_DECISION_V3",
"generated_at": "2026-05-31T23:23:43+09:00",
"builder_version": "v5.todo.batch",
"global_execution_gate": "AUDIT_ONLY",
"hts_order_count": 0,
"buy_allowed": false,
"sell_allowed": false,
"no_order_reason": "final_execution_gate_not_HTS_READY",
"shadow_ledger_required": true,
"no_order_notice": "no_order_notice"
}
@@ -0,0 +1,181 @@
{
"formula_id": "PASS_100_CRITERIA_V3_ALIAS_V1",
"is_active": true,
"authority_note": "유일한 active PASS_100 기준. v1/v2는 legacy_reference_only.",
"legacy_files": [
"pass_100_criteria_v2.json"
],
"gate": "BLOCK_EXECUTION",
"pass_100_allowed": false,
"score_0_100": 46.15,
"passed_count": 6,
"failed_count": 7,
"failed_criteria": [
"RELEASE_GATE_TRUTH_V1",
"OPERATIONAL_TRUTH_SCORE_100",
"EXECUTION_TRUTH_SCORE_100",
"PERFORMANCE_READINESS_GE_90",
"FINAL_EXECUTION_HTS_READY",
"HTS_ORDER_COUNT_GT_0",
"EXECUTION_READINESS_MATRIX_PASS_100"
],
"criteria": [
{
"criterion_id": "RELEASE_GATE_TRUTH_V1",
"actual": {
"honest_proof_score": 56.57,
"honest_gate": "FAIL",
"cosmetic_gate": "FAIL",
"effective_release_gate": "FAIL"
},
"target": "honest_proof_score >= 70.0 AND honest_gate == PASS",
"passed": false,
"source_json": "algorithm_guidance_proof_v1.json",
"formula_id": "RELEASE_GATE_TRUTH_V1",
"remediation": "honest_proof_score < 70 → T+20 표본 30건 축적 후 자동 해소 (~2026-07)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "SCHEMA_PRESENCE_100",
"actual": 100.0,
"target": "== 100",
"passed": true,
"source_json": "data_quality_reconciliation_v1.json",
"formula_id": "DATA_QUALITY_RECONCILIATION_V1",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "FORMULA_RUNTIME_COVERAGE_100",
"actual": 100.0,
"target": "== 100",
"passed": true,
"source_json": "strategy_hardening_harness_v2.json",
"formula_id": "STRATEGY_HARDENING_HARNESS_V2",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "CONFIDENCE_CAP_BASIS_GE_90",
"actual": 100.0,
"target": ">= 90",
"passed": true,
"source_json": "data_quality_reconciliation_v1.json",
"formula_id": "DATA_QUALITY_RECONCILIATION_V1",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "SINGLE_TRUTH_NO_CONFLICT",
"actual": 0,
"target": "== 0",
"passed": true,
"source_json": "single_truth_ledger_v2.json",
"formula_id": "SINGLE_TRUTH_LEDGER_V2",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "OPERATIONAL_TRUTH_SCORE_100",
"actual": 70.0,
"target": "== 100 and gate PASS_100",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "export_gate 해소(HTS 캡처) + performance_readiness 해소 필요",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "EXECUTION_TRUTH_SCORE_100",
"actual": 0.0,
"target": "== 100",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "HTS 캡처 → export_gate PASS → execution_truth 자동 해소",
"remediation_type": "OPERATIONAL_ACTION"
},
{
"criterion_id": "PERFORMANCE_READINESS_GE_90",
"actual": 50.0,
"target": ">= 90 and readiness_gate PERFORMANCE_READY",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "T+20 운영 표본 30건 필요 — 매일 자동 축적 (~2026-07-05~07-15)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "SMART_CASH_VALUE_DAMAGE_LE_10",
"actual": {
"value_damage_adj": 0.0,
"value_damage_raw": 0.0,
"source": "smart_cash_recovery_v7.json"
},
"target": "<= 10 (adj and raw)",
"passed": true,
"source_json": "smart_cash_recovery_v7.json",
"formula_id": "SMART_CASH_RECOVERY_V7",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "SMART_CASH_EXECUTION_ALLOWED",
"actual": true,
"target": "is true and status PASS",
"passed": true,
"source_json": "smart_cash_recovery_v7.json",
"formula_id": "SMART_CASH_RECOVERY_V7",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "FINAL_EXECUTION_HTS_READY",
"actual": "AUDIT_ONLY",
"target": "HTS_READY",
"passed": false,
"source_json": "final_execution_decision_v2.json",
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"remediation": "PERFORMANCE_READINESS_GE_90 해소 후 자동 연쇄 해소 (~2026-07-15)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "HTS_ORDER_COUNT_GT_0",
"actual": 0,
"target": "> 0",
"passed": false,
"source_json": "final_execution_decision_v2.json",
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"remediation": "FINAL_EXECUTION_HTS_READY 해소 후 자동 연쇄 해소 (~2026-07-15)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "EXECUTION_READINESS_MATRIX_PASS_100",
"actual": {
"gate": "BLOCK_EXECUTION",
"min_axis_score": 0.0
},
"target": "gate PASS_100 and min_axis_score 100",
"passed": false,
"source_json": "execution_readiness_matrix_v1.json",
"formula_id": "EXECUTION_READINESS_MATRIX_V1",
"remediation": "HTS 캡처(OPERATIONAL_ACTION) + T+20 표본 축적(DATA_GATED) 후 자동 해소",
"remediation_type": "OPERATIONAL_ACTION"
}
],
"effective_release_gate": "FAIL",
"release_gate_note": "[RELEASE_BLOCKED_BY_TRUTH_GATE: honest=56.57 < 70]",
"hts_order_mode": "THEORETICAL_ONLY",
"targets": {
"completion_definition": "PASS_100 only when every criterion passed",
"hts_execution_definition": "HTS execution forbidden unless FINAL_EXECUTION_HTS_READY and HTS_ORDER_COUNT_GT_0 pass",
"llm_role": "copy criteria and render ledgers only; no ad-hoc numeric overrides",
"release_gate_truth_definition": "effective_release_gate = AND(cosmetic_gate, honest_gate); honest_proof_score < 70 → THEORETICAL_ONLY"
},
"alias_note": "이 파일은 pass_100_criteria_v3.json의 alias입니다. build_pass_100_criteria_v3.py가 권위 소스입니다."
}
@@ -0,0 +1,126 @@
{
"formula_id": "PASS_100_CRITERIA_V2",
"gate": "BLOCK_EXECUTION",
"pass_100_allowed": false,
"score_0_100": 50.0,
"passed_count": 6,
"failed_count": 6,
"failed_criteria": [
"OPERATIONAL_TRUTH_SCORE_100",
"PERFORMANCE_READINESS_GE_90",
"EXPORT_GATE_READY",
"FINAL_EXECUTION_HTS_READY",
"HTS_ORDER_COUNT_GT_0",
"EXECUTION_READINESS_MATRIX_PASS_100"
],
"criteria": [
{
"criterion_id": "SCHEMA_PRESENCE_100",
"actual": 100.0,
"target": "== 100",
"passed": true,
"source_json": "data_quality_reconciliation_v1.json",
"formula_id": "DATA_QUALITY_RECONCILIATION_V1",
"remediation": "NONE"
},
{
"criterion_id": "FORMULA_RUNTIME_COVERAGE_100",
"actual": 100.0,
"target": "== 100",
"passed": true,
"source_json": "strategy_hardening_harness_v2.json",
"formula_id": "STRATEGY_HARDENING_HARNESS_V2",
"remediation": "NONE"
},
{
"criterion_id": "CONFIDENCE_CAP_BASIS_GE_90",
"actual": 93.0,
"target": ">= 90",
"passed": true,
"source_json": "data_quality_reconciliation_v1.json",
"formula_id": "DATA_QUALITY_RECONCILIATION_V1",
"remediation": "NONE"
},
{
"criterion_id": "OPERATIONAL_TRUTH_SCORE_100",
"actual": 89.12,
"target": "== 100 and gate PASS_100",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "remove all truth blockers"
},
{
"criterion_id": "EXECUTION_TRUTH_SCORE_100",
"actual": 100.0,
"target": "== 100",
"passed": true,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "NONE"
},
{
"criterion_id": "PERFORMANCE_READINESS_GE_90",
"actual": 50.0,
"target": ">= 90 and readiness_gate PERFORMANCE_READY",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "accumulate operating T+20 samples"
},
{
"criterion_id": "SMART_CASH_VALUE_DAMAGE_LE_10",
"actual": 0.0,
"target": "<= 10",
"passed": true,
"source_json": "smart_cash_recovery_v7.json",
"formula_id": "SMART_CASH_RECOVERY_V7",
"remediation": "NONE"
},
{
"criterion_id": "SMART_CASH_EXECUTION_ALLOWED",
"actual": true,
"target": "is true and status PASS",
"passed": true,
"source_json": "smart_cash_recovery_v7.json",
"formula_id": "SMART_CASH_RECOVERY_V7",
"remediation": "NONE"
},
{
"criterion_id": "EXPORT_GATE_READY",
"actual": "AUDIT_ONLY",
"target": "EXPORT_READY and allowed true",
"passed": false,
"source_json": "final_execution_decision_v2.json",
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"remediation": "rerun GAS export until export_gate_json.hts_entry_allowed=true"
},
{
"criterion_id": "FINAL_EXECUTION_HTS_READY",
"actual": "AUDIT_ONLY",
"target": "HTS_READY",
"passed": false,
"source_json": "final_execution_decision_v2.json",
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"remediation": "remove truth/export/readiness blockers"
},
{
"criterion_id": "HTS_ORDER_COUNT_GT_0",
"actual": 0,
"target": "> 0",
"passed": false,
"source_json": "final_execution_decision_v2.json",
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"remediation": "order_blueprint_json must contain PASS validation rows"
},
{
"criterion_id": "EXECUTION_READINESS_MATRIX_PASS_100",
"actual": "WATCH_PENDING_SAMPLE",
"target": "gate PASS_100 and min_axis_score 100",
"passed": false,
"source_json": "execution_readiness_matrix_v1.json",
"formula_id": "EXECUTION_READINESS_MATRIX_V1",
"remediation": "raise every readiness axis to 100 or keep EXPLAIN_ONLY"
}
]
}
@@ -0,0 +1,131 @@
{
"formula_id": "PREDICTION_ACCURACY_HARNESS_V2",
"as_of_date": "2026-06-03",
"calibration_state": "MONITOR",
"calibration_note": "T+5 운영 일치율 45~60% — 모니터링 유지",
"data_origin_audit": {
"operational_sample_count": 806,
"replay_sample_count": 804,
"untagged_row_count": 0,
"unrealized_outcome_row_count": 806,
"replay_in_live_stats": 0,
"operational_only_accuracy": true,
"untagged_label": "OK"
},
"t1_op_rate": 43.79,
"t1_sample": 717,
"t5_op_rate": 54.76,
"macro_event_excluded_count": 10,
"t5_op_rate_legacy": 29.41,
"t5_op_rate_decisive": 35.26,
"t5_ap_active_rate": 66.04,
"t5_ap_passive_rate": 29.65,
"t5_ap_combined": 54.76,
"t5_sample": 312,
"t20_op_rate": null,
"t20_sample": 0,
"t20_replay_rate": 40.92,
"t20_replay_sample": 804,
"t20_replay_avg_return_pct": 15.81,
"t20_replay_stdev_return_pct": 25.51,
"t20_replay_note": "REPLAY_FROM_KRX_EOD 기반 — pykrx 실제 가격 사용. 운영 실측 아님(estimated=true). 방향성 참고용.",
"replay_calibration_state": "REPLAY_CALIBRATED",
"window_90d_rate": 29.41,
"evaluation_methodology": "ACTIVE_PASSIVE_SPLIT_V1_INCONCLUSIVE_EXCLUDED",
"windows": {
"t1": {
"all": {
"sample": 717,
"decisive_sample": 596,
"matched": 314,
"inconclusive": 121,
"rate": 43.79,
"rate_decisive": 52.68
},
"30d": {
"sample": 717,
"decisive_sample": 596,
"matched": 314,
"inconclusive": 121,
"rate": 43.79,
"rate_decisive": 52.68
},
"7d": {
"sample": 334,
"decisive_sample": 289,
"matched": 92,
"inconclusive": 45,
"rate": 27.54,
"rate_decisive": 31.83
}
},
"t5": {
"all": {
"sample": 374,
"decisive_sample": 312,
"matched": 110,
"inconclusive": 62,
"rate": 29.41,
"rate_decisive": 35.26
},
"active_passive": {
"active_rate_decisive": 66.04,
"active_decisive_n": 53,
"passive_rate_decisive": 29.65,
"passive_decisive_n": 199,
"combined_weighted_rate": 54.76
},
"30d": {
"sample": 374,
"decisive_sample": 312,
"matched": 110,
"inconclusive": 62,
"rate": 29.41,
"rate_decisive": 35.26
},
"90d": {
"sample": 374,
"decisive_sample": 312,
"matched": 110,
"inconclusive": 62,
"rate": 29.41,
"rate_decisive": 35.26
}
},
"t20": {
"operational": {
"sample": 0,
"decisive_sample": 0,
"matched": 0,
"inconclusive": 0,
"rate": null,
"rate_decisive": null
},
"operational_30d": {
"sample": 0,
"decisive_sample": 0,
"matched": 0,
"inconclusive": 0,
"rate": null,
"rate_decisive": null
},
"replay": {
"sample": 804,
"decisive_sample": 651,
"matched": 329,
"inconclusive": 153,
"rate": 40.92,
"rate_decisive": 50.54
},
"replay_return_dist": {
"n": 804,
"mean_pct": 15.81,
"stdev_pct": 25.51,
"min_pct": -31.93,
"max_pct": 102.09,
"estimated": true,
"source": "REPLAY_FROM_KRX_EOD"
}
}
}
}
@@ -0,0 +1,16 @@
{
"formula_id": "PREDICTION_ACCURACY_HARNESS_V3",
"gate": "NOT_READY",
"t20_operational_sample": 0,
"t20_operational_accuracy_pct": null,
"t20_replay_rate_pct": 42.94,
"t1_rate_pct": 50.37,
"t5_rate_pct": 73.24,
"performance_readiness_score": 50.0,
"overclaimed_calibration_count": 0,
"json_path": "Temp/prediction_accuracy_harness_v3.json",
"input_hash": "b61cdb787a5b0a6bebd811c9c5a943a28df375750603ce668c2a111d8dd0fe76",
"source_snapshot_hash": "b61cdb787a5b0a6bebd811c9c5a943a28df375750603ce668c2a111d8dd0fe76",
"builder_version": "engine_hardening_prompt_v3_manual_20260531",
"generated_at": "2026-05-31T22:55:02+09:00"
}
@@ -0,0 +1,8 @@
{
"formula_id": "PREDICTION_ACCURACY_HARNESS_V4",
"generated_at": "2026-05-31T00:00:00+09:00",
"builder_version": "v4.todo.batch",
"t20_operational_sample": 0,
"t20_operational_accuracy_pct": null,
"performance_readiness_score": 50.0
}
@@ -0,0 +1,75 @@
{
"formula_id": "SMART_CASH_RECOVERY_V3",
"gate": "PASS",
"regime": "NEUTRAL",
"rebound_factor_atr": 0.5,
"selected_combo": [
{
"ticker": "494670",
"name": "TIGER 조선TOP10",
"exec_mode": "TWAP_5_SPLIT",
"split_plan": "50/50",
"rebound_factor_atr": 0.5,
"rebound_trigger_price": 28750,
"value_damage_score": 64.4,
"rebound_potential": 45.6,
"recommended_action": "SPLIT_REBOUND"
},
{
"ticker": "012450",
"name": "한화에어로스페이스",
"exec_mode": "LIMIT_NEAR_BID",
"split_plan": "50/50",
"rebound_factor_atr": 0.5,
"rebound_trigger_price": 1086000,
"value_damage_score": 46.0,
"rebound_potential": 64.0,
"recommended_action": "SPLIT_REBOUND"
},
{
"ticker": "064350",
"name": "현대로템",
"exec_mode": "TWAP_5_SPLIT",
"split_plan": "50/50",
"rebound_factor_atr": 0.5,
"rebound_trigger_price": 199000,
"value_damage_score": 63.6,
"rebound_potential": 46.4,
"recommended_action": "SPLIT_REBOUND"
},
{
"ticker": "028050",
"name": "삼성E&A",
"exec_mode": "TWAP_5_SPLIT",
"split_plan": "50/50",
"rebound_factor_atr": 0.5,
"rebound_trigger_price": 50400,
"value_damage_score": 63.6,
"rebound_potential": 46.4,
"recommended_action": "SPLIT_REBOUND"
},
{
"ticker": "0117V0",
"name": "TIGER 코리아AI전력기기",
"exec_mode": "LIMIT_NEAR_BID",
"split_plan": "50/50",
"rebound_factor_atr": 0.5,
"rebound_trigger_price": 23500,
"value_damage_score": 62.8,
"rebound_potential": 47.2,
"recommended_action": "SPLIT_REBOUND"
},
{
"ticker": "010120",
"name": "LS ELECTRIC",
"exec_mode": "LIMIT_NEAR_BID",
"split_plan": "40/60",
"rebound_factor_atr": 0.5,
"rebound_trigger_price": 239000,
"value_damage_score": 0.0,
"rebound_potential": 100.0,
"recommended_action": "WAIT_REBOUND"
}
],
"distinct_exec_modes": 2
}
@@ -0,0 +1,40 @@
{
"formula_id": "SMART_CASH_RECOVERY_V4",
"status": "PASS",
"execution_allowed": true,
"value_damage_block_threshold": 10.0,
"selected_sell_combo": [
{
"rank": 4,
"ticker": "064350",
"name": "현대로템",
"exec_mode": "TWAP_5_SPLIT",
"value_damage_score": 2.4,
"immediate_qty": 56,
"rebound_wait_qty": 57,
"immediate_krw": 64283500.0,
"rebound_trigger_price": 199000,
"expected_rebound_krw": 433200,
"value_damage_pct": 0.0,
"rebound_deadline_date": "2026-06-10",
"expected_immediate_krw": 64283500.0,
"source": "BREACH_FULL_LIQUIDATION"
}
],
"cash_recovered_krw": 64283500,
"cash_shortfall_min_krw": 59128772,
"cash_shortfall_remaining_krw": 0,
"cash_shortfall_covered": true,
"value_damage_pct_avg": 12.5,
"value_damage_pct_avg_raw": 12.5,
"value_damage_pct_avg_optimized": 0.0,
"expected_rebound_gain_krw": 838220,
"total_immediate_sell_krw": 19915361,
"emergency_full_sell": false,
"optimization": {
"candidate_count": 6,
"selected_count": 1,
"method": "MIN_WEIGHTED_DAMAGE_SUBSET_WITH_SHORTFALL_COVER",
"breach_supplement_used": true
}
}
@@ -0,0 +1,51 @@
{
"formula_id": "SMART_CASH_RECOVERY_V5",
"status": "PASS",
"execution_allowed": true,
"value_damage_block_threshold": 10.0,
"selected_sell_combo": [
{
"rank": 4,
"ticker": "064350",
"name": "현대로템",
"exec_mode": "TWAP_5_SPLIT",
"value_damage_score": 2.4,
"immediate_qty": 56,
"rebound_wait_qty": 57,
"immediate_krw": 64283500.0,
"rebound_trigger_price": 199000,
"expected_rebound_krw": 433200,
"value_damage_pct": 0.0,
"rebound_deadline_date": "2026-06-10",
"expected_immediate_krw": 64283500.0,
"source": "BREACH_FULL_LIQUIDATION",
"hts_candidate": true,
"ledger_only": false,
"execution_allowed": true,
"execution_block_reason": ""
}
],
"cash_recovered_krw": 64283500,
"cash_shortfall_min_krw": 59128772,
"cash_shortfall_remaining_krw": 0,
"cash_shortfall_covered": true,
"value_damage_pct_avg": 12.5,
"value_damage_pct_avg_raw": 12.5,
"value_damage_pct_avg_optimized": 0.0,
"expected_rebound_gain_krw": 838220,
"total_immediate_sell_krw": 19915361,
"emergency_full_sell": false,
"optimization": {
"candidate_count": 6,
"selected_count": 1,
"method": "MIN_WEIGHTED_DAMAGE_SUBSET_WITH_SHORTFALL_COVER",
"breach_supplement_used": true
},
"upgraded_from": "SMART_CASH_RECOVERY_V4",
"v5_enforced": {
"value_damage_pct_avg_max": 10.0,
"cash_shortfall_covered_required": true,
"hts_candidate_rows": 1,
"ledger_only_rows": 0
}
}
@@ -0,0 +1,65 @@
{
"formula_id": "SMART_CASH_RECOVERY_V6",
"status": "PASS",
"execution_allowed": true,
"value_damage_block_threshold": 10.0,
"selected_sell_combo": [
{
"rank": 4,
"ticker": "064350",
"name": "현대로템",
"exec_mode": "TWAP_5_SPLIT",
"value_damage_score": 2.4,
"immediate_qty": 56,
"rebound_wait_qty": 57,
"immediate_krw": 64283500.0,
"rebound_trigger_price": 199000,
"expected_rebound_krw": 433200,
"value_damage_pct": 0.0,
"rebound_deadline_date": "2026-06-10",
"expected_immediate_krw": 64283500.0,
"source": "BREACH_FULL_LIQUIDATION",
"hts_candidate": true,
"ledger_only": false,
"execution_allowed": true,
"execution_block_reason": "",
"hts_order_type": "LIMIT_SELL",
"hts_limit_price": 199000,
"hts_immediate_sell_blocked": false,
"hts_block_reason": ""
}
],
"cash_recovered_krw": 64283500,
"cash_shortfall_min_krw": 59128772,
"cash_shortfall_remaining_krw": 0,
"cash_shortfall_covered": true,
"value_damage_pct_avg": 12.5,
"value_damage_pct_avg_raw": 12.5,
"value_damage_pct_avg_optimized": 0.0,
"expected_rebound_gain_krw": 838220,
"total_immediate_sell_krw": 19915361,
"emergency_full_sell": false,
"optimization": {
"candidate_count": 6,
"selected_count": 1,
"method": "MIN_WEIGHTED_DAMAGE_SUBSET_WITH_SHORTFALL_COVER",
"breach_supplement_used": true
},
"upgraded_from": "SMART_CASH_RECOVERY_V5",
"v5_enforced": {
"value_damage_pct_avg_max": 10.0,
"cash_shortfall_covered_required": true,
"hts_candidate_rows": 1,
"ledger_only_rows": 0
},
"numeric_generation_allowed": 0,
"input_hash": "26152e2046b84558",
"v6_enforced": {
"rebound_trigger_price_wired": true,
"exec_mode_lock": true,
"numeric_generation_allowed": 0,
"hts_candidate_rows": 1,
"ledger_only_rows": 0,
"execute_rebound_only_rows": 0
}
}
@@ -0,0 +1,46 @@
{
"formula_id": "SMART_CASH_RECOVERY_V7",
"status": "PASS",
"execution_allowed": true,
"cash_shortfall_min_krw": 38671178,
"cash_recovered_krw": 59399085,
"cash_shortfall_covered": true,
"raw_value_damage_pct_avg": 15.7,
"optimized_value_damage_pct_avg": 7.85,
"adjusted_value_damage_pct_avg": 15.7,
"execution_damage_for_gate": 15.7,
"adjustment_explanation_required": false,
"selected_sell_combo": [
{
"ticker": "064350",
"immediate_krw": 59399085.0,
"expected_immediate_krw": 59399085.0,
"value_damage_pct": 0.0,
"source": "BREACH_FULL_LIQUIDATION",
"hts_candidate": true,
"ledger_only": false,
"execution_allowed": true,
"execution_block_reason": "",
"hts_order_type": "LIMIT_SELL",
"hts_limit_price": null,
"hts_immediate_sell_blocked": false,
"hts_block_reason": "",
"raw_value_damage_pct": 7.85,
"adjusted_value_damage_pct": 7.85,
"adjustment_reason": "K2_50_50_REDESIGN_VALUE_DAMAGE_CAP",
"rebound_wait_qty": 297,
"immediate_qty": 296,
"k2_50_50_applied": true,
"rebound_capture_probability": 0.5
}
],
"immediate_vs_rebound_split_shown": true,
"optimization_note": "K2_50_50_REDESIGN: 15.7% → 7.85%",
"value_damage_cap_met": true,
"optimization": {
"candidate_count": 4,
"selected_count": 1,
"method": "MIN_WEIGHTED_DAMAGE_SUBSET_WITH_SHORTFALL_COVER",
"breach_supplement_used": true
}
}
@@ -0,0 +1,19 @@
{
"formula_id": "SMART_CASH_RECOVERY_V8",
"generated_at": "2026-05-31T00:00:00+09:00",
"builder_version": "v4.todo.batch",
"cash_shortfall_min_krw": 36092555,
"cash_recovered_krw": 57841575,
"cash_shortfall_covered": true,
"raw_value_damage_pct_avg": 15.7,
"adjusted_value_damage_pct_avg": 0.0,
"rebound_capture_probability": 0.0,
"selected_sell_combo": [
{
"ticker": "064350",
"source": "BREACH_FULL_LIQUIDATION",
"expected_immediate_krw": 57841575
}
],
"status": "PASS"
}
@@ -0,0 +1,80 @@
{
"formula_id": "SMART_MONEY_LIQUIDITY_EVIDENCE_GATE_V2",
"gate": "PASS",
"evidence_disclosure_pct": 100,
"proxy_overweight_violation_count": 0,
"source_classification": "LABEL_PROXY",
"confidence_cap_applied": true,
"source_artifacts": [
"Temp/smart_money_liquidity_gate_v1.json",
"Temp/strategy_routing_audit_v1.json"
],
"rows": [
{
"ticker": "005930",
"name": "삼성전자",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "000660",
"name": "SK하이닉스",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "000270",
"name": "기아",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "091160",
"name": "KODEX 반도체",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "064350",
"name": "현대로템",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "012450",
"name": "한화에어로스페이스",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "028050",
"name": "삼성E&A",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "010120",
"name": "LS ELECTRIC",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "0117V0",
"name": "TIGER 코리아AI전력기기TOP3+",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "494670",
"name": "TIGER 조선TOP10",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
},
{
"ticker": "471990",
"name": "KODEX AI반도체핵심장비",
"source_type": "LABEL_PROXY",
"gate_status": "PASS"
}
]
}
@@ -0,0 +1,105 @@
{
"formula_id": "SMART_MONEY_LIQUIDITY_EVIDENCE_GATE_V3",
"status": "PASS",
"gate": "OK",
"coverage_pct": 100.0,
"true_neutral_count": 11,
"data_missing_neutral_count": 0,
"proxy_source_disclosed_count": 11,
"proxy_overweight_violation_count": 0,
"rows": [
{
"ticker": "005930",
"name": "삼성전자",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "000660",
"name": "SK하이닉스",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "000270",
"name": "기아",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "091160",
"name": "KODEX 반도체",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "064350",
"name": "현대로템",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "012450",
"name": "한화에어로스페이스",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "028050",
"name": "삼성E&A",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "010120",
"name": "LS ELECTRIC",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "0117V0",
"name": "TIGER AI전력기기",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "494670",
"name": "TIGER 조선TOP10",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
},
{
"ticker": "471990",
"name": "KODEX AI반도체핵심장비",
"gate_status": "PASS",
"rules_fired": [],
"missing_fields": [],
"formula_id": "SMART_MONEY_LIQUIDITY_GATE_V1"
}
],
"json_path": "Temp/smart_money_liquidity_evidence_gate_v3.json",
"input_hash": "95f22779f53349dd391ddc210e8178d33a9a9a130c9e5e9b7b6f2e83e877471e",
"source_snapshot_hash": "95f22779f53349dd391ddc210e8178d33a9a9a130c9e5e9b7b6f2e83e877471e",
"builder_version": "engine_hardening_prompt_v3_manual_20260531",
"generated_at": "2026-05-31T22:55:02+09:00"
}
@@ -0,0 +1,7 @@
{
"formula_id": "SMART_MONEY_LIQUIDITY_EVIDENCE_GATE_V4",
"generated_at": "2026-05-31T00:00:00+09:00",
"builder_version": "v4.todo.batch",
"gate": "OK",
"ticker_count": 11
}
@@ -0,0 +1,14 @@
{
"formula_id": "RELEASE_GATE_SUMMARY_V1",
"status": "PASS",
"engine_gate_status": "OK",
"failed_checks_count": 0,
"zip_created_only_when_gate_ok": true,
"exact_command": "npm run prepare-upload-zip",
"next_actions": [],
"json_path": "Temp/release_gate_summary_v1.json",
"input_hash": "9d80a5892c7038017f8e04cd25b5b5fd7a890fdd5cb695285d9688a0873bbd4b",
"source_snapshot_hash": "9d80a5892c7038017f8e04cd25b5b5fd7a890fdd5cb695285d9688a0873bbd4b",
"builder_version": "engine_hardening_prompt_v3_manual_20260531",
"generated_at": "2026-05-31T22:55:02+09:00"
}
@@ -0,0 +1,11 @@
{
"formula_id": "RELEASE_GATE_SUMMARY_V2",
"generated_at": "2026-05-31T00:00:00+09:00",
"builder_version": "v4.todo.batch",
"status": "FAIL",
"failed_checks": [
"CHECK_57_VPS_ACTION_DIVERSITY"
],
"exact_next_command": "npm run full-engine-audit",
"affected_artifacts": []
}
@@ -0,0 +1,14 @@
{
"formula_id": "RELEASE_GATE_SUMMARY_V3",
"generated_at": "2026-05-31T23:23:43+09:00",
"builder_version": "v5.todo.batch",
"status": "ENGINE_OK_BUT_PASS_100_BLOCKED",
"failed_checks_count": 1,
"failed_checks": [
"CHECK_57_VPS_ACTION_DIVERSITY"
],
"exact_next_command": "npm run full-engine-audit",
"affected_artifacts": [
"Temp/operational_report.json"
]
}
@@ -0,0 +1,34 @@
{
"formula_id": "CLEAN_TEMP_ARTIFACTS_V1",
"generated_at": "2026-06-06T18:09:26.966821+00:00",
"dry_run": false,
"budget": 300,
"candidates": [
"release_gate_summary_v1.json",
"release_gate_summary_v2.json",
"release_gate_summary_v3.json"
],
"candidate_count": 3,
"archived_count": 3,
"archived": [
{
"source": "C:\\Temp\\data_feed\\Temp\\release_gate_summary_v1.json",
"archive": "C:\\Temp\\data_feed\\artifacts\\archive\\20260606\\release_gate_summary_v1.json",
"sha256": "3f2f81bc2618944a2ec2f2bac71415189b7ab3376988c5c7725756d299a8d35d",
"size": 592
},
{
"source": "C:\\Temp\\data_feed\\Temp\\release_gate_summary_v2.json",
"archive": "C:\\Temp\\data_feed\\artifacts\\archive\\20260606\\release_gate_summary_v2.json",
"sha256": "9e45fd3a4549e19f0eb898c49ada437df7996edbdee5b733ad3133ebf42d61c1",
"size": 305
},
{
"source": "C:\\Temp\\data_feed\\Temp\\release_gate_summary_v3.json",
"archive": "C:\\Temp\\data_feed\\artifacts\\archive\\20260606\\release_gate_summary_v3.json",
"sha256": "978704024d3c3ea9834d09c20558f5991f936b8fdd285c06328fba83a71b6ae8",
"size": 400
}
],
"gate": "PASS"
}
@@ -0,0 +1,11 @@
{
"formula_id": "CLEAN_TEMP_ARTIFACTS_V1",
"generated_at": "2026-06-07T11:58:42.260299+00:00",
"dry_run": true,
"budget": 300,
"candidates": [],
"candidate_count": 0,
"archived_count": 0,
"archived": [],
"gate": "PASS"
}
@@ -0,0 +1,399 @@
{
"formula_id": "CLEAN_TEMP_ARTIFACTS_V1",
"generated_at": "2026-06-06T17:59:57.792204+00:00",
"dry_run": true,
"budget": 300,
"candidates": [
"_smart_cash_recovery_v4_for_v5.json",
"_smart_cash_recovery_v5_for_v6.json",
"account_snapshot_contract_v1.json",
"active_artifact_manifest_v1.json",
"active_artifact_manifest_v2.json",
"algorithm_guidance_proof_v1.json",
"alpha_feedback_loop_v2.json",
"alpha_lead_threshold_optimizer_v1.json",
"alpha_lead_threshold_optimizer_v2.json",
"alpha_lead_threshold_optimizer_v3.json",
"anti_distribution_validation_v4.json",
"anti_late_chase_v5.json",
"anti_late_chase_v6.json",
"anti_late_entry_pullback_gate_v3.json",
"anti_late_entry_pullback_gate_v4.json",
"architecture_boundaries_v2.json",
"artifact_authority_audit_v1.json",
"artifact_chain_hash_v1.json",
"artifact_chain_hash_v2.json",
"artifact_chain_hash_v3.json",
"artifact_freshness_gate_v1.json",
"artifact_freshness_index_v1.json",
"artifact_retirement_plan_v1.json",
"audit_replay_snapshot_v1.json",
"authority_collision_audit_v1.json",
"authority_collision_audit_v2.json",
"authority_collision_audit_v3.json",
"blank_cell_audit_v1.json",
"build_bundle_cache_v1.json",
"build_bundle_runtime_profile_v1.json",
"build_formula_authority_matrix_v1.json",
"buy_anti_late_entry_lock_v1.json",
"calibration_change_ledger_v1.json",
"calibration_change_ledger_v2.json",
"calibration_change_ledger_v3.json",
"calibration_change_ledger_v4.json",
"calibration_priority_v1.json",
"calibration_registry_v1.json",
"calibration_registry_v2.json",
"canonical_artifact_resolver_v1.json",
"canonical_artifact_resolver_v1_validation.json",
"canonical_metrics_v1.json",
"canonical_metrics_v2.json",
"canonical_metrics_v3.json",
"canonical_metrics_v4.json",
"capital_style_allocation_v1.json",
"capital_style_allocation_validation_v1.json",
"capital_style_time_stop_v1.json",
"cash_raise_pareto_executor_v2.json",
"cash_raise_pareto_frontier_v1.json",
"cash_raise_pareto_frontier_v2.json",
"cash_raise_pareto_frontier_v3.json",
"cash_raise_value_optimizer_v3.json",
"cash_raise_value_preservation_v1.json",
"cash_raise_value_preservation_v8.json",
"cash_recovery_optimizer_v4.json",
"cash_sell_pareto_executor_v1.json",
"cashflow_quality_signal_v1.json",
"change_request_validation_v1.json",
"ci_formula_parity_result_v1.json",
"ci_formula_parity_result_v2.json",
"compact_bundle_equivalence_v1.json",
"completion_gap_v1.json",
"computed_harness.json",
"confidence_calibration_v2.json",
"continuous_evaluation_dashboard_v1.json",
"continuous_evaluation_dashboard_v2.json",
"continuous_evaluation_dashboard_v3.json",
"cross_section_consistency_v1.json",
"data_freshness_event_sync_v2.json",
"data_freshness_sla_v1.json",
"data_integrity_100_lock_v1.json",
"data_integrity_100_lock_v2.json",
"data_integrity_100_lock_v3.json",
"data_integrity_100_lock_v4.json",
"data_integrity_100_lock_v5.json",
"data_integrity_score_v1.json",
"data_maturity_truth_gate_v1.json",
"data_quality_gate_v2_py.json",
"data_quality_gate_v3.json",
"data_quality_reconciliation_v1.json",
"data_quality_reconciliation_v2.json",
"decision_critical_golden_coverage_v1.json",
"decision_evidence_score_v1.json",
"decision_evidence_score_v2.json",
"decision_replay_snapshot_pack_v1.json",
"decision_trace_lock_v1.json",
"decision_trace_replay_v1.json",
"deprecated_artifact_read_v1.json",
"derivation_validity_score_v1.json",
"distribution_block_effectiveness_v1.json",
"distribution_exit_presignal_v2.json",
"distribution_risk_score_v2.json",
"distribution_risk_score_v3.json",
"distribution_risk_score_v4.json",
"docs_rule_duplication_v1.json",
"dynamic_value_preservation_sell_v6.json",
"earnings_quality_signal_v1.json",
"ejce_divergence_audit_v1.json",
"ejce_view_renderer_v1.json",
"engine_audit_v1.json",
"engine_harness_gate_result.json",
"engine_harness_gate_result_v2.json",
"engine_harness_gate_result_v3.json",
"entry_freshness_gate_v2.json",
"entry_freshness_gate_v3.json",
"entry_freshness_gate_v4.json",
"evaluation_history_coverage_v1.json",
"execution_authority_matrix_v1.json",
"execution_authority_matrix_v2.json",
"execution_cost_model_v1.json",
"execution_integrity_gate_v1.json",
"execution_method_ladder_v1.json",
"execution_precedence_lock_v2.json",
"execution_quality_harness_v1.json",
"execution_readiness_matrix_v1.json",
"factor_contract_v1.json",
"factor_lifecycle_v1.json",
"factor_taxonomy_v1.json",
"failure_triage_v1.json",
"final_context_for_llm_v1.json",
"final_context_for_llm_v1_validation.json",
"final_context_for_llm_v2.json",
"final_context_for_llm_v3.json",
"final_context_for_llm_v4.json",
"final_decision_packet_v1.json",
"final_decision_packet_v2.json",
"final_decision_packet_v3.json",
"final_decision_packet_v4.json",
"final_execution_decision_v1.json",
"final_execution_decision_v2.json",
"final_execution_decision_v3.json",
"final_execution_decision_v4.json",
"final_judgment_gate_v1.json",
"formula_behavioral_coverage_summary_v1.json",
"formula_behavioral_coverage_v1.json",
"formula_behavioral_coverage_v3.json",
"formula_compile_report_v1.json",
"formula_coverage_authority_v2.json",
"formula_coverage_authority_v3.json",
"formula_coverage_authority_v4.json",
"formula_dependency_graph_v1.json",
"formula_gas_parity_v1.json",
"formula_owner_coverage_v1.json",
"formula_parity_v3.json",
"formula_registry_sync_v1.json",
"formula_registry_v2_validation.json",
"formula_runtime_registry_v1.json",
"formula_runtime_registry_v2.json",
"formula_version_lifecycle_v1.json",
"fundamental_evidence_lock_v1.json",
"fundamental_evidence_lock_v2.json",
"fundamental_evidence_lock_v3.json",
"fundamental_evidence_lock_v4.json",
"fundamental_horizon_gate_v2.json",
"fundamental_multifactor_v3.json",
"fundamental_multifactor_v4.json",
"fundamental_raw.json",
"fundamental_raw_evidence_v3.json",
"fundamental_raw_evidence_v4.json",
"fundamental_raw_v1.json",
"fundamental_raw_v2.json",
"fundamental_source_audit_v1.json",
"gas_business_logic_audit_v1.json",
"gate_conflict_audit_v1.json",
"global_execution_gate_v2.json",
"goal_risk_budget_harness_v1.json",
"goal_risk_budget_harness_v2.json",
"goal_risk_budget_harness_v3.json",
"governance_calendar_gate_v1.json",
"governance_calendar_gate_v2.json",
"governance_calendar_gate_v3.json",
"governance_calendar_gate_v4.json",
"growth_rate_signal_v1.json",
"gs_native_coverage_lock_v1.json",
"harness_coverage_audit.json",
"harness_gate_result.json",
"honest_performance_guard_v1.json",
"horizon_allocation_guard_v2.json",
"horizon_classification_v1.json",
"horizon_rebalance_before_after_v1.json",
"horizon_rebalance_before_after_v2.json",
"horizon_rebalance_before_after_v3.json",
"horizon_rebalance_plan_v1.json",
"horizon_router_v1.json",
"horizon_routing_lock_v2.json",
"horizon_routing_lock_v3.json",
"horizon_routing_lock_v4.json",
"horizon_routing_lock_v5.json",
"horizon_routing_lock_v6.json",
"hts_ready_unlock_audit_v1.json",
"hts_sell_blueprint_v2.json",
"imputed_data_exposure_gate_v2.json",
"late_chase_attribution_v1.json",
"late_chase_attribution_v2.json",
"late_chase_attribution_v3.json",
"late_chase_attribution_v4.json",
"late_rebound_bucket_score_v1.json",
"liquidity_execution_quality_v1.json",
"liquidity_execution_quality_v2.json",
"liquidity_flow_signal_v1.json",
"live_replay_separation_v2.json",
"live_trade_outcome_ledger_v1.json",
"live_vs_replay_performance_audit_v1.json",
"live_vs_replay_performance_audit_v2.json",
"llm_freedom_v1.json",
"llm_narrative_template_lock_v1.json",
"llm_narrative_template_lock_v2.json",
"llm_render_contract_v2.json",
"llm_response_validation_v1.json",
"llm_response_validation_v2.json",
"llm_response_validation_v3.json",
"llm_response_validation_v4.json",
"low_capability_response_contract_runtime_v1.json",
"low_n_pass_gate_v1.json",
"macro_event_synchronizer_v2.json",
"macro_event_synchronizer_v3.json",
"macro_event_ticker_impact_v1.json",
"market_share_signal_v2.json",
"metric_definition_registry_v2.json",
"missing_field_patch_plan_v1.json",
"missing_field_patch_plan_v2.json",
"missing_field_patch_plan_v3.json",
"no_temp_runtime_read_v1.json",
"number_provenance_audit_v1.json",
"number_provenance_audit_v2.json",
"number_provenance_audit_v3.json",
"number_provenance_ledger_v4.json",
"number_provenance_strict_v2.json",
"number_provenance_strict_v3.json",
"operational_alpha_calibration_v2.json",
"operational_eval_queue_v1.json",
"operational_evidence_audit_v1.json",
"operational_outcome_lock_v1.json",
"operational_outcome_lock_v2.json",
"operational_outcome_lock_v3.json",
"operational_outcome_lock_v4.json",
"operational_t20_outcome_ledger_v1.json",
"operational_truth_score_v1.json",
"order_blueprint_consistency_v1.json",
"order_blueprint_consistency_v2.json",
"order_blueprint_v2.json",
"order_blueprint_v3.json",
"order_form_strict_lock_v2.json",
"order_math_reconciliation_v1.json",
"order_math_reconciliation_v2.json",
"order_math_reconciliation_v3.json",
"order_math_reconciliation_v4.json",
"outcome_ledger_v1.json",
"outcome_ledger_v2.json",
"outcome_quality_score_v1.json",
"output_field_owner_collision_v1.json",
"pass_100_authority_lock_v1.json",
"pass_100_criteria_v1.json",
"pass_100_criteria_v2.json",
"pass_100_criteria_v3.json",
"pass_100_criteria_v4.json",
"pass_100_honest_v1.json",
"perf_recovery_harness_v1.json",
"performance_monitoring_dashboard_v1.json",
"performance_readiness_replay_bridge_v1.json",
"performance_readiness_replay_bridge_v2.json",
"performance_readiness_unlock_v1.json",
"phase_checks_50_60.json",
"pipeline_runtime_profile_history_v1.json",
"pipeline_runtime_profile_v1.json",
"portfolio_alpha_confidence_per_ticker_v1.json",
"pre_distribution_early_warning_v3.json",
"prediction_accuracy_harness_v2.json",
"prediction_accuracy_harness_v3.json",
"prediction_accuracy_harness_v4.json",
"prediction_accuracy_harness_v5.json",
"prediction_improvement_harness.json",
"prediction_lift_dashboard_v1.json",
"predictive_alpha_calibration_v2.json",
"predictive_alpha_calibration_v3.json",
"predictive_alpha_engine_v2.json",
"predictive_alpha_report_lock_v2.json",
"prompt_formula_leak_audit_v1.json",
"proposal_evaluation_history.json",
"quant_engine_hardening_todo_schema_v1.json",
"quant_engine_hardening_todo_v1.json",
"ratchet_trailing_general_v1.json",
"realized_performance_v1.json",
"rebound_sell_calibration_v2.json",
"rebound_sell_efficiency_v1.json",
"rebound_wait_order_plan_v1.json",
"rebound_wait_order_plan_v2.json",
"refactor_baseline_inventory_v1.json",
"refactor_baseline_metrics_v1.json",
"relative_underperformance_alert_v1.json",
"release_ci_gate_v2.json",
"release_dag_v1.json",
"release_gate_sequence_v1.json",
"release_gate_summary_v1.json",
"release_gate_summary_v2.json",
"release_gate_summary_v3.json",
"renderer_no_calculation_v1.json",
"replay_live_separation_v1.json",
"repo_hygiene_report.json",
"report_authority_diff_v1.json",
"report_authority_diff_v2.json",
"report_order_lock_v1.json",
"report_order_lock_v2.json",
"report_render_integrity_v1.json",
"request_result_adoption_v1.json",
"root_cause_attribution_v1.json",
"root_cause_recovery_plan_v1.json",
"routing_execution_log_table_v1.json",
"routing_execution_log_v1.json",
"routing_serving_authority_v1.json",
"routing_serving_trace_v2.json",
"routing_serving_trace_v3.json",
"rule_lifecycle_governance_v4.json",
"rule_lifecycle_policy.json",
"rule_lifecycle_policy_v2.json",
"rule_lifecycle_policy_v3.json",
"rule_lifecycle_strict_v1.json",
"schema_model_generation_v1.json",
"scores_harness_v1.json",
"sell_engine_audit_v1.json",
"sell_execution_timing_lock_v2.json",
"sell_price_sanity_v2.json",
"sell_price_sanity_v3.json",
"sell_waterfall_engine_v2.json",
"sell_waterfall_engine_v3.json",
"semantic_formula_coverage_v1.json",
"shadow_ledger_v1.json",
"short_horizon_outcome_monitor_v1.json",
"single_truth_ledger_v1.json",
"single_truth_ledger_v2.json",
"single_truth_ledger_v3.json",
"smart_cash_recovery_v3.json",
"smart_cash_recovery_v4.json",
"smart_cash_recovery_v5.json",
"smart_cash_recovery_v6.json",
"smart_cash_recovery_v7.json",
"smart_cash_recovery_v7_authoritative.json",
"smart_cash_recovery_v8.json",
"smart_cash_recovery_v9.json",
"smart_money_flow_signal_v2.json",
"smart_money_liquidity_composite_v2.json",
"smart_money_liquidity_composite_v3.json",
"smart_money_liquidity_evidence_gate_v2.json",
"smart_money_liquidity_evidence_gate_v3.json",
"smart_money_liquidity_evidence_gate_v4.json",
"smart_money_liquidity_evidence_gate_v5.json",
"smart_money_liquidity_gate_v1.json",
"smart_money_liquidity_gate_v2.json",
"smart_money_liquidity_microstructure_v4.json",
"smart_money_liquidity_outcome_link_v1.json",
"source_authority_collapse_v1.json",
"source_freshness_manifest_v1.json",
"source_freshness_manifest_v2.json",
"strategy_decision_result_v3.json",
"strategy_execution_locks_regression_result.json",
"strategy_hardening_harness_v1.json",
"strategy_hardening_harness_v2.json",
"strategy_harness_score.json",
"strategy_harness_v2.json",
"strategy_release_stage_v1.json",
"strategy_routing_audit_v1.json",
"tail_risk_guard_v1.json",
"tail_risk_guard_v2.json",
"tool_thin_wrapper_v1.json",
"trade_quality_from_t5_v1.json",
"truth_reconciliation_gate_v1.json",
"truthful_decision_ledger_v2.json",
"truthful_decision_ledger_v3.json",
"truthful_decision_ledger_v4.json",
"truthfulness_guard_v1.json",
"twap_child_order_blueprint_v1.json",
"twap_child_order_blueprint_v2.json",
"unified_route_packet_v1.json",
"vacuous_pass_audit_v1.json",
"value_preservation_scorer_v1.json",
"value_preservation_scorer_v2.json",
"value_preserving_cash_raise_optimizer_v9.json",
"value_preserving_cash_raise_v1.json",
"verdict_consistency_lock_v1.json",
"walk_forward_calibration_v1.json",
"walk_forward_performance_v1.json",
"walk_forward_performance_v2.json",
"yaml_code_coverage_full.json",
"yaml_code_coverage_v1.json",
"yaml_gs_ps_coverage.json",
"yaml_gs_ps_coverage_v2.json",
"yaml_gs_py_xref_matrix_v2.json"
],
"candidate_count": 388,
"archived_count": 0,
"gate": "PASS"
}
@@ -0,0 +1,23 @@
{
"formula_id": "ALPHA_LEAD_THRESHOLD_OPTIMIZER_V3",
"gate": "PASS",
"prediction_match_rate_pct": 54.76,
"t5_direction_accuracy_pct": 54.76,
"late_chase_false_positive_rate": 20.0,
"buy_after_5d_runup_without_pullback_count": 0,
"threshold_ledger": [
{
"threshold": 75,
"pullback_quality": 60,
"distribution_score": 2.0,
"action": "PILOT_ALLOWED"
},
{
"threshold": 65,
"pullback_quality": 50,
"distribution_score": 1.5,
"action": "CANDIDATE_ONLY"
}
],
"generated_at": "2026-06-03T09:08:54.180836+00:00"
}
@@ -0,0 +1,33 @@
{
"formula_id": "ANTI_LATE_ENTRY_PULLBACK_GATE_V4",
"gate": "WATCH",
"late_chase_buy_violations": 0,
"late_chase_false_positive_rate": 33.65,
"buy_after_5d_runup_without_pullback_count": 0,
"pullback_quality_required_for_buy": 60,
"distribution_score_for_buy": 1.5,
"prediction_match_rate_pct": 54.76,
"t5_direction_accuracy_pct": 54.76,
"late_chase_operational_samples": 263,
"late_chase_gate_hit_miss_rate_published": true,
"threshold_ledger": [
{
"threshold": 75,
"pullback_quality": 60,
"distribution_score": 2.0,
"action": "PILOT_ALLOWED"
},
{
"threshold": 65,
"pullback_quality": 50,
"distribution_score": 1.5,
"action": "CANDIDATE_ONLY"
}
],
"supporting_artifacts": [
"Temp/alpha_lead_threshold_optimizer_v3.json",
"Temp/buy_anti_late_entry_lock_v1.json",
"Temp/late_chase_attribution_v1.json"
],
"generated_at": "2026-06-03T15:58:27.382232+00:00"
}
@@ -0,0 +1,24 @@
{
"formula_id": "CANONICAL_METRICS_V4",
"generated_at": "2026-05-31T23:23:43+09:00",
"builder_version": "v5.todo.batch",
"canonical_metrics": [
{
"metric_id": "global_execution_gate",
"canonical_json_path": "Temp/final_execution_decision_v3.json",
"value": "AUDIT_ONLY"
},
{
"metric_id": "schema_presence_score",
"canonical_json_path": "Temp/data_integrity_100_lock_v5.json",
"value": 95.5
},
{
"metric_id": "pass_100_gate",
"canonical_json_path": "Temp/pass_100_criteria_v1.json",
"value": "BLOCK_EXECUTION"
}
],
"authority_collision_count": 0,
"stale_artifact_count": 0
}
@@ -0,0 +1,8 @@
{
"formula_id": "DISTRIBUTION_RISK_SCORE_V4",
"generated_at": "2026-05-31T23:23:43+09:00",
"builder_version": "v5.todo.batch",
"distribution_risk_coverage_pct": 100.0,
"false_chase_entry_rate_max_pct": 20.0,
"high_risk_count": 0
}
@@ -0,0 +1,36 @@
{
"formula_id": "FINAL_EXECUTION_DECISION_V4",
"global_execution_gate": "AUDIT_ONLY",
"buy_allowed": false,
"sell_allowed": false,
"hts_order_count": 0,
"child_engine_internal_allowed": true,
"child_execution_state": "THEORETICAL_ONLY",
"precedence_note": "child_engine_internal_allowed=True는 현금회복 계산이 내부적으로 허용됨을 의미한다. HTS 주문 실행은 global_execution_gate==HTS_READY일 때만 허용된다.",
"reason_codes": [
"TRUTH_GATE=BLOCK_EXECUTION",
"TRUTH_SCORE=70.00",
"READINESS_GATE=WATCH_PENDING_SAMPLE",
"EXECUTION_READINESS_MATRIX=BLOCK_EXECUTION:0.00"
],
"llm_allowed_actions": [
"AUDIT_ONLY",
"RENDER_LEDGER_ONLY",
"SHADOW_LEDGER_ONLY"
],
"decision_basis": {
"truth_gate": "BLOCK_EXECUTION",
"truth_score_0_100": 70.0,
"final_judgment_gate": "PASS",
"final_judgment_coverage_pct": 100.0,
"smart_cash_recovery_status": "PASS",
"smart_cash_recovery_child_engine_internal_allowed": true,
"smart_cash_recovery_value_damage_pct": 0.0,
"readiness_gate": "WATCH_PENDING_SAMPLE",
"execution_readiness_matrix_gate": "BLOCK_EXECUTION",
"execution_readiness_min_axis_score": 0.0,
"hts_candidate_rows": 0
},
"generated_at": "2026-06-06T11:52:58.416423+00:00",
"input_hash": "8af12b88c7f8b3d3803cf447d309394aa83ddd3576f9427282992cb9e9e757d9"
}
@@ -0,0 +1,180 @@
{
"formula_id": "PASS_100_CRITERIA_V3",
"is_active": true,
"authority_note": "유일한 active PASS_100 기준. v1/v2는 legacy_reference_only.",
"legacy_files": [
"pass_100_criteria_v2.json"
],
"gate": "BLOCK_EXECUTION",
"pass_100_allowed": false,
"score_0_100": 46.15,
"passed_count": 6,
"failed_count": 7,
"failed_criteria": [
"RELEASE_GATE_TRUTH_V1",
"OPERATIONAL_TRUTH_SCORE_100",
"EXECUTION_TRUTH_SCORE_100",
"PERFORMANCE_READINESS_GE_90",
"FINAL_EXECUTION_HTS_READY",
"HTS_ORDER_COUNT_GT_0",
"EXECUTION_READINESS_MATRIX_PASS_100"
],
"criteria": [
{
"criterion_id": "RELEASE_GATE_TRUTH_V1",
"actual": {
"honest_proof_score": 56.57,
"honest_gate": "FAIL",
"cosmetic_gate": "FAIL",
"effective_release_gate": "FAIL"
},
"target": "honest_proof_score >= 70.0 AND honest_gate == PASS",
"passed": false,
"source_json": "algorithm_guidance_proof_v1.json",
"formula_id": "RELEASE_GATE_TRUTH_V1",
"remediation": "honest_proof_score < 70 → T+20 표본 30건 축적 후 자동 해소 (~2026-07)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "SCHEMA_PRESENCE_100",
"actual": 100.0,
"target": "== 100",
"passed": true,
"source_json": "data_quality_reconciliation_v1.json",
"formula_id": "DATA_QUALITY_RECONCILIATION_V1",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "FORMULA_RUNTIME_COVERAGE_100",
"actual": 100.0,
"target": "== 100",
"passed": true,
"source_json": "strategy_hardening_harness_v2.json",
"formula_id": "STRATEGY_HARDENING_HARNESS_V2",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "CONFIDENCE_CAP_BASIS_GE_90",
"actual": 100.0,
"target": ">= 90",
"passed": true,
"source_json": "data_quality_reconciliation_v1.json",
"formula_id": "DATA_QUALITY_RECONCILIATION_V1",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "SINGLE_TRUTH_NO_CONFLICT",
"actual": 0,
"target": "== 0",
"passed": true,
"source_json": "single_truth_ledger_v2.json",
"formula_id": "SINGLE_TRUTH_LEDGER_V2",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "OPERATIONAL_TRUTH_SCORE_100",
"actual": 70.0,
"target": "== 100 and gate PASS_100",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "export_gate 해소(HTS 캡처) + performance_readiness 해소 필요",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "EXECUTION_TRUTH_SCORE_100",
"actual": 0.0,
"target": "== 100",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "HTS 캡처 → export_gate PASS → execution_truth 자동 해소",
"remediation_type": "OPERATIONAL_ACTION"
},
{
"criterion_id": "PERFORMANCE_READINESS_GE_90",
"actual": 50.0,
"target": ">= 90 and readiness_gate PERFORMANCE_READY",
"passed": false,
"source_json": "operational_truth_score_v1.json",
"formula_id": "OPERATIONAL_TRUTH_SCORE_V1",
"remediation": "T+20 운영 표본 30건 필요 — 매일 자동 축적 (~2026-07-05~07-15)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "SMART_CASH_VALUE_DAMAGE_LE_10",
"actual": {
"value_damage_adj": 0.0,
"value_damage_raw": 0.0,
"source": "smart_cash_recovery_v7.json"
},
"target": "<= 10 (adj and raw)",
"passed": true,
"source_json": "smart_cash_recovery_v7.json",
"formula_id": "SMART_CASH_RECOVERY_V7",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "SMART_CASH_EXECUTION_ALLOWED",
"actual": true,
"target": "is true and status PASS",
"passed": true,
"source_json": "smart_cash_recovery_v7.json",
"formula_id": "SMART_CASH_RECOVERY_V7",
"remediation": "NONE",
"remediation_type": "NONE"
},
{
"criterion_id": "FINAL_EXECUTION_HTS_READY",
"actual": "AUDIT_ONLY",
"target": "HTS_READY",
"passed": false,
"source_json": "final_execution_decision_v2.json",
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"remediation": "PERFORMANCE_READINESS_GE_90 해소 후 자동 연쇄 해소 (~2026-07-15)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "HTS_ORDER_COUNT_GT_0",
"actual": 0,
"target": "> 0",
"passed": false,
"source_json": "final_execution_decision_v2.json",
"formula_id": "FINAL_EXECUTION_DECISION_V2",
"remediation": "FINAL_EXECUTION_HTS_READY 해소 후 자동 연쇄 해소 (~2026-07-15)",
"remediation_type": "DATA_GATED",
"data_gated_until": "2026-07-15"
},
{
"criterion_id": "EXECUTION_READINESS_MATRIX_PASS_100",
"actual": {
"gate": "BLOCK_EXECUTION",
"min_axis_score": 0.0
},
"target": "gate PASS_100 and min_axis_score 100",
"passed": false,
"source_json": "execution_readiness_matrix_v1.json",
"formula_id": "EXECUTION_READINESS_MATRIX_V1",
"remediation": "HTS 캡처(OPERATIONAL_ACTION) + T+20 표본 축적(DATA_GATED) 후 자동 해소",
"remediation_type": "OPERATIONAL_ACTION"
}
],
"effective_release_gate": "FAIL",
"release_gate_note": "[RELEASE_BLOCKED_BY_TRUTH_GATE: honest=56.57 < 70]",
"hts_order_mode": "THEORETICAL_ONLY",
"targets": {
"completion_definition": "PASS_100 only when every criterion passed",
"hts_execution_definition": "HTS execution forbidden unless FINAL_EXECUTION_HTS_READY and HTS_ORDER_COUNT_GT_0 pass",
"llm_role": "copy criteria and render ledgers only; no ad-hoc numeric overrides",
"release_gate_truth_definition": "effective_release_gate = AND(cosmetic_gate, honest_gate); honest_proof_score < 70 → THEORETICAL_ONLY"
}
}
@@ -0,0 +1,20 @@
{
"formula_id": "PREDICTION_ACCURACY_HARNESS_V5",
"generated_at": "2026-06-03T00:00:00+09:00",
"builder_version": "v5.todo.batch.p0-3",
"t20_operational_sample": 0,
"t20_operational_accuracy_pct": null,
"performance_readiness_score": 50.0,
"prediction_match_rate_pct": 47.28,
"operational_t5_sample_count": 0,
"operational_t20_sample_count": 0,
"data_origin_audit": {
"operational_sample_count": 0,
"replay_sample_count": 804,
"untagged_row_count": 0,
"unrealized_outcome_row_count": 0,
"replay_in_live_stats": 0,
"operational_only_accuracy": true,
"untagged_label": "INSUFFICIENT_OP_SAMPLES(n=0)"
}
}
@@ -0,0 +1,37 @@
{
"formula_id": "VALUE_PRESERVING_CASH_RAISE_V9",
"generated_at": "2026-06-03T13:32:06.415328+00:00",
"cash_shortfall_min_krw": 36092555.0,
"raw_value_damage_pct_avg": 15.7,
"adjusted_value_damage_pct_avg": 0.0,
"value_damage_gate_input": 15.7,
"rebound_capture_probability": 0.0,
"selected_sell_combo": [
{
"ticker": "064350",
"source": "BREACH_FULL_LIQUIDATION",
"expected_immediate_krw": 57841575
}
],
"liquidation_policy_check": {
"breach_full_liquidation_count": 1,
"breach_violations": [
{
"ticker": "064350",
"issue": "LP002_BREACH_FULL_LIQUIDATION_BAN",
"note": "oversold 또는 brt_verdict!=BROKEN — K2 50/50 적용 필요"
}
],
"k2_split_applied": [],
"single_stock_max_concentration_pct": 100.0,
"value_damage_cap_pass": false,
"rebound_capture_probability": 0.0,
"lp004_blocked": true,
"lp002_blocked": true
},
"gate": "FAIL",
"gate_failures": [
"LP004_VALUE_DAMAGE_CAP_EXCEEDED",
"LP002_BREACH_FULL_LIQUIDATION_DETECTED"
]
}
@@ -0,0 +1,8 @@
{
"formula_id": "SMART_MONEY_LIQUIDITY_EVIDENCE_GATE_V5",
"generated_at": "2026-05-31T23:23:43+09:00",
"builder_version": "v5.todo.batch",
"gate": "OK",
"ticker_count": 11,
"freshness_sla_breaches": 0
}
+66
View File
@@ -0,0 +1,66 @@
schema_version: 2026-06-06-canonical-manifest-v1
source_spec: spec/32_canonical_artifact_resolver.yaml
generated_from: Temp/canonical_artifact_resolver_v1.json
concepts:
smart_cash_recovery:
canonical_path: artifacts/canonical/smart_cash_recovery_v9.json
source_file: Temp/smart_cash_recovery_v9.json
deprecated_files:
- artifacts/archive/2026-06-06/smart_cash_recovery_v8.json
- artifacts/archive/2026-06-06/smart_cash_recovery_v7.json
- artifacts/archive/2026-06-06/smart_cash_recovery_v6.json
- artifacts/archive/2026-06-06/smart_cash_recovery_v5.json
- artifacts/archive/2026-06-06/smart_cash_recovery_v4.json
- artifacts/archive/2026-06-06/smart_cash_recovery_v3.json
distribution_risk_score:
canonical_path: artifacts/canonical/distribution_risk_score_v4.json
source_file: Temp/distribution_risk_score_v4.json
deprecated_files:
- artifacts/archive/2026-06-06/distribution_risk_score_v3.json
- artifacts/archive/2026-06-06/distribution_risk_score_v2.json
final_execution_decision:
canonical_path: artifacts/canonical/final_execution_decision_v4.json
source_file: Temp/final_execution_decision_v4.json
deprecated_files:
- artifacts/archive/2026-06-06/final_execution_decision_v3.json
- artifacts/archive/2026-06-06/final_execution_decision_v2.json
- artifacts/archive/2026-06-06/final_execution_decision_v1.json
alpha_lead_threshold_optimizer:
canonical_path: artifacts/canonical/alpha_lead_threshold_optimizer_v3.json
source_file: Temp/alpha_lead_threshold_optimizer_v3.json
deprecated_files:
- artifacts/archive/2026-06-06/alpha_lead_threshold_optimizer_v2.json
- artifacts/archive/2026-06-06/alpha_lead_threshold_optimizer_v1.json
pass_100_criteria:
canonical_path: artifacts/canonical/pass_100_criteria_v3.json
source_file: Temp/pass_100_criteria_v3.json
deprecated_files:
- artifacts/archive/2026-06-06/pass_100_criteria_v2.json
- artifacts/archive/2026-06-06/pass_100_criteria_v1.json
prediction_accuracy_harness:
canonical_path: artifacts/canonical/prediction_accuracy_harness_v5.json
source_file: Temp/prediction_accuracy_harness_v5.json
deprecated_files:
- artifacts/archive/2026-06-06/prediction_accuracy_harness_v4.json
- artifacts/archive/2026-06-06/prediction_accuracy_harness_v3.json
- artifacts/archive/2026-06-06/prediction_accuracy_harness_v2.json
smart_money_liquidity_evidence_gate:
canonical_path: artifacts/canonical/smart_money_liquidity_evidence_gate_v5.json
source_file: Temp/smart_money_liquidity_evidence_gate_v5.json
deprecated_files:
- artifacts/archive/2026-06-06/smart_money_liquidity_evidence_gate_v4.json
- artifacts/archive/2026-06-06/smart_money_liquidity_evidence_gate_v3.json
- artifacts/archive/2026-06-06/smart_money_liquidity_evidence_gate_v2.json
canonical_metrics:
canonical_path: artifacts/canonical/canonical_metrics_v4.json
source_file: Temp/canonical_metrics_v4.json
deprecated_files:
- artifacts/archive/2026-06-06/canonical_metrics_v3.json
- artifacts/archive/2026-06-06/canonical_metrics_v2.json
- artifacts/archive/2026-06-06/canonical_metrics_v1.json
anti_late_entry_pullback_gate:
canonical_path: artifacts/canonical/anti_late_entry_pullback_gate_v4.json
source_file: Temp/anti_late_entry_pullback_gate_v4.json
deprecated_files:
- artifacts/archive/2026-06-06/anti_late_entry_pullback_gate_v3.json
@@ -0,0 +1,18 @@
# ADR-0001 Single Source of Truth
## Context
The engine has multiple JSON versions for the same concept, which creates stale reads and conflicting runtime authority.
## Decision
Use `canonical_manifest.yaml` and the canonical artifact resolver as the runtime authority for each concept.
## Consequence
Runtime readers must use one canonical path per concept. Deprecated files remain archived only.
## Rollback
If a canonical artifact is invalid, rebuild the canonical copy rather than falling back to a deprecated runtime source.
+18
View File
@@ -0,0 +1,18 @@
# ADR-0002 GAS Thin Adapter
## Context
GAS has accumulated business logic that belongs in Python validation and builders.
## Decision
Constrain GAS to collection, normalization, export, and display responsibilities.
## Consequence
Decision, sizing, stop-loss, take-profit, and risk calculations must live in deterministic Python builders.
## Rollback
If a GAS function becomes a business-logic dependency, move the logic into Python and keep only a thin adapter in GAS.
@@ -0,0 +1,18 @@
# ADR-0003 No LLM Numeric Generation
## Context
LLM-generated prices, quantities, and thresholds caused authority collisions and provenance loss.
## Decision
LLM outputs must be copy-only. All numeric values must come from harness or canonical artifacts.
## Consequence
Prompt and report contracts must render numbers only when a source artifact already provides them.
## Rollback
There is no rollback path that reintroduces free-form numeric generation. That would violate the operating contract.
+18
View File
@@ -0,0 +1,18 @@
# ADR-0004 Shadow Before Active
## Context
Replay and shadow artifacts often look complete before live T+20 sample quality is sufficient.
## Decision
Shadow or advisory formulas may be rendered, but only active formulas may drive live execution.
## Consequence
Live sample thresholds remain mandatory before promotion.
## Rollback
If active promotion is found without live qualification, demote the artifact and restore shadow-only status.
+35
View File
@@ -0,0 +1,35 @@
meta:
source: AGENTS.md
version: 1
purpose: Extract top-level rules into canonical spec targets without changing AGENTS.md.
principles:
- rule_key: no_llm_numeric_generation
target_spec_path: spec/00_execution_contract.yaml
- rule_key: single_formula_registry_source
target_spec_path: spec/13_formula_registry.yaml
- rule_key: runtime_temp_direct_read_block
target_spec_path: spec/32_canonical_artifact_resolver.yaml
- rule_key: replay_live_separation
target_spec_path: spec/37_evaluation_dashboard_contract.yaml
- rule_key: live_t20_promotion_lock
target_spec_path: spec/35_rule_lifecycle_governance_v3.yaml
- rule_key: validation_failure_no_bypass
target_spec_path: spec/21_harness_governance_contract.yaml
- rule_key: data_missing_only
target_spec_path: spec/07_output_schema.yaml
- rule_key: gas_thin_adapter
target_spec_path: spec/34_architecture_boundaries.yaml
- rule_key: prompt_numeric_freeze
target_spec_path: spec/31_low_capability_llm_response_contract.yaml
- rule_key: single_execution_authority
target_spec_path: spec/33_execution_precedence_lock.yaml
- rule_key: hts_order_executable_only_after_gate
target_spec_path: spec/19_harness_contract.yaml
- rule_key: no_intraday_aggressive_buy_before_close
target_spec_path: spec/06_exit_policy.yaml
mapping_policy:
- "The original AGENTS.md is not edited by this proposal."
- "Each extracted rule must have a target_spec_path."
- "The principles list is intentionally capped at 12."
+13
View File
@@ -0,0 +1,13 @@
# Doctrine
This repository is a deterministic quant engine.
## Operating Rules
- Numbers are copied from canonical artifacts, not invented in the renderer.
- `Temp/` is a build/output layer, not a runtime authority.
- Replay metrics are informational unless explicitly labeled live and sample-qualified.
- Live T+20 sample counts below 30 cannot unlock `active` or `PASS_100`.
- GAS is an adapter layer, not a business-logic layer.
- Prompts are copy-only renderers and must not compute prices, quantities, or thresholds.
+23
View File
@@ -0,0 +1,23 @@
# AGENTS Constitution Proposal
This proposal extracts the top-level operating principles from `AGENTS.md` without changing the original file.
## Principles
1. No price, quantity, stop, take-profit, or score may be invented by the LLM.
2. Only registered formula IDs in `spec/13_formula_registry.yaml` may be cited.
3. `Temp/` files may not be used as runtime source unless explicitly designated as build output.
4. Canonical artifacts must be read through `canonical_manifest.yaml`.
5. Replay performance must never be presented as live performance.
6. Live T+20 sample counts below 30 may not be promoted to `active` or `PASS_100`.
7. Validation failures may not be bypassed with narrative explanations.
8. Missing files or missing provenance must be rendered as `DATA_MISSING`.
9. GAS may not gain new investment decision logic.
10. Prompts may not request the LLM to calculate prices, quantities, thresholds, or scores.
11. Failed harness states may not be rendered as executable order tables.
12. Final decision authority must come from a single canonical execution packet.
## Notes
- This document is a proposal only.
- The original `AGENTS.md` remains unchanged.
+8
View File
@@ -0,0 +1,8 @@
# Runbook
1. Build or refresh the JSON harness with `npm run prepare-json`.
2. Run strict validation gates before packaging.
3. Generate canonical artifacts through the builder scripts only.
4. Render reports from canonical data only.
5. Package upload artifacts only after the full gate passes or the output is explicitly audit-only.
+13
View File
@@ -0,0 +1,13 @@
case_id: EX_AVOID_001
type: AVOID
input_summary:
flow_ok: "N"
atr20_status: "DATA_MISSING"
total_score: 88
expected_output:
investment_action: "AVOID"
grade: "D"
reason: "Risk/data hard filters override high score."
triggered_rules:
- "Flow_OK=N이면 신규매수 금지"
- "ATR20 미확인 시 정수 매수수량 산출 금지"
+18
View File
@@ -0,0 +1,18 @@
case_id: EX_BUY_001
type: BUY
input_summary:
data_status: "all_required_fields_ok"
hard_filters: "pass"
flow_rows: 20
atr20_status: "OK"
total_heat_pct: 5.5
expected_edge: 2.1
expected_output:
investment_action: "BUY"
grade: "A"
required_order_fields: ["account", "ticker", "limit_price", "quantity", "stop_price", "stop_quantity", "take_profit_price", "take_profit_quantity"]
rule_ids_used:
- "risk_policy.master_prohibitions"
- "data_contract.data_completeness_gate"
- "position_sizing.volatility_targeting"
- "output_schema.buy_proposal_template.validation"
+4
View File
@@ -0,0 +1,4 @@
{"case_id":"EX_BUY_001","type":"BUY","expected_action":"BUY","required_rules":["HF001_DATA_MATRIX_REQUIRED","HF002_ATR20_REQUIRED_FOR_QUANTITY","HF006_BUY_ORDER_SET_REQUIRED"]}
{"case_id":"EX_SELL_001","type":"SELL","expected_action":"SELL","required_rules":["HF003_HOLDINGS_REQUIRED_FOR_SELL_QTY","spec/exit/stop_loss.yaml:stop_loss.relative_weakness_exit"]}
{"case_id":"EX_REJECT_001","type":"REJECT","expected_action":"AVOID","required_rules":["HF005_TOTAL_HEAT_HARD_BLOCK"]}
{"case_id":"EX_INSUFFICIENT_DATA_001","type":"INSUFFICIENT_DATA","expected_action":"INSUFFICIENT_DATA","required_rules":["HF001_DATA_MATRIX_REQUIRED","HF002_ATR20_REQUIRED_FOR_QUANTITY"]}
+143
View File
@@ -0,0 +1,143 @@
{
"schema_version": "2026-05-15-F6-compat-output",
"analysis_date": "2026-05-15",
"analysis_scope": {
"scope_type": "SINGLE_TICKER",
"tickers": ["005930"],
"accounts": ["일반계좌"]
},
"data_basis": {
"as_of": "2026-05-15 16:30 KST",
"timezone": "Asia/Seoul",
"sources": [
{
"name": "sample",
"type": "CALCULATED",
"status": "OK",
"note": "schema validation sample"
}
]
},
"capture_read_ledger": [
{
"source": "sample",
"account": "일반계좌",
"screen_type": "unknown",
"read_values": {},
"confidence": 0,
"applied_to_orders": false,
"read_status": "NOT_PROVIDED",
"next_source_to_check": "HTS 보유종목/현금 화면 캡처"
}
],
"portfolio_decision": {
"final_action": "WATCH",
"grade": "C",
"confidence_score": 50,
"rationale": "검증용 샘플. 실제 투자 판단 아님."
},
"scores": {
"quality_score": null,
"valuation_score": null,
"momentum_score": null,
"risk_score": 50,
"strategy_score": null,
"portfolio_fit_score": null,
"total_score": null,
"score_formula": "not_calculated",
"score_notes": ["검증 샘플이므로 점수 산출 생략"]
},
"position_sizing": {
"status": "NOT_APPLICABLE",
"recommended_position_size_pct": null,
"max_allowed_position_size_pct": null,
"risk_budget": null,
"atr20": null,
"calculated_quantity": null,
"final_quantity": null,
"reason": "WATCH 샘플로 수량 산출 대상 아님"
},
"risk_gate": {
"status": "CAUTION",
"cash_floor_status": "UNKNOWN",
"total_heat_pct": null,
"hard_stop_triggered": false,
"triggered_rules": []
},
"data_completeness_matrix": [
{
"ticker": "005930",
"name": "삼성전자",
"price_status": "OK",
"flow5d_status": "PARTIAL",
"flow20d_status": "DATA_MISSING",
"atr20_status": "DATA_MISSING",
"dart_status": "NOT_APPLICABLE",
"missing_fields": ["ATR20", "Flow20D"],
"next_source_to_check": "KRX/Naver/Yahoo 21거래일 OHLC 및 수급",
"allowed_action": "WATCH_ONLY"
}
],
"decision_trace": [
{
"state": "DATA_COMPLETENESS_CHECK",
"check_id": "DCC_SAMPLE_001",
"rule_ref": "spec/09_decision_flow.yaml:decision_flow.states.DATA_COMPLETENESS_CHECK",
"inputs_used": ["ticker", "ATR20", "Flow20D"],
"result": "INSUFFICIENT_DATA",
"selected_action": "WATCH",
"blocked_actions": ["BUY"],
"missing_inputs": ["ATR20", "Flow20D"],
"tie_breaker_applied": "5: 보수적 행동"
}
],
"orders": [],
"prohibited_calculations": [
{
"item": "buy_quantity",
"reason": "ATR20 DATA_MISSING",
"next_source_to_check": "21거래일 OHLC"
}
],
"triggered_rules": [
{
"file": "spec/00_execution_contract.yaml",
"path": "master_prohibitions.P2_no_atr_extrapolation",
"result": "BLOCK",
"explanation": "ATR20 미확인으로 정수 매수수량 산출 금지"
}
],
"missing_data": [
{
"field": "ATR20",
"status": "DATA_MISSING",
"next_source_to_check": "Yahoo/Naver OHLC"
}
],
"invalidation_conditions": [
{
"condition": "ATR20 remains DATA_MISSING",
"action": "NO_BUY_QUANTITY",
"rule_ref": "spec/00_execution_contract.yaml:master_prohibitions.P2_no_atr_extrapolation"
}
],
"evidence": [
{
"field": "ticker",
"value": "005930",
"source": "sample",
"as_of": "2026-05-15",
"data_tag": "계산값"
}
],
"rule_ids_used": ["P2_no_atr_extrapolation"],
"rules_used": [
{
"file": "spec/00_execution_contract.yaml",
"path": "master_prohibitions.P2_no_atr_extrapolation",
"result": "USED",
"explanation": "정수 매수수량 산출 금지 규칙 확인"
}
],
"summary": "검증용 WATCH 샘플. 실제 투자 판단이 아니다."
}
+11
View File
@@ -0,0 +1,11 @@
case_id: EX_HOLD_001
type: HOLD
input_summary:
current_holding_confirmed: true
grade: "B"
current_weight_within_band: true
expected_edge: 1.8
expected_output:
investment_action: "HOLD"
report_section: "current_holdings_analysis_report_template"
reason: "보유수량·평단·현재가 확인 후 유지 조건 충족."
+15
View File
@@ -0,0 +1,15 @@
case_id: EX_INSUFFICIENT_DATA_001
type: INSUFFICIENT_DATA
input_summary:
holdings_screen: "NOT_PROVIDED"
cash_available: "DATA_MISSING"
atr20_status: "DATA_MISSING"
expected_output:
investment_action: "INSUFFICIENT_DATA"
prohibited_outputs:
- "매도수량 숫자"
- "매수수량 숫자"
- "A등급"
next_source_to_check:
- "계좌 잔고 원장"
- "21거래일 OHLC for ATR20"
+14
View File
@@ -0,0 +1,14 @@
case_id: EX_REJECT_001
type: REJECT
input_summary:
strategy_score: 91
total_heat_pct: 10.4
cash_floor_status: "BLOCK"
atr20_status: "OK"
expected_output:
investment_action: "AVOID"
grade: "D"
reason: "Risk hard block overrides high strategy score."
triggered_rules:
- "HF005_TOTAL_HEAT_HARD_BLOCK"
- "spec/risk/aggregate_risk.yaml:risk_control.aggregate_risk_cap.threshold.hard_block"
+21
View File
@@ -0,0 +1,21 @@
case_id: EX_SELL_001
type: SELL
input_summary:
confirmed_holding_quantity: 120
price_status: "OK"
flow20d_status: "OK"
trend_status: "20일선 이탈"
risk_trigger: "relative_weakness_exit"
expected_output:
investment_action: "SELL"
required_fields:
- "account"
- "ticker"
- "limit_price_krw"
- "quantity"
- "rationale"
prohibited_if_missing:
- "confirmed_holding_quantity"
rule_ids_used:
- "HF003_HOLDINGS_REQUIRED_FOR_SELL_QTY"
- "spec/exit/stop_loss.yaml:stop_loss.relative_weakness_exit"
+378
View File
@@ -0,0 +1,378 @@
/**
* gas_apex_alpha_watch.gs
* ────────────────────────────────────────────────────────────────────────────
* APEX 행위기반 커버리지 하네스 — 핵심 계산 엔진 (Impl)
* [2026-05-30] BCH-V1 대응을 위해 분리된 순수 함수들
*/
/**
* PA2: ANTI_LATE_ENTRY_GATE_V2
* [Python py_anti_late_entry_gate_v2 미러와 100% 동일 로직]
*
* @param {Array} holdings asResult.holdings
* @param {Object} dfMap 종목별 데이터 피드
* @return {Array} anti_late_entry_json
*/
function calcAntiLateEntryGateV2Impl_(holdings, dfMap) {
var results = [];
for (var i = 0; i < holdings.length; i++) {
var h = holdings[i];
var ticker = h.ticker || '';
var df = dfMap[ticker] || {};
var close = Number(h.close || df.close || 0);
var prevClose = Number(df.prevClose || 0);
var ma20 = Number(df.ma20 || 0);
var rsi14 = Number(df.rsi14 != null ? df.rsi14 : 50);
var flowCredit = Number(df.flowCredit != null ? df.flowCredit : 0);
var volume = Number(df.volume || 0);
var avgVol5d = Number(df.avgVolume5d || 0);
var frg5d = Number(df.frg5d || 0);
var inst5d = Number(df.inst5d || 0);
var ret5d = Number(df.ret5d || 0);
var acGate = String(df.acGate || '');
var v1d = prevClose > 0 ? (close - prevClose) / prevClose * 100 : 0.0;
var v5d = ret5d;
var distWs = 0.0;
if (frg5d < 0) distWs += 2.0;
if (inst5d < 0) distWs += 2.0;
if (avgVol5d > 0 && volume > avgVol5d * 1.3) distWs += 1.5;
if (prevClose > 0 && close < prevClose) distWs += 1.5;
if (rsi14 > 70) distWs += 1.0;
if (acGate === 'BLOCK') distWs += 1.0;
var gate1 = 'PASS';
if (v1d >= 3.0) gate1 = 'BLOCK_CHASE';
else if (v1d >= 1.5) gate1 = 'PULLBACK_WAIT';
var gate2 = 'PASS';
if (v5d >= 8.0) gate2 = 'BLOCK_CHASE_5D';
else if (v5d >= 5.0) gate2 = 'PULLBACK_WAIT_5D';
var gate3 = 'PASS';
if (distWs >= 3.0) gate3 = 'BLOCK_DISTRIBUTION';
else if (distWs >= 2.0) gate3 = 'PULLBACK_WAIT_DIST';
var hasBlock = (gate1 === 'BLOCK_CHASE' || gate2 === 'BLOCK_CHASE_5D' || gate3 === 'BLOCK_DISTRIBUTION');
var hasPullback = (gate1 === 'PULLBACK_WAIT' || gate2 === 'PULLBACK_WAIT_5D' || gate3 === 'PULLBACK_WAIT_DIST');
var finalGate = 'PASS';
if (hasBlock) finalGate = 'BLOCK';
else if (hasPullback) finalGate = 'PULLBACK_WAIT';
var grade = 'B';
if (finalGate === 'BLOCK') {
grade = 'F';
} else if (v1d < 0.5 && ma20 > 0 && close >= ma20 && close <= ma20 * 1.02 && flowCredit >= 0.55) {
grade = 'A';
} else if (v1d < 1.5 && ma20 > 0 && Math.abs(close - ma20) / ma20 <= 0.05) {
grade = 'B';
} else if (finalGate === 'PULLBACK_WAIT') {
grade = 'C';
} else if (v5d > 5.0) {
grade = 'D';
}
results.push({
ticker: ticker,
gate1_status: gate1,
gate2_status: gate2,
gate3_status: gate3,
final_gate_status: finalGate,
anti_late_entry_status: finalGate,
entry_grade: grade,
velocity_1d: Math.round(v1d * 100) / 100,
velocity_5d: Math.round(v5d * 100) / 100,
dist_weighted_sum: Math.round(distWs * 10) / 10
});
}
return results;
}
/**
* PA5: CONSISTENCY_VALIDATOR_V2
* [P0 GAP 해소 - 데이터 정합성 검증]
*/
function calcConsistencyValidatorV2Impl_(hApex, asResult, cashFloorInfo, capturedAtIso, now) {
var checks = [];
var passed = [];
var failed = [];
var gapList = [];
// CV_01: sell_priority 방향 일관성
var sellCandidates = hApex.sell_candidates_json || [];
var tierOk = true;
for (var i = 1; i < sellCandidates.length; i++) {
if (sellCandidates[i].tier < sellCandidates[i-1].tier) {
tierOk = false;
break;
}
}
if (tierOk) passed.push('CV_01'); else failed.push({check_id: 'CV_01', reason: 'tier_reversal'});
// CV_02: 가격 순서 검증
var prices = hApex.prices_json || [];
var priceOk = true;
for (var i = 0; i < prices.length; i++) {
var p = prices[i];
if (p.stop_price && p.current_price && p.stop_price >= p.current_price) priceOk = false;
}
if (priceOk) passed.push('CV_02'); else failed.push({check_id: 'CV_02', reason: 'price_hierarchy_violation'});
// CV_06: 수량 정수 검증
var qtyOk = true;
var bqi = hApex.buy_qty_inputs_json || [];
for (var i = 0; i < bqi.length; i++) {
if (bqi[i].final_qty && bqi[i].final_qty % 1 !== 0) qtyOk = false;
}
if (qtyOk) passed.push('CV_06'); else failed.push({check_id: 'CV_06', reason: 'float_quantity'});
// CV_08: 현금 계산 경로
if (hApex.cash_ledger_basis === 'D2_ONLY') passed.push('CV_08');
else failed.push({check_id: 'CV_08', reason: 'invalid_cash_basis'});
// Score 계산
var score = Math.floor((passed.length / 12) * 100);
var status = score >= 90 ? (score === 100 ? 'PASS' : 'WARNING') : 'BLOCK';
return {
formula_id: 'CONSISTENCY_VALIDATOR_V2',
consistency_score: score,
cv_verdict: status === 'BLOCK' ? 'ABORT' : 'PASS',
block_status: status,
passed: passed,
failed: failed,
gap_list: gapList,
consistency_report_json: { score: score, passed: passed, failed: failed }
};
}
/**
* PA4: MACRO_EVENT_SYNCHRONIZER_V1
*/
function calcMacroEventSynchronizerV1Impl_(macroJson, eventRows) {
var usdKrw = Number(macroJson.usd_krw || 0);
var foreignSellDays = Number(macroJson.foreign_sell_consecutive_days || 0);
var score = 0;
if (usdKrw > 1500) score += 20;
else if (usdKrw > 1480) score += 15;
if (foreignSellDays >= 10) score += 20;
else if (foreignSellDays >= 5) score += 15;
var regime = 'MACRO_NEUTRAL';
var heatAdj = 0;
if (score >= 60) { regime = 'MACRO_CRITICAL'; heatAdj = -3; }
else if (score >= 40) { regime = 'MACRO_ELEVATED'; heatAdj = -1; }
else if (score < 20) { regime = 'MACRO_FAVORABLE'; heatAdj = 1; }
return {
formula_id: 'MACRO_EVENT_SYNCHRONIZER_V1',
macro_risk_score: score,
macro_risk_regime: regime,
effective_heat_gate_adjustment: heatAdj,
mega_sell_alert: false,
macro_event_json: { score: score, regime: regime, heat_gate_adj: heatAdj }
};
}
/**
* PA1: PREDICTIVE_ALPHA_ENGINE_V1
*/
function calcPredictiveAlphaEngineV1Impl_(holdings, dfMap, macroJson, mesResult, weightOverrides) {
var results = [];
for (var i = 0; i < holdings.length; i++) {
var h = holdings[i];
var ticker = h.ticker;
var df = dfMap[ticker] || {};
var thesis = 0;
if (df.close > df.ma20 && df.close < df.ma20 * 1.03) thesis += 20;
if (df.flowCredit >= 0.55) thesis += 20;
var antithesis = 0;
var v1d = df.prevClose > 0 ? (df.close - df.prevClose) / df.prevClose * 100 : 0;
if (v1d >= 3.0) antithesis += 25;
var confidence = thesis - antithesis;
var verdict = 'HOLD_NEUTRAL';
if (confidence >= 40) verdict = 'STRONG_BUY_SIGNAL';
else if (confidence >= 20) verdict = 'MODERATE_BUY_SIGNAL';
else if (confidence < -30) verdict = 'EXIT_SIGNAL';
else if (confidence < -10) verdict = 'TRIM_SIGNAL';
results.push({
ticker: ticker,
direction_confidence: confidence,
thesis_score: thesis,
antithesis_score: antithesis,
synthesis_verdict: verdict,
predictive_alpha_json: { confidence: confidence, verdict: verdict }
});
}
return results;
}
/**
* MACRO_REGIME_ADAPTIVE_GATE_V2
*/
function calcMacroRegimeAdaptiveGateV2Impl_(macroJson, mesResult, hApex) {
var totalScore = mesResult.macro_risk_score || 0;
var regime = 'MODERATE_RISK';
var heatThreshold = 10.0;
var sizeScale = 1.0;
if (totalScore >= 75) { regime = 'EXTREME_RISK'; heatThreshold = 5.0; sizeScale = 0.25; }
else if (totalScore >= 50) { regime = 'HIGH_RISK'; heatThreshold = 7.0; sizeScale = 0.50; }
else if (totalScore < 25) { regime = 'LOW_RISK'; heatThreshold = 12.0; sizeScale = 1.10; }
return {
formula_id: 'MACRO_REGIME_ADAPTIVE_GATE_V2',
total_mrag_score: totalScore,
regime_label: regime,
effective_heat_gate_threshold: heatThreshold,
effective_position_size_scale: sizeScale,
mrag_v2_json: { score: totalScore, regime: regime }
};
}
/**
* applyAlegGate4And5Impl_
*/
function applyAlegGate4And5Impl_(alegRows, paeRows, hApex) {
var results = [];
var paeMap = {};
for (var i = 0; i < paeRows.length; i++) paeMap[paeRows[i].ticker] = paeRows[i];
for (var i = 0; i < alegRows.length; i++) {
var row = alegRows[i];
var pae = paeMap[row.ticker] || {};
if (pae.synthesis_verdict === 'EXIT_SIGNAL' || pae.synthesis_verdict === 'TRIM_SIGNAL') {
row.gate4_status = 'BLOCK_PAE';
row.final_gate_status = 'BLOCK';
row.anti_late_entry_status = 'BLOCK';
} else {
row.gate4_status = 'PASS';
}
results.push(row);
}
return results;
}
/**
* Suite Aggregators
*/
function applyApexMacroAlphaSuiteImpl_(holdings, dfMap, hApex) {
// Placeholder for macro alpha suite
return hApex;
}
function applyApexMacroEventSuiteImpl_(hApex) {
// Placeholder for macro event suite
return hApex;
}
function applyApexPredictiveAlphaSuiteImpl_(holdings, dfMap, hApex) {
var macroJson = hApex.macro_event_json || {};
var mesResult = hApex.macro_event_json || {};
var paeRows = calcPredictiveAlphaEngineV1Impl_(holdings, dfMap, macroJson, mesResult, null);
hApex.predictive_alpha_json = paeRows;
// portfolio_alpha_confidence: mean direction_confidence across all holdings
var sum = 0, n = 0;
(paeRows || []).forEach(function(r) {
if (typeof r.direction_confidence === 'number') { sum += r.direction_confidence; n++; }
});
hApex.portfolio_alpha_confidence = n > 0 ? Math.round(sum / n * 100) / 100 : 0;
return hApex;
}
function applyApexWatchBreakoutSuiteImpl_(holdings, dfMap, hApex) {
var slgRows = hApex.satellite_lifecycle_gate_json || [];
var aleRows = hApex.anti_late_entry_json || [];
hApex.watch_breakout_candidates_json = calcWatchBreakoutRealtimeGateV1_(holdings, dfMap, slgRows, aleRows);
return hApex;
}
// ---- TASK-006: ANTI_LATE_ENTRY_GATE_V2_CALIBRATED ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function calibrateAntiLateEntryV2_(proposalHistory, captureDate) {
// RC 수정: velocity 버킷별 T+5 승률 계산 (실측 표본 >= 30 충족 후 활성화)
var buckets = { LOW: {n:0,wins:0}, MID: {n:0,wins:0}, HIGH: {n:0,wins:0} };
var totalBuys = 0, chaseBuys = 0;
(proposalHistory || []).forEach(function(p) {
if (p.origin === 'REPLAY' || p.action !== 'BUY') return;
if (p.realized_return_pct_t5 === undefined) return; // 미채움 제외
totalBuys++;
var v = parseFloat(p.velocity_1d || 0);
var win = parseFloat(p.realized_return_pct_t5 || 0) > 0;
var bucket = v < 1.0 ? 'LOW' : v < 3.0 ? 'MID' : 'HIGH';
buckets[bucket].n++;
if (win) buckets[bucket].wins++;
if (v >= 3.0) chaseBuys++;
});
var minSamples = 30;
var validated = Object.keys(buckets).every(function(k) { return buckets[k].n >= minSamples; });
return {
formula_id: 'ANTI_LATE_ENTRY_GATE_V2_CALIBRATED',
validated: validated,
unvalidated_label: validated ? null : '[UNVALIDATED_LIVE: n<30 per bucket]',
chase_entry_rate_pct: totalBuys > 0 ? (chaseBuys / totalBuys * 100).toFixed(1) : null,
buckets: buckets,
threshold_source: validated ? 'DYNAMIC' : 'EXPERT_PRIOR',
velocity_1d_block_pct: 3.0
};
}
// ---- TASK-007: DISTRIBUTION_BLOCK_EFFECTIVENESS_V1 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function trackDistributionBlockEffectiveness_(proposalHistory) {
var blocked = (proposalHistory || []).filter(function(p) {
return p.blocked_reason === 'DISTRIBUTION_CONFIRMED' || p.blocked_reason === 'DISTRIBUTION_BLOCK';
});
var avoidedLoss = blocked.filter(function(p) {
return p.t5_return_if_not_blocked !== undefined && parseFloat(p.t5_return_if_not_blocked) < 0;
});
var blockedN = blocked.length;
var avoidedLossRate = blockedN > 0 ? (avoidedLoss.length / blockedN) : null;
return {
formula_id: 'DISTRIBUTION_BLOCK_EFFECTIVENESS_V1',
blocked_sample_count: blockedN,
avoided_loss_rate: avoidedLossRate,
target_avoided_loss_rate: 0.60,
effectiveness_label: blockedN < 30
? '[UNVALIDATED_LOW_N: n=' + blockedN + ' < 30]'
: (avoidedLossRate >= 0.60 ? 'EFFECTIVE' : 'REVIEW_THRESHOLD')
};
}
// ---- TASK-010: SMART_MONEY_LIQUIDITY_OUTCOME_LINK_V1 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function linkSmartMoneyOutcome_(proposalHistory) {
var buckets = {};
(proposalHistory || []).forEach(function(p) {
if (p.origin === 'REPLAY' || !p.liquidity_label) return;
var lbl = p.liquidity_label;
if (!buckets[lbl]) buckets[lbl] = {returns:[], slippages:[]};
if (p.realized_return_pct_t5 !== undefined) buckets[lbl].returns.push(parseFloat(p.realized_return_pct_t5));
if (p.slippage_pct !== undefined) buckets[lbl].slippages.push(parseFloat(p.slippage_pct));
});
var table = Object.keys(buckets).map(function(lbl) {
var d = buckets[lbl];
var n = d.returns.length;
var wins = d.returns.filter(function(r){return r>0;}).length;
return {
liquidity_label: lbl,
sample_count: n,
t5_avg_return_pct: n > 0 ? d.returns.reduce(function(a,b){return a+b;},0)/n : null,
t5_win_rate: n > 0 ? wins/n : null,
label: n < 30 ? '[UNVALIDATED: n=' + n + ' < 30]' : 'VALIDATED'
};
});
return {formula_id: 'SMART_MONEY_LIQUIDITY_OUTCOME_LINK_V1', table: table};
}
+705
View File
@@ -0,0 +1,705 @@
// Consolidated runtime core: macro flow + macro calc + consistency
// ---- from gas_apex_macro_flow.gs ----
function applyApexMacroAlphaSuiteImpl_(holdings, dfMap, hApex) {
Logger.log('[HARNESS_SUB] L3-B2a-i: applyApexMacroEventSuite_');
hApex = applyApexMacroEventSuite_(hApex);
Logger.log('[HARNESS_SUB] L3-B2a-ii: applyApexPredictiveAlphaSuite_');
hApex = applyApexPredictiveAlphaSuite_(holdings, dfMap, hApex);
// [Phase 2] SMART_MONEY_DISTRIBUTION_GUARD_V1: T+5 예측 적중률 연동 매수 차단
if (typeof hApex.prediction_accuracy_rate === 'number' && hApex.prediction_accuracy_rate < 50) {
Logger.log('[HARNESS_SUB] Phase 2: prediction_accuracy_rate < 50% (' + hApex.prediction_accuracy_rate + '%). 신규 매수 전면 차단.');
hApex.global_buy_allowed = false;
(hApex.buy_permission_json || []).forEach(function(bp) {
if (bp.buy_permission_state !== 'BLOCKED') {
bp.buy_permission_state = 'BLOCKED';
bp.block_reason = (bp.block_reason ? bp.block_reason + ' | ' : '') + 'PREDICTION_ACCURACY_LOW(<50%)';
}
});
}
return hApex;
}
function applyApexMacroEventSuiteImpl_(hApex) {
var macroJson = getMacroJson();
var eventRiskFullRows = (function() {
try { return getEventRiskJson().events || []; } catch(e) { return []; }
})();
var mesResult = calcMacroEventSynchronizerV1_(macroJson, eventRiskFullRows);
hApex.macro_event_json = mesResult;
hApex.macro_risk_score = mesResult.macro_risk_score;
hApex.macro_risk_regime = mesResult.macro_risk_regime;
hApex.mega_sell_alert = mesResult.mega_sell_alert;
var mragResult = calcMacroRegimeAdaptiveGate_(macroJson, mesResult, hApex);
hApex.mrag_v2_json = mragResult;
if (mesResult.heat_gate_adj && mesResult.heat_gate_adj !== 0) {
var me1Threshold = (hApex.heat_gate_threshold_pct || 12) + mesResult.heat_gate_adj;
hApex.effective_heat_gate_threshold = Math.min(me1Threshold, mragResult.effective_heat_gate_threshold);
} else {
hApex.effective_heat_gate_threshold = mragResult.effective_heat_gate_threshold;
}
hApex.effective_position_size_scale = mragResult.effective_position_size_scale;
if (mragResult.stale_events_count > 0) {
hApex.stale_events_alert = mragResult.stale_events;
}
var fomcDaysRem = mesResult.fomc_days_remaining;
var usCpiDaysRem = mesResult.us_cpi_days_remaining;
var ipoDaysRem = mesResult.large_ipo_days_remaining;
var fomcGateActive = typeof fomcDaysRem === 'number' && fomcDaysRem <= 7;
var usCpiGateActive = typeof usCpiDaysRem === 'number' && usCpiDaysRem <= 2;
var ipoGateActive = typeof ipoDaysRem === 'number' && ipoDaysRem <= 3;
hApex.fomc_position_size_gate = fomcGateActive ? 'ACTIVE' : 'INACTIVE';
hApex.us_cpi_position_size_gate = usCpiGateActive ? 'ACTIVE' : 'INACTIVE';
hApex.ipo_position_size_gate = ipoGateActive ? 'ACTIVE' : 'INACTIVE';
if (fomcGateActive) {
(hApex.buy_permission_json || []).forEach(function(bp) {
bp.fomc_size_limit = 0.5;
bp.fomc_size_gate_reason = 'FOMC_' + fomcDaysRem + 'D_REMAINING';
});
}
if (usCpiGateActive) {
(hApex.buy_permission_json || []).forEach(function(bp) {
bp.us_cpi_size_limit = 0.5;
bp.us_cpi_size_gate_reason = 'US_CPI_' + usCpiDaysRem + 'D_REMAINING';
});
}
if (ipoGateActive) {
(hApex.buy_permission_json || []).forEach(function(bp) {
bp.ipo_size_limit = 0.7;
bp.ipo_size_gate_reason = 'LARGE_IPO_' + ipoDaysRem + 'D_REMAINING';
});
}
return hApex;
}
// ---- from gas_apex_macro_calc_core.gs ----
function calcMacroEventSynchronizerV1Impl_(macroJson, eventRows) {
var indicators = macroJson.indicators || [];
var byName = {};
indicators.forEach(function(m) { byName[m.Name] = m; });
var usdKrw = typeof macroJson.usd_krw === 'number' ? macroJson.usd_krw : 0;
var vix = typeof macroJson.vix === 'number' ? macroJson.vix : 0;
var sp500Ret5d = typeof macroJson.sp500_ret5d === 'number' ? macroJson.sp500_ret5d : 0;
// 외국인 순매도 연속일 (macro 시트 누적)
var fscRow = byName['Foreign_Sell_Consecutive_Days'] || byName['ForeignSellConsecutiveDays'] || {};
var foreignSellDays = typeof fscRow.Close === 'number' ? Math.round(fscRow.Close) : 0;
// 외국인 당일 순매도 금액
var fskRow = byName['Foreign_Sell_KRW_Today'] || byName['ForeignSellKRWToday'] || {};
var foreignSellKrwToday = typeof fskRow.Close === 'number' ? fskRow.Close : 0;
// 국내 CPI
var cpiRow = byName['Domestic_CPI'] || byName['CPI_Domestic'] || {};
var domesticCpi = typeof cpiRow.Close === 'number' ? cpiRow.Close : 0;
// FOMC / US_CPI / IPO 잔여 일수 (event_risk 시트)
var fomcDaysRemaining = null;
var usCpiDaysRemaining = null;
var largeIpoDaysRemaining = null;
var eventRowsSafe = Array.isArray(eventRows) ? eventRows : [];
function _nearestDays(typeStr) {
var list = eventRowsSafe.filter(function(e) {
var t = (e.Type || e.type || '').toUpperCase();
var d = typeof e.DaysLeft === 'number' ? e.DaysLeft : (typeof e.daysLeft === 'number' ? e.daysLeft : -1);
return t === typeStr && d >= 0;
});
if (!list.length) return null;
list.sort(function(a, b) {
return (a.DaysLeft || a.daysLeft || 999) - (b.DaysLeft || b.daysLeft || 999);
});
return list[0].DaysLeft || list[0].daysLeft || null;
}
fomcDaysRemaining = _nearestDays('FOMC');
usCpiDaysRemaining = _nearestDays('US_CPI');
largeIpoDaysRemaining = _nearestDays('IPO');
// ── macro_risk_score 산출 (max 100) ─────────────────────────────────────────
var breakdown = [];
var macroRiskScore = 0;
function addMacroScore(label, condition, score) {
if (condition) macroRiskScore += score;
breakdown.push({ factor: label, score: condition ? score : 0, triggered: !!condition });
}
addMacroScore('usd_krw_critical', usdKrw > 1500, 20);
addMacroScore('usd_krw_weak', usdKrw > 1480 && usdKrw <= 1500, 15);
addMacroScore('foreign_mega', foreignSellDays >= 10, 20);
addMacroScore('foreign_high', foreignSellDays >= 5 && foreignSellDays < 10, 15);
addMacroScore('fomc_near', fomcDaysRemaining !== null && fomcDaysRemaining <= 5, 15);
addMacroScore('us_cpi_near', usCpiDaysRemaining !== null && usCpiDaysRemaining <= 2, 10);
addMacroScore('cpi_high', domesticCpi > 2.5, 10);
addMacroScore('vix_elevated', vix > 20, 10);
addMacroScore('us500_drop', sp500Ret5d < -3.0, 10);
macroRiskScore = Math.min(100, macroRiskScore);
// ── macro_risk_regime 분류 ───────────────────────────────────────────────────
var macroRiskRegime, heatGateAdj;
if (macroRiskScore >= 60) { macroRiskRegime = 'MACRO_CRITICAL'; heatGateAdj = -3; }
else if (macroRiskScore >= 40) { macroRiskRegime = 'MACRO_ELEVATED'; heatGateAdj = -1; }
else if (macroRiskScore >= 20) { macroRiskRegime = 'MACRO_NEUTRAL'; heatGateAdj = 0; }
else { macroRiskRegime = 'MACRO_FAVORABLE'; heatGateAdj = +1; }
// ── event_matrix ────────────────────────────────────────────────────────────
var eventMatrix = [];
if (fomcDaysRemaining !== null && fomcDaysRemaining <= 7) {
eventMatrix.push({ event: 'FOMC_WEEK', buy_gate_downgrade: true, sell_block: false,
days_remaining: fomcDaysRemaining });
}
// US CPI 발표 2일 이내 — 신규매수 자제 (예상치 상회 시 급락 위험)
if (usCpiDaysRemaining !== null && usCpiDaysRemaining <= 2) {
eventMatrix.push({ event: 'US_CPI_IMMINENT', buy_gate_downgrade: true, sell_block: false,
days_remaining: usCpiDaysRemaining,
note: '미국 CPI 발표 임박 — 예상치 대비 서프라이즈 위험. 신규매수 자제' });
}
// 대형 IPO 5일 이내 — 공모자금 쏠림으로 시장 유동성 흡수 주의
if (largeIpoDaysRemaining !== null && largeIpoDaysRemaining <= 5) {
eventMatrix.push({ event: 'LARGE_IPO_WINDOW', buy_gate_downgrade: true, sell_block: false,
days_remaining: largeIpoDaysRemaining,
note: '대형 IPO 상장 임박 — 공모자금 유동성 흡수. 소형주·위성 포지션 매수 자제' });
}
// mega_sell_alert: 외국인 순매도 >= 1조원
var megaSellAlert = foreignSellKrwToday >= 1000000000000;
var buyGateBlockUntil = null;
if (megaSellAlert) {
var blockDate = new Date();
var bizAdded = 0;
while (bizAdded < 3) {
blockDate.setDate(blockDate.getDate() + 1);
var wd = blockDate.getDay();
if (wd !== 0 && wd !== 6) bizAdded++;
}
buyGateBlockUntil = Utilities.formatDate(blockDate, 'Asia/Seoul', 'yyyy-MM-dd');
eventMatrix.push({ event: 'MEGA_SELL_ALERT', foreign_sell_krw: foreignSellKrwToday,
buy_gate_block_until: buyGateBlockUntil });
}
return {
macro_risk_score: macroRiskScore,
macro_risk_regime: macroRiskRegime,
macro_risk_breakdown: breakdown,
foreign_sell_consecutive_days: foreignSellDays,
foreign_sell_krw_today: foreignSellKrwToday,
mega_sell_alert: megaSellAlert,
buy_gate_block_until: buyGateBlockUntil,
effective_heat_gate_adjustment: heatGateAdj,
heat_gate_adj: heatGateAdj,
fomc_days_remaining: fomcDaysRemaining,
us_cpi_days_remaining: usCpiDaysRemaining,
large_ipo_days_remaining: largeIpoDaysRemaining,
event_matrix: eventMatrix,
formula_id: 'MACRO_EVENT_SYNCHRONIZER_V1'
};
}
function calcMacroRegimeAdaptiveGateV2Impl_(macroJson, mesResult, hApex) {
var macro = macroJson || {};
var mes = mesResult || {};
// ── LAYER_1: 미시 리스크 (Market Internals, 0~25) ──────────────────
var l1 = 0;
var vkospi = toNumber_(macro['vkospi'] || macro.vkospi) || 0;
var mrsScoreL1 = toNumber_(macro['mrs_score'] || macro.mrs_score || (hApex && hApex.mrs_score)) || 0;
var breadthAdv = toNumber_(macro['breadth_advance_decline'] || macro.breadth_advance_decline) || 0;
if (breadthAdv > 0 && breadthAdv < 0.45) l1 += 10; // 하락 종목 비율 55% 초과
if (vkospi > 30) l1 += 10; // VKOSPI 공포
if (mrsScoreL1 <= 3) l1 += 5; // MRS 저점
l1 = Math.min(l1, 25);
// ── LAYER_2: 거시 리스크 (Macro, 0~25) ────────────────────────────
var l2 = 0;
var macroRiskScore = toNumber_(mes.macro_risk_score) || 0;
l2 = Math.min(25, Math.round(macroRiskScore / 100 * 25));
// ── LAYER_3: 글로벌 리스크 (Global, 0~25) ─────────────────────────
var l3 = 0;
var usRetWeek = toNumber_(macro['us500_1w_change'] || macro.us500_1w_change) || 0;
var vix = toNumber_(macro['vix'] || macro.vix) || 0;
var globalOvrd = String(macro['global_risk_override'] || '').toUpperCase();
if (usRetWeek < -3) l3 += 10; // S&P500 주간 -3% 이하
if (vix >= 30) l3 += 10; // VIX 공포
else if (vix >= 25) l3 += 7; // VIX 경계
if (globalOvrd === 'MANUAL_HIGH') l3 = 25; // 수동 override
l3 = Math.min(l3, 25);
// ── LAYER_4: 이벤트 리스크 (Event, 0~25) ──────────────────────────
var l4 = 0;
var fomcDays = typeof mes.fomc_days_remaining === 'number' ? mes.fomc_days_remaining : 99;
var usCpiDays = typeof mes.us_cpi_days_remaining === 'number' ? mes.us_cpi_days_remaining : 99;
var largeIpoDays = typeof mes.large_ipo_days_remaining === 'number' ? mes.large_ipo_days_remaining : 99;
var megaSell = mes.mega_sell_alert === true;
if (fomcDays <= 5) l4 += 15;
else if (fomcDays <= 7) l4 += 8;
if (megaSell) l4 += 10;
// US CPI: 발표 2일 이내 +8, 3일 이내 +4 (금리 경로 재평가 리스크)
if (usCpiDays <= 2) l4 += 8;
else if (usCpiDays <= 3) l4 += 4;
// 대형 IPO: 상장 3일 이내 +5 (공모자금 유동성 흡수)
if (largeIpoDays <= 3) l4 += 5;
l4 = Math.min(l4, 25);
var totalScore = l1 + l2 + l3 + l4;
// ── HEAT_GATE 임계값 / POSITION_SIZE_SCALE 조정 ────────────────────
var effectiveHeatThreshold, effectivePositionScale, regimeLabel;
if (totalScore >= 80) {
effectiveHeatThreshold = 5; effectivePositionScale = 0.25; regimeLabel = 'EVENT_SHOCK';
} else if (totalScore >= 60) {
effectiveHeatThreshold = 7; effectivePositionScale = 0.50; regimeLabel = 'RISK_OFF';
} else if (totalScore >= 40) {
effectiveHeatThreshold = 10; effectivePositionScale = 1.00; regimeLabel = 'NEUTRAL';
} else {
effectiveHeatThreshold = 12; effectivePositionScale = 1.10; regimeLabel = 'RISK_ON';
}
// ── 이벤트 날짜 검증 (STALE_EVENT 탐지) ────────────────────────────
var eventDateResults = [];
var staleEvents = [];
var analysisDate = new Date();
(mes.events_used || []).forEach(function(ev) {
if (!ev || !ev.event_date) return;
var evDate = new Date(ev.event_date);
var valid = evDate >= analysisDate;
var r = { event_type: ev.event_type || 'UNKNOWN', event_date: ev.event_date, valid: valid,
status: valid ? 'VALID' : 'STALE_EVENT' };
if (!valid) staleEvents.push(r);
eventDateResults.push(r);
});
return {
micro_risk_score: l1,
macro_risk_score_normalized: l2,
global_risk_score: l3,
event_risk_score: l4,
total_mrag_score: totalScore,
effective_heat_gate_threshold: effectiveHeatThreshold,
effective_position_size_scale: effectivePositionScale,
regime_label: regimeLabel,
event_date_validation_results: eventDateResults,
stale_events: staleEvents,
stale_events_count: staleEvents.length,
formula_id: 'MACRO_REGIME_ADAPTIVE_GATE_V2'
};
}
// ---- from gas_apex_consistency_core.gs ----
function calcConsistencyValidatorV2Impl_(hApex, asResult, cashFloorInfo, capturedAtIso, now) {
var passed = [], failed = [], gapList = [];
function chk(id, name, testFn) {
try {
var r = testFn();
if (r.ok) {
passed.push(id);
} else {
failed.push({ check_id: id, name: name, reason: r.reason || 'failed' });
if (r.gaps) r.gaps.forEach(function(g) { gapList.push(g); });
}
} catch(e) {
failed.push({ check_id: id, name: name, reason: 'exception:' + e.message });
}
}
// CV_01: sell_candidates tier 비감소
chk('CV_01', 'sell_priority 방향 일관성', function() {
var cands = hApex.sell_candidates_json || [];
for (var i = 1; i < cands.length; i++) {
var ta = cands[i-1].tier, tb = cands[i].tier;
if (typeof ta === 'number' && typeof tb === 'number' && tb < ta) {
return { ok: false, reason: 'tier_reversal idx=' + i + '(' + tb + '<' + ta + ')' };
}
}
return { ok: true };
});
// CV_02: stop < close < tp1 (< tp2)
chk('CV_02', '가격 순서 검증', function() {
var prices = hApex.prices_json || [];
for (var i = 0; i < prices.length; i++) {
var p = prices[i];
var stop = p.stop_price || 0, curr = p.current_price || p.close || 0, tp1 = p.tp1_price || 0;
if (stop > 0 && curr > 0 && stop >= curr) {
return { ok: false, reason: p.ticker + ':stop(' + stop + ')>=close(' + curr + ')' };
}
if (curr > 0 && tp1 > 0 && curr >= tp1) {
return { ok: false, reason: p.ticker + ':close(' + curr + ')>=tp1(' + tp1 + ')' };
}
}
return { ok: true };
});
// CV_03: heat vs weight 비례성 (구조 확인용)
chk('CV_03', 'heat vs 보유 비중 일치', function() {
var holdings = asResult.holdings || [];
// heat_pct는 손실위험 기준, weight_pct는 평가비중 — 직접 비교 불가
// 보유 종목 존재 확인 (구조 레벨 검증)
if (holdings.length > 0 && !hApex.execution_quality_json) {
return { ok: false, reason: 'execution_quality_json 없음 (보유종목 있음)' };
}
return { ok: true };
});
// CV_04: enum 유효성 (synthesis_verdict, rs_verdict)
chk('CV_04', 'enum 값 유효성', function() {
var VALID_SYNTH = ['STRONG_BUY_SIGNAL','MODERATE_BUY_SIGNAL','HOLD_NEUTRAL','TRIM_SIGNAL','EXIT_SIGNAL'];
var VALID_RS = ['LEADER','NEUTRAL','LAGGARD','BROKEN','UNKNOWN','N/A',''];
var paeList = hApex.predictive_alpha_json || [];
for (var i = 0; i < paeList.length; i++) {
var v = paeList[i].synthesis_verdict;
if (v && VALID_SYNTH.indexOf(v) < 0) {
return { ok: false, reason: paeList[i].ticker + ':invalid synthesis_verdict=' + v };
}
}
var saqgList = hApex.saqg_json || [];
for (var j = 0; j < saqgList.length; j++) {
var rv = saqgList[j].rs_verdict;
if (rv && VALID_RS.indexOf(rv) < 0) {
return { ok: false, reason: saqgList[j].ticker + ':invalid rs_verdict=' + rv };
}
}
return { ok: true };
});
// CV_05: 상호 충돌 게이트 탐지 [PROPOSAL47_B5 확장: MACRO_CRITICAL 추가]
chk('CV_05', '상호 충돌 게이트 탐지', function() {
var sfg = hApex.satellite_failure_gate_json || {};
var sfgTriggered = sfg.sfg_v1 === 'TRIGGERED';
var megaSell = hApex.mega_sell_alert === true;
var macroCritical = hApex.macro_risk_regime === 'MACRO_CRITICAL';
var buyPerms = hApex.buy_permission_json || [];
for (var i = 0; i < buyPerms.length; i++) {
var bp = buyPerms[i];
var eligible = bp.buy_permission_state === 'ELIGIBLE' || bp.buy_permission_state === 'STAGED_BUY';
if (eligible && sfgTriggered) {
return { ok: false, reason: bp.ticker + ':buy=ELIGIBLE but sfg=TRIGGERED' };
}
if (eligible && megaSell && hApex.buy_gate_block_until) {
return { ok: false, reason: bp.ticker + ':buy=ELIGIBLE but mega_sell_alert=true' };
}
if (eligible && macroCritical) {
return { ok: false, reason: bp.ticker + ':buy=ELIGIBLE but macro_risk_regime=MACRO_CRITICAL' };
}
}
return { ok: true };
});
// CV_06: 수량 정수 검증
chk('CV_06', '수량 정수 검증', function() {
var sqList = hApex.smart_sell_quantities_json || [];
for (var i = 0; i < sqList.length; i++) {
var sq = sqList[i];
if (typeof sq.sell_qty === 'number' && sq.sell_qty !== Math.floor(sq.sell_qty)) {
return { ok: false, reason: sq.ticker + ':sell_qty 소수점=' + sq.sell_qty };
}
}
return { ok: true };
});
// CV_07: 데이터 신선도
chk('CV_07', '날짜 신선도', function() {
if (!capturedAtIso) return { ok: true };
var capMs = new Date(capturedAtIso).getTime();
if (isNaN(capMs)) return { ok: true };
var nowMs = (now && now.getTime) ? now.getTime() : Date.now();
var diffDays = (nowMs - capMs) / 86400000;
if (diffDays > 3) return { ok: false, reason: 'STALE_BLOCK:' + Math.round(diffDays) + '일 경과' };
if (diffDays > 1) return { ok: false, reason: 'STALE_WARN:' + Math.round(diffDays) + '일 경과' };
return { ok: true };
});
// CV_08: 현금 계산 경로 — GAS는 settlementCashD2Krw만 사용 (항상 통과)
chk('CV_08', '현금 계산 경로', function() {
return { ok: true };
});
// CV_09: 라우팅 completeness — Sprint B 핵심 출력 존재 확인
chk('CV_09', '라우팅 completeness', function() {
var required = ['data_freshness_json','satellite_lifecycle_gate_json',
'portfolio_correlation_gate_json','satellite_failure_gate_json','buy_permission_json'];
var missing = required.filter(function(k) { return hApex[k] === undefined; });
if (missing.length > 0) {
return { ok: false, reason: 'missing:' + missing.join(','),
gaps: missing.map(function(k) { return { type: 'HARNESS_KEY_MISSING', item: k }; }) };
}
return { ok: true };
});
// CV_10: LLM 출력 checksum — 보고서 렌더링 시 검증 (GAS 단계 통과)
chk('CV_10', 'LLM 출력 checksum', function() {
return { ok: true };
});
// CV_11: GAS 하네스 키 동기화 — hApex 필수 키 존재 확인 [PROPOSAL47/48: 신규 키 추가]
chk('CV_11', 'GAS 하네스 키 동기화', function() {
var required = ['buy_permission_json','saqg_json','satellite_failure_gate_json',
'data_freshness_json','macro_event_json','predictive_alpha_json','anti_late_entry_json',
'watch_breakout_candidates_json','portfolio_alpha_confidence',
'anti_whipsaw_reentry_json','alpha_history_summary_json'];
var missing = required.filter(function(k) { return hApex[k] === undefined; });
if (missing.length > 0) {
return { ok: false, reason: 'HARNESS_KEY_MISSING:' + missing.join(','),
gaps: missing.map(function(k) { return { type: 'HARNESS_KEY_MISSING', item: k }; }) };
}
return { ok: true };
});
// CV_12: YAML-to-GAS 커버리지 — PA1~PA4 출력 확인 (자기 자신 consistency_report_json 제외)
chk('CV_12', 'YAML-to-GAS 커버리지', function() {
var paKeys = ['predictive_alpha_json','anti_late_entry_json',
'cash_preservation_sell_json','macro_event_json'];
var missing = paKeys.filter(function(k) { return hApex[k] === undefined; });
if (missing.length > 0) {
return { ok: false, reason: 'GAS_COVERAGE_GAP:' + missing.join(','),
gaps: missing.map(function(k) { return { type: 'GAS_COVERAGE_GAP', item: k }; }) };
}
return { ok: true };
});
var score = Math.round(passed.length / 12 * 100);
var blockStatus = score < 90 ? 'BLOCK' : (score < 100 ? 'WARNING' : 'PASS');
return {
consistency_score: score,
cv_verdict: blockStatus,
passed: passed,
failed: failed,
gap_list: gapList,
block_status: blockStatus,
formula_id: 'CONSISTENCY_VALIDATOR_V2'
};
}
// ---- TASK-001: RELEASE_GATE_TRUTH_V1 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function buildReleaseGateTruthV1_(hApex) {
// RC1 수정: honest_proof_score >= 70 이어야만 릴리스 허용
// effective_release_gate = AND(cosmetic_gate, honest_gate)
var agp = hApex['algorithm_guidance_proof_v1'] || {};
var honestScore = agp['honest_proof_score'] || 0;
var honestGate = agp['honest_gate'] || 'FAIL';
var cosmeticGate = agp['gate'] || 'FAIL';
var effectiveGate = (honestGate === 'PASS' && cosmeticGate === 'PASS') ? 'PASS' : 'FAIL';
return {
formula_id: 'RELEASE_GATE_TRUTH_V1',
honest_proof_score: honestScore,
honest_gate: honestGate,
cosmetic_gate: cosmeticGate,
effective_release_gate: effectiveGate,
hts_order_mode: honestScore >= 70 ? 'HTS_ALLOWED' : 'THEORETICAL_ONLY',
release_blocked_note: honestScore < 70
? '[RELEASE_BLOCKED_BY_TRUTH_GATE: honest=' + honestScore + ' < 70]'
: null
};
}
// ---- TASK-002: NON_VACUOUS_PASS_GUARD_V1 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function guardNonVacuousPass_(gateObj, minSamples) {
// RC2 수정: effective_n < minSamples 인 게이트를 WATCH_PENDING_SAMPLE로 강제 강등
minSamples = minSamples || 30;
var nFields = ['sample_count','row_count','evaluated_count','samples','n','sample_n'];
var effectiveN = null;
for (var i = 0; i < nFields.length; i++) {
if (gateObj[nFields[i]] !== undefined && gateObj[nFields[i]] !== null) {
effectiveN = parseInt(gateObj[nFields[i]], 10);
break;
}
}
if (effectiveN === null) effectiveN = 0;
var gateVal = (gateObj['gate'] || '').toUpperCase();
if (effectiveN < minSamples && gateVal === 'PASS') {
return {
gate: 'WATCH_PENDING_SAMPLE',
label: '[PASS_INVALID_LOW_N: n=' + effectiveN + ' < ' + minSamples + ']',
vacuous: true
};
}
return { gate: gateVal, vacuous: false };
}
// ---- TASK-004: OPERATIONAL_SAMPLE_BACKFILL_V1 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function evaluateOperationalOutcomeBatch_(proposalHistory, dataFeed, captureDate) {
// RC4 수정: LIVE/PAPER 제안의 T+5/T+20 실측 결과를 채움
// live=0 상태이므로 현재는 scaffolded — 실측 표본 누적 후 활성화
var results = [];
var opT5Count = 0;
var opT20Count = 0;
(proposalHistory || []).forEach(function(p) {
if (!p.origin || p.origin === 'REPLAY') return; // REPLAY 제외
var today = captureDate ? new Date(captureDate) : new Date();
var entryDate = p.entry_date ? new Date(p.entry_date) : null;
if (!entryDate) return;
var elapsedDays = Math.floor((today - entryDate) / 86400000);
var result = { id: p.id, origin: p.origin, entry_date: p.entry_date };
if (elapsedDays >= 5 && p.realized_return_pct_t5 === undefined) {
result.t5_pending = true; // 실측 미채움
} else if (p.realized_return_pct_t5 !== undefined) {
opT5Count++;
result.t5_filled = true;
}
if (elapsedDays >= 20 && p.realized_return_pct_t20 === undefined) {
result.t20_pending = true;
} else if (p.realized_return_pct_t20 !== undefined) {
opT20Count++;
result.t20_filled = true;
}
results.push(result);
});
return {
formula_id: 'OPERATIONAL_SAMPLE_BACKFILL_V1',
operational_t5_sample_count: opT5Count,
operational_t20_sample_count: opT20Count,
unvalidated_label: opT5Count < 30 ? '[UNVALIDATED_LIVE: n=' + opT5Count + ' < 30]' : null,
results: results
};
}
// ---- TASK-005: EVALUATION_WINDOW_HONESTY_V1 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function labelEvaluationWindow_(outcomeQualityJson) {
// RC5 수정: t20_source != operational_t20이면 T20_PROXY 플래그
var t20Source = (outcomeQualityJson && outcomeQualityJson.t20_source) || null;
var isProxy = (t20Source !== 'operational_t20');
return {
formula_id: 'EVALUATION_WINDOW_HONESTY_V1',
t20_source: t20Source,
t20_is_proxy: isProxy,
t20_label: isProxy ? 'T+20(추정,프록시)' : 'T+20(실측)',
release_gate_t20_alpha_blocked: isProxy,
proxy_note: isProxy
? '[T20_PROXY: t20_source=' + t20Source + ' - 실측 T+20 표본 0건]'
: null
};
}
// ---- TASK-008: VALUE_PRESERVING_CASH_RAISE_V9 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function calcValuePreservingCashRaiseV9_(sellCandidates, shortfallKrw, regimeLabel) {
// RC 수정: BREACH_FULL_LIQUIDATION 금지, K2 50/50 강제
var REBOUND_FACTORS = {EVENT_SHOCK:0.7, RISK_OFF:0.6, NEUTRAL:0.5, RISK_ON:0.3};
var reboundFactor = REBOUND_FACTORS[regimeLabel] || 0.5;
var result = [];
var totalDamagePct = 0, count = 0, breachCount = 0;
(sellCandidates || []).forEach(function(c) {
var qty = parseInt(c.qty || c.quantity || 0, 10);
var isOversold = c.rsi14 !== undefined && parseFloat(c.rsi14) < 30;
var brtNotBroken = c.brt_verdict !== 'BROKEN';
var emergency = !!c.emergency_full_sell;
if ((isOversold || brtNotBroken) && !emergency) {
// K2 50/50
var imm = Math.floor(qty / 2);
var wait = qty - imm;
var reboundTrigger = parseFloat(c.prev_close || 0) + reboundFactor * parseFloat(c.atr20 || 0);
result.push({
ticker: c.ticker,
immediate_qty: imm,
rebound_wait_qty: wait,
rebound_trigger_price: Math.round(reboundTrigger),
k2_applied: true
});
} else {
if (c.source === 'BREACH_FULL_LIQUIDATION' && !emergency) breachCount++;
result.push({ticker: c.ticker, immediate_qty: qty, rebound_wait_qty: 0, k2_applied: false});
}
totalDamagePct += parseFloat(c.value_damage_pct || 0);
count++;
});
var avgDamage = count > 0 ? totalDamagePct / count : 0;
return {
formula_id: 'VALUE_PRESERVING_CASH_RAISE_V9',
selected_sell_combo: result,
raw_value_damage_pct_avg: avgDamage,
rebound_capture_probability: result.some(function(r){return r.k2_applied;}) ? 0.5 : 0.0,
breach_full_liquidation_count: breachCount,
gate: (avgDamage <= 10 && breachCount === 0) ? 'PASS' : 'FAIL'
};
}
// ---- TASK-009: CAPITAL_STYLE_ALLOCATION_V2 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function calcCapitalStyleAllocationV2_(ticker, proposalHistory, convictionScore) {
// 투자성향별 실측 승률로 가중치 보정 (표본 < 30 시 EXPERT_PRIOR 유지)
var styles = ['SCALP','SWING','MOMENTUM','POSITION'];
var result = {};
styles.forEach(function(style) {
var samples = (proposalHistory || []).filter(function(p) {
return p.ticker === ticker && p.style === style && p.origin !== 'REPLAY'
&& p.realized_return_pct_t5 !== undefined;
});
var n = samples.length;
var wins = samples.filter(function(p){return parseFloat(p.realized_return_pct_t5||0)>0;}).length;
result[style] = {
sample_n: n,
win_rate: n >= 30 ? (wins/n) : null,
weight_source: n >= 30 ? 'DYNAMIC' : 'EXPERT_PRIOR',
label: n < 30 ? '[UNVALIDATED_WEIGHT: n=' + n + ' < 30]' : null
};
});
// conviction 게이트
var recPct = convictionScore < 35 ? 0
: convictionScore < 50 ? 1.5
: convictionScore < 65 ? 3.0
: convictionScore < 80 ? 5.0 : 7.0;
return {
formula_id: 'CAPITAL_STYLE_ALLOCATION_V2',
ticker: ticker,
conviction_score: convictionScore,
recommended_pct: recPct,
styles: result
};
}
// ---- TASK-011: DETERMINISTIC_ROUTING_ENGINE_V2 ----
// [GAS_STUB_ONLY: requires Google Sheets deployment]
function buildRoutingExecutionLogV2_(hApex) {
// 기존 11단계 로그에 단계12(RELEASE_GATE_TRUTH) 추가
var agp = hApex['algorithm_guidance_proof_v1'] || {};
var p100 = hApex['pass_100_criteria_v3'] || {};
var honestScore = agp['honest_proof_score'] || 0;
var effectiveGate = p100['effective_release_gate'] || (honestScore >= 70 ? 'PASS' : 'FAIL');
var step12 = {
step: 12,
formula_id: 'RELEASE_GATE_TRUTH_V1',
label: '릴리스 진실 게이트',
status: effectiveGate,
honest_proof_score: honestScore,
effective_release_gate: effectiveGate,
hts_order_count_if_blocked: effectiveGate !== 'PASS' ? 0 : null,
blocked_note: effectiveGate !== 'PASS'
? '[RELEASE_BLOCKED_BY_TRUTH_GATE: honest=' + honestScore + ' < 70]'
: null
};
// 기존 routing_execution_log에 step12 추가
var existing = hApex['routing_execution_log'] || {};
var steps = Array.isArray(existing.steps) ? existing.steps.slice() : [];
steps.push(step12);
return Object.assign({}, existing, {
steps: steps,
stage_count_target: 12,
effective_release_gate: effectiveGate
});
}
+8
View File
@@ -0,0 +1,8 @@
// gas_data_collect.gs — compatibility stub (P5-T02 GAS file split)
//
// 실제 구현은 src/gas_adapter_parts/ 로 이동:
// gdc_01_fetch_fundamentals.gs — fetch 인프라·Naver/Yahoo fetchers·펀더멘털·runDataFeed (L1-L2405)
// gdc_02_account_satellite.gs — 계좌스냅샷·티커셋업·위성배치·가격맵 (L2406-L4460)
//
// GAS 프로젝트에 모든 파일을 함께 추가하면 동일한 글로벌 네임스페이스에서 동작합니다.
// Ownership: data_feed 팀, QEDD P5-T02
+21
View File
@@ -0,0 +1,21 @@
/**
* gas_data_feed.gs — Google Apps Script 버전 (compatibility stub)
*
* ⚠️ 이 파일은 P5-T02 GAS 역할 분리 작업의 호환성 스텁입니다.
* 실제 함수 구현은 src/gas_adapter_parts/ 아래 분리된 파일로 이동했습니다.
*
* gdf_01_price_metrics.gs — 가격 지표·RSI·Entry/Exit·점수·매도우선순위 (L1-L2347)
* gdf_02_harness_assembly.gs — 하네스 조립·라우팅·레짐·위성 (L2348-L4560)
* gdf_03_portfolio_gates.gs — 포트폴리오 게이트·섹터·액션·실행 (L4561-L6806)
* gdf_04_execution_quality.gs — 실행품질·Apex·PA1 피드백·매크로 (L6807-L9015)
* gdf_05_alpha_engines.gs — 알파엔진·서빙·거래품질·패턴 (L9016-L10302)
*
* GAS 프로젝트에 모든 파일을 함께 추가하면 동일한 글로벌 네임스페이스에서 동작합니다.
*
* 배포 방법:
* 1. script.google.com → 새 프로젝트
* 2. 이 파일 + src/gas_adapter_parts/gdf_*.gs + src/gas_adapter_parts/gdc_*.gs 붙여넣기
* 3. 트리거 설정: runDataFeed → 시간 기반 → 매일 → 16:30~17:30
*
* Ownership: data_feed 팀, QEDD P5-T02 GAS file split
*/
+907
View File
@@ -0,0 +1,907 @@
/**
* gas_event_calendar.gs — Market Event Calendar Harness (v2: Yahoo + Naver scrapers)
*
* 스크래핑 전략:
* - Yahoo Finance: __NEXT_DATA__ JSON 추출 (Next.js 내장 데이터)
* - Naver Finance: HTML 테이블 파싱 (경제지표 일정 + 실적발표)
* - 공통: fetchWithCache_() — CacheService(4h) + 지수 백오프 + stale fallback
*
* 블록킹 대응:
* - Chrome UA + Referer 헤더로 봇 판정 회피
* - 429/503 → 재시도, 403/401 → 즉시 stale 사용
* - 파싱 실패 시 빈 배열 반환 (에러 전파 없음)
*/
const CFG = {
SPREADSHEET_ID: '1e1TNlLfnT69nvw-I1wU_oBHmEtI2pfbld3e0fFmtrZM',
SHEET_NAME: 'event_calendar',
TIME_ZONE: 'Asia/Seoul',
DATE_FORMAT: 'yyyy-MM-dd',
ALERT_EMAIL: '',
REQUIRED_HEADERS: ['Date', 'Event', 'Type', 'Impact', 'Alert'],
ALL_HEADERS: ['Date','Event','Type','Impact','Alert','DaysLeft','AlertStatus','LastCheckedAt','Source','SourceUrl','Key'],
IMPACT_ALERT_WINDOW_DAYS: { HIGH: 7, MEDIUM: 3, LOW: 1 },
VALID_TYPES: ['FOMC','US_CPI','US_PPI','US_PCE','US_NFP','EARNINGS','EXPIRY','BOK','KR_CPI','BOJ','FX','BOND','CUSTOM'],
VALID_IMPACTS: ['HIGH','MEDIUM','LOW'],
JSON_SOURCE_PROPERTY: 'EVENT_JSON_URL',
CSV_SOURCE_PROPERTY: 'EVENT_CSV_URL',
// 캐시·재시도
CACHE_TTL_SEC: 4 * 60 * 60,
STALE_PROP_PREFIX: 'stale_url:',
MAX_RETRIES: 2,
RETRY_BASE_MS: 1500,
// 스크래핑
YAHOO_DAYS_AHEAD: 60, // Yahoo: 오늘부터 N일 앞까지 수집
SCRAPE_SLEEP_MS: 700, // 요청 간 대기 (ms) — rate limit 회피
CHROME_UA: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
};
/* ── 메뉴 ─────────────────────────────────────────────────────────────────── */
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Market Calendar')
.addItem('초기 설정', 'setup')
.addItem('검증 및 정렬', 'validateAndSort')
.addItem('임박 이벤트 알림 발송', 'sendEventAlerts')
.addSeparator()
.addItem('Trading Economics 새로고침', 'refreshFromTradingEconomics')
.addItem('Naver Finance 새로고침', 'refreshFromNaver')
.addItem('외부 URL 소스 새로고침', 'refreshFromSources')
.addSeparator()
.addItem('프로퍼티 캐시 청소', 'cleanUpProperties')
.addItem('샘플 데이터 삽입', 'loadSampleDataIfEmpty')
.addItem('매일 실행 트리거 설치', 'createDailyTrigger')
.addItem('트리거 삭제', 'deleteProjectTriggers')
.addToUi();
}
function setup() {
cleanUpProperties(); // 한도 초과 상태 해제를 위해 프로퍼티 캐시 청소 먼저 수행
ensureSheetAndHeaders_();
validateAndSort();
createDailyTrigger();
toast_('event_calendar 설정 완료', 5);
}
function runDaily() {
// refreshFromTradingEconomics(); // 로컬 수집 방식을 사용하므로 원격 실행은 건너뜁니다.
refreshFromNaver();
refreshFromSources();
validateAndSort();
sendEventAlerts();
}
/* ── 검증·정렬 ────────────────────────────────────────────────────────────── */
function validateAndSort() {
const sheet = ensureSheetAndHeaders_();
const hmap = getHeaderMap_(sheet);
const lastRow = sheet.getLastRow();
if (lastRow < 2) { toast_('데이터 없음', 3); return; }
const now = Utilities.formatDate(new Date(), CFG.TIME_ZONE, 'yyyy-MM-dd HH:mm:ss');
const today = todayKst_();
const range = sheet.getRange(2, 1, lastRow - 1, sheet.getLastColumn());
const values = range.getValues();
const I = {
date: hmap.Date-1, event: hmap.Event-1, type: hmap.Type-1,
impact: hmap.Impact-1, days: hmap.DaysLeft-1,
status: hmap.AlertStatus-1, checked: hmap.LastCheckedAt-1, key: hmap.Key-1,
};
const rows = values.map(row => {
const d = coerceDate_(row[I.date]);
const event = String(row[I.event] || '').trim();
const type = String(row[I.type] || '').trim().toUpperCase();
const impact = String(row[I.impact] || '').trim().toUpperCase();
const errs = [];
if (!d) errs.push('ERROR: invalid date');
if (!event) errs.push('ERROR: empty event');
if (type && !CFG.VALID_TYPES.includes(type)) errs.push('WARN: unknown type');
if (impact && !CFG.VALID_IMPACTS.includes(impact)) errs.push('WARN: unknown impact');
if (d) { row[I.date] = d; row[I.days] = daysBetween_(today, d); } else row[I.days] = '';
if (!row[I.key] && d && event) row[I.key] = buildKey_(d, event, type);
if (errs.length) row[I.status] = errs.join(' | ');
row[I.checked] = now;
row[I.type] = type;
row[I.impact] = impact;
return row;
});
rows.sort((a, b) => {
const da = coerceDate_(a[I.date]), db = coerceDate_(b[I.date]);
if (!da && !db) return 0; if (!da) return 1; if (!db) return -1;
return da - db;
});
range.setValues(rows);
sheet.getRange(2, hmap.Date, Math.max(lastRow-1,1), 1).setNumberFormat(CFG.DATE_FORMAT);
applyFormatting_(sheet, hmap);
toast_('검증 및 정렬 완료', 3);
}
/* ── 이메일 알림 ──────────────────────────────────────────────────────────── */
function sendEventAlerts() {
Logger.log('[sendEventAlerts] 이메일 알림 발송 기능 비활성화 (사용자 요청)');
toast_('이메일 알림 발송 건너뜀 (비활성화)', 3);
return;
const todayStr = Utilities.formatDate(new Date(), CFG.TIME_ZONE, CFG.DATE_FORMAT);
const props = PropertiesService.getScriptProperties();
const due = [];
rows.forEach(item => {
const impact = String(item.Impact || '').toUpperCase();
const daysLeft = Number(item.DaysLeft);
if (!impact || isNaN(daysLeft) || daysLeft < 0) return;
if (daysLeft > (CFG.IMPACT_ALERT_WINDOW_DAYS[impact] || 0)) return;
const sentKey = `sent:${todayStr}:${item.Key || buildKey_(coerceDate_(item.Date), item.Event, item.Type)}`;
if (props.getProperty(sentKey)) return;
due.push({ ...item, DaysLeft: daysLeft, sentKey });
});
if (!due.length) { toast_('오늘 발송할 알림 없음', 3); return; }
const to = CFG.ALERT_EMAIL || Session.getActiveUser().getEmail();
if (!to) throw new Error('ALERT_EMAIL 또는 사용자 이메일 필요');
MailApp.sendEmail({ to, subject: `[Market Calendar] 임박 이벤트 ${due.length}건`, body: buildEmailBody_(due) });
due.forEach(item => {
props.setProperty(item.sentKey, '1');
if (hmap.AlertStatus) sheet.getRange(item.__row, hmap.AlertStatus).setValue(`SENT: ${todayStr}`);
});
toast_(`알림 발송 완료: ${due.length}건`, 4);
}
/* ═══════════════════════════════════════════════════════════════════════════ *
* Trading Economics 스크래퍼
* ══════════════════════════════════════════════════════════════════════════ */
function refreshFromTradingEconomics() {
return; // 로컬 수집 방식으로 이관되어 비활성화
let sourceName = 'Trading Economics';
let events = fetchTradingEconomicsCalendar_(CFG.YAHOO_DAYS_AHEAD);
if (!events.length) {
Logger.log('[TradingEconomics] 차단 또는 결과 없음. Yahoo Finance 오늘 날짜 수집으로 Fallback합니다.');
events = fetchYahooCalendar_();
sourceName = 'Yahoo Fallback';
}
if (!events.length) {
toast_('캘린더: 수집된 이벤트 없음 (야후/TE 모두 차단 또는 일정 없음)', 4);
return;
}
upsertEvents_(events);
toast_(`${sourceName} 갱신: ${events.length}건`, 4);
}
function fetchTradingEconomicsCalendar_(daysAhead) {
Logger.log('[TradingEconomics] 로컬(클라이언트) 수집 방식을 사용하므로 구글 서버의 직접 호출은 건너뜁니다.');
return [];
const today = todayKst_();
const startDateStr = Utilities.formatDate(today, CFG.TIME_ZONE, 'yyyy-MM-dd');
const end = new Date(today.getFullYear(), today.getMonth(), today.getDate() + (daysAhead || 60));
const endDateStr = Utilities.formatDate(end, CFG.TIME_ZONE, 'yyyy-MM-dd');
const cache = CacheService.getScriptCache();
const cacheKey = `te_cal_parsed:${startDateStr}:${endDateStr}`;
const cachedData = cache.get(cacheKey);
if (cachedData !== null) {
try {
const parsed = JSON.parse(cachedData);
if (Array.isArray(parsed)) {
return parsed;
}
} catch(e) {
Logger.log(`[TradingEconomics] 캐시 파싱 실패: ${e.message}`);
}
}
const url = "https://tradingeconomics.com/calendar";
const headers = {
'User-Agent': CFG.CHROME_UA,
'Cookie': `cal-custom-range=${startDateStr}|${endDateStr}`
};
let events = [];
try {
const html = fetchWithCache_(url, CFG.CACHE_TTL_SEC, headers);
if (html) {
events = parseTradingEconomicsHtml_(html);
if (events.length > 0) {
cache.put(cacheKey, JSON.stringify(events), 12 * 60 * 60);
}
}
} catch(e) {
Logger.log(`[TradingEconomics] 실패: ${e.message}`);
}
return events;
}
function fetchYahooCalendar_() {
const events = [];
const today = todayKst_();
const dateStr = Utilities.formatDate(today, CFG.TIME_ZONE, CFG.DATE_FORMAT);
const cache = CacheService.getScriptCache();
const cacheKey = `yahoo_cal_parsed:${dateStr}`;
const cachedData = cache.get(cacheKey);
if (cachedData !== null) {
try {
const parsed = JSON.parse(cachedData);
if (Array.isArray(parsed)) {
return parsed;
}
} catch(e) {
Logger.log(`[Yahoo Fallback] 캐시 파싱 실패: ${e.message}`);
}
}
// 야후는 day 파라미터가 무시되므로 오늘 날짜 1일치만 fetch합니다.
const url = `https://finance.yahoo.com/calendar/economic?day=${dateStr}`;
const headers = { 'User-Agent': 'Mozilla/5.0 (compatible; GAS/1.0)' };
try {
const html = fetchWithCache_(url, CFG.CACHE_TTL_SEC, headers);
if (html) {
const dailyEvents = parseYahooHtml_(html, today);
if (dailyEvents.length > 0) {
cache.put(cacheKey, JSON.stringify(dailyEvents), 12 * 60 * 60);
events.push(...dailyEvents);
}
}
} catch(e) {
Logger.log(`[Yahoo Fallback] 실패: ${e.message}`);
}
return events;
}
function parseYahooHtml_(html, dateHint) {
const trMatches = html.match(/<tr[^>]*data-testid="data-table-v2-row"[\s\S]*?<\/tr>/gi);
if (!trMatches || !trMatches.length) {
Logger.log('[Yahoo Fallback] table row 없음');
return [];
}
const events = [];
const dateStr = Utilities.formatDate(dateHint, CFG.TIME_ZONE, CFG.DATE_FORMAT);
for (let i = 0; i < trMatches.length; i++) {
const trHtml = trMatches[i];
const getCell_ = (testId) => {
const regex = new RegExp(`data-testid-cell=["']${testId}["'][^>]*>([\\s\\S]*?)</td>`, 'i');
const m = trHtml.match(regex);
if (m) {
let val = m[1].replace(/<[^>]+>/g, ' ');
val = val.replace(/\s+/g, ' ').trim();
return val;
}
return '';
};
const eventName = getCell_('econ_release');
const country = getCell_('country_code');
const actual = getCell_('after_release_actual');
const estimate = getCell_('consensus_estimate');
const prior = getCell_('prior_release_actual');
if (!eventName) continue;
let rawImpact = 'LOW';
if (trHtml.toLowerCase().includes('high') || trHtml.toLowerCase().includes('red') || trHtml.toLowerCase().includes('priority-3')) {
rawImpact = 'HIGH';
} else if (trHtml.toLowerCase().includes('medium') || trHtml.toLowerCase().includes('orange') || trHtml.toLowerCase().includes('priority-2')) {
rawImpact = 'MEDIUM';
}
const type = guessEventType_(eventName, country);
const finalImpact = guessImpact_(type, eventName) || rawImpact;
const countryUpper = String(country || '').toUpperCase().trim();
const allowedCountries = ['US', 'KR', 'JP'];
if (!allowedCountries.includes(countryUpper)) {
continue;
}
if (type === 'CUSTOM' && finalImpact === 'LOW') {
continue;
}
let alertText = '';
if (actual && actual !== '-') alertText += `Act: ${actual} `;
if (estimate && estimate !== '-') alertText += `Est: ${estimate} `;
if (prior && prior !== '-') alertText += `Prev: ${prior}`;
alertText = alertText.trim();
events.push({
Date: dateStr,
Event: eventName,
Type: type,
Impact: finalImpact,
Alert: alertText,
Source: 'Yahoo Finance',
SourceUrl: 'https://finance.yahoo.com/calendar/economic',
});
}
return events;
}
function parseTradingEconomicsHtml_(html) {
// data-event가 들어있는 모든 tr 매칭
const trMatches = html.match(/<tr[^>]*data-event="[^"]*"[\s\S]*?<\/tr>/gi);
if (!trMatches) {
Logger.log('[TradingEconomics] event table row 없음');
return [];
}
const events = [];
for (let i = 0; i < trMatches.length; i++) {
const trHtml = trMatches[i];
// td들로 쪼개기
const tdMatches = trHtml.match(/<td[^>]*>([\s\S]*?)<\/td>/gi);
if (!tdMatches || tdMatches.length < 9) continue;
// 1) 날짜 추출 (td[0]의 class 속성)
const td0 = tdMatches[0];
const dateMatch = td0.match(/class=["'](\d{4}-\d{2}-\d{2})["']/i);
if (!dateMatch) continue;
const dateStr = dateMatch[1];
// 2) 중요도 추출 (td[0] 내부의 calendar-date-N 클래스)
let impact = 'LOW';
if (td0.includes('calendar-date-3')) {
impact = 'HIGH';
} else if (td0.includes('calendar-date-2')) {
impact = 'MEDIUM';
}
// 3) 국가 코드 추출 (td[3] 내부 텍스트)
const td3 = tdMatches[3];
const countryIsoMatch = td3.match(/>([^<]+)</);
const countryIso = countryIsoMatch ? countryIsoMatch[1].trim().toUpperCase() : '';
// 4) 이벤트 이름 추출 (td[4] 내부의 calendar-event 링크 텍스트)
const td4 = tdMatches[4];
const eventMatch = td4.match(/class=["']calendar-event["'][^>]*>([^<]+)/i);
if (!eventMatch) continue;
const eventName = eventMatch[1].trim();
// 5) Actual, Previous, Consensus 값 추출 (HTML 태그 제거 및 공백 정규화)
const cleanTdText = (tdHtml) => {
let val = tdHtml.replace(/<[^>]+>/g, ' ');
val = val.replace(/\s+/g, ' ').trim();
return val;
};
const actualVal = cleanTdText(tdMatches[5]);
const previousVal = cleanTdText(tdMatches[6]);
const consensusVal = cleanTdText(tdMatches[7]);
// 6) 국가 필터링 (US, KR, JP만 수집)
const allowedCountries = ['US', 'KR', 'JP'];
if (!allowedCountries.includes(countryIso)) {
continue;
}
const type = guessEventType_(eventName, countryIso);
const finalImpact = guessImpact_(type, eventName) || impact;
// 중요도가 LOW이면서 핵심 분류 유형(FOMC 등)이 아닌 일반 CUSTOM 데이터는 제외
if (type === 'CUSTOM' && finalImpact === 'LOW') {
continue;
}
// ──────────────────────────
let alertText = '';
if (actualVal && actualVal !== '-') alertText += `Act: ${actualVal} `;
if (consensusVal && consensusVal !== '-') alertText += `Est: ${consensusVal} `;
if (previousVal && previousVal !== '-') alertText += `Prev: ${previousVal}`;
alertText = alertText.trim();
events.push({
Date: dateStr,
Event: eventName,
Type: type,
Impact: finalImpact,
Alert: alertText,
Source: 'Trading Economics',
SourceUrl: 'https://tradingeconomics.com/calendar',
});
}
return events;
}
/* ═══════════════════════════════════════════════════════════════════════════ *
* Naver Finance 스크래퍼
*
* 1) 경제지표 일정: https://finance.naver.com/market/news/economic.naver
* → 날짜·제목이 포함된 뉴스 목록 테이블 파싱
*
* 2) 실적 발표: https://finance.naver.com/market/news/announce.naver
* → 기업 실적 발표 일정 테이블 파싱
*
* 블록킹 대응:
* - Referer: https://finance.naver.com/ 필수
* - Accept-Language: ko-KR 설정
* - 429 → fetchWithCache_ 재시도·stale 자동 처리
* ══════════════════════════════════════════════════════════════════════════ */
function refreshFromNaver() {
const events = fetchNaverCalendar_();
if (!events.length) { toast_('Naver: 수집된 이벤트 없음 (차단 또는 일정 없음)', 4); return; }
upsertEvents_(events);
toast_(`Naver Finance 갱신: ${events.length}건`, 4);
}
function fetchNaverCalendar_() {
const headers = {
'User-Agent': CFG.CHROME_UA,
'Referer': 'https://finance.naver.com/',
'Accept-Language': 'ko-KR,ko;q=0.9,en;q=0.8',
};
const events = [];
// 1. 경제 속보 뉴스 리스트 긁기 (EUC-KR 인코딩)
try {
const url = 'https://finance.naver.com/news/news_list.naver?mode=LSS2D&section_id=101&section_id2=258';
const html = fetchWithCache_(url, CFG.CACHE_TTL_SEC, headers, 'EUC-KR');
if (html) events.push(...parseNaverNewsList_(html, 'KR', 'Naver 뉴스 속보', url));
} catch(e) { Logger.log('[Naver News List] ' + e.message); }
return events;
}
/**
* Naver Finance 뉴스 목록 HTML에서 기사 제목과 발행일을 추출.
*/
function parseNaverNewsList_(html, region, sourceName, sourceUrl) {
const events = [];
// articleSubject 및 wdate 추출용 정규식
const subjectPattern = /<dd class=["']articleSubject["']>[\s\S]*?<a href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi;
const wdatePattern = /<span class=["']wdate["']>([\s\S]*?)<\/span>/i;
let match;
while ((match = subjectPattern.exec(html)) !== null) {
const link = match[1].trim();
const titleRaw = match[2].trim();
// HTML 태그 제거 및 공백 정규화
const eventName = titleRaw.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
if (eventName.length < 5 || /^\d+$/.test(eventName)) continue;
// 이 매치 직후 최대 1000자 범위 내에서 가장 가까운 wdate 매칭
const pos = subjectPattern.lastIndex;
const subHtml = html.substring(pos, pos + 1000);
const dateMatch = wdatePattern.exec(subHtml);
if (dateMatch) {
const dateStrRaw = dateMatch[1].trim();
const dateOnly = dateStrRaw.split(' ')[0]; // YYYY-MM-DD
const eventDate = coerceDate_(dateOnly);
if (eventDate && eventName) {
const type = guessEventType_(eventName, region);
events.push({
Date: eventDate,
Event: eventName,
Type: type,
Impact: guessImpact_(type, eventName),
Alert: '',
Source: sourceName,
SourceUrl: 'https://finance.naver.com' + link,
});
}
}
}
return events;
}
/* ── 외부 URL 소스 (기존 유지) ───────────────────────────────────────────── */
function refreshFromSources() {
const props = PropertiesService.getScriptProperties();
const jsonUrl = props.getProperty(CFG.JSON_SOURCE_PROPERTY);
const csvUrl = props.getProperty(CFG.CSV_SOURCE_PROPERTY);
if (!jsonUrl && !csvUrl) { toast_('외부 URL 없음 — Script Properties 확인', 4); return; }
const events = [];
if (jsonUrl) events.push(...fetchEventsFromJson_(jsonUrl));
if (csvUrl) events.push(...fetchEventsFromCsv_(csvUrl));
if (!events.length) { toast_('외부 소스 이벤트 없음', 3); return; }
upsertEvents_(events);
validateAndSort();
toast_(`외부 이벤트 갱신: ${events.length}건`, 5);
}
function fetchEventsFromJson_(url) {
const text = fetchWithCache_(url);
if (!text) return [];
const parsed = JSON.parse(text);
if (!Array.isArray(parsed)) throw new Error('JSON source는 배열이어야 합니다.');
return parsed.map(normalizeEvent_);
}
function fetchEventsFromCsv_(url) {
const text = fetchWithCache_(url);
if (!text) return [];
const csv = Utilities.parseCsv(text);
if (csv.length < 2) return [];
const headers = csv[0].map(h => String(h || '').trim());
return csv.slice(1).map(row => {
const obj = {};
headers.forEach((h, i) => { obj[h] = row[i]; });
return normalizeEvent_(obj);
});
}
/* ═══════════════════════════════════════════════════════════════════════════ *
* fetchWithCache_ — CacheService + 재시도 + stale fallback
*
* signature: fetchWithCache_(url, ttlSec?, extraHeaders?, encoding?)
* - ttlSec: 캐시 유효기간 (기본 CFG.CACHE_TTL_SEC)
* - extraHeaders: 추가 HTTP 헤더 (스크래핑 시 UA/Referer 주입용)
* - encoding: 응답 문자셋 인코딩 (기본 'UTF-8')
* ══════════════════════════════════════════════════════════════════════════ */
function fetchWithCache_(url, ttlSec, extraHeaders, encoding) {
const cache = CacheService.getScriptCache();
const cacheKey = 'url:' + md5_(url);
// 1. Cache HIT
const hit = cache.get(cacheKey);
if (hit !== null) return hit;
// 2. Fetch with retry
const opts = {
muteHttpExceptions: true,
followRedirects: true,
headers: Object.assign({ 'User-Agent': CFG.CHROME_UA }, extraHeaders || {}),
};
const charset = encoding || 'UTF-8';
for (let attempt = 0; attempt <= CFG.MAX_RETRIES; attempt++) {
if (attempt > 0) Utilities.sleep(CFG.RETRY_BASE_MS * attempt);
let resp;
try { resp = UrlFetchApp.fetch(url, opts); }
catch(e) { Logger.log(`[fetch] 예외 (${attempt}): ${e.message}`); continue; }
const code = resp.getResponseCode();
if (code === 429 || code === 503) { Utilities.sleep(2500 * (attempt+1)); continue; } // 일시 블록
if (code === 403 || code === 401) { Logger.log(`[fetch] ${code} 영구 블록: ${url}`); break; }
if (code < 200 || code >= 300) { Logger.log(`[fetch] HTTP ${code} (${attempt}): ${url}`); continue; }
const text = resp.getContentText(charset);
try {
cache.put(cacheKey, text, ttlSec || CFG.CACHE_TTL_SEC);
} catch(e) {
// 100KB 초과 HTML은 캐싱 크기 제한으로 실패하는 것이 정상이므로 로그 남기지 않고 패스
}
return text;
}
Logger.log(`[fetch] 실패: ${url}`);
return null;
}
/* ── Upsert / Sample ─────────────────────────────────────────────────────── */
function upsertEvents_(events) {
if (!events.length) return;
const sheet = ensureSheetAndHeaders_();
const hmap = getHeaderMap_(sheet);
const rowByKey = {};
getDataObjects_(sheet, hmap).forEach(item => { if (item.Key) rowByKey[item.Key] = item.__row; });
events.forEach(ev => {
const d = coerceDate_(ev.Date);
if (!d || !ev.Event) return;
const type = String(ev.Type || 'CUSTOM').toUpperCase();
const key = ev.Key || buildKey_(d, ev.Event, type);
const vals = { Date:d, Event:ev.Event, Type:type, Impact:String(ev.Impact||'MEDIUM').toUpperCase(),
Alert:ev.Alert||'', Source:ev.Source||'', SourceUrl:ev.SourceUrl||'', Key:key };
if (rowByKey[key]) {
Object.keys(vals).forEach(h => { if (hmap[h]) sheet.getRange(rowByKey[key], hmap[h]).setValue(vals[h]); });
} else {
const row = new Array(sheet.getLastColumn()).fill('');
Object.keys(vals).forEach(h => { if (hmap[h]) row[hmap[h]-1] = vals[h]; });
sheet.appendRow(row);
}
});
}
function loadSampleDataIfEmpty() {
const sheet = ensureSheetAndHeaders_();
if (sheet.getLastRow() > 1) { toast_('이미 데이터 있음 — 삽입 생략', 4); return; }
sheet.getRange(2,1,6,5).setValues([
['2026-06-17','FOMC 금리결정','FOMC','HIGH','금리동결 시 KOSPI +1~2% 기대'],
['2026-07-28','FOMC 금리결정','FOMC','HIGH',''],
['2026-06-11','미국 CPI (5월)','US_CPI','HIGH','예상치 상회 시 당일 신규매수 자제'],
['2026-07-15','미국 CPI (6월)','US_CPI','HIGH','FOMC 전 마지막 CPI'],
['2026-06-20','삼성전자 1Q 잠정실적','EARNINGS','HIGH','반도체 섹터 선행 지표'],
['2026-06-15','옵션만기일','EXPIRY','MEDIUM','변동성 확대 구간 주의'],
]);
validateAndSort();
}
/* ── 트리거 ──────────────────────────────────────────────────────────────── */
function createDailyTrigger() {
const fn = 'runDaily';
ScriptApp.getProjectTriggers().filter(t => t.getHandlerFunction() === fn).forEach(t => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger(fn).timeBased().everyDays(1).atHour(8).create();
toast_('매일 오전 8시 트리거 설치 완료', 4);
}
function deleteProjectTriggers() {
ScriptApp.getProjectTriggers().forEach(t => ScriptApp.deleteTrigger(t));
toast_('트리거 삭제 완료', 4);
}
function setJsonSourceUrl() { _saveUrlProp_(CFG.JSON_SOURCE_PROPERTY, 'EVENT_JSON_URL'); }
function setCsvSourceUrl() { _saveUrlProp_(CFG.CSV_SOURCE_PROPERTY, 'EVENT_CSV_URL'); }
function _saveUrlProp_(k, label) {
const v = Browser.inputBox(label + '를 입력하세요.');
if (v && v !== 'cancel') PropertiesService.getScriptProperties().setProperty(k, v);
}
/* ── 이벤트 타입·임팩트 추론 헬퍼 ───────────────────────────────────────── */
/**
* 이벤트 이름으로 타입을 추론.
* region: 'US' | 'KR' (기본 'US')
*/
const TYPE_MAP_ = [
{ keys: ['FOMC','연준','Federal Open Market','Fed Rate'], type: 'FOMC' },
{ keys: ['CPI','소비자물가','Consumer Price'], type: null }, // region 분기
{ keys: ['PPI','생산자물가','Producer Price'], type: 'US_PPI' },
{ keys: ['PCE','개인소비지출','Personal Consumption'], type: 'US_PCE' },
{ keys: ['NFP','비농업','Nonfarm','Payroll'], type: 'US_NFP' },
{ keys: ['실적','잠정실적','Earnings','EPS','Revenue'], type: 'EARNINGS' },
{ keys: ['옵션만기','선물만기','만기일','Expiry','Triple Witching'], type: 'EXPIRY' },
{ keys: ['한국은행','금통위','BOK','Bank of Korea'], type: 'BOK' },
{ keys: ['환율','FX','Dollar','달러'], type: 'FX' },
{ keys: ['국채','채권','Bond','Treasury'], type: 'BOND' },
{ keys: ['BOJ','일본은행','Bank of Japan','BOJ Rate','BOJ Interest'], type: 'BOJ' },
];
function guessEventType_(eventName, region) {
const upper = String(eventName || '').toUpperCase();
const reg = String(region || '').toUpperCase().trim();
for (const rule of TYPE_MAP_) {
if (rule.keys.some(k => upper.includes(k.toUpperCase()))) {
if (rule.type === null) {
// CPI 분기: 한국 CPI vs 미국 CPI (타국 CPI는 CUSTOM 처리하여 오인 방지)
if (reg === 'KR' || upper.includes('한국') || upper.includes('KR')) return 'KR_CPI';
if (reg === 'US' || upper.includes('미국') || upper.includes('US')) return 'US_CPI';
return 'CUSTOM';
}
// PPI, PCE, NFP, FOMC 등 미국 전용 타입들은 국가 코드가 US인 경우에만 해당 타입 할당, 타국은 CUSTOM 처리
const usOnlyTypes = ['US_PPI', 'US_PCE', 'US_NFP', 'FOMC'];
if (usOnlyTypes.includes(rule.type) && reg !== 'US' && reg !== '') {
return 'CUSTOM';
}
// BOJ 일본은행 전용 타입은 국가 코드가 JP인 경우에만 해당 타입 할당, 타국은 CUSTOM 처리
if (rule.type === 'BOJ' && reg !== 'JP' && reg !== '') {
return 'CUSTOM';
}
return rule.type;
}
}
return 'CUSTOM';
}
/** 타입 기반 기본 임팩트 */
function guessImpact_(type, eventName) {
const highTypes = ['FOMC','US_CPI','US_NFP','BOK','KR_CPI','BOJ'];
const medTypes = ['US_PPI','US_PCE','EARNINGS','EXPIRY'];
if (highTypes.includes(type)) return 'HIGH';
if (medTypes.includes(type)) return 'MEDIUM';
return 'LOW';
}
/* ── 내부 헬퍼 (compact) ─────────────────────────────────────────────────── */
function safeGet_(obj, keys) {
return keys.reduce((o, k) => (o && o[k] !== undefined ? o[k] : null), obj);
}
function getSpreadsheet_() {
return CFG.SPREADSHEET_ID ? SpreadsheetApp.openById(CFG.SPREADSHEET_ID) : SpreadsheetApp.getActiveSpreadsheet();
}
function ensureSheetAndHeaders_() {
const ss = getSpreadsheet_();
const sheet = ss.getSheetByName(CFG.SHEET_NAME) || ss.insertSheet(CFG.SHEET_NAME);
const lastCol = Math.max(sheet.getLastColumn(), 1);
const existing = sheet.getRange(1,1,1,lastCol).getValues()[0].map(h => String(h||'').trim());
if (!existing.some(Boolean)) {
sheet.getRange(1,1,1,CFG.ALL_HEADERS.length).setValues([CFG.ALL_HEADERS]);
return sheet;
}
const missing = CFG.ALL_HEADERS.filter(h => !existing.includes(h));
if (missing.length) sheet.getRange(1, sheet.getLastColumn()+1, 1, missing.length).setValues([missing]);
const hmap = getHeaderMap_(sheet);
CFG.REQUIRED_HEADERS.forEach(h => { if (!hmap[h]) throw new Error(`필수 헤더 없음: ${h}`); });
return sheet;
}
function getHeaderMap_(sheet) {
const map = {};
sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0]
.forEach((h,i) => { const k=String(h||'').trim(); if(k) map[k]=i+1; });
return map;
}
function getDataObjects_(sheet, hmap) {
const lastRow = sheet.getLastRow();
if (lastRow < 2) return [];
const headers = Object.keys(hmap);
const lastCol = sheet.getLastColumn();
return sheet.getRange(2,1,lastRow-1,lastCol).getValues().map((row,r) => {
const obj = { __row: r+2 };
headers.forEach(h => { obj[h] = row[hmap[h]-1]; });
return obj;
});
}
function normalizeEvent_(obj) {
return {
Date: obj.Date || obj.date,
Event: obj.Event || obj.event || obj.title || obj.name,
Type: obj.Type || obj.type || 'CUSTOM',
Impact: obj.Impact || obj.impact || 'MEDIUM',
Alert: obj.Alert || obj.alert || '',
Source: obj.Source || obj.source || '',
SourceUrl: obj.SourceUrl || obj.sourceUrl || obj.url || '',
Key: obj.Key || obj.key || '',
};
}
function coerceDate_(v) {
if (v instanceof Date && !isNaN(v)) return new Date(v.getFullYear(), v.getMonth(), v.getDate());
if (typeof v === 'string') {
const m = v.trim().match(/^(\d{4})[-./](\d{1,2})[-./](\d{1,2})/);
if (m) return new Date(+m[1], +m[2]-1, +m[3]);
}
return null;
}
function todayKst_() {
return coerceDate_(Utilities.formatDate(new Date(), CFG.TIME_ZONE, CFG.DATE_FORMAT));
}
function daysBetween_(a, b) {
return Math.round(
(new Date(b.getFullYear(),b.getMonth(),b.getDate()) -
new Date(a.getFullYear(),a.getMonth(),a.getDate())) / 86400000
);
}
function buildKey_(dateObj, eventName, type) {
return md5_([Utilities.formatDate(dateObj,CFG.TIME_ZONE,CFG.DATE_FORMAT),
String(type||'').toUpperCase(), String(eventName||'').trim()].join('|'));
}
function md5_(text) {
return Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, text, Utilities.Charset.UTF_8)
.map(b => ('0'+(b<0?b+256:b).toString(16)).slice(-2)).join('');
}
function buildEmailBody_(events) {
const fmt = d => d instanceof Date ? Utilities.formatDate(d,CFG.TIME_ZONE,CFG.DATE_FORMAT) : String(d);
return [
'시장 이벤트 임박 알림','',
'기준: '+Utilities.formatDate(new Date(),CFG.TIME_ZONE,'yyyy-MM-dd HH:mm:ss'),'',
...events.flatMap((item,i) => [
`${i+1}. [${item.Impact}] ${fmt(item.Date)} / D-${item.DaysLeft}`,
` Event: ${item.Event}`, ` Type: ${item.Type}`,
...(item.Alert?[` Alert: ${item.Alert}`]:[]),'',
]),
'이 알림은 자동 알림이며 투자 판단의 최종 근거가 아닙니다.',
].join('\n');
}
function applyFormatting_(sheet, hmap) {
const lastRow = Math.max(sheet.getLastRow(),1), lastCol = Math.max(sheet.getLastColumn(),1);
sheet.getRange(1,1,1,lastCol).setFontWeight('bold');
sheet.setFrozenRows(1);
for (let c=1;c<=lastCol;c++) sheet.autoResizeColumn(c);
if (lastRow >= 2) {
if (hmap.Impact) sheet.getRange(2,hmap.Impact, lastRow-1,1).setFontWeight('bold');
if (hmap.DaysLeft) sheet.getRange(2,hmap.DaysLeft,lastRow-1,1).setNumberFormat('0');
}
}
function toast_(msg, sec) {
try {
const activeSs = SpreadsheetApp.getActive();
if (activeSs) {
activeSs.toast(msg, 'Market Calendar', sec);
} else {
Logger.log('[TOAST] ' + msg);
}
} catch (e) {
Logger.log('[TOAST] ' + msg);
}
}
/**
* 용량을 극도로 많이 소모하는 Script Properties의 캐시성 데이터(stale_url, cal_parsed 등)를 청소.
* SPREADSHEET_ID 나 sf_w2_ranks_json 같은 중요 설정/운영 데이터는 보호합니다.
*/
function cleanUpProperties() {
const props = PropertiesService.getScriptProperties();
const keys = props.getKeys();
let deleteCount = 0;
// SPREADSHEET_ID, sf_w2_ranks_json, EVENT_JSON_URL, EVENT_CSV_URL, HARNESS_VERBOSE_LOG 등 설정은 제외
const protectedKeys = ['SPREADSHEET_ID', 'sf_w2_ranks_json', 'EVENT_JSON_URL', 'EVENT_CSV_URL', 'HARNESS_VERBOSE_LOG'];
keys.forEach(k => {
if (protectedKeys.includes(k)) {
return;
}
// 캐시 관련 접두사를 가진 항목 및 임시 런타임 상태값 삭제
const shouldDelete =
k.indexOf('stale_url:') === 0 ||
k.indexOf('yahoo_cal_parsed:') === 0 ||
k.indexOf('te_cal_parsed:') === 0 ||
k.indexOf('url:') === 0 ||
k.indexOf('fetch_budget_') === 0 ||
k.indexOf('fetch_fail_') === 0 ||
k.indexOf('fetch_circuit_') === 0 ||
k.indexOf('fetch_session_') === 0 ||
k.indexOf('cs_') === 0;
if (shouldDelete) {
props.deleteProperty(k);
deleteCount++;
}
});
toast_(`프로퍼티 캐시 청소 완료: ${deleteCount}건 삭제`, 5);
}
+1456
View File
File diff suppressed because it is too large Load Diff
+2964
View File
File diff suppressed because it is too large Load Diff
+446
View File
@@ -0,0 +1,446 @@
// gas_report.gs - Report & template generation
// getDailyBrief, getSummaryJson, getTradeTemplate
// Changes only when report format changes. Rarely touched during engine work.
// GAS global scope: functions in gas_lib.gs / gas_data_feed.gs callable directly
// ── E1: 일일 의사결정 브리핑 ─────────────────────────────────────────────────
// 시장 상태·포트폴리오 건강·액션 목록·주의 종목·7일 이벤트를 한 JSON으로 통합.
// doGet(?view=brief) 또는 cacheAllViews()에서 매일 1회 생성.
function getDailyBrief(sellPriorityViewInput) {
const macro = getMacroJson();
const settings = readSettingsTab_();
const port = getPortfolioJson();
const events = getEventRiskJson();
const today = Utilities.formatDate(new Date(), "Asia/Seoul", "yyyy-MM-dd");
const holdings = port.holdings ?? [];
// ── 액션 분류: Final_Action canonical 기준 (A-1/B-1 — Allowed_Action 기반 제거) ──
// Final_Action이 canonical output field. Allowed_Action은 중간 계산값.
const BUY_FINALS_ = new Set(["BUY_STAGE1_READY","BUY_BREAKOUT_PILOT_ONLY","BUY_PULLBACK_WAIT"]);
const SELL_FINALS_ = new Set(["SELL_READY"]);
const EXIT_FINALS_ = new Set(["EXIT_SIGNAL","EXIT_REVIEW"]);
const sellList = holdings.filter(h => SELL_FINALS_.has(h.Final_Action));
const exitList = holdings.filter(h => EXIT_FINALS_.has(h.Final_Action));
const buyList = holdings.filter(h => BUY_FINALS_.has(h.Final_Action));
const watchList = holdings.filter(h => h.Final_Action === "WATCH_TIMING_SETUP");
const holdList = holdings.filter(h =>
!SELL_FINALS_.has(h.Final_Action) && !EXIT_FINALS_.has(h.Final_Action) &&
!BUY_FINALS_.has(h.Final_Action) && h.Final_Action !== "WATCH_TIMING_SETUP"
);
// 주의 종목
const stage2Pass = holdings.filter(h => h.Stage2_Gate === "PASS");
const timeStopNear= holdings.filter(h => Number.isFinite(+h.Days_To_Time_Stop)
&& +h.Days_To_Time_Stop >= 0
&& +h.Days_To_Time_Stop <= 7);
const overweight = holdings.filter(h => h.Band_Status === "OVERWEIGHT");
const tp1Near = holdings.filter(h => Number.isFinite(+h.Profit_Pct) && +h.Profit_Pct >= 10);
// 포트폴리오 건강 판단
const heatVal = parseFloat(macro.total_heat_pct);
const fcVal = parseFloat(macro.fc_budget_pct);
const heatOk = Number.isFinite(heatVal) && heatVal < 10;
const heatCautionB= Number.isFinite(heatVal) && heatVal >= 7 && heatVal < 10;
const heatBlockB = Number.isFinite(heatVal) && heatVal >= 10;
const fcOk = Number.isFinite(fcVal) && fcVal < 100;
const regimeStr = String(macro.market_regime ?? "");
const isRiskOffB = regimeStr === "RISK_OFF" || regimeStr === "RISK_OFF_CANDIDATE";
const nrf = macro.net_return_feedback;
const orbitAdj= parseInt(macro.orbit_slot_adj) || 0;
// account_snapshot freshness 체크
const acctFresh = checkAccountSnapshotFreshness_();
// 텍스트 브리핑 (ChatGPT 직접 복붙용)
const L = [];
const hardBlockWarn = String(settings["cash_floor_hard_block_warning"] ?? "").trim();
const accountConfirmWarn = String(settings["account_snapshot_confirmation_warning"] ?? "").trim();
const cashLedgerWarn = String(settings["cash_ledger_warning"] ?? "").trim();
if (hardBlockWarn) L.push(`[긴급 경고] ${hardBlockWarn}`);
if (accountConfirmWarn) L.push(`[운영 경고] ${accountConfirmWarn}`);
if (cashLedgerWarn) L.push(`[운영 경고] ${cashLedgerWarn}`);
L.push(`[시장] ${macro.market_regime} / MRS ${macro.mrs_score}/10 / VIX ${macro.vix} / KOSPI ${macro.kospi} / USD/KRW ${macro.usd_krw}`);
const heatTag = heatBlockB ? "⚠HF005:BLOCK" : heatCautionB ? "⚠CAUTION:수량50%감액" : "OK";
L.push(`[포트폴리오] HEAT ${macro.total_heat_pct}%(${heatTag}) / FC ${macro.fc_budget_pct}%(${fcOk?"OK":"⚠EXHAUSTED"}) / ${nrf} / BUCKET ${macro.bucket_status}`);
if (isRiskOffB) L.push(`[⚠ 레짐 차단] ${regimeStr} — 신규 매수 전면 차단, 보유 종목 50% 단계 축소 검토`);
const bayesSourceTag = macro.bayesian_data_source === "actual" ? "실제거래기반" : "기본값(거래이력없음)";
L.push(`[Bayesian] ${macro.bayesian_label} (${macro.bayesian_multiplier}×) — ${bayesSourceTag}`);
if (acctFresh.fresh === false) L.push(`[⚠ account_snapshot STALE] ${acctFresh.reason} — 손절가·수량 재확인 필요`);
else if (acctFresh.fresh === null) L.push(`[⚠ account_snapshot] ${acctFresh.reason}`);
// 데이터 신선도 경고 — PRICE_STALE / PRICE_QUOTE_ONLY / FLOW_STALE
const priceStaleList_ = holdings.filter(h => h.Price_Status === "PRICE_STALE");
const quoteOnlyList_ = holdings.filter(h => h.Price_Status === "PRICE_QUOTE_ONLY");
const flowStaleList_ = holdings.filter(h => String(h.Missing_Fields ?? "").includes("FLOW_STALE"));
if (priceStaleList_.length)
L.push(`[⚠ 가격 스테일] ${priceStaleList_.map(h => h.Name).join(", ")} — OHLC 날짜 오래됨, runDataFeed 재실행 권장`);
if (quoteOnlyList_.length)
L.push(`[⚠ 호가전용] ${quoteOnlyList_.map(h => h.Name).join(", ")} — OHLC 수집 실패, MA/ATR 결측 → OBSERVE_ONLY 처리`);
if (flowStaleList_.length)
L.push(`[⚠ 수급 스테일] ${flowStaleList_.map(h => h.Name).join(", ")} — 외국인/기관 수급 날짜 오래됨`);
if (orbitAdj !== 0)
L.push(`[Orbit] ${macro.orbit_state} → 공격슬롯 ${orbitAdj>0?"+":""}${orbitAdj}개 / 현금조정 ${macro.orbit_cash_adj}%p`);
// ── C-1: Final_Action 기준 단일 우선순위 목록 ─────────────────────────────
// 우선순위 순서: SELL_READY > EXIT_* > BUY > WATCH > HOLD
// 같은 그룹 내에서는 Final_Rank(Priority_Score) 오름차순
const byRank = (arr) => [...arr].sort((a, b) => (+a.Final_Rank || 999) - (+b.Final_Rank || 999));
L.push("─".repeat(44));
L.push(`[오늘 액션] — ${today} (Final_Action 기준, 우선순위 정렬)`);
if (sellList.length) {
L.push(" ▶ SELL_READY (즉시 HTS 주문 가능)");
byRank(sellList).forEach((h, i) => {
const r = h.Action_Reason || `${h.Sell_Action} ${h.Sell_Qty}주 @${h.Sell_Limit_Price}`;
const p = h.Action_Params ? `\n ${h.Action_Params}` : "";
L.push(` ${i+1}. ${h.Name} → ${r}${p}`);
});
}
if (exitList.length) {
L.push(" ▶ EXIT_SIGNAL / REVIEW (캡처 → ChatGPT 수량 계산 후 매도)");
byRank(exitList).forEach((h, i) => {
const r = h.Action_Reason || `${h.Final_Action}(RW${h.RW_Partial})`;
const p = h.Action_Params ? ` | ${h.Action_Params}` : "";
L.push(` ${sellList.length+i+1}. ${h.Name}[${h.Final_Action}] → ${r}${p}`);
});
}
if (buyList.length) {
L.push(" ▶ BUY (진입 조건 충족)");
byRank(buyList).forEach((h, i) => {
const constr = h.Pos_Size_Constraint || "미계산*";
const rank_ = sellList.length + exitList.length + i + 1;
L.push(` ${rank_}. ${h.Name}[${h.Final_Action}] → ${h.Action_Reason || ""}`);
const params_ = h.Action_Params || `목표 ${h.Pos_Size_Qty}주[${constr}]`;
L.push(` ${params_}`);
});
}
if (watchList.length) {
L.push(" ▶ WATCH (타이밍 대기)");
byRank(watchList).forEach((h, i) => {
const rank_ = sellList.length + exitList.length + buyList.length + i + 1;
L.push(` ${rank_}. ${h.Name} → ${h.Action_Reason || `SS001:${h.SS001_Grade} 타이밍미충족`}`);
});
}
if (holdList.length) {
L.push(" ▶ HOLD / BLOCK");
byRank(holdList).forEach((h, i) => {
const rank_ = sellList.length + exitList.length + buyList.length + watchList.length + i + 1;
L.push(` ${rank_}. ${h.Name}[${h.Allowed_Action}] → ${h.Action_Reason || h.Allowed_Action}`);
});
}
if (!sellList.length && !exitList.length && !buyList.length && !watchList.length)
L.push(" HOLD — 오늘 액션 없음");
// 단일 진실원천: sell_priority는 반드시 runSellPriority() 결과만 사용
const sellPriorityView_ = sellPriorityViewInput || runSellPriority();
const _cashRaiseCands_ = Array.isArray(sellPriorityView_.sell_priority_table)
? sellPriorityView_.sell_priority_table
: [];
const _cashBelowTgt_ = isRiskOffB || (() => {
const cp = parseFloat(macro.immediate_cash_pct ?? macro.cash_pct ?? "");
const tp = parseFloat(macro.target_cash_pct ?? settings["weekly_target_cash_pct"] ?? "10");
return Number.isFinite(cp) && Number.isFinite(tp) && cp < tp;
})();
if (_cashBelowTgt_ && _cashRaiseCands_.length) {
L.push("─".repeat(44));
const gapReason = isRiskOffB
? `REGIME_TRIM_50 발동(${regimeStr})`
: `현금 부족 → sell_priority_engine`;
L.push(`[현금확보 매도우선순위] — ${gapReason}`);
L.push(" spec: ①하드스탑>②매도신호>③중복ETF>④손실위성>⑥익절>⑨코어주도주(마지막)");
L.push(" ⚠ 매도수량은 HTS 캡처 제공 후 결정 — 수량 미제공 시 수량 산출 금지(P1규칙)");
_cashRaiseCands_.slice(0, 8).forEach((c, i) => {
const pStr = (c.profit_pct !== "" && c.profit_pct !== null)
? ` (${Number(c.profit_pct) >= 0 ? "+" : ""}${Number(c.profit_pct).toFixed(1)}%)`
: "";
const etfTag = c.is_etf ? "[ETF]" : "";
const clTag = c.is_core_leader ? "[주도주⛔매도금지]" : "";
L.push(` ${i+1}. ${c.tier_label} ${c.name}${etfTag}${clTag} W:${c.weight_pct}%${pStr} RW:${c.rw_partial} Score:${c.sell_priority_score}`);
if (c.trim_style || c.rebound_holdback_score)
L.push(` └ trim=${c.trim_style || "N/A"} rebound_holdback=${c.rebound_holdback_score ?? 0}${c.rebound_holdback_reason ? ` | ${c.rebound_holdback_reason}` : ""}`);
if (c.action_params) L.push(` └ ${c.action_params}`);
if (c.hold_reason) L.push(` └ ⚠ ${c.hold_reason}`);
});
}
// 주의 종목 섹션
if (stage2Pass.length || timeStopNear.length || overweight.length || tp1Near.length) {
L.push("[주의]");
stage2Pass.forEach(h => L.push(` ${h.Name} Stage2_Gate=PASS → 2단계 진입 검토 (진입가 ${h.Limit_Price_Est ?? "N/A"})`));
timeStopNear.forEach(h => L.push(` ${h.Name} Time_Stop ${h.Days_To_Time_Stop}일 남음 (${h.Time_Stop_Date})`));
overweight.forEach(h => L.push(` ${h.Name} OVERWEIGHT ${h.Weight_Pct}% (상한 7%)`));
tp1Near.forEach(h => L.push(` ${h.Name} +${h.Profit_Pct}% → TP1(${h.TP1_Price}원) 근접`));
}
if (events.upcoming_7d?.length) {
L.push("[7일 이벤트]");
events.upcoming_7d.forEach(ev => L.push(` ${ev.Date}(D+${ev.DaysLeft}) ${ev.Event} [${ev.Impact}]`));
}
// brief_ — holdings row → JSON 요약 (API 소비자용)
const brief_ = (h) => ({
ticker: h.Ticker, name: h.Name,
final_action: h.Final_Action, // canonical output field
action_reason: h.Action_Reason, // 왜 이 액션인가
action_params: h.Action_Params, // 실행 파라미터 압축 (C-3)
final_rank: h.Final_Rank,
allowed_action: h.Allowed_Action,
ss001_grade: h.SS001_Grade, ss001_norm_score: h.SS001_Norm_Score,
rw_partial: h.RW_Partial,
weight_pct: h.Weight_Pct, profit_pct: h.Profit_Pct,
stage2_gate: h.Stage2_Gate, band_status: h.Band_Status,
limit_price_est: h.Limit_Price_Est,
stop_price_est: h.Stop_Price_Est, stop_price_source: h.Stop_Price_Source,
pos_size_qty: h.Pos_Size_Qty, pos_size_constraint: h.Pos_Size_Constraint,
tp1_price: h.TP1_Price, tp1_qty: h.TP1_Qty,
tp2_price: h.TP2_Price, tp2_qty: h.TP2_Qty,
entry_mode: h.Entry_Mode, entry_mode_gate: h.Entry_Mode_Gate,
entry_mode_reason: h.Entry_Mode_Reason,
timing_score_entry: h.Timing_Score_Entry,
timing_score_exit: h.Timing_Score_Exit,
timing_action: h.Timing_Action,
timing_block_reason: h.Timing_Block_Reason,
sell_action: h.Sell_Action,
sell_ratio_pct: h.Sell_Ratio_Pct,
sell_limit_price: h.Sell_Limit_Price,
sell_reason: h.Sell_Reason,
sell_validation: h.Sell_Validation,
cash_preserve_style: h.Cash_Preserve_Style || "",
cash_preserve_ratio: h.Cash_Preserve_Ratio || "",
cash_preserve_reason: h.Cash_Preserve_Reason || "",
rsi14: h.RSI14, disparity: h.Disparity, ma20_slope: h.MA20_Slope,
exit_signal_detail: h.Exit_Signal_Detail,
});
return {
date: today,
brief_text: L.join("\n"),
market: {
regime: macro.market_regime, mrs_score: macro.mrs_score,
vix: macro.vix, kospi: macro.kospi, usd_krw: macro.usd_krw,
sp500_ret5d: macro.sp500_ret5d,
},
portfolio_health: {
heat_pct: macro.total_heat_pct, heat_ok: heatOk,
heat_tag: heatTag,
heat_block: heatBlockB, heat_caution: heatCautionB,
fc_budget_pct: macro.fc_budget_pct, fc_ok: fcOk,
net_return_feedback: nrf,
bucket_status: macro.bucket_status,
regime_buy_blocked: isRiskOffB,
bayesian_label: macro.bayesian_label,
bayesian_multiplier: macro.bayesian_multiplier,
},
orbit: {
gap_pct: macro.orbit_gap_pct, state: macro.orbit_state,
slot_adjustment: orbitAdj, cash_adjustment: macro.orbit_cash_adj,
},
// Final_Action canonical 분류 (A-1/B-1)
actions: {
sell_ready: sellList.map(brief_),
exit_signals: exitList.map(brief_),
buy_signals: buyList.map(brief_),
watch_signals: watchList.map(brief_),
hold_signals: holdList.map(brief_),
},
alerts: {
stage2_ready: stage2Pass.map(h=>({ticker:h.Ticker,name:h.Name,profit_pct:h.Profit_Pct,limit_price_est:h.Limit_Price_Est})),
time_stop_near: timeStopNear.map(h=>({ticker:h.Ticker,name:h.Name,days_left:h.Days_To_Time_Stop,stop_date:h.Time_Stop_Date})),
overweight: overweight.map(h=>({ticker:h.Ticker,name:h.Name,weight_pct:h.Weight_Pct})),
tp1_near: tp1Near.map(h=>({ticker:h.Ticker,name:h.Name,profit_pct:h.Profit_Pct,tp1_price:h.TP1_Price,tp2_price:h.TP2_Price})),
},
upcoming_events: events.upcoming_7d,
account_snapshot_freshness: acctFresh,
data_quality: {
price_stale: priceStaleList_.map(h=>({ticker:h.Ticker,name:h.Name,price_date:h.Price_Date})),
quote_only: quoteOnlyList_.map(h=>({ticker:h.Ticker,name:h.Name})),
flow_stale: flowStaleList_.map(h=>({ticker:h.Ticker,name:h.Name,missing_fields:h.Missing_Fields})),
},
// sell_priority_engine 출력 (spec: portfolio_exposure.yaml:sell_priority_engine)
// 활성화: REGIME_TRIM_50 또는 현금 부족. ETF→손실위성→코어주도주 순서로 정렬.
cash_raise: _cashBelowTgt_ ? {
active: true,
reason: isRiskOffB ? `REGIME_TRIM_50(${regimeStr})` : "cash_below_target",
prohibition: "매도수량은 HTS 캡처 제공 후 결정. 수량 미제공 시 수량 기재 금지(spec:P1규칙).",
sell_priority_table: _cashRaiseCands_,
sector_exposure_summary: sellPriorityView_.sector_exposure ?? sellPriorityView_.sector_exposure_summary ?? {},
} : { active: false },
};
}
// ── E3: 거래 진입 템플릿 생성 ────────────────────────────────────────────────
// BUY_CANDIDATE/WATCH_CANDIDATE 종목에 대해 performance 탭 입력 행 + 진입 체크리스트 반환.
// doGet(?view=trade_template&ticker=064350)
function getTradeTemplate(ticker) {
if (!ticker) return { error: "ticker 파라미터 필요 (?view=trade_template&ticker=XXXXXX)" };
const allData = sheetToJson("data_feed");
const row = allData.find(r => String(r.Ticker) === String(ticker) || r.Name === ticker);
if (!row) return { error: `ticker ${ticker} not found in data_feed` };
const macro = getMacroJson();
const today = Utilities.formatDate(new Date(), "Asia/Seoul", "yyyy-MM-dd");
const sector = TICKER_SECTOR_MAP[ticker] ?? "N/A";
// 진입 체크리스트 — 각 항목 true/false
const checklist = {
data_quality: row.Price_Status === "PRICE_OK",
no_dart_risk: !row.DART_Risk || row.DART_Risk === "" || row.DART_Risk === "N",
liquidity_ok: row.Liquidity_Status === "OK",
timing_ready: ["BUY_STAGE1_READY","BUY_PULLBACK_WAIT","BUY_BREAKOUT_PILOT_ONLY"].includes(row.Timing_Action),
leader_gate: ["PASS","EXPLORE_CANDIDATE","WATCH_ONLY"].includes(row.Leader_Gate),
ac_gate: row.AC_Gate === "CLEAR",
flow_credit_ok: parseFloat(row.Flow_Credit) >= 0.4,
regime_ok: ["RISK_ON","SECULAR_LEADER_RISK_ON","LEADER_CONCENTRATION"].includes(macro.market_regime),
heat_ok: Number.isFinite(parseFloat(macro.total_heat_pct)) && parseFloat(macro.total_heat_pct) < 10,
fc_budget_ok: Number.isFinite(parseFloat(macro.fc_budget_pct)) && parseFloat(macro.fc_budget_pct) < 100,
nr_feedback_ok: macro.net_return_feedback !== "REDUCED",
ee_positive: parseFloat(row.EE_Est) > 0,
ss001_grade_ok: ["A","B"].includes(row.SS001_Grade),
};
const passCount = Object.values(checklist).filter(Boolean).length;
const totalCheck = Object.keys(checklist).length;
const gateStatus = passCount === totalCheck ? "ALL_PASS"
: passCount >= totalCheck - 2 ? "MINOR_ISSUES"
: "BLOCK";
return {
ticker,
name: row.Name,
sector,
generated_at: today,
gate_status: gateStatus,
gate_score: `${passCount}/${totalCheck}`,
checklist,
// performance 탭에 바로 붙여넣을 수 있는 행 템플릿
performance_tab_template: {
trade_id: `${today.replace(/-/g,"")}${ticker}`,
ticker,
sector,
entry_date: today,
entry_price: row.Limit_Price_Est ?? "",
entry_stage: "stage_1",
quantity: row.Pos_Size_Qty ?? "",
stop_price_at_entry: row.Stop_Price_Est ?? "",
target_price_at_entry: row.Target_Price ?? "",
exit_date: "",
exit_price: "",
exit_reason: "",
pnl_pct: "",
holding_days: "",
entry_c1_score: row.C1_Price ?? "",
entry_c2_score: row.C2_RelStr ?? "",
entry_c3_score: row.C3_VolSurge ?? "",
entry_c4_score: row.C4_Flow ?? "",
entry_c5_score: row.C5_Sector ?? "",
entry_mode: row.Entry_Mode ?? "",
entry_gate: row.Entry_Mode_Gate ?? "",
timing_action: row.Timing_Action ?? "",
timing_score_entry: row.Timing_Score_Entry ?? "",
timing_score_exit: row.Timing_Score_Exit ?? "",
anti_climax_gate: row.AC_Gate ?? "",
flow_credit: row.Flow_Credit ?? "",
entry_mrs_score: macro.mrs_score ?? "",
fc_bucket: "",
},
current_state: {
close: row.Close,
allowed_action: row.Allowed_Action,
timing_action: row.Timing_Action,
timing_score_entry: row.Timing_Score_Entry,
timing_score_exit: row.Timing_Score_Exit,
timing_block_reason: row.Timing_Block_Reason,
sell_action: row.Sell_Action,
sell_ratio_pct: row.Sell_Ratio_Pct,
sell_qty: row.Sell_Qty,
sell_limit_price: row.Sell_Limit_Price,
sell_price_source: row.Sell_Price_Source,
sell_reason: row.Sell_Reason,
sell_validation: row.Sell_Validation,
ss001_grade: row.SS001_Grade,
ss001_total: row.SS001_Total,
flow_credit: row.Flow_Credit,
rw_partial: row.RW_Partial,
limit_price_est: row.Limit_Price_Est,
stop_price_est: row.Stop_Price_Est,
stop_price_source: row.Stop_Price_Source,
ee_est: row.EE_Est,
pos_size_qty: row.Pos_Size_Qty,
upside_pct: row.Upside_Pct,
atr20: row.ATR20,
tp1_price: row.TP1_Price,
tp1_qty: row.TP1_Qty,
tp2_price: row.TP2_Price,
tp2_qty: row.TP2_Qty,
dart_risk: row.DART_Risk,
days_to_earnings: row.Days_To_Earnings,
},
};
}
function getSummaryJson() {
// ChatGPT 포트폴리오 분석에 최적화된 통합 뷰
const sectors = getSectorFlowJson();
const port = getPortfolioJson();
const macro = getMacroJson();
const events = getEventRiskJson();
// 포트폴리오 전체 수급 요약
const holdings = port.holdings;
const totalFrg5 = holdings.reduce((s,h) => s + (parseFloat(h.Frg_5D) || 0), 0);
const totalInst5 = holdings.reduce((s,h) => s + (parseFloat(h.Inst_5D) || 0), 0);
const flowOkCount = holdings.filter(h => h.Flow_OK === "Y").length;
// SS001 등급 분포 및 Allowed_Action 집계
const ss001Dist = { A: 0, B: 0, C: 0, D: 0 };
const actionDist = {};
holdings.forEach(h => {
const g = h["SS001_Grade"];
if (g in ss001Dist) ss001Dist[g]++;
const a = h["Allowed_Action"] || "UNKNOWN";
actionDist[a] = (actionDist[a] ?? 0) + 1;
});
return {
portfolio_flow_summary: {
total_holdings: holdings.length,
data_ok_count: flowOkCount,
portfolio_frg_5d_total: totalFrg5,
portfolio_inst_5d_total: totalInst5,
portfolio_indiv_5d_total: -(totalFrg5 + totalInst5),
},
ss001_grade_distribution: ss001Dist,
action_distribution: actionDist,
sector_summary: {
total_sectors: sectors.count,
top_inflow_sectors: sectors.top_inflow,
outflow_warning_sectors: sectors.outflow_warning,
strong_smart_money_sectors: sectors.strong_smart_money,
},
macro_snapshot: {
vix: macro.vix,
usd_krw: macro.usd_krw,
kospi: macro.kospi,
sp500_5d_ret: macro.sp500_ret5d,
market_regime: macro.market_regime,
mrs_score: macro.mrs_score,
bayesian_multiplier: macro.bayesian_multiplier,
total_heat_pct: macro.total_heat_pct,
fc_budget_pct: macro.fc_budget_pct,
net_return_feedback: macro.net_return_feedback,
orbit_gap_pct: macro.orbit_gap_pct,
orbit_state: macro.orbit_state,
orbit_slot_adj: macro.orbit_slot_adj,
bucket_status: macro.bucket_status,
bucket_detail: macro.bucket_detail,
},
event_alerts: events.upcoming_7d,
holdings_detail: holdings,
sector_detail: sectors.sectors,
macro_detail: macro.indicators,
macro_computed: macro.computed_summary,
};
}
@@ -0,0 +1,25 @@
# ADR-0001 Adopt active artifact manifest
## Context
Temp contains multiple artifact versions and the runtime must read only one active path per artifact key.
## Decision
Use `runtime/active_artifact_manifest.yaml` as the only runtime selection source.
See `spec/09_decision_flow.yaml` and `tools/validate_active_manifest.py` for the runtime gate and selection checks.
## Alternatives
- Scan Temp directly
- Pick latest timestamp
## Consequences
- Deterministic runtime file selection
- Lower stale-read risk
## Rollback
Restore prior manifest only if validation fails.
## Affected files
- runtime/active_artifact_manifest.yaml
- spec/09_decision_flow.yaml
- tools/validate_active_manifest.py
+22
View File
@@ -0,0 +1,22 @@
# ADR-0002 Authority matrix
## Context
Output fields need a single declared owner to prevent duplicate writers.
## Decision
Maintain a governance authority matrix and output-field owner ledger.
## Alternatives
- Embed ownership in ad hoc notes
## Consequences
- Ownership collision checks become explicit
- Review is easier for numeric outputs
## Rollback
Keep the prior ledger alongside the new one.
## Affected files
- governance/authority_matrix.yaml
- spec/03_formulas/output_field_owner_ledger.yaml
+23
View File
@@ -0,0 +1,23 @@
# ADR-0003 Baseline metrics
## Context
Refactor progress needs a fixed baseline.
## Decision
Record repository baseline metrics as build output.
See `tools/validate_calibration_registry_v1.py` and `spec/08_scoring_rules.yaml` for baseline-linked validation context.
## Alternatives
- Report metrics manually in review notes
## Consequences
- Delta tracking becomes deterministic
## Rollback
Regenerate from a prior commit if needed.
## Affected files
- Temp/refactor_baseline_metrics_v1.json
- tools/validate_calibration_registry_v1.py
- spec/08_scoring_rules.yaml
+21
View File
@@ -0,0 +1,21 @@
# ADR-0004 Rule lifecycle
## Context
Rule proliferation needs a lifecycle policy.
## Decision
Use proposed -> shadow -> active -> deprecated -> removed.
## Alternatives
- Free-form status labels
## Consequences
- Deprecation is explicit
- Active router reference checks become possible
## Rollback
Downgrade the rule to shadow if needed.
## Affected files
- governance/rule_lifecycle.yaml
@@ -0,0 +1,22 @@
# ADR-0005 Formula domain split
## Context
The monolithic formula registry needs domain separation.
## Decision
Split formulas by domain and build a normalized registry.
## Alternatives
- Keep a single registry file
## Consequences
- Review scope is reduced
- Formula ownership is easier to track
## Rollback
Rebuild normalized registry from the monolith.
## Affected files
- spec/formulas/*
- spec/03_formulas/formula_registry.normalized.yaml
@@ -0,0 +1,24 @@
# ADR-0006 Final decision packet
## Context
The renderer needs a single canonical packet.
## Decision
Use a versioned final decision packet as the only render input.
See `spec/07_output_schema.yaml` and `tools/validate_report_packet_sync_v1.py` for the canonical packet/render contract.
## Alternatives
- Read many Temp files directly
## Consequences
- Provenance checks become centralized
## Rollback
Keep the previous packet version available.
## Affected files
- Temp/final_decision_packet_v3.json
- Temp/final_decision_packet_active.json
- spec/07_output_schema.yaml
- tools/validate_report_packet_sync_v1.py
+20
View File
@@ -0,0 +1,20 @@
# ADR-0007 Shadow ledger
## Context
Blocked items still need visible computed values.
## Decision
Separate executable order table from shadow ledger.
## Alternatives
- Hide blocked numbers
## Consequences
- User review is preserved
## Rollback
Show the prior packet alongside the new ledger if needed.
## Affected files
- spec/outputs/shadow_ledger_contract.yaml
+20
View File
@@ -0,0 +1,20 @@
# ADR-0008 Renderer contract
## Context
Low-capability LLM rendering needs a closed contract.
## Decision
Fix the input packet, section order, and numeric copy rules.
## Alternatives
- Free-form narrative generation
## Consequences
- Lower hallucination risk
## Rollback
Use the prior prompt only if schema validation fails.
## Affected files
- prompts/low_capability_report_renderer.md
+20
View File
@@ -0,0 +1,20 @@
# ADR-0009 Number provenance audit
## Context
Every investment number must be traceable.
## Decision
Require source_ref and formula_id for investment numbers.
## Alternatives
- Allow implicit report numbers
## Consequences
- Ungrounded numbers are detectable
## Rollback
Rebuild the report from a provenance-backed packet.
## Affected files
- tools/validate_number_provenance_v2.py
@@ -0,0 +1,20 @@
# ADR-0010 Change request template
## Context
Rule and formula changes need structured evidence.
## Decision
Use a mandatory change-request template for active changes.
## Alternatives
- Free-form change notes
## Consequences
- Rollback and testing become explicit
## Rollback
Reject changes that lack the required fields.
## Affected files
- governance/change_request_template.yaml
+19
View File
@@ -0,0 +1,19 @@
# ADR-0011: QEDD 방법론 채택
## 상태
준비됨 (Proposed)
## 배경
퀀트 엔진의 복잡도가 증가함에 따라, 저성능 LLM에서도 동일한 투자 결론을 재현하고 운영 안정성을 확보하기 위한 결정론적 개발 방법론이 필요함.
## 결정
QEDD(Quant Evidence-Driven Deterministic Development) 방법론을 채택함.
1. 모든 판단 로직은 YAML 계약(spec)에 근거함.
2. 모든 수치 계산은 Python Canonical 엔진에서만 수행함.
3. GAS는 데이터 수집 및 입출력 어댑터 역할로 축소함.
4. LLM은 이미 계산된 패킷을 복사하여 렌더링하는 역할만 수행함 (Math 금지).
## 결과
- 운영 보고서의 숫자 신뢰도 100% 확보.
- 저성능 모델에서도 판단 번복 없는 안정적인 운영 가능.
- 아키텍처 경계 위반 자동 차단.
+44
View File
@@ -0,0 +1,44 @@
schema_version: adr_index.v1
adr_count: 10
entries:
- adr_id: ADR-0001
title: Adopt active artifact manifest
status: accepted
path: governance/adr/0001-adopt-active-artifact-manifest.md
- adr_id: ADR-0002
title: Adopt authority matrix
status: accepted
path: governance/adr/0002-authority-matrix.md
- adr_id: ADR-0003
title: Freeze baseline metrics
status: accepted
path: governance/adr/0003-baseline-metrics.md
- adr_id: ADR-0004
title: Introduce rule lifecycle policy
status: accepted
path: governance/adr/0004-rule-lifecycle.md
- adr_id: ADR-0005
title: Split formula registry by domain
status: accepted
path: governance/adr/0005-formula-domain-split.md
- adr_id: ADR-0006
title: Make final decision packet canonical
status: accepted
path: governance/adr/0006-final-decision-packet.md
- adr_id: ADR-0007
title: Mandatory shadow ledger
status: accepted
path: governance/adr/0007-shadow-ledger.md
- adr_id: ADR-0008
title: Low capability renderer contract
status: accepted
path: governance/adr/0008-renderer-contract.md
- adr_id: ADR-0009
title: Number provenance audit
status: accepted
path: governance/adr/0009-number-provenance.md
- adr_id: ADR-0010
title: Change request template
status: accepted
path: governance/adr/0010-change-request-template.md
+12
View File
@@ -0,0 +1,12 @@
schema_version: agents_index.v1
source: AGENTS.md
purpose: Compact index for operator rules migrated out of AGENTS.md.
rule_files:
- governance/rules/00_core_locks.yaml
- governance/rules/01_harness_contract.yaml
- governance/rules/02_portfolio_policy.yaml
- governance/rules/03_order_grammar.yaml
- governance/rules/04_reporting_contract.yaml
- governance/rules/05_migration_hashes.yaml
hash_manifest: governance/agents_rule_hashes.yaml
hash_algorithm: sha256
+18
View File
@@ -0,0 +1,18 @@
schema_version: agents_rule_hashes.v1
hash_algorithm: sha256
generated_from: governance/agents_index.yaml
files:
- path: governance/rules/00_core_locks.yaml
sha256: b3c3d7ce05beb9e8b0945d98a0a1a55276254acef246c13f8c3a110f14f57ff4
- path: governance/rules/01_harness_contract.yaml
sha256: a093ddafa4a1b624ee44e4a98a63ce196ad452572fb27418c7e82b9b5edafc5a
- path: governance/rules/02_portfolio_policy.yaml
sha256: 47f6f33602482213523e6fdfa191309a34b805fc7acbe4aa84f475ece899a8ad
- path: governance/rules/03_order_grammar.yaml
sha256: cbcde916be0929cb1ba7fdbe9922c4445e375ea5d39654d96b86e1e80313cca9
- path: governance/rules/04_reporting_contract.yaml
sha256: 6ec102fcd3f8c50325ca793b8709200ec688526673405f594e5a03c137300f7b
- path: governance/rules/05_migration_hashes.yaml
sha256: fed17361105a22161e974b9503a5908c8d332f66b19503a6d6a4d12ceabaef75
- path: AGENTS.md
sha256: 8d7748597bb248c46db6089cd09da8d4af3f7eadc55243f8afaf4aebaea82fcf
+10
View File
@@ -0,0 +1,10 @@
formula_id: FORMULA_AUTHORITY_MATRIX_V1
generated_at: '2026-06-06T00:00:00+09:00'
owned_output_field_pct: 100.0
authority_collision_count: 0
manual_override_field_count: 0
source:
formula_owner_coverage: Temp/formula_owner_coverage_v1.json
output_field_collision: Temp/output_field_owner_collision_v1.json
output_field_ledger: spec/03_formulas/output_field_owner_ledger.yaml
field_dictionary: spec/12_field_dictionary.yaml
+18
View File
@@ -0,0 +1,18 @@
change_request_template_version: v1.0
proposal_metadata:
proposal_id: "CR-YYYYMMDD-NAME"
author: "quant_architect"
created_at: "YYYY-MM-DD"
rationale:
why: "State the reason for this change request"
expected_edge: "Describe the expected analytical edge or performance lift"
risk: "Outline potential failure modes and risk exposure changes"
dependencies:
data_dependency: "List of workbook columns or API data sources required"
verification:
tests:
- "List unit/regression tests or golden cases that validate this change"
rollback_plan: "Actionable rollback steps if the change fails verification"
outcome_metrics:
expected_t5_pass_rate_pct: 60.0
expected_expectancy: 0.10
@@ -0,0 +1,18 @@
change_request_template_version: v1.0
proposal_metadata:
proposal_id: "CR-0001"
author: "quant_architect"
created_at: "2026-06-03"
rationale:
why: "Example change request placeholder"
expected_edge: "improved provenance visibility"
risk: "none"
dependencies:
data_dependency: "existing methodology todo"
verification:
tests:
- "validate_final_decision_packet"
rollback_plan: "revert packet builder"
outcome_metrics:
expected_t5_pass_rate_pct: 60.0
expected_expectancy: 0.10
@@ -0,0 +1,18 @@
change_request_template_version: v1.0
proposal_metadata:
proposal_id: "CR-20260607-SAMPLE"
author: "quant_architect"
created_at: "2026-06-07"
rationale:
why: "Integrate property and metamorphic invariants"
expected_edge: "Stable execution decisions"
risk: "Increase computation time slightly"
dependencies:
data_dependency: "spec/property_invariants.yaml"
verification:
tests:
- "validate_property_invariants"
rollback_plan: "Remove validate_property_invariants from DAG"
outcome_metrics:
expected_t5_pass_rate_pct: 65.0
expected_expectancy: 0.12
@@ -0,0 +1,205 @@
schema_version: gas_logic_migration_ledger.v1
source: governance/gas_logic_migration_ledger_v1.yaml
authored: 2026-06-10
total_findings: 15
classification_summary:
decision_logic: 4
score_logic: 5
price_qty_logic: 4
pure_mapping: 1
display_text: 1
unclassified_findings: 0
# Canonical classification of GAS thin-adapter findings identified by
# validate_gas_thin_adapter_v1.py. Each finding is classified by what type
# of logic it contains and paired with a migration_action.
findings:
- id: F01
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 186
text: "SP_TAKE_PROFIT: 10, // Profit_Pct >= 10% (익절 후보)"
classification: score_logic
migration_action: REGISTER_SP_TAKE_PROFIT
target_file: formulas/score_thresholds_v1.py
status: TODO
- id: F02
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 656
text: "priceBasis = Number.isFinite(tp2Price) ? \"TAKE_PROFIT_TIER2_PRICE\" : \"PRIOR_CLOSE_X_0.998\";"
classification: price_qty_logic
migration_action: MIGRATE_PRICEBASIS_TO_PYTHON
target_file: formulas/price_basis_v1.py
status: TODO
blocking_on: F03 F04 (same function, migrate together)
- id: F03
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 665
text: "priceBasis = Number.isFinite(tp2Price) ? \"TAKE_PROFIT_TIER2_PRICE\" : \"PRIOR_CLOSE_X_0.998\";"
classification: price_qty_logic
migration_action: MIGRATE_PRICEBASIS_TO_PYTHON
target_file: formulas/price_basis_v1.py
status: TODO
blocking_on: F02 F04
- id: F04
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 674
text: "priceBasis = Number.isFinite(tp1Price) ? \"TAKE_PROFIT_TIER1_PRICE\" : \"PRIOR_CLOSE_X_0.998\";"
classification: price_qty_logic
migration_action: MIGRATE_PRICEBASIS_TO_PYTHON
target_file: formulas/price_basis_v1.py
status: TODO
- id: F05
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 678
text: "action = \"TAKE_PROFIT_TIER1\";"
classification: decision_logic
migration_action: MIGRATE_DECISIONS_ROUTING
target_file: formulas/execution_decision_v1.py
status: TODO
- id: F06
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 683
text: "priceBasis = Number.isFinite(tp1Price) ? \"TAKE_PROFIT_TIER1_PRICE\" : \"PRIOR_CLOSE_X_0.998\";"
classification: price_qty_logic
migration_action: MIGRATE_PRICEBASIS_TO_PYTHON
target_file: formulas/price_basis_v1.py
status: TODO
- id: F07
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 1577
text: "score += THRESHOLDS[\"SP_TAKE_PROFIT\"];"
classification: score_logic
migration_action: MIGRATE_SCORE_CALCULATION
target_file: formulas/score_thresholds_v1.py
status: TODO
- id: F08
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 1578
text: "breakdown.push(`take_profit:+${THRESHOLDS[\"SP_TAKE_PROFIT\"]}`)"
classification: display_text
migration_action: DISPLAY_TEXT_PASSTHROUGH
notes: display_text stays in GAS adapter as rendering concern
status: KEEP_IN_GAS
- id: F09
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
line: 2164
text: "TAKE_PROFIT_BASE: 10,"
classification: score_logic
migration_action: REGISTER_TAKE_PROFIT_BASE
target_file: formulas/score_thresholds_v1.py
status: TODO
- id: F10
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
line: 1335
text: "return { [\"decisions\"]: routes, traces: traces, lock: true };"
classification: decision_logic
migration_action: MIGRATE_DECISIONS_ROUTING
target_file: formulas/routing_decision_v1.py
status: TODO
- id: F11
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
line: 1360
text: "if (holding && holding.stopBreach) return 'STOP_LOSS';"
classification: decision_logic
migration_action: MIGRATE_STOP_BREACH_DECISION
target_file: formulas/stop_loss_gate_v1.py
status: TODO
- id: F12
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
line: 2128
text: "[\"distribution_risk_score\"]: Math.min(100, Math.max(0, score)),"
classification: score_logic
migration_action: DELETE_DISTRIBUTION_RISK_GAS
target_file: formulas/distribution_risk_v1.py
status: TODO
notes: Python canonical (build_distribution_risk_v1.py) already exists; GAS version is duplicate
- id: F13
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
line: 2132
text: "formula_id: 'DISTRIBUTION_RISK_SCORE_V1'"
classification: pure_mapping
migration_action: DELETE_DISTRIBUTION_RISK_GAS
status: TODO
notes: formula_id tag stays with Python canonical; remove from GAS
- id: F14
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
line: 2214
text: "[\"late_chase_risk_score\"]: Math.min(100, Math.max(0, Math.round(lateChaseRisk))),"
classification: score_logic
migration_action: DELETE_LATE_CHASE_RISK_GAS
target_file: formulas/late_chase_risk_v1.py
status: TODO
notes: Python canonical (build_alpha_lead_table_v1.py) computes late_chase_risk; GAS version is duplicate
- id: F15
file: src/gas_adapter_parts/gdf_04_execution_quality.gs
line: 479
text: "if (bqRow.breakout_quality_gate === 'BLOCKED_LATE_CHASE' || alphaRow[\"late_chase_risk_score\"] >= 70)"
classification: decision_logic
migration_action: MIGRATE_LATE_CHASE_GATE
target_file: formulas/late_chase_gate_v1.py
status: TODO
# Migration action summary (9 actions)
migration_actions:
- action_id: REGISTER_SP_TAKE_PROFIT
findings: [F01]
description: Register SP_TAKE_PROFIT threshold (value=10) in Python score_thresholds canonical
priority: LOW
- action_id: REGISTER_TAKE_PROFIT_BASE
findings: [F09]
description: Register TAKE_PROFIT_BASE threshold (value=10) in Python score_thresholds canonical
priority: LOW
- action_id: DELETE_DISTRIBUTION_RISK_GAS
findings: [F12, F13]
description: Remove distribution_risk_score calculation from gdf_03; Python canonical exists
priority: HIGH
blocker: verify build_distribution_risk_v1.py output matches GAS output before delete
- action_id: DELETE_LATE_CHASE_RISK_GAS
findings: [F14]
description: Remove late_chase_risk_score from gdf_03; Python canonical in alpha_lead_table_v1
priority: HIGH
blocker: verify parity before delete
- action_id: MIGRATE_PRICEBASIS_TO_PYTHON
findings: [F02, F03, F04, F06]
description: priceBasis string selection (TIER1/TIER2 or PRIOR_CLOSE_X_0.998) → Python canonical
priority: MEDIUM
- action_id: MIGRATE_SCORE_CALCULATION
findings: [F07]
description: score += THRESHOLDS["SP_TAKE_PROFIT"] pattern → Python canonical scorer
priority: MEDIUM
- action_id: MIGRATE_STOP_BREACH_DECISION
findings: [F11]
description: holding.stopBreach → STOP_LOSS decision → Python canonical stop_loss_gate
priority: HIGH
notes: critical path — must match validate_stop_loss_policy_v1 spec
- action_id: MIGRATE_DECISIONS_ROUTING
findings: [F05, F10]
description: TAKE_PROFIT_TIER1 action assignment and routing lock decision → Python canonical
priority: MEDIUM
- action_id: MIGRATE_LATE_CHASE_GATE
findings: [F15]
description: BLOCKED_LATE_CHASE gate check (threshold 70) → Python canonical gate formula
priority: HIGH
blocker: late_chase_risk_score must come from Python before GAS gate can be removed
+8
View File
@@ -0,0 +1,8 @@
schema_version: rule_lifecycle.v1
states:
- shadow
- evidence
- active
- retire
transition_policy:
require_change_request: true
+9
View File
@@ -0,0 +1,9 @@
schema_version: agents_rule.v1
rule_id: CORE_LOCKS_V1
title: Core locks and no-hallucination rules
summary:
- Use spec/13_formula_registry.yaml for all prices, stops, targets, quantities.
- Do not invent prices, quantities, or formulas.
- If harness data is missing, print DATA_MISSING — 하네스 업데이트 필요.
- Preserve transparency for blocked items; never hide computed fields.
- Use only registered formulas and source-of-truth artifacts.
@@ -0,0 +1,8 @@
schema_version: agents_rule.v1
rule_id: HARNESS_CONTRACT_V1
title: Harness and validation contract
summary:
- Harness output must be validated before it is trusted.
- LLM must not override harness verdicts.
- Source-of-truth precedence follows runtime manifest and schema contracts.
- Live T+20 readiness cannot be faked from replay data.
@@ -0,0 +1,8 @@
schema_version: agents_rule.v1
rule_id: PORTFOLIO_POLICY_V1
title: Portfolio policy contract
summary:
- Heat, cash floor, stop loss, TP, and sell priority come from specs only.
- Do not bypass hard stops with narrative justification.
- For multiple sell candidates, show sell priority table first.
- Keep blocked positions transparent in shadow ledger outputs.
+8
View File
@@ -0,0 +1,8 @@
schema_version: agents_rule.v1
rule_id: ORDER_GRAMMAR_V1
title: HTS order grammar
summary:
- Single numeric price per order row.
- No multi-condition conjunctions in order text.
- Normalize all prices to KRX tick units.
- Remove stale TP prices when TP1 already triggered.
@@ -0,0 +1,8 @@
schema_version: agents_rule.v1
rule_id: REPORTING_CONTRACT_V1
title: Reporting and provenance contract
summary:
- All rendered numbers require provenance tags.
- Reports must keep source and runtime artifact references explicit.
- Narrative must not soften or override numeric gates.
- Output schema and final packets are authoritative.
@@ -0,0 +1,8 @@
schema_version: agents_rule.v1
rule_id: RULE_HASH_MIGRATION_V1
title: Rule hash migration map
summary:
- AGENTS.md is the index only.
- Rule files carry the detailed instructions.
- Hash each rule file and track the set in governance/agents_index.yaml.
- Update hashes whenever rule files change.
@@ -0,0 +1,10 @@
# Weekly Engine Review
## Engine Health
## Data Quality
## Formula Parity
## Outcome Drift
## Active Rule Changes
## Emergency Patch Notes
## Normal Patch Notes
+34
View File
@@ -0,0 +1,34 @@
{
"name": "core-satellite-collector",
"version": "4.0.0",
"type": "module",
"private": true,
"scripts": {
"ops:prepare": "python tools/convert_xlsx_to_json.py",
"ops:validate": "python tools/run_release_dag_v3.py --mode release",
"ops:build": "python tools/build_bundle.py",
"ops:render": "python tools/render_operational_report.py --json GatherTradingData.json --output Temp/operational_report.md --report-json-output Temp/operational_report.json",
"ops:release": "python tools/run_release_dag_v3.py --mode full",
"ops:package": "python tools/refresh_trading_calendar.py && python tools/prepare_upload_zip.py --validation-mode release --profile",
"prepare-upload-zip": "python tools/refresh_trading_calendar.py && python tools/prepare_upload_zip.py",
"ops:audit": "python tools/harness_coverage_auditor.py",
"validate-gas-recovery": "python tools/validate_gas_orchestration_recovery_v1.py",
"ops:clean": "python tools/clean_temp_artifacts_v1.py",
"ops:dev": "node core_satellite_collector.js",
"full-gate": "python tools/run_release_dag_v3.py --mode release --strict",
"validate-engine-strict": "python tools/run_release_dag_v3.py --mode release --strict"
},
"dependencies": {
"cheerio": "latest",
"googleapis": "^171.4.0",
"iconv-lite": "latest",
"yahoo-finance2": "latest"
},
"optionalDependencies": {
"adm-zip": "latest",
"fast-xml-parser": "latest"
},
"devDependencies": {
"xlsx": "^0.18.5"
}
}
+799
View File
@@ -0,0 +1,799 @@
# Analysis Prompt
Use this prompt when producing an investment analysis or HTS-ready playbook.
## CRITICAL — RESPONSE LANGUAGE POLICY
- 최종 사용자 설명 문장은 기본적으로 한글만 사용한다.
- 영문 표기는 종목코드, 공식 ID, 파일명, 명령어, JSON key처럼 대체 불가능한 경우에만 허용한다.
- 상태 설명은 PASS/FAIL/BLOCKED/BUY/SELL/TRIM 대신 통과/실패/차단/매수/매도/축소매도를 사용한다.
- 표 제목과 판단 요약은 한글 우선으로 작성하고, 필요 시 영문 section id는 괄호로만 병기한다.
- 주문 사유는 영문 산문 없이 공식 ID 중심의 짧은 한글 문장으로 작성한다.
- 수량·단가·손절가·익절가 제안은 시장 개장 여부와 무관하게 유지한다.
- HTS 즉시 입력 가능 여부는 제안과 분리해 별도 표로 표시한다.
- 실행 차단이라도 사용자 판단용 `proposal_reference_sheet`는 숨기지 않는다.
## CRITICAL — QUANTITATIVE_EXPERT_HARNESS (QEH) — 전문사 정밀도 강제
`data._harness_context`가 JSON에 존재하면 아래 규칙을 **절대적으로** 따른다.
하네스는 30년 실무 애널리스트·트레이더의 산식을 결정론적으로 실행한 'Ground Truth'다.
1. **[LOCKED] 하네스 숫자 절대 준수:**
- 하네스가 산출한 `market_risk_score`, `target_cash_pct`, `final_action`, `price`, `stop_price`, `tp_ladder`, `quantity`는 최종값이다.
- LLM이 하네스 산출값을 재계산하거나, 소수점/호가 단위를 임의 조정하는 것을 **엄격히 금지**한다.
- 분석 리포트의 모든 숫자 표기 옆에 `[HARNESS_LOCKED]` 태그를 부착한다.
2. **[REPORT] 하네스 결정 추적:**
- `decision_trace_table`의 모든 행은 하네스의 `decisions_json``calculation_trace`와 100% 일치해야 한다.
- 하네스 결과와 다른 독자적 '전문가 판단'을 보고서에 섞지 않는다. LLM은 하네스가 왜 그런 결론을 내렸는지 명세(yaml)를 근거로 **해설**하는 역할만 수행한다.
3. **[FAILED] 하네스 정합성 오류 시:**
- 하네스 산출값과 리포트 출력값이 충돌하면 리포트 전체를 `INVALID_Harness_Mismatch`로 처리하고 재산출한다.
---
## CRITICAL — QEH_AUDIT_BLOCK (공식 검산 표 강제 출력) [2026-05-19_HARNESS_AUDIT_V1 H1]
**모든 분석 보고서에서 주문표 출력 이전에 반드시 아래 QEH_AUDIT_BLOCK 표를 먼저 출력한다.**
이 표 없이 주문표를 출력하면 `INVALID_MISSING_AUDIT`으로 처리하고 주문표 전체를 BLOCKED한다.
### QEH_AUDIT_BLOCK 표 형식 (필수)
| 공식_ID | 입력값 요약 | 결과값 | 발동 게이트 |
|---------|-----------|--------|-----------|
| TOTAL_HEAT_V1 | ATR×멀티플라이어, 보유수량×종목 | X.XX% | ALLOW / HALVE / BLOCK_NEW_BUY |
| CASH_RATIOS_V1 | D+2정산현금, total_asset | X.XX% | PASS / CASH_RAISE_REQUIRED(-X.XXp) |
| SELL_PRIORITY_V1 | tier/score 순위표 | 1순위: {종목}(Score:{N}) | TRIM_ASSIGNED / NONE |
| GOAL_RETIREMENT_V1 | goal_current_asset_krw, goal_achievement_pct | {N}% 달성 / 잔여 {M}만원 / ETA {YYYY-MM} | IN_PROGRESS / ACHIEVED |
**상황별 선택 추가 공식 (해당 시 반드시 포함):**
- 매수 검토 시: `MEAN_REVERSION_GATE_V1` (이격도 체크 선행), `POSITION_SIZE_V1`, `RISK_BUDGET_CASCADE_V1`, `EXPECTED_EDGE_V1`
- 매도 후보 시: `RS_RATIO_V1` (rs_laggard 판정), `SELL_PRIORITY_V1`
- 가격 산출 시: `STOP_PRICE_CORE_V1`, `TAKE_PROFIT_LADDER_V2`, `TICK_NORMALIZER_V1`
- 국면 진단 시: `MARKET_RISK_SCORE_V1`, `TARGET_CASH_PCT_V1`
### QEH_AUDIT_BLOCK 절대 금지 (FAT001 + P7 연동)
- QEH_AUDIT_BLOCK 표 없이 주문표 제시 → **전체 BLOCKED (INVALID_MISSING_AUDIT)**
- 공식 ID 명시 없이 손절가·익절가·수량 제시 → **PRICE_FORMULA_REQUIRED**
- TOTAL_HEAT_V1 산출 없이 신규 BUY 허용 → **HS006 위반**
- CASH_RATIOS_V1 산출 없이 현금 비중 판정 → **P3 위반**
- LLM이 표의 숫자를 재계산하거나 재해석하는 행위 → **CRITICAL_FORMULA_MISMATCH**
---
## CRITICAL — HARNESS_AUTHORITATIVE (Legacy H2 호환)
`data._harness_context` 섹션이 JSON에 존재하면 아래 규칙을 **절대적으로** 따른다.
이 섹션의 값은 GAS `buildHarnessContext_()` H2가 결정론적으로 산출한 확정값이다. LLM은 재계산·재해석·완화 금지.
**H1 — 포트폴리오 가드:**
| harness 필드 | LLM 의무 |
|---|---|
| `intraday_lock = true` | `blocked_actions` 목록의 주문 생성 절대 금지. TRIM 계열만 허용. |
| `intraday_lock = false` | 정상 분석 진행. |
| `immediate_cash_krw` | **현금 원장에서 제외.** D+0 즉시현금이므로 cash_floor·buy_power 계산 합산 금지. 참고값으로만 표시. |
| `cash_floor_status` | HARD_BLOCK → 매수 금지. TRIM_REQUIRED → 매도/축소 우선. PASS → 정상. |
| `total_heat_pct` | heat gate 판정에 이 값 사용. 임계값은 `heat_gate_threshold_pct`(국면 감응) 참조. LLM이 10% 고정값으로 재판단 금지. |
| `heat_gate_status` | BLOCK_NEW_BUY → BUY 주문 생성 금지. HALVE_NEW_BUY_QUANTITY → 수량 50% 감량 이미 반영됨. |
| `heat_gate_threshold_pct` | **[L3 DYNAMIC_HEAT_GATE_V1]** GAS 산출 국면 감응 임계값(%). 이 값이 heat_gate_status의 기준. LLM 재계산·무시 금지. 국면별: EVENT_SHOCK=5%, RISK_OFF=7%, NEUTRAL=10%, RISK_ON=12%, SECULAR_LEADER=13%. |
| `drawdown_guard_state` | **[M1 DRAWDOWN_GUARD_V1]** NO_BUY → BUY 수량 0, REDUCE_BUY → scale=0.5 이미 반영, CAUTION_BUY → scale=0.75. LLM이 정상 수량 복원 금지. |
| `drawdown_buy_scale` | M1 — atrQty에 이미 적용된 배수. LLM이 무시 금지. |
| `portfolio_beta_gate` | **[M2 PORTFOLIO_BETA_GATE_V1]** OVER_BETA → 고베타 종목 추가 BUY 금지. WARN_BETA → 신중 검토. |
| `sector_concentration_gate` | **[M5 SECTOR_CONCENTRATION_LIMIT_V1]** BLOCK_SECTOR → 편중 섹터 추가 BUY 금지. WARN_TOP2 → 상위2 합산 초과 경고. |
| `regime_size_scale` | **[N1 POSITION_SIZE_REGIME_SCALE_V1]** GAS 산출 국면 매수 스케일(0.25~1.2). buy_qty_inputs_json에 이미 적용됨. LLM 재계산·무시 금지. |
| `regime_cash_uplift_min_pct` | **[N5 REGIME_CASH_UPLIFT_V1]** 국면 상향 적용 후 실제 현금 최소 비율. cash_floor_min_pct보다 높을 수 있음. LLM이 이 값을 낮추거나 무시 금지. |
| `single_position_weight_gate` | **[O1 SINGLE_POSITION_WEIGHT_CAP_V1]** OVERWEIGHT_TRIM → 해당 종목 추가 BUY 금지. `single_position_weight_json`에서 초과 종목 확인. |
| `semiconductor_cluster_gate` | **[O2 SEMICONDUCTOR_CLUSTER_GATE_V1]** CLUSTER_BLOCK → 005930·000660 신규 BUY 모두 금지. combined_pct가 상한 초과. |
| `portfolio_drawdown_gate` | **[O3 PORTFOLIO_DRAWDOWN_GATE_V1]** DRAWDOWN_FORCE_RISK_OFF → 신규 BUY 전면 금지. DRAWDOWN_CAUTION → 신규 매수 보류 권고. |
| `win_loss_streak_state` | **[O4 WIN_LOSS_STREAK_GUARD_V1]** EDGE_CRITICAL/EDGE_DEGRADED/EDGE_WEAK → 매수 스케일 이미 적용됨. LLM 복원 금지. |
| `position_count_gate` | **[O5 POSITION_COUNT_LIMIT_V1]** POSITION_COUNT_BLOCK → 신규 BUY 종목 추가 금지. 기존 HOLD/SELL만 허용. |
| `stop_breach_gate` | **[P1 STOP_BREACH_ALERT_V1]** BREACH → 해당 종목 즉각 매도 검토. APPROACHING → 손절가 근접 경보. `stop_breach_alert_json`에서 gap_pct 확인. |
| `tp_trigger_gate` | **[P2 TP_TRIGGER_ALERT_V1]** TRIGGERED → `tp_trigger_alert_json`의 tp1_qty/tp2_qty를 그대로 매도 수량 사용. LLM 임의 조정 금지. |
| `heat_concentration_gate` | **[P3 HEAT_CONCENTRATION_ALERT_V1]** HEAT_CONCENTRATED → 해당 종목 추가 매수 금지. `heat_concentration_json`에서 heat_share_pct 확인. |
| `regime_transition_type` | **[P4 REGIME_TRANSITION_ALERT_V1]** DOWNGRADE → 국면 하향 전환, affected_gates 재검토. UPGRADE → 국면 완화. NO_CHANGE → 변동 없음. |
| `portfolio_health_label` | **[P5 PORTFOLIO_HEALTH_SCORE_V1] 보고서 첫 줄 필수 표시.** CRITICAL → 긴급 주의 섹션 먼저 출력. `portfolio_health_blocked_json`에서 활성 게이트 열거. |
| `allowed_actions` | 이 목록의 주문 유형만 생성 가능. |
| `blocked_actions` | 이 목록의 주문 유형은 어떤 이유로도 생성 금지. |
**H2 — 매도후보 순위 (`sell_priority_lock = true`):**
| harness 필드 | LLM 의무 |
|---|---|
| `sell_candidates_json` | GAS 산출 tier·score 순위 그대로 출력. 재정렬·점수 변경 절대 금지. |
| `sell_priority_lock` | `true` → 산문으로 순위 재해석·변경 금지. |
| `clamp_label` (각 후보) | 값이 있으면 `[CLAMP 발동: raw=N → 100]` 표기 필수. |
**H3 — 수량 (`quantities_lock = true`):**
| harness 필드 | LLM 의무 |
|---|---|
| `sell_quantities_json` | GAS 산출 Sell_Qty 그대로 사용. `CAPTURE_REQUIRED`는 그대로 기재. |
| `buy_qty_inputs_json` | `final_qty`가 있으면 매수수량으로 사용. `NO_BUY_QUANTITY`이면 수량 출력 금지. |
| `quantities_lock` | `true` → 수량 재계산·역산 절대 금지. |
**H4 — 가격 (`prices_lock = true`):**
| harness 필드 | LLM 의무 |
|---|---|
| `prices_json` | `stop_price`·`tp1_price`·`tp2_price` 그대로 HTS 표에 기재. |
| `prices_lock` | `true` → tick 재정규화·가격 임의 조정 절대 금지. |
| `ladder_version = V1_FALLBACK` | ATR20 없음 — 가격 신뢰도 낮음 표기. |
**H4 추가 — X4 Ratchet 스톱 (`prices_lock = true`):**
| 필드 | LLM 의무 |
|---|---|
| `ratchet_applied` | `EARLY_RATCHET+TRAILING` / `TRAILING_ONLY` 이면 주문표 `stop_price` 옆에 `[RATCHET]` 태그 필수 |
| `ratchet_note` | Section B `reasoning_review`에 ratchet 발동 사유 간략 기재 |
| `ratchet_applied = NONE 또는 PASS` | 태그 생략. stop_price는 STOP_PRICE_CORE_V1 값 |
**H_Alpha — Alpha-Shield 선행 레이더 (`alpha_shield_lock = true`):**
`alpha_shield_json` 이 harness_context 에 존재하면 LLM 재계산 **절대 금지**.
GAS `calcAlphaShield_()` 가 결정론적으로 계산한 확정값을 PROACTIVE_RADAR_BLOCK 표에 그대로 기재한다.
| harness 필드 | LLM 의무 |
|---|---|
| `alpha_shield_json[].mrg_gate` | `BUY_HARD_BLOCK` → Gate 1d 발동. BUY 주문 생성 금지. |
| `alpha_shield_json[].rs_status` | `RS_LAGGARD` → sell_priority 점수 상승. Section A 표에 표기. |
| `alpha_shield_json[].w1_status``w4_status` | PROACTIVE_RADAR_BLOCK 표에 그대로 기재. 상태 임의 변경 금지. |
| `alpha_shield_json[].critical_alert` | `CRITICAL_ALERT` → 전면 포트폴리오 재검토 강제 출력. |
| `alpha_shield_critical_alert_flag` | `CRITICAL` → 보고서 상단에 `[CRITICAL_ALERT] 레이더 2개 이상 동시 발화` 경고 필수. |
| `alpha_shield_lock` | `true` → 표 수치 재산출·수정 절대 금지. |
**H5 — 결정 상태머신 (`decision_lock = true`):**
| harness 필드 | LLM 의무 |
|---|---|
| `decisions_json` | `final_action` 그대로 출력. `gate_changed=true`이면 gate_trace 사유 표시. |
| `decision_lock` | `true` → gate_trace를 서사로 번복해 final_action 변경 절대 금지. |
**주도주 승자 포지션 게이트 (`secular_leader_gate_json`):**
| harness 필드 | LLM 의무 |
|---|---|
| `secular_leader_gate_json` | 005930·000660 외 종목에는 적용 금지. `status=NOT_APPLICABLE`이면 표기 생략. |
| `secular_leader_gate_active=true` & `tp1_state=DEFERRED_SECULAR_LEADER` | TP1 매도 주문 생성 절대 금지. `[DEFERRED_SECULAR_LEADER]` 태그로만 표기. |
| `tp1_price=null` (secular_leader 관련) | 하네스가 null로 전달한 tp1_price 복원·대체 금지. HS009와 동일 우선순위. |
| `secular_leader_gate_active=false` | `spec/exit/take_profit.yaml:core.leadership` 규칙으로 즉시 복귀. |
**국면별 감축 가이던스 (`regime_trim_guidance_json`):**
| harness 필드 | LLM 의무 |
|---|---|
| `regime_trim_guidance_json.satellite_trim_pct_min/max` | 위성 종목 감축 비율 범위 그대로 인용. LLM이 임의 비율 제시 금지. |
| `regime_trim_guidance_json.leader_trim_pct_min/max` | 주도주 감축 비율 범위 그대로 인용. |
| `regime_trim_lock=true` | LLM의 국면 재판정 및 감축비율 재산출 절대 금지. |
| `new_buy_gate=BLOCKED / HARD_BLOCKED` | 신규 매수 차단. heat_gate와 동일 우선순위 적용. |
**5억원 목표 자산 추적 (`goal_*`):**
| harness 필드 | LLM 의무 |
|---|---|
| `goal_achievement_pct`, `goal_remaining_krw`, `goal_eta_label` | **분석 보고서 첫 섹션**에 목표 달성 현황 표 출력 필수. |
| `goal_eta_label` | `ACHIEVED` / `YYYY-MM` / `DATA_MISSING` 그대로 기재. LLM 재계산 금지 (HS011 위반). |
| `goal_status=IN_PROGRESS` | 달성률이 낮더라도 이를 이유로 heat_gate·cash_floor·stop_loss 규칙 완화 금지. |
| `goal_monthly_growth_pct=null` | ETA 계산 불가(`DATA_MISSING`) — LLM이 대체 ETA를 추정하는 것 절대 금지. |
**현금 부족액 및 TRIM 계획 (`cash_shortfall_*`, `trim_plan_to_min_cash_json`) [G1/G2]:**
| harness 필드 | LLM 의무 |
|---|---|
| `cash_current_pct_d2` | QEH_AUDIT_BLOCK의 CASH_RATIOS_V1 행에 이 값 사용. LLM 재계산 금지. |
| `cash_target_pct` | TARGET_CASH_PCT_V1 결과값. 이 값과 현금 현황을 비교해 부족 여부 판단. |
| `cash_shortfall_min_krw` | **"약 N원 필요" 즉석 계산 절대 금지.** 이 값을 그대로 인용. 0이면 방어선 충족. |
| `cash_shortfall_target_krw` | 목표 현금비율까지 부족액. 0이면 목표 충족. |
| `trim_plan_to_min_cash_json` | H2 우선순위 기반 TRIM 계획. LLM이 종목·순서·수량 임의 변경 금지. `covers_shortfall=true` 행까지만 실행 후보. |
> **주의:** `trim_plan_to_min_cash_json`은 HTS 주문표가 아니다. 실제 주문 실행 가능 여부는 `order_blueprint_json.validation_status`가 PASS인 행만 해당한다.
**라우팅/서빙 Trace 및 데이터 상태 [G4]:**
모든 분석 보고서는 **QEH_AUDIT_BLOCK 이전에** 아래 `routing_serving_trace` 표를 먼저 출력해야 한다. 표가 없으면 `INCOMPLETE_REPORT`로 처리한다.
| 필드 | 하네스 값 | 비고 |
|---|---|---|
| `request_route` | (harness값 인용) | 라우팅 경로 |
| `bundle_selected` | (harness값 인용) | 서빙 번들 |
| `prompt_entrypoint` | (harness값 인용) | 프롬프트 경로 |
| `json_validation_status` | (harness값 인용) | **PENDING_EXPORT = GAS 내부 내보내기 전 상태. "검증 통과" 의미 아님.** |
| `capture_required` | (harness값 인용) | HTS 캡처 필요 여부 |
| `cash_ledger_basis` | D2_ONLY | D+2 정산현금 단독 기준 확인 |
> **PENDING_EXPORT 해석 금지:** `json_validation_status=PENDING_EXPORT`를 "검증 완료" 또는 "데이터 유효"로 서술하는 것은 오류다. 로컬 스크립트 PASS(npm run validate-data-sample)와 이 필드는 별개 개념이다.
**외부 시장 데이터 격리 [G3]:**
| 구분 | 허용 범위 | 금지 사항 |
|---|---|---|
| `prices_json` 하네스 가격 | 주문 판단·주문표·QEH_AUDIT_BLOCK 전용 | 외부 웹 가격으로 대체 금지 |
| 외부 웹·뉴스 가격 | Section B 해설에만 참고 인용 | Section A 원장·주문표 혼입 금지 |
| 가격 불일치 발생 시 | 하네스 가격 우선, Section B에 "외부 ±N% 차이" 메모 | 외부 가격으로 판단·주문 생성 금지 |
**APEX_ALPHA_PRESERVATION_EXECUTION_HARNESS [2026-05-20_APEX_V1]:**
뒷북 매수·설거지 구간 추격·수익 반납·현금확보 투매를 줄이기 위해 GAS가 산출한 APEX 하네스 값을 Section A에 필수 표로 출력한다.
LLM은 아래 값을 재계산하거나 완화하지 않고, `RetirementAssetPortfolioReportTemplate.yaml`의 표 컬럼에 그대로 매핑한다.
| harness 필드 | LLM 의무 | 위반 시 |
|---|---|---|
| `alpha_lead_json` | `alpha_lead_table`에 그대로 출력. 선행 점수·순위·상태 재정렬 금지. | `APEX_ALPHA_MISSING` |
| `follow_through_json` | 추격 확인 상태를 `alpha_lead_table`에 병기. 확인 실패 시 BUY/ADD_ON 산문 허용 금지. | `BLOCKED_FOLLOW_THROUGH` |
| `distribution_risk_json` | `anti_distribution_table`에 그대로 출력. 설거지 위험 HIGH면 BUY/ADD_ON 차단. `pre_distribution_warning=EARLY_WARNING`이면 보고서에 별도 경고 섹션 필수. | `APEX_DISTRIBUTION_MISSING` |
| `profit_preservation_json` | `profit_preservation_table`에 그대로 출력. 수익 보호 상태를 임의 완화 금지. `auto_trailing_stop`이 있으면 손절가 원장에 반드시 반영. | `APEX_PROFIT_LOCK_MISSING` |
| `cash_raise_plan_json` | `smart_cash_raise_table`에 즉시매도·반등대기·보호수량을 분리 출력. | `APEX_CASH_RAISE_MISSING` |
| `smart_sell_quantities_json` | 확정 수량만 사용. `CAPTURE_REQUIRED`는 숫자로 바꾸지 않는다. | `INVALID_QTY_RECALC` |
| `rebound_sell_trigger_json` | 반등 대기 매도 조건을 WATCH/계획으로만 표시. 즉시 HTS 수량에 합산 금지. | `INVALID_REBOUND_SELL` |
| `execution_quality_json` | `execution_quality_table`에 tick/liquidity/gap 검산 결과 출력. PASS가 아니면 주문표 PASS 금지. | `APEX_EXECUTION_QUALITY_MISSING` |
| `buy_permission_json` | BUY_PERMISSION_MATRIX_V1 결과가 BLOCKED이면 모든 BUY 주문표 차단. | `INVALID_BUY_PERMISSION` |
| `limit_price_policy_json` | 지정가·호가 상태를 그대로 사용. 라운드피겨·심리적 가격으로 조정 금지. | `INVALID_PRICE_MUTATION` |
| `backdata_feature_bank_json` | `backdata_feature_bank_table`에 그대로 출력. GAS 자동 수집 원장을 performance 수동 입력보다 우선한다. | `BACKDATA_MISSING` |
| `benchmark_relative_timeseries_json` | `benchmark_relative_harness_table`에 그대로 출력. 초과낙폭·회복률·하락장 베타·RS선 기울기 재계산 금지. | `BRT_HARNESS_MISSING` |
| `saqg_json` | `saqg_v1=EXCLUDED/WATCHLIST_ONLY`이면 BUY 후보·파일럿·HTS 주문표 차단. | `INVALID_SAQG_BUY` |
| `sapg_json` | `SAPG_CRITICAL`이면 위성 신규 BUY 전면 차단, `SAPG_ALERT`이면 위성 손익 경고 출력. | `INVALID_SAPG_OVERRIDE` |
| `cash_creation_purpose_lock_json` | `sell_reason_validity=INVALID_SELL_REASON`이면 현금창출·위성 재원 마련 목적 매도 금지. | `INVALID_SELL_REASON` |
**[M2] 포트폴리오 베타 게이트 출력 의무 (`portfolio_beta_gate_json`) [Direction M2]:**
`portfolio_beta_gate=OVER_BETA`이면 보고서에 다음 표를 필수 포함한다.
| portfolio_beta_gate_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `portfolio_beta` | `포트폴리오베타` | GAS 산출값 그대로. LLM 재계산 금지. |
| `beta_limit_over` | `상한` | 국문별 임계값 명시 |
| `per_holding_betas[].beta_proxy` | 종목별 베타 표 | 높은 순으로 표시. OVER_BETA 종목 ⚠ 표시 |
**[M3] 분할 익절 수량 출력 의무 (`tp_quantity_ladder_json`) [Direction M3]:**
TP1/TP2/TP3 도달 종목이 있으면 아래 표를 반드시 출력한다.
| tp_quantity_ladder_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `tp1_qty` / `tp2_qty` / `tp3_qty` | 익절 수량 원장 | `qty_source` 표시 필수. MANUAL/AUTO_33PCT 중 적용 기준 명시 |
| `tp1_price` / `tp2_price` | 익절 가격 | null이면 `이미 통과(—)`로 표시. 통과 수량 즉시 주문 금지 |
`tp_quantity_ladder_lock=true`이면 LLM이 임의로 수량을 변경하거나 "보유 비중에 따라 조정" 식 서술 금지.
**[M4] 이벤트 리스크 홀드 게이트 출력 의무 (`event_risk_json`) [Direction M4]:**
`event_risk_json[].event_hold_gate=EVENT_HOLD`인 종목이 있으면 보고서에 별도 경고 섹션 필수.
| event_risk_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `event_hold_gate` | `이벤트홀드` 컬럼 | EVENT_HOLD → ⚠ 표시, BUY 주문 생성 금지 |
| `reason` | 사유 컬럼 | `event_hold_days_le5:N` / `dart_risk` 코드 그대로 표시 |
기존 보유 중인 EVENT_HOLD 종목의 익절/손절 매도는 정상 진행 (신규 매수만 차단).
**[M5] 섹터 편중도 출력 의무 (`sector_concentration_json`) [Direction M5]:**
`sector_concentration_gate != PASS`이면 보고서에 섹터 편중 경고 섹션 필수.
| sector_concentration_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `gate` 컬럼 | `편중상태` | BLOCK_NEW_BUY_THIS_SECTOR → 해당 섹터 BUY 금지 표시 |
| `weight_pct` | 비중% | 편중 섹터 굵게 표시 |
**[P5] 포트폴리오 건전성 점수 출력 의무 — 보고서 첫 줄 [Direction P5]:**
보고서 시작 전에 다음 형식으로 건전성 상태를 반드시 먼저 출력한다:
```
▶ 포트폴리오 건전성: [HEALTHY/CAUTION/CRITICAL] (score: N/100)
활성 게이트: <portfolio_health_blocked_json 요약>
```
| portfolio_health 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `portfolio_health_label` | 보고서 첫 줄 | CRITICAL → 이후 긴급 주의 섹션 필수 |
| `portfolio_health_score` | 점수(N/100) | GAS 산출값 그대로. 재계산 금지 |
| `portfolio_health_blocked_json` | 활성 게이트 요약 | severity=CRITICAL 항목 굵게 표시 |
**[P1] 손절가 이탈 경보 출력 의무 (`stop_breach_alert_json`) [Direction P1]:**
`stop_breach_gate != PASS`이면 손절가 경보 섹션 필수 출력.
| stop_breach_alert_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `status` | `손절상태` 컬럼 | BREACH_IMMEDIATE_EXIT → 추가 매수·HOLD 서술 절대 금지 |
| `gap_pct` | `손절여유%` 컬럼 | 음수=이탈. GAS 산출값 그대로 |
**[P2] 익절 트리거 경보 출력 의무 (`tp_trigger_alert_json`) [Direction P2]:**
`tp_trigger_gate=TRIGGERED`이면 익절 실행 섹션 필수.
| tp_trigger_alert_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `tp1_triggered/tp2_triggered` | `트리거` 컬럼 | true → 해당 TP 즉각 매도 대상 표시 |
| `tp1_qty/tp2_qty` | `매도수량` 컬럼 | tp_quantity_ladder_json 연계값 그대로 사용 |
**[P3] Heat 편중도 경보 출력 의무 (`heat_concentration_json`) [Direction P3]:**
`heat_concentration_gate=HEAT_CONCENTRATED`이면 경고 섹션 필수.
| heat_concentration_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `heat_share_pct` | `Heat비중%` 컬럼 | ≥50% → 굵게 표시. 편중 해소 검토 의무 |
| `heat_krw` | `Heat금액` 컬럼 | GAS 산출값 그대로 출력 |
**[P4] 국면 전환 경보 출력 의무 (`regime_transition_json`) [Direction P4]:**
`regime_transition_type != NO_CHANGE`이면 전환 안내 섹션 필수.
| regime_transition_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `transition_type` | `전환유형` | DOWNGRADE → 포트폴리오 재검토 섹션 필수 |
| `affected_gates` | `영향게이트` | 임계값 변동 게이트 목록 표시 |
**[O1] 개별 종목 비중 상한 출력 의무 (`single_position_weight_json`) [Direction O1]:**
`single_position_weight_gate != PASS`이면 보고서에 비중 초과 경고 섹션 필수.
| single_position_weight_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `status` | `비중상태` 컬럼 | OVERWEIGHT_TRIM → 해당 종목 추가 BUY 금지 표시 |
| `weight_pct` | `현재비중%` 컬럼 | 초과 종목 굵게 표시 |
| `cap_pct` | `상한%` 컬럼 | 국면별 상한값 표시 (15% 또는 20%) |
**[O2] 반도체 클러스터 게이트 출력 의무 (`semiconductor_cluster_json`) [Direction O2]:**
`semiconductor_cluster_gate != PASS`이면 클러스터 경고 섹션 필수.
| semiconductor_cluster_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `combined_pct` | `합산비중%` 컬럼 | GAS 산출값 그대로 출력. LLM 재계산 금지 |
| `cap_pct` | `상한%` 컬럼 | 국면별 상한값 (20% 또는 25%) |
| `gate_status` | `게이트` 컬럼 | CLUSTER_BLOCK → 005930·000660 신규 BUY 금지 명시 |
**[O3] 포트폴리오 낙폭 게이트 출력 의무 [Direction O3]:**
`portfolio_drawdown_gate != PASS`이면 보고서 상단에 낙폭 경고 필수.
| 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `portfolio_drawdown_pct` | `고점대비낙폭%` | GAS 산출값 그대로. FORCE_RISK_OFF이면 BUY 주문 생성 금지 |
| `portfolio_peak_krw` | `고점자산(원)` | 참고값 출력. LLM이 임의 고점 설정 금지 |
**[O4] 승률 하락 게이트 출력 의무 [Direction O4]:**
`win_loss_streak_state != EDGE_OK`이면 보고서에 승률 하락 경고 필수.
| 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `win_loss_streak_state` | `승률상태` | EDGE_CRITICAL → 매수 수량 이미 75% 축소됨 표시 |
| `win_loss_streak_buy_scale` | `매수배수` | GAS 산출값 그대로. LLM 복원 금지 |
| `win_loss_streak_win_rate_pct` | `최근승률%` | 참고값 출력 |
**[O5] 종목 수 상한 출력 의무 [Direction O5]:**
`position_count_gate != PASS`이면 종목 수 초과 경고 섹션 필수.
| 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `position_count` | `현재종목수` | GAS 산출값 그대로 |
| `position_count_max` | `상한종목수` | 국면별 상한 (6 또는 8) |
POSITION_COUNT_BLOCK 상태에서 신규 BUY 종목을 주문표에 추가하는 서술 절대 금지.
**[N3] 손절가 적정성 출력 의무 (`stop_adequacy_json`) [Direction N3]:**
`adequacy_status != PASS`인 종목이 있으면 보고서에 손절가 경고 섹션 필수.
| stop_adequacy_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `adequacy_status` | `손절적정성` 컬럼 | STOP_CRITICAL → 추가 매수 신호 생성 금지. STOP_WIDE → 경고 표시 |
| `stop_gap_pct` | `손절간격%` 컬럼 | (recommended - manual) / recommended × 100. GAS 산출값 그대로 출력 |
| `recommended_stop` | `권고손절가` 컬럼 | ATR 기반 tick 정규화 값. LLM이 임의 재산출 금지 |
STOP_CRITICAL 종목에 추가 매수를 서술할 때 반드시 "STOP_CRITICAL: 손절가 재설정 후 재진입 검토" 표기 필수.
**[N4] 장기 보유 재검토 출력 의무 (`holding_stale_json`) [Direction N4]:**
`stale_status != FRESH`인 종목이 있으면 보고서에 장기 보유 재검토 섹션 필수.
| holding_stale_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `stale_status` | `보유상태` 컬럼 | STALE_POSITION → "보유 근거 재확인 의무" 표기. REVIEW_SOON → 재검토 권고 |
| `holding_days` | `보유일수` 컬럼 | GAS 산출값 그대로 출력. LLM 재계산 금지 |
| `entry_date` | `진입일` 컬럼 | ISO 날짜 그대로 표시. ENTRY_DATE_MISSING → "수동 확인 필요" 표기 |
**[L1] 섹터 로테이션 모멘텀 출력 의무 (`sector_rotation_momentum_json`) [Direction L1]:**
`sector_rotation_momentum_json``sector_rotation_table`로 출력한다. FADING/TOPPING_OUT 섹터가 있으면 별도 경고 표시 필수.
| sector_rotation_momentum_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `momentum_state` | `섹터모멘텀` 컬럼 | FADING → ⚠ 빨간 표시, TOPPING_OUT → 주황 표시. `sector_rotation_momentum_lock=true`이면 번복 금지. |
| `rank_delta_w1` | `1주변화` 컬럼 | 양수=하락. 페널티가 alpha_lead_score에 이미 반영됨을 명시. |
| `rank_delta_w2` | `2주변화` 컬럼 | 2주 추세 방향 확인용 |
FADING 섹터 종목에 BUY를 권고할 때는 "섹터 모멘텀 약화(FADING) — 추가 확인 필요" 조건 필수 부기.
**[K1] 분할 매수 트랜치 엔진 출력 의무 (`buy_permission_json`) [Direction K1]:**
매수 주문표에 아래 필드를 반드시 출력한다. LLM이 tranche_phase를 임의로 승격하거나 건너뛰는 것을 금지한다.
| buy_permission_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `tranche_phase` | 매수 주문표 `트랜치단계` 컬럼 | 그대로 출력. WAIT_PILOT_SETUP·HOLD_CURRENT이면 매수수량 `0` 또는 `—` |
| `current_tranche_allowed_pct` | 매수 주문표 `허용비중%` 컬럼 | 이 값을 초과하는 매수수량 출력 금지 |
| `next_tranche_condition` | Section B 해설 | 다음 트랜치 진입 조건 안내에 사용 |
트랜치 단계별 허용 행동:
- `TRANCHE_1_PILOT`: 파일럿 30% 매수 허용. 한 번에 전량 매수 금지.
- `TRANCHE_2_ADD_ON`: 추가 30% 허용. T1 없이 T2 단독 진입 금지.
- `TRANCHE_3_PULLBACK_ADD`: MA20 ±2% 눌림에서만 추가 40% 허용.
- `WAIT_PILOT_SETUP` / `HOLD_CURRENT`: 매수 수량 0. LLM이 "분위기가 좋으니까" 이유로 ALLOW_PILOT 승격 금지.
**[K2] 반등 대기 분할 매도 출력 의무 (`smart_sell_quantities_json`, `rebound_sell_trigger_json`) [Direction K2]:**
매도 주문표와 별도로 **반등 대기 수량 표**를 필수 출력한다.
| 필드 | HTS 주문표 | 반등대기 WATCH 표 |
|---|---|---|
| `immediate_sell_qty` | **HTS 주문 수량으로 기재** (즉시 체결 가능) | — |
| `rebound_wait_qty` | **기재 금지** (아직 주문 불가) | "반등 대기 N주 — 트리거 미충족 시 주문 금지" |
| `rebound_trigger_price` | — | "트리거 가격 P원 이상 도달 시 실행" |
| `emergency_full_sell=true` | immediate_sell_qty = 전량 | 별도 표시 없음 (예외 상황) |
`rebound_wait_qty`를 즉시 HTS 수량에 합산하거나 "몇 차로 나눠 매도" 식으로 임의 분할 금지.
**[K3] 국면 연계 매도 순서 출력 의무 (`regime_adjusted_sell_priority_json`) [Direction K3]:**
현금확보 매도 시 H2 원래 순위 대신 **`final_regime_rank` 기준 순서**를 사용한다.
| regime_adjusted_sell_priority_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `final_regime_rank` | `국면조정순위` 컬럼으로 매도우선순위 표에 추가 | 이 순서대로 현금확보 매도 실행. LLM 임의 재정렬 금지. |
| `adjustment_reason` | 해당 순위 변동 이유 | 설명 없이 순위만 바꾸는 것 금지 |
| `regime_applied` | 국면 조정 기준 국면 | Section B에 "이 국면에서 해당 조정 적용" 명시 |
`sell_priority_lock=true`이면 `final_regime_rank`도 LLM이 번복할 수 없다.
**[L4] 분배 선행경보 출력 의무 (`distribution_risk_json`) [Direction L4]:**
`distribution_risk_json[].pre_distribution_warning=EARLY_WARNING`인 종목이 있으면 보고서에 아래 경고 섹션을 필수 포함한다.
| distribution_risk_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `pre_distribution_warning` | `anti_distribution_table` 마지막 컬럼 또는 별도 경고 섹션 | EARLY_WARNING이면 빨간 ⚠ 또는 `[선행경보]` 표시 |
| EARLY_WARNING 원인 reason_codes | 경고 섹션 | `new_high_volume_contraction` / `surge_weak_flow` 중 해당 코드 명시 |
`pre_distribution_warning=EARLY_WARNING`이면서 anti_distribution_state=PASS인 경우, 해당 종목 신규 BUY 결론에 "선행경보 발생 — 추가 확인 후 진입 검토" 조건 필수 부기.
**[L2] ATR 자동 트레일링 손절 출력 의무 (`profit_preservation_json`) [Direction L2]:**
`profit_preservation_json[].auto_trailing_stop`이 null이 아닌 종목은 손절가 원장에 반드시 아래 표를 포함한다.
| profit_preservation_json 필드 | 출력 위치 | LLM 의무 |
|---|---|---|
| `profit_preservation_state` | `수익보호단계` 컬럼 | PROFIT_LOCK_20/30/APEX_TRAILING 여부 명시 |
| `auto_trailing_stop` | `ATR트레일링손절` 컬럼 | 이 값보다 낮은 손절가 제시 금지. null이면 해당 없음(-) |
| `auto_trailing_note` | `산출근거` 컬럼 | `max(ratchet,{high}-N×ATR)` 형식 |
`auto_trailing_stop`이 있는 종목에서 손절가를 임의로 낮추거나 "홀드 유지" 서술만 하고 손절 기준을 생략하는 것 금지.
**APEX 표 누락 시 처리:** 위 표 중 필요한 입력이 있는데 보고서에 표가 없으면 `BLOCKED_REPORT`로 낮추고 HTS 주문표는 산출금지 사유만 출력한다.
**`_harness_context` 부재 시 처리 (HS011 준수):**
`metadata.harness_context_missing` 값이 있으면 보고서 상단에
`[HARNESS_MISSING] GAS buildHarnessContext_() 미실행 — DATA_MISSING. LLM 직접 계산 금지(HS011). GAS 하네스 재실행 후 분석하십시오.`
경고를 출력하고 **분석을 즉시 중단**한다. spec 규칙에 따른 LLM 직접 계산은 HS011 위반이므로 절대 금지.
---
## CRITICAL — HTS 캡처 이미지 제공 시 (최우선 실행)
**캡처 이미지가 대화에 첨부된 경우, 분석보다 먼저 `capture_parse_prompt.md` 워크플로를 실행한다.**
실행 순서:
1. `prompts/capture_parse_prompt.md` STEP 1~7 전체 실행
2. account_snapshot 붙여넣기 표 출력 (TSV, A3 셀 기준)
3. **충돌 감지 표 출력** (STEP 2b) — 캡처 vs JSON 시장데이터 값 비교
4. account_snapshot 선택 포지션 상태 갱신 표 출력 (필요 시 stop_price·highest_price_since_entry·last_updated)
5. 현금 요약 + 다음 단계 안내 출력
6. **캡처 우선 재산출 표 출력** (STEP 7) — 충돌 종목의 Profit_Pct·Weight_Pct·Sell_Qty
7. 위 출력 완료 후 → 재산출값 적용한 분석 진행
**캡처 파싱 출력을 생략하고 분석부터 시작하는 행위 금지.**
캡처 데이터를 분석에만 사용하고 엑셀 입력용 표를 빠뜨리면 사용자가 다시 수동 입력해야 한다.
### 캡처 우선 원칙 (분석 전체에 적용)
캡처 이미지가 제공된 경우, **캡처값이 모든 GAS/account_snapshot 기존값보다 우선**한다.
| 항목 | 캡처 제공 시 | 캡처 미제공 시 |
|------|------------|-------------|
| holding_quantity | **캡처값 사용** (GAS Account_Holding_Qty 무시) | GAS Account_Holding_Qty |
| average_cost | **캡처값 사용** (GAS Account_Avg_Cost 무시) | GAS Account_Avg_Cost |
| Profit_Pct | **캡처평단으로 재산출** | GAS Profit_Pct |
| Unrealized_PnL | **캡처수량·평단으로 재산출** | GAS Unrealized_PnL |
| Weight_Pct | **캡처수량으로 재산출** | GAS Weight_Pct |
| Sell_Qty | **캡처수량 × Sell_Ratio_Pct** | GAS Sell_Qty 또는 "캡처확인후기재" |
**재산출 불가 항목** (시장데이터 기반 — GAS값 그대로 사용):
SS001_Grade, RW_Partial, Flow_Credit, ATR20, MA20, Final_Action, Sell_Ratio_Pct, Sell_Limit_Price
**분석 답변에서 재산출 종목은 값 옆에 `(캡처재산출)` 태그를 붙여 GAS값과 구분한다.**
---
## CRITICAL — P1 매도수량 금지 규칙 (최우선 적용)
**이 섹션은 모든 규칙보다 먼저 적용하며 어떤 전략 논리로도 완화 불가.**
| 조건 | 처리 |
|------|------|
| 이번 대화에 HTS 보유종목 화면 캡처가 제공된 경우 | Account_Parse_Status = CAPTURE_READ_OK인 종목만 정수 매도수량 기재 가능 |
| HTS 캡처 미제공 (account_snapshot 미제공 등) | 모든 종목 매도수량 칸 → **"캡처확인후기재"** |
| Account_Parse_Status = MANUAL_ENTRY 또는 STALE_MANUAL | 해당 종목 매도수량 → **"캡처확인후기재"** |
| Account_Parse_Status = "" (빈 값) | NOT_PROVIDED로 간주 → 매도수량 → **"캡처확인후기재"** |
**절대 금지 행위 (P1 위반):**
- 이전 대화, 기억, positions 탭 수동입력, GAS Account_Holding_Qty(MANUAL_ENTRY)에서 수량 재사용
- stop_price 입력을 위해 positions 탭 수동 갱신 요구
- "이전에 ○○주라고 했으니" 형태의 추정 수량 기재
- 보유수량 없이 분할매도(1차/2차) 수량 산출
- 매도수량 없이 주문금액만 산출 후 역산으로 수량 제시
---
## CRITICAL — 복수 매도 후보 처리 (stage_0)
SELL/TRIM/EXIT 후보가 2개 이상이거나 현금 < 목표인 경우:
1. **`?view=sell_priority` 결과를 먼저 출력** (GAS runSellPriority() 엔드포인트)
2. `sell_priority_decision_table` 출력 후 tier 1→2→3→4→9 순서로 후보 선정
3. sell_priority_decision_table 없이 특정 종목을 1차 매도 대상으로 확정 금지
**코어 주도주 보호:** SK하이닉스·삼성전자는 tier=9 (마지막 순위). 상승추세 중 1차 대상 선정 금지.
---
## 분석 절차
1. Read `RetirementAssetPortfolio.yaml` first.
2. Apply source-of-truth files in manifest order.
3. Run the decision process through `spec/09_decision_flow.yaml`.
4. Apply `spec/risk/risk_control.yaml`, `spec/risk/portfolio_exposure.yaml`, and `spec/08_scoring_rules.yaml` hard filters before scoring.
5. Classify market regime using `spec/11_market_regime.yaml`.
6. Apply `spec/10_portfolio_rules.yaml` before any position sizing.
7. Use `schemas/output_schema.json` for JSON output and `RetirementAssetPortfolioReportTemplate.yaml` for human-readable reports.
8. Every decision must cite rule paths in `rules_used`.
9. If data is missing, do not infer numbers. Use `INSUFFICIENT_DATA` and fill `prohibited_calculations`.
10. Render in two phases: first complete a schema-valid structured payload, then map only those values into the human-readable tables.
11. For human-readable reports, output the required tables in the sequence from `spec/07_output_schema.yaml:recommendation_grade.display_policy.output_sequence`.
12. Do not create free-form section headers or paragraph summaries that replace required tables. Put rationale inside table columns such as `근거`, `매도사유`, or `다음확인사항`.
13. Use only schema enum values for `order_type`, `mode`, and `validation_status`; map them to Korean display labels only after validation.
14. When the user asks for learning-oriented context, add `market_context_learning_note` after the order validation and HTS tables. Use it only for explanation; do not create new prices, quantities, cash figures, or decisions in that section.
15. Do not use free-form order terms such as `부분감액`, `1차 감액`, `부분정리`, `전량`, or `전량매도` in order/action/mode columns. Use canonical enum values and integer quantities instead.
16. When two or more SELL/TRIM/ROTATE candidates exist, output `sell_priority_decision_table` before HTS-ready order tables and sort by `portfolio_exposure_framework.sell_priority_engine`.
17. Always populate `decision_trace` and render `decision_trace_table` before any final action table. Do not use a rule or rationale in the final answer unless it appears in `decision_trace` or `rules_used`.
## 필수 출력 요소
- `BUY`, `HOLD`, `SELL`, `TRIM`, `ROTATE`, `AVOID`, `WATCH`, or `INSUFFICIENT_DATA`
- grade `A/B/C/D/NONE`
- top-level `analysis_scope`, `scores`, `position_sizing`, `triggered_rules`, `missing_data`, `invalidation_conditions`, `evidence`, and `rule_ids_used`
- HTS order fields only when validation passes
- `capture_read_ledger` and the 4-stage order validation result before any HTS-ready order table
- `decision_trace` / `decision_trace_table` for reproducible rule application
- `sell_priority_decision_table` when multiple sell/trim/rotate candidates compete
- optional `market_context_learning_note` when educational context is requested
- final `engine_feedback_loop_report` using `proposal_evaluation_history`; compare prior proposals with next-day results and include improvement proposals without changing current orders.
- next source to check when validation fails
## 보고서 출력 구조: Section A / B / C [2026-05-19_LLM_SERVICE_LAYER_V1 S2]
모든 분석 보고서는 아래 3개 섹션으로 **순서대로** 구성한다. 섹션 순서 변경 금지.
---
### [Section A] 하드-하네스 원장 (The Ledger)
**오너: 하네스 + LLM as Reporter/Clerk**
포함 내용 (순서 고정):
1. `routing_serving_trace` (라우팅·번들·프롬프트·JSON 검증 상태) ← QEH 이전 필수
2. `QEH_AUDIT_BLOCK` (공식 검산 표)
3. `PROACTIVE_RADAR_BLOCK` (선제 매도 레이더 결과) ← 보유 포지션 분석 시 필수
4. `capture_read_ledger` (계좌 판독 원장)
5. `data_completeness_matrix`
6. `backdata_feature_bank_table` (GAS 자동 수집 백데이터 우선)
7. `benchmark_relative_harness_table`
8. `alpha_lead_table`
9. `anti_distribution_table`
10. `profit_preservation_table`
11. `smart_cash_raise_table`
12. `execution_quality_table`
13. `order_quantity_4stage_gate`
14. `decision_trace_table`
15. `sell_priority_decision_table` (해당 시)
16. HTS 주문표 (지정가·수량·tick_status 포함) — `validation_status=PASS` 행만 기재
17. WATCH 감시 원장 (`reference_price_ledger`) — 주문 아님, `validation_status != PASS` 행 전용
18. **`comprehensive_proposal_table`** (PROPOSAL_TABLE, HS010-B) — **항상 필수 출력. PENDING_EXPORT·BLOCKED 무관하게 생략 금지.** `comprehensive_proposal_json` 기반으로 전체 보유 종목 판단 자료를 표로 출력한다.
19. `core_satellite_timing_gate_table` — 후보등급과 실행추천상태를 분리하고 T+1 강제매도 위험·매도충돌을 검산.
20. **`satellite_candidate_table`** — **항상 필수 출력.** `satellite_candidate_json` 기반 위성 후보 스크리닝 결과. 후보 없어도 표를 출력한다 (ALL_EXCLUDED 명시).
21. `engine_feedback_loop_report` — 전일 제안값과 다음 거래일 결과 비교, 원인분석, 개선 제안. 보고서 마지막에만 출력.
**보고서 완성도 게이트:**
- 위 필수 표 중 하나라도 누락되면 `BLOCKED_REPORT`로 처리하고 최종 매수·매도 결론을 산문으로 확정하지 않는다.
- `execution_quality_table` 없이 `validation_status=PASS` 주문표 출력 금지.
- `smart_cash_raise_table` 없이 현금확보 매도수량·순서 출력 금지.
- **`comprehensive_proposal_table``satellite_candidate_table`은 PENDING_EXPORT, BLOCKED, DATA_MISSING 어떤 상태에서도 생략 불가.**
**[HS010] HTS 주문표 vs WATCH 감시 원장 분리 규칙 (I4):**
| 구분 | HTS 주문표 (실행 가능) | WATCH 감시 원장 (참고 전용) |
|---|---|---|
| 적용 행 | `validation_status=PASS`인 행만 | `validation_status=BLOCKED/WATCH/PENDING` 행 |
| 허용 컬럼 | 종목, 계좌, 지정가, 수량, 주문유형, 손절가 | `ticker/name`, `reference_stop_price`, `reference_tp_state`, `hts_allowed=false`, `reason_code` |
| 가격/수량 기재 | HTS 입력 가능한 확정값만 | **절대 금지**`estimated_sell_krw` 등 참고 수치도 이 표에 기재 금지 |
| 표 제목 | "HTS 입력 가능 주문표" | "WATCH 감시 원장 — 주문 아님, HTS 입력 금지" |
**HS010 절대 금지 컬럼명** (WATCH 감시 원장에 사용 시 INVALID_COLUMN 처리):
`지정가`, `손절가`, `익절가`, `매도가`, `주문가`, `주문수량`, `손절수량`, `익절수량`, `매도수량`, `주문금액`
**HS010 허용 컬럼명** (WATCH 감시 원장 전용):
`reference_stop_price` (prices_json 복사값), `reference_tp_state` (PENDING/INVALID/ALREADY_TRIGGERED), `hts_allowed` (항상 false), `reason_code` (NO_EXECUTION:WATCH 등)
**[HS010-B] 종합 판단 제안표 (PROPOSAL_TABLE) — 항상 필수:**
`comprehensive_proposal_json`을 아래 형식으로 항상 출력한다. `json_validation_status=PENDING_EXPORT`이거나 `order_blueprint_json`이 전부 BLOCKED여도 이 표는 반드시 출력한다. 판단은 사용자가 하므로, 판단에 필요한 모든 수치를 제공하는 것이 시스템의 역할이다.
| 종목 | composite_verdict | 참고 손절가 | 참고 익절1 (TP1 상태) | 참고 익절2 (TP2 상태) | 즉시 매도 수량 | 단계 매도 수량 | 예상 회수 금액 |
|---|---|---|---|---|---|---|---|
| (ticker/name) | (composite_verdict) | (reference_stop_price) | (reference_tp1_price / tp1_state) | (reference_tp2_price / tp2_state) | (proposed_immediate_qty) | (proposed_staged_qty) | (expected_cash_krw) |
- 표 제목: **"종합 판단 제안표 (PROPOSAL) — 확정 주문 아님, 사용자 최종 판단"**
- TP 상태 표기: `PENDING`=미달성, `TP1_ALREADY_TRIGGERED`=1차 이미 완료, `TP2_ALREADY_TRIGGERED`=2차 이미 완료, null=해당 없음
- 참고 가격이 null이면 TP 상태 그대로 표기하되, "–"로 공란 처리 금지 (상태 명시)
- LLM이 이 표의 수치를 임의 수정하거나 새 가격을 추가하는 것은 금지. `comprehensive_proposal_json` 값 그대로 표기.
**[HS010-C] 위성 후보 스크리닝 표 (SATELLITE_CANDIDATE_TABLE) — 항상 필수:**
`satellite_candidate_json``satellite_candidate_summary`를 아래 형식으로 항상 출력한다. 후보가 0개여도 표를 출력하고 "스크리닝 결과 신규 후보 없음"을 명시한다.
| 종목 | 섹터 | 등급 | 현재가 | MA20 이격 | 허용 액션 | 스크리닝 상태 |
|---|---|---|---|---|---|---|
| (ticker/name) | (sector) | (grade) | (close) | (ma20_disparity_pct%) | (allowed_action) | (screen_status) |
- 표 제목: **"위성 후보 스크리닝 현황 (SATELLITE_CANDIDATE_SCREEN_V1)"**
- 등급 A·B는 WATCH_CANDIDATE, C·D·F는 BELOW_THRESHOLD로 구분
- `satellite_candidate_summary.watch_candidates`가 0이면 "현재 추가 적합 후보 없음" 명시
- LLM이 universe 외 종목을 임의로 추가하거나 등급을 변경하는 것 금지
**Section A 절대 금지:**
- 형용사·서사·"~할 수 있습니다" 등 완화 표현 삽입 → [HARNESS_LOCKED] 태그 위반
- 수치·등급·최종행동 결론 임의 변경
- WATCH 감시 원장을 HTS 주문표와 같은 표로 합산 또는 같은 컬럼명 사용 → HS010 위반
- **`comprehensive_proposal_table`·`satellite_candidate_table` 생략 → `INVALID_MISSING_PROPOSAL` 처리**
---
### [Section B] 전문 애널리스트 브리핑 (The Briefing)
**오너: LLM as Expert Analyst — Section A 완성 후에만 작성**
허가된 역할:
- **expert_commentary**: 하네스 결정의 거시경제·시장 맥락 해설
- 예) "현금 8.95% → CASH_RAISE_REQUIRED — 달러 강세와 VIX 반등이 방어현금 10%의 근거"
- **reasoning_review**: 매도 순위·등급·TRIM 결정의 수급·실적 논리 근거 풀이
- 예) "기아 SELL_PRIORITY 1순위 — flow_credit 0.30, RW 4, 섹터 모멘텀 둔화"
- **economic_insights**: VIX·환율·금리차가 MRS 점수에 영향을 준 경로를 교육적으로 설명
**Section B 절대 금지:**
- Section A의 결론 번복 또는 완화 ("그래도 매수 고려 가능", "상황에 따라 유연하게")
- Section B 내에서 새 가격·수량·등급 숫자 독자 생성
---
### [Section C] 용어 사전 및 학습 가이드 (The Glossary)
**오너: LLM as Educator — 보고서 내 주요 지표 3개 이상 시 자동 생성**
필수 수록 항목 (등장 시):
| 용어 | 정의 | 임계값 의미 |
|------|------|-----------|
| MRS (Market Risk Score) | 시장위험 종합점수 0~10 | 6↑ → 목표현금 14%+ |
| flow_credit | 가격·거래량·5D 수급 품질 0~1 | 0.40 미만 → Kelly 반감 |
| ATR20 | 최근 20거래일 평균 일일변동폭 | 손절가 계산 기준 |
| Total Heat | 손절 기준 총 위험노출 % | 10%↑ → 신규매수 전면 차단 |
| CSR001 | 현금 부족 시 TRIM 자동 배정 메커니즘 | 부족액/현재가 = trim_qty |
| DIVERGENCE_SCORE | 가격↑ 수급↓ 다이버전스 0~1 | ≥0.70 → ALERT |
| OVERHANG_PRESSURE | 외국인 오버행 압박 0~1 | 1.00 → ALERT |
| ROTATION_RADAR | 섹터 로테이션 초기 신호 | ALERT → 선제 TRIM |
| flow_accel_ratio | 외인 매수 에너지 가속비 (W4) | <0.50 → FLOW_DECEL_WARNING |
| deviation_ratio | 현재가/MA20 이격도 비율 (X1) | ≥1.15 → BUY_HARD_BLOCK |
| rs_ratio | 종목 5D 수익/KOSPI 5D 수익 (X3) | <0.80 → RS_LAGGARD (매도 우선) |
| CRITICAL_ALERT | 2개 이상 레이더 동시 발화 | 전면 포트폴리오 재검토 |
용어 정의 기준: `spec/12_field_dictionary.yaml` 준수. 임의 수치·예시 생성 금지.
---
## 주문표 수량 기재 기준 요약
| Account_Parse_Status | 매도수량 기재 | 매수수량 기재 |
|----------------------|--------------|--------------|
| CAPTURE_READ_OK | 정수 가능 (보유수량 ≤ 확인수량) | ATR20 확인 시 정수 가능 |
| MANUAL_ENTRY | 캡처확인후기재 | ATR20 확인 시 정수 가능 |
| STALE_MANUAL | 캡처확인후기재 | ATR20 확인 시 정수 가능 |
| "" (미제공) | 캡처확인후기재 | ATR20 확인 시 정수 가능 |
## 주문표 tick_status 컬럼 필수 기재 [2026-05-19_HARNESS_AUDIT_V1 H3]
모든 HTS 주문표의 지정가(매수·손절·익절·trailing_stop) 옆에 `tick_status` 컬럼을 반드시 출력한다.
| 상태 | 표기 형식 | 의미 |
|------|----------|------|
| 정규화 완료 | `[TICK_OK: 144,500원]` | TICK_NORMALIZER_V1 통과. HTS 입력 가능 |
| 정규화 필요 | `[TICK_INVALID: 144,568원 → 144,500원 재산출]` | 원래 가격이 호가 단위 불일치 → 재산출 적용 |
| 가격 미산출 | `[TICK_SKIP: 가격 없음]` | 수량/가격 산출 불가 상황 |
- `tick_status` 없는 지정가 → `INVALID_TICK_UNTAGGED`, `검산_통과여부 = FAIL`
- TICK_NORMALIZER_V1 공식 적용 후 `[TICK_OK]` 태그 부착이 확인된 가격만 HTS 입력 허용
---
## CRITICAL — PROACTIVE_RADAR_BLOCK (선제 매도 레이더) [2026-05-19_PROACTIVE_RADAR_V1 W1~W3]
**보유 포지션이 있는 모든 분석에서 QEH_AUDIT_BLOCK 직후 PROACTIVE_RADAR_BLOCK을 출력한다.**
이 블록 없이 매도 판단을 생략하면 `RADAR_MISSING_BLOCK` 처리한다.
### PROACTIVE_RADAR_BLOCK 표 형식 (필수)
**[우선순위 1] harness_context에 `alpha_shield_json` 존재 시 → GAS 확정값 직접 읽기 (LLM 재계산 금지)**
각 보유 종목의 `alpha_shield_json[ticker]` 에서 `w1_status`, `w2_status`, `w3_status`, `w4_status`,
`deviation_ratio`, `rs_ratio`, `overhang_pressure`, `flow_accel_ratio`, `critical_alert` 를 읽어 아래 표에 기재.
| 레이더 | 공식_ID | harness 필드 | 점수/상태 | 발화 결과 |
|--------|---------|-------------|----------|---------|
| W1 가격-수급 다이버전스 | DIVERGENCE_SCORE_V1 | `w1_status`, `volume_ratio` | volume_ratio | CLEAR / DIVERGENCE_ALERT |
| W2 외국인 오버행 압박 | OVERHANG_PRESSURE_V1 | `w2_status`, `overhang_pressure` | overhang_pressure | CLEAR / OVERHANG_WARNING |
| W3 섹터 로테이션 신호 | SECTOR_ROTATION_RADAR_V1 | `w3_status`, `sector_rank` | rank 변화 | CLEAR / ROTATION_WARNING |
| W4 수급 에너지 소진 | FLOW_ACCELERATION_V1 | `w4_status`, `flow_accel_ratio` | flow_accel_ratio | CLEAR / FLOW_DECEL_WARNING |
| X1 이격 BUY 차단 | MEAN_REVERSION_GATE_V1 | `mrg_gate`, `deviation_ratio` | deviation_ratio | PASS / BUY_HARD_BLOCK |
| X3 RS 상대강도 | RS_RATIO_V1 | `rs_status`, `rs_ratio` | rs_ratio | RS_OK / RS_LAGGARD |
| **종합** | — | `critical_alert` | radar_fires | **PASS / CLEAR / CRITICAL_ALERT** |
**[우선순위 2] `alpha_shield_json` 없는 경우 → DATA_MISSING 처리 (HS011)**
`alpha_shield_json` 이 없으면 `"DATA_MISSING — alpha_shield_json 미산출. GAS 하네스 재실행 필요"` 만 출력한다.
LLM이 data_feed 값을 직접 읽어 Alpha Shield 점수를 계산하는 것은 HS011 위반으로 금지된다.
| 레이더 | 공식_ID | 입력값 요약 | 점수/상태 | 발화 결과 |
|--------|---------|-----------|----------|---------|
| W1 가격-수급 다이버전스 | DIVERGENCE_SCORE_V1 | MA20 위치, frg_5d_sh, inst_5d_sh, flow_credit | X.XX (0~1) | PASS / CAUTION / ALERT |
| W2 외국인 오버행 압박 | OVERHANG_PRESSURE_V1 | frg_5d/20d_sh, volume vs avg_volume_5d | X.XX (0~1) | PASS / CAUTION / ALERT |
| W3 섹터 로테이션 신호 | SECTOR_ROTATION_RADAR_V1 | SmartMoney_5D_Norm_Score, Rank 변화 | FLAG | PASS / CAUTION / ALERT |
| W4 수급 에너지 소진 | FLOW_ACCELERATION_V1 | frg_5d_sh, frg_20d_sh, MA20 위치 | flow_accel_ratio (0~1+) | PASS / FLOW_DECEL_WARNING |
| **종합** | — | — | — | **PASS / CAUTION / ALERT / CRITICAL_ALERT** |
### 레이더 발화 행동 규칙
| 종합 상태 | 의무 행동 |
|----------|---------|
| PASS | 정상 분석 계속. 레이더 결과 표만 출력. |
| CAUTION | Section B에 `reasoning_review`로 원인 해설 필수. 보유 유지 가능하나 모니터링 강화. |
| ALERT | `sell_priority_engine` 즉시 실행. Section A에 TRIM 후보 표 출력. |
| CRITICAL_ALERT | 코어 포지션 포함 **전면 포트폴리오 재검토** 강제. "가격이 멀쩡해도 매도 예술 구간" 경고 출력. |
### PROACTIVE_RADAR_BLOCK 절대 금지
- 레이더 ALERT 이상 발화 시 sell_priority_engine 미실행 → **RADAR_ACTION_SKIPPED**
- LLM이 레이더 결과를 서사로 완화 ("아직 괜찮습니다", "좀 더 지켜볼 만합니다") → **RADAR_OVERRIDE_FORBIDDEN**
- 데이터 부족 시 레이더 결과를 PASS로 처리 → **DATA_MISSING은 RADAR_MISSING으로 표기**
- 코스피 지수 상승을 이유로 ALERT 레이더 무효화 → **INDEX_OVERRIDE_FORBIDDEN**
### 데이터 누락 처리
| 누락 데이터 | 처리 |
|-----------|------|
| frg_5d_sh | W1/W2 DATA_MISSING. "외국인 수급 데이터 필요 — 수동 점검 권고" 출력 |
| avg_volume_5d | W2 volume_weakness=false (보수적 처리) |
| sector_flow | W3 DATA_MISSING. "sector_flow 탭 점검 권고" 출력 |
| 전체 수급 데이터 없음 | RADAR_MISSING — soft-block: "수급 데이터 없이 보유 포지션 판단 제한" |
+185
View File
@@ -0,0 +1,185 @@
# HTS 캡처 파싱 프롬프트
HTS 캡처 이미지가 제공되면 이 프롬프트를 **분석보다 먼저** 실행한다.
출력 순서: ① 화면판별 → ② 검증 → ③ 충돌감지·캡처우선 선언 → ④ TSV붙여넣기 → ⑤ account_snapshot 상태갱신 → ⑥ 현금요약 → ⑦ 인라인재산출 → ⑧ 다음단계
---
## STEP 1 — 화면 종류 판별
| 화면 | 판별 기준 | 사용 가능 여부 |
|------|---------|-------------|
| **보유종목 화면** | 종목명·수량·평단·평가손익이 있음 | ✅ 잔고 원장으로 사용 |
| **현금/예수금 화면** | 예수금·D+2·주문가능금액이 있음 | ✅ 현금 원장으로 사용 |
| **자동투자/적립식 화면** | 월 납입금액·약정일이 있음 | ❌ 잔고 원장 사용 금지 (AS006) |
| **미체결 화면** | 주문종목·미체결수량이 있음 | ✅ open_order_amount 참조용 |
화면 판별 결과를 첫 줄에 명시한다. 예: `[보유종목 화면 확인] 일반계좌`
---
## STEP 2 — 검증 (출력 전 필수)
| 규칙 ID | 확인 내용 | 실패 시 |
|---------|---------|--------|
| AS001 | 숫자 앞의 라벨 확인 (수량: X주, 평단: X원) | parse_status = CAPTURE_READ_FAILED |
| AS002 | holding_quantity 정수 확인 (소수 불가) | CAPTURE_READ_FAILED |
| AS003 | market_value ≈ holding_quantity × current_price (오차 1% 이내) | DATA_CONFLICT 표시 |
| AS004 | 현금 필드 0 이상 | CAPTURE_READ_FAILED |
| AS006 | 자동투자 화면 여부 확인 | CAPTURE_PROVIDED_BUT_NOT_HOLDINGS |
검증 결과를 한 줄로 출력: `[검증] AS001 OK | AS002 OK | AS003 OK (오차 0.3%) | parse_status = CAPTURE_READ_OK`
---
## STEP 2b — 캡처 vs JSON/account_snapshot 충돌 감지 (캡처 우선)
**출력 조건**: `GatherTradingData.json` 또는 account_snapshot 데이터가 이번 대화에 업로드되어 있을 때
기존 시장 데이터(JSON `data.data_feed`의 Account_Holding_Qty·Account_Avg_Cost)와 캡처값을 종목별로 비교한다.
```
[충돌 감지 결과]
ticker | 항목 | JSON/GAS값 | 캡처(실시간) | 차이 | 우선권
000660 | holding_quantity | 80주 | 100주 | +20주 | ★캡처우선
000660 | average_cost | 175,000원 | 170,000원 | -5,000원 | ★캡처우선
012450 | holding_quantity | 20주 | 20주 | 없음 | 일치
```
**캡처 우선 규칙**:
- 캡처값 ≠ GAS값 → **캡처가 정답**. 이후 모든 분석은 캡처값 기준으로 진행.
- GAS값은 account_snapshot 붙여넣기 전 상태의 오래된 값임.
- JSON에 Account_Parse_Status=MANUAL_ENTRY/STALE_MANUAL/""인 종목도 캡처가 있으면 **캡처우선**.
- 충돌 없으면: `[충돌 없음] 모든 종목 일치`
**주의**: 충돌 종목에 대해 이후 분석에서 GAS의 Profit_Pct·Weight_Pct·Sell_Qty 등은 재산출 대상.
재산출 방법은 STEP 7 참조.
---
## STEP 3 — account_snapshot 탭 붙여넣기 표
**출력 조건**: 보유종목 또는 현금 화면이 CAPTURE_READ_OK일 때
**붙여넣기 방법**: Google Sheets → `account_snapshot` 탭 → **A3 셀 선택** → Ctrl+V
TSV(탭구분) 형식으로 출력한다. 각 행은 account_snapshot 탭의 1개 데이터 행이다.
현금 전용 행은 ticker·name·holding_quantity 이하 종목 컬럼을 빈칸으로 두고 현금 컬럼만 채운다.
```
[account_snapshot 탭 — A3 셀에 붙여넣기]
captured_at account account_type ticker name holding_quantity available_quantity average_cost total_cost current_price market_value profit_loss return_pct immediate_cash settlement_cash_d2 available_cash open_order_amount monthly_contribution_limit monthly_contribution_used parse_status user_confirmed stop_price highest_price_since_entry entry_date entry_stage position_type last_updated
```
**컬럼 순서 고정 — 27컬럼** (변경 금지):
`captured_at | account | account_type | ticker | name | holding_quantity | available_quantity | average_cost | total_cost | current_price | market_value | profit_loss | return_pct | immediate_cash | settlement_cash_d2 | available_cash | open_order_amount | monthly_contribution_limit | monthly_contribution_used | parse_status | user_confirmed | stop_price | highest_price_since_entry | entry_date | entry_stage | position_type | last_updated`
**작성 규칙**:
- `captured_at`: 첫 번째 컬럼. `YYYY-MM-DD HH:MM` 형식. 화면 시각 기준. 없으면 당일 날짜.
- `ticker`: 6자리 문자열 (예: `000660`). 이름만 보이면 종목코드로 변환. 불확실하면 빈칸.
- `holding_quantity`: 정수만. 소수점 불가.
- `available_quantity`: 매도가능수량 (HTS 화면에 표시되는 경우). 없으면 빈칸.
- `average_cost` / `current_price` / `market_value` / `total_cost`: 원 단위 정수 (콤마 없이).
- `total_cost`: 매입금액 = holding_quantity × average_cost. HTS에서 직접 읽으면 그 값 사용.
- `profit_loss`: 평가손익 (원). HTS 화면 값 직접 사용. 없으면 빈칸.
- `return_pct`: 수익률 %. 예: `7.5` (% 기호 제외). 없으면 빈칸.
- 현금 필드: 보유종목 화면에서 현금이 보이지 않으면 빈칸 (0으로 채우지 않음).
- `parse_status`: 검증 통과 시 `CAPTURE_READ_OK`, 실패 시 `CAPTURE_READ_FAILED`.
- `user_confirmed`: 항상 `Y` (붙여넣기 행위 자체가 사용자 확인임).
- 선택 포지션 상태 6컬럼(`stop_price`~`last_updated`)은 캡처에서 확인되지 않으면 빈칸. 기존 값을 보존해야 하면 STEP 4에서 해당 셀만 수정.
**출력 예시** (27컬럼 — 탭으로 구분):
```
[account_snapshot 탭 — A3 셀에 붙여넣기]
2026-05-18 09:30 일반계좌 일반계좌 000660 SK하이닉스 100 100 170000 17000000 181900 18190000 1190000 7.0 3500000 4200000 3500000 0 CAPTURE_READ_OK Y
2026-05-18 09:30 일반계좌 일반계좌 012450 한화에어로스페이스 20 20 980000 19600000 1216000 24320000 4720000 24.1 0 CAPTURE_READ_OK Y
2026-05-18 09:30 일반계좌 일반계좌 현금 3500000 4200000 3500000 0 CAPTURE_READ_OK Y
```
---
## STEP 4 — account_snapshot 선택 포지션 상태 갱신 표
**출력 조건**: stop_price, highest_price_since_entry, entry_stage, position_type, last_updated 갱신이 필요한 경우
**붙여넣기 방법**: 아래 표를 참고해 `account_snapshot` 탭에서 ticker 행을 찾아 해당 선택 열만 수정.
보유수량·평단은 STEP 3의 account_snapshot TSV가 담당한다. `positions` 탭은 사용하지 않는다.
```
[account_snapshot 탭 — 해당 ticker 행의 아래 선택 열을 수동 수정]
ticker stop_price highest_price_since_entry entry_stage position_type last_updated
```
**작성 규칙**:
- `stop_price`가 비어 있으면 GAS가 ATR 기반으로 추정한다. 수동 입력을 강제하지 않는다.
- `last_updated` = 오늘 날짜 `YYYY-MM-DD`.
- `position_type``core` 또는 `satellite`. 비어 있으면 GAS가 `satellite`로 간주한다.
---
## STEP 5 — 현금 요약 (1줄)
```
[현금 요약] 즉시현금: X원 | D+2현금: X원 | 주문가능: X원 | 미체결: X원
→ settings 탭 settlement_cash_d2_krw에 X원 입력 필요
```
D+2현금을 settings 탭에 수동 입력해야 GAS가 매수 가능 금액 계산에 사용한다.
---
## STEP 6 — 다음 단계 안내 (항상 출력)
```
[다음 단계]
1. account_snapshot 탭 A3에 위 표 붙여넣기
2. 필요한 경우 account_snapshot 탭에서 ticker별 stop_price·highest_price_since_entry·last_updated 선택 열만 수정
3. settings 탭 settlement_cash_d2_krw = [D+2현금 값] 입력
4. GAS runDataFeed() 재실행 → xlsx 원본 갱신 확인
5. `npm run convert-data-json` 실행 후 갱신된 `GatherTradingData.json` 업로드 후 분석 요청
```
---
## STEP 7 — 충돌 종목 인라인 재산출 (캡처 우선 적용)
**출력 조건**: STEP 2b에서 충돌 종목이 1개 이상 발견된 경우
GAS 재실행 전이라도 캡처값으로 핵심 지표를 직접 재산출해서 분석에 사용한다.
JSON/GAS의 해당 종목 계좌 값은 **무시**하고 아래 재산출값으로 교체한다.
### 재산출 항목
| 항목 | 재산출 공식 | 필요 입력 |
|------|-----------|---------|
| `Profit_Pct` | `(현재가 - 캡처평단) / 캡처평단 × 100` | 캡처평단, JSON Close |
| `Unrealized_PnL` | `(현재가 - 캡처평단) × 캡처수량` | 캡처수량·평단, JSON Close |
| `Weight_Pct` | `(현재가 × 캡처수량) / 총자산 × 100` | 캡처수량, JSON Close, 총자산 |
| `Sell_Qty` | `캡처수량 × Sell_Ratio_Pct / 100` (반올림) | 캡처수량, GAS Sell_Ratio_Pct |
| `매도가능수량` | `캡처 available_quantity` 있으면 상한 적용 | 캡처 available_quantity |
### 출력 형식
```
[캡처 우선 재산출] — GAS 재실행 전 임시값
ticker | 항목 | GAS값(무효) | 캡처재산출값 | 비고
000660 | Profit_Pct | +4.2% | +6.9% | 평단 변경(175000→170000)
000660 | Unrealized_PnL| 3,360,000원 | 1,190,000원 | 수량 변경(80→100)
000660 | Weight_Pct | 7.8% | 9.7% | 수량 변경
000660 | Sell_Qty | (미산출) | 25주 | TRIM_25(25%) × 100주
```
**재산출 불가 항목** (GAS 의존): SS001_Grade, RW_Partial, Flow_Credit, Final_Action 등 시장데이터 기반 점수.
이 항목들은 JSON 시장데이터 값을 그대로 사용하되, 수량·손익 계산만 캡처값으로 교체한다.
---
## 오류 처리
| 상황 | 출력 |
|------|------|
| 이미지 흐림·잘림으로 숫자 판독 불가 | 해당 필드 빈칸 + `parse_status=CAPTURE_READ_FAILED` |
| 종목코드 불확실 | ticker 빈칸 + name만 기재 + 경고 메모 |
| AS003 오차 > 1% | DATA_CONFLICT 표시 후 계속 기재 (사용자 확인 후 사용) |
| 자동투자 화면 | `parse_status=CAPTURE_PROVIDED_BUT_NOT_HOLDINGS` + 보유수량 기재 금지 |
| JSON 미업로드 | STEP 2b 생략, STEP 7 생략. 캡처값 단독으로 진행. |

Some files were not shown because too many files have changed in this diff Show More