meta:
  title: "GatherTradingData.json — Raw Data Mapping"
  parent_file: "RetirementAssetPortfolio.yaml"
  version: "2026-05-16-F15_valuation_mapping"
  language: "ko-KR"
  timezone: "Asia/Seoul"
  role: "canonical"
  has_code_implementation: true
  code_path: "src/quant_engine/convert_xlsx_to_json.py"
  purpose: >
    제공 raw JSON의 data.<sheet> 배열과 컬럼을 canonical field로 매핑한다.
    xlsx는 JSON 재생성 소스이며 일반 LLM 분석에서는 직접 파싱하지 않는다.
    Snapshot Admin의 workbook inventory와 migration classification은
    GatherTradingData.xlsx를 직접 읽어서 계산한다.
    이 파일은 시장/종목/섹터/매크로 데이터만 담당하며 계좌·보유·현금 데이터는
    spec/15_account_snapshot_contract.yaml이 담당한다.

raw_json:
  file: "GatherTradingData.json"
  source_workbook: "GatherTradingData.xlsx"
  schema_version: "2026-05-18-json-raw-data-v1"
  role: "market_raw_json"
  root_paths:
    metadata: "metadata"
    data: "data"
  required_paths:
    data_feed: "data.data_feed"
    sector_flow: "data.sector_flow"
    macro: "data.macro"
    event_risk: "data.event_risk"
    core_satellite: "data.core_satellite"
  validation_tool: "tools/validate_data_sample_json.py"
  conversion_tool: "tools/convert_xlsx_to_json.py"
raw_workbook:
  file: "GatherTradingData.xlsx"
  role: "market_raw_workbook_source_for_json"
  header_policy:
    header_search_rows: 8
    meta_row_allowed: true
  required_sheets:
    data_feed:
      role: "보유/관심 핵심 종목 시장 데이터"
      required_columns: ["Ticker", "Name", "Close", "ATR20", "Flow_OK", "Frg_5D", "Inst_5D"]
      recommended_columns:
        - "Open"
        - "PrevClose"
        - "High"
        - "Low"
        - "Volume"
        - "AvgVolume_5D"
        - "MA20"
        - "MA60"
        - "Ret5D"
        - "Ret10D"
        - "Ret20D"
        - "Ret60D"
        - "AvgTradeValue_5D_KRW"
        - "AvgTradeValue_20D_KRW"
        - "TradeValue_Unit"
        - "Timing_Action"
        - "Timing_Score_Entry"
        - "Timing_Score_Exit"
        - "Entry_Mode"
        - "Entry_Mode_Gate"
        - "Entry_Mode_Reason"
        - "Candidate_Quality_Grade"
        - "T1_Forced_Sell_Risk_Score"
        - "T1_Forced_Sell_Risk_State"
        - "Sell_Conflict_Score"
        - "Sell_Conflict_State"
        - "Execution_Recommendation_State"
        - "Forward_PE"
        - "PBR"
        - "EPS_Revision_Status"
        - "EPS_Growth_1Y_Pct"   # Yahoo earningsTrend +1y 성장률 → KOSDAQ PEG 계산 (A2)
        - "DividendYield"
        - "DPS"                  # 주당 배당금 Yahoo lastDividendValue (A4)
        - "ROE_Pct"              # 자기자본이익률(%) Yahoo financialData.returnOnEquity×100
        - "Operating_Margin_Pct" # 영업이익률(%)  Yahoo financialData.operatingMargins×100
        - "Debt_To_Equity"       # 부채비율(D/E)  Yahoo defaultKeyStatistics.debtToEquity
        - "Current_Ratio"        # 유동비율        Yahoo financialData.currentRatio
        - "FCF_B"                # 잉여현금흐름(억원) Yahoo financialData.freeCashflow÷1e8
        - "Revenue_Growth_Pct"   # 매출 성장률(%) Yahoo financialData.revenueGrowth×100
        - "Beta"
        - "High52W"
        - "Low52W"
        - "Pct_52W_High"
        - "Pct_From_52W_Low"
        - "Target_Price"
        - "Upside_Pct"
        - "Earnings_Date"
        - "Days_To_Earnings"
        - "Ex_Dividend_Date"     # 배당락일 (A4)
        - "Days_To_Ex_Div"       # 배당락일 잔여 일수
        - "Timing_Score_Entry"   # 진입 타이밍 종합 점수(0~100)
        - "Timing_Score_Exit"    # 청산/축소 타이밍 종합 점수(0~100)
        - "Timing_Action"        # BUY_STAGE1_READY/NO_BUY_OVERHEATED 등 실행 액션
        - "Timing_Block_Reason"  # 타이밍 액션 산출 핵심 사유
        - "Sell_Action"          # HOLD/TRIM_50/EXIT_100 등 매도 액션
        - "Sell_Ratio_Pct"       # 보유수량 확인 시 적용할 매도 비율
        - "Sell_Qty"             # 보유수량 확인 시 정수 매도수량. 미확인 시 blank
        - "Sell_Limit_Price"     # HTS 입력용 매도 지정가
        - "Sell_Price_Source"    # TP/Trailing/Close 등 가격 출처
        - "Sell_Price_Basis"     # PRIOR_CLOSE_X_0.998/TRAILING_STOP_TRIGGER 등 산출 기준
        - "Sell_Execution_Window" # INTRADAY_AFTER_09_30/CLOSE_REVIEW_OR_NEXT_OPEN 등 실행 시간대
        - "Sell_Order_Type"      # LIMIT_SELL/PROTECTIVE_LIMIT_SELL
        - "Sell_Reason"          # RW_EXIT/TIME_STOP/PROFIT_PROTECT 등 근거
        - "Sell_Validation"      # PASS/NO_HOLDING_QTY/NO_SELL_PRICE 등 검산상태
        - "Account_Holding_Qty"  # account_snapshot에서 확인한 보유수량
        - "Account_Avg_Cost"     # account_snapshot에서 확인한 평단
        - "Account_Market_Value" # account_snapshot에서 확인한 평가금액
        - "Account_Parse_Status" # CAPTURE_READ_OK 등 캡처 판독 상태
        - "Rule_Sell_Qty"        # 룰엔진 기본 매도수량
        - "Rebalance_Target_Cash_Pct" # 주간 D+2 현금 목표
        - "Rebalance_Need_KRW"   # 목표 D+2 현금까지 부족액
        - "Override_Sell_Qty"    # 부족현금만 채우는 최소 조정 매도수량
        - "Override_Reason"      # 조정 수량 사유
        - "Override_Validation"  # PASS_USER_CASH_TARGET 등
        - "Final_Action"         # 룰엔진 최종 액션. LLM 임의 재판단 금지
        - "Action_Priority"      # 낮을수록 우선 처리
        - "Priority_Score"       # 동일 액션 내 정렬 점수
        - "Final_Rank"           # Action_Priority ASC, Priority_Score DESC 기준 순위
        - "Decision_Source"      # RULE_ENGINE / RULE_ENGINE_WITH_MISSING_DATA
        - "Limit_Price_Est"
        - "Stop_Price_Est"       # account_snapshot 우선, ATR 추정 폴백 (A7)
        - "Stop_Price_Source"    # 출처 표시
        - "EE_Est"               # Bayesian multiplier 반영 기대우위 (S1)
        - "Pos_Size_Qty"         # POSITION_SIZE_V1 간략 추정 수량 (A6)
        - "Breakout_Score"
        - "Breakout_Gate"
        - "AC_S1"
        - "AC_S2"
        - "AC_S3"
        - "AC_S4"
        - "AC_S5"
        - "AC_Total"
        - "AC_Gate"
        - "C1_Price"
        - "C2_RelStr"
        - "C3_VolSurge"
        - "C4_Flow"
        - "C5_Sector"
        - "Leader_Scan_Total"
        - "Leader_Gate"
        - "RW1"
        - "RW2"
        - "RW3"
        - "RW4"
        - "RW5"
        - "RW_Partial"
    core_satellite:
      role: "위성 후보군 시장 데이터"
      required_columns: ["Ticker", "Name", "Sector", "Close", "ATR20", "Flow_OK", "Rotation_Score", "Alert_Level"]
      recommended_columns:
        - "Open"
        - "PrevClose"
        - "High"
        - "Low"
        - "Volume"
        - "AvgVolume_5D"
        - "MA20"
        - "MA60"
        - "Ret10D"
        - "Ret20D"
        - "Ret60D"
        - "AvgTradeValue_5D_KRW"
        - "AvgTradeValue_20D_KRW"
        - "TradeValue_Unit"
    sector_flow:
      role: "섹터 수급·상대강도 canonical 분석 시트"
      required_columns: ["Sector", "Sector_Score", "Sector_Rank", "Alert_Level"]
      status: "canonical"
      usage_limit: >
        옛 sector_flow_v2의 역할을 sector_flow가 대신한다.
        ETF_Code/ETF_Ret* 컬럼명과 Rotation_* 컬럼은 legacy 호환용 별칭이며 실제 의미는 Proxy_Ticker/Proxy_Ret* 및 Sector_Score/Sector_Rank이다.
        Frg_5D_SUM/Inst_5D_SUM은 v2 원화 수급 집계값을 legacy 호환 컬럼에 매핑한 값으로 해석한다.
      added_columns:
        - "Sector_Median_PE"
        - "Sector_Median_PBR"
        - "ETF_Ret10D"       # ETF 10일 수익률 → RW2 상대약세 판단 입력
        - "Rotation_Rank"    # Sector_Score 내림차순 순위 (1=최고) → C5, RW1 판단
    sector_universe:
      role: "sector_flow canonical 섹터 구성 원장"
      optional_sheet: true
      fallback: "시트가 없으면 gas_data_feed.gs DEFAULT_SECTOR_UNIVERSE_V2 사용 후 sector_universe 기본 템플릿 생성"
      external_seed_policy: "sector_targets.json은 legacy seed/archive이며 실행 입력·LLM 업로드 대상이 아니다."
      canonical_source_note: "sector_flow의 구성종목 universe는 sector_universe 시트 또는 DEFAULT_SECTOR_UNIVERSE_V2만 사용한다."
      required_columns:
        - "Sector"
        - "Proxy_Ticker"
        - "Proxy_Name"
        - "Proxy_Type"
        - "Base_Ticker"
        - "Constituent_Code"
        - "Constituent_Name"
        - "Weight"
        - "Is_ETF"
        - "Enabled"
        - "Effective_Date"
        - "Source"
      aggregation_rule: >
        Is_ETF=Y 행은 proxy/실행상품 식별용으로 보존하되 sector_flow의 구성종목 smart money,
        Flow_Breadth_5D, Coverage_Weight 산출에서는 제외한다. ETF 자체 수급·NAV·괴리율은 후속 etf_raw가 담당한다.
    sector_flow_v2:
      status: "deprecated"
      note: "sector_flow가 sector_flow_v2 canonical 분석 시트를 대체한다."
      quality_gate:
        A: "Coverage_Weight >= 0.80 AND Flow_Rows_Min >= 20 AND Stale_Count=0 AND 원화 수급/거래대금 산출 가능"
        B: "Coverage_Weight >= 0.60 AND 핵심 가격·수급 대부분 정상"
        C: "coverage 부족 또는 proxy/거래대금 일부 누락. Decision_Use=WATCH_ONLY"
        D: "가격·수급 핵심 실패. Decision_Use=INVALID"
      usage_rule: "Data_Quality C/D는 Strong_Buy 또는 섹터 단독 강매도 근거로 사용 금지"
    etf_raw:
      role: "ETF 실행 유동성·자체 수급 원천 시트"
      optional_sheet: true
      required_columns:
        - "Sector"
        - "ETF_Ticker"
        - "ETF_Name"
        - "Close"
        - "NAV"
        - "iNAV"
        - "Premium_Discount_Pct"
        - "Tracking_Error"
        - "AUM"
        - "Bid"
        - "Ask"
        - "Spread_Pct"
        - "AvgTradeValue_5D_KRW"
        - "AvgTradeValue_20D_KRW"
        - "ETF_Frg_5D_KRW"
        - "ETF_Inst_5D_KRW"
        - "LP_Quality_Flag"
        - "ETF_Liquidity_Score"
        - "ETF_NAV_Risk"
        - "ETF_Liquidity_Status"
        - "ETF_Execution_Use"
        - "ETF_Data_Status"
        - "NAV_Source"
        - "NAV_Source_Date"
      limitation: >
        Phase 3 interim은 Yahoo/Naver 기반 가격·스프레드·거래대금·ETF 자체 수급을 자동 산출하고,
        NAV/iNAV/괴리율/추적오차/AUM은 etf_nav_manual 시트 값이 있으면 반영한다.
        수동 NAV 입력이 없으면 blank 및 ETF_NAV_Risk=NAV_DATA_MISSING으로 둔다.
        이 경우 ETF_Execution_Use는 WATCH_ONLY이며 ETF 매매 실행 핵심 근거로 사용하지 않는다.
    etf_nav_manual:
      role: "ETF NAV·괴리율·추적오차 수동 검증 입력 시트"
      optional_sheet: true
      required_columns:
        - "ETF_Ticker"
        - "ETF_Name"
        - "Close"
        - "NAV"
        - "iNAV"
        - "Premium_Discount_Pct"
        - "Tracking_Error"
        - "AUM"
        - "Source_Date"
        - "Source"
        - "Enabled"
      usage_rule: >
        KRX/KIND/운용사 자료를 수동 확인해 입력한다. Enabled=Y 행만 etf_raw에 반영한다.
        Premium_Discount_Pct가 비어 있으면 Close와 NAV 또는 iNAV로 자동 산출한다.
      importer: "tools/import_etf_nav_manual.py — KRX/KIND/운용사 CSV/XLSX export를 etf_nav_manual 시트로 변환. --enable 옵션은 NAV/iNAV와 Source_Date가 있는 etf_raw 매칭 행만 Enabled=Y 처리."
    rebalance:
      role: "리밸런싱 실행 계획 시트 (bucket drift → 레짐 적응 밴드 → 비용효익 게이트 → 3단계 분할 실행)"
      optional_sheet: true
      generator: "tools/build_rebalance_engine_v1.py (Python) + GAS src/gas_adapter_parts/gdf_06_rebalance.gs:runRebalanceSheet_()"
      artifact: "Temp/rebalance_engine_v1.json"
      layout: "multi-section — 4섹션이 단일 시트에 순서대로 배치됨 (구분: === SECTION_NAME === 행)"
      sections:
        SUMMARY:
          description: "포트폴리오 전체 요약 (key-value 2열 형식)"
          fields:
            - "Run_Date"              # 실행 시각 (ISO-8601 KST)
            - "Regime"                # 시장 레짐 (RISK_ON/NEUTRAL/RISK_OFF 등)
            - "Regime_Band"           # 적용된 밴드 레이블 (예: RISK_ON ±15%p)
            - "Total_Portfolio_KRW"   # 전체 포트폴리오 평가금액 (원)
            - "Core_Pct"              # 현재 코어 비중 (%)
            - "Satellite_Pct"         # 현재 위성 비중 (%)
            - "Cash_Pct"              # 현재 현금 비중 (%)
            - "Target_Core_Pct"       # 목표 코어 비중 (%)
            - "Target_Sat_Pct"        # 목표 위성 비중 (%)
            - "Target_Cash_Pct"       # 목표 현금 비중 (%)
            - "Rebalance_Needed"      # 리밸런싱 필요 여부 (true/false)
            - "Holdings_Count"        # 보유 종목 수
            - "Orders_Count"          # 생성된 주문 수
            - "Min_Actionable_Drift_Pct" # 최소 실행 기준 드리프트 (%)
        BUCKETS:
          description: "버킷별 드리프트 분석 (행 형식)"
          columns:
            - "Bucket"                # Core / Satellite / Cash
            - "Target_Pct"            # 목표 비중 (%)
            - "Current_Pct"           # 현재 비중 (%)
            - "Drift_Pct"             # 드리프트 (현재 − 목표, %)
            - "Band_Min"              # 레짐 적응 하단 경계 (%)
            - "Band_Max"              # 레짐 적응 상단 경계 (%)
            - "Regime_Band"           # 적용 밴드 레이블
            - "Drift_Status"          # NORMAL / WARN / BREACH_LOW / BREACH_HIGH
        TICKERS:
          description: "종목별 드리프트 분석 + 강제 신호 + 3단계 분할 수량 (행 형식)"
          columns:
            - "Ticker"                # 종목 코드
            - "Name"                  # 종목명
            - "Bucket"                # Core / Satellite
            - "Target_Pct"            # 버킷 내 equal-weight 목표 비중 (%)
            - "Current_Pct"           # 현재 보유 비중 (%)
            - "Drift_Pct"             # 드리프트 (현재 − 목표, %)
            - "Band_Min"              # 레짐 적응 하단 경계 (%)
            - "Band_Max"              # 레짐 적응 상단 경계 (%)
            - "Regime_Band"           # 적용 밴드 레이블
            - "Drift_Status"          # NORMAL/WARN/BREACH_LOW/BREACH_HIGH/FORCE_ABS_FLOOR/FORCE_TIME_STOP
            - "Force_Signal"          # ABS_FLOOR / TIME_STOP / (empty)
            - "Gate_Status"           # PASS / BLOCKED_BY_COST / FORCE_OVERRIDE
            - "Action"                # SELL / BUY / WATCH / HOLD
            - "Stage1_Qty"            # 1단계 수량 (전체의 30%)
            - "Stage1_Price"          # 1단계 지정가 (원)
            - "Stage2_Qty"            # 2단계 수량 (전체의 30%)
            - "Stage2_Price"          # 2단계 지정가 (원)
            - "Stage3_Qty"            # 3단계 수량 (전체의 40%)
            - "Stage3_Price"          # 3단계 지정가 (원)
            - "Trade_Value_KRW"       # 예상 거래금액 (원)
            - "Cost_Est_KRW"          # 예상 비용 (수수료+세금, 원)
            - "Net_Benefit_Pct"       # 비용 차감 순 드리프트 개선 효과 (%)
            - "Close"                 # 직전 종가 (원)
        ORDERS:
          description: "실행 주문 목록 (행 형식) — gate_status=PASS 또는 FORCE_OVERRIDE 종목만 포함"
          columns:
            - "Order_No"              # 주문 순번
            - "Ticker"                # 종목 코드
            - "Name"                  # 종목명
            - "Bucket"                # Core / Satellite
            - "Action"                # SELL / BUY
            - "Stage"                 # 1 / 2 / 3
            - "Qty"                   # 수량
            - "Limit_Price_KRW"       # 지정가 (원)
            - "Trade_Value_KRW"       # 예상 거래금액 (원)
            - "Reason"                # 주문 근거 (BREACH_HIGH / ABS_FLOOR / TIME_STOP 등)
    sector_flow_history:
      role: "sector_flow 누적 스냅샷 및 RW1/RW3 이력 근거"
      optional_sheet: true
      required_columns:
        - "Snapshot_Date"
        - "Sector"
        - "Sector_Score"
        - "Sector_Rank"
        - "SmartMoney_5D_KRW"
        - "SmartMoney_20D_KRW"
        - "Flow_Breadth_5D"
        - "Alert_Level"
        - "Data_Quality"
        - "Decision_Use"
        - "ETF_Execution_Use"
      usage_rule: "legacy sector_flow의 RW1/RW3는 sector_flow_history를 우선 사용하고, 이력이 없을 때만 기존 sector_flow/PropertiesService 값을 fallback으로 사용한다."
    backdata_feature_bank:
      role: "GAS 자동 수집 진입-청산 백데이터 원장"
      optional_sheet: true
      required_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"
        - "RSI14_At_Entry"
        - "Late_Chase_Risk_Score"
        - "Follow_Through_Score"
        - "Breakout_Score"
        - "Rebound_Preservation_Score"
        - "Setup_Decision"
        - "Exit_Reason"
        - "PnL_Pct"
        - "Holding_Days"
        - "MAE_Pct"
        - "MFE_Pct"
      usage_rule: >
        GAS가 data_feed / alpha_lead_json / sell_priority / performance를 합쳐 자동 생성한
        1차 원장이다. 사용자가 직접 등록한 기록은 Source_Origin=MANUAL_CORRECTION일 때만
        보정용으로 해석하며, 신규 전략 판단의 primary source로 쓰지 않는다.
      source_priority:
        1: "GAS daily snapshot"
        2: "performance trade journal"
        3: "manual correction"
    macro:
      role: "행 기반 macro indicator table"
      required_columns: ["Symbol", "Name", "Category", "Close", "Status"]
      added_columns:
        - "Ret2D"        # USD/JPY 2일 변화율 → MRS usd_jpy_score 입력
        - "Ret10D"       # KOSPI/KOSDAQ 10일 수익률 → C2 daily_leader_scan
        - "MA60"         # KOSPI MA60 → RISK_ON 판정 조건
        - "HYG_HY_Bond"  # 신용위험 proxy (credit_stress_status 산출 기반)
      computed_rows:
        MRS_COMPUTED:    "MARKET_RISK_SCORE_V1 자동 계산 결과 (score/10, target_cash_pct)"
        REGIME_PRELIM:   "1차 시장국면 판정 (sector_flow 미포함 간이 판정)"
      row_based_mapping:
        vix_close: "row where Symbol or Name contains VIX -> Close"
        kospi_close: "row where Symbol or Name contains KOSPI -> Close"
        usd_krw: "row where Symbol or Name contains USD/KRW or USDKRW -> Close"
        sp500_ret5d: "row where Symbol or Name contains S&P500 -> Ret5D"
      missing_policy:
        kospi_ma20: "JSON 미제공 시 formula_registry.MARKET_RISK_SCORE_V1의 missing_points 적용"
        usd_jpy_2d_change_pct: "JSON 미제공 시 missing_points 적용"
        credit_stress_status: "JSON 미제공 시 caution equivalent missing_points 적용"
    event_calendar:
      role: "이벤트 일정 입력 탭. 운영자가 직접 관리하는 source-of-truth. GAS seedEventCalendar_()가 최초 seed를 제공하며 이후 수동 갱신."
      required_columns: ["Date", "Event", "Type", "Impact", "Alert"]
      update_policy: "수동 갱신 (FOMC 연 8회, CPI 월 1회, IPO/만기 수시). seedEventCalendar_()로 초기화 가능."
      note: "event_risk 탭의 원본 소스. GAS 코드에 날짜를 hardcode하지 않는다."
    event_risk:
      role: "이벤트 리스크 calendar (event_calendar 탭에서 DaysLeft 계산 후 기록된 runtime output)"
      required_columns: ["Date", "Event", "Impact"]
    core_satellite_status:
      role: "core_satellite 청크 실행 완료 상태"
      optional_sheet: true
      required_columns:
        - "Status"
        - "Universe_Count"
        - "Processed_Count"
        - "Coverage_Pct"
        - "Chunk_Size"
        - "Total_Chunks"
        - "Next_Chunk_Idx"
        - "Updated_At"
      usage_rule: "Status=COMPLETE AND Coverage_Pct>=99.9일 때 core_satellite 전체 갱신 완료로 본다."
  transient_sheets:
    cs_chunk_N:
      role: "core_satellite 생성 중 임시 청크"
      lifecycle: "runCoreSatelliteFinalize()가 core_satellite 병합을 완료하면 삭제"
      deletion_allowed_when: "core_satellite_status.Status=COMPLETE AND Coverage_Pct>=99.9"
      prohibition:
        - "FINALIZING 또는 IN_PROGRESS 상태에서 삭제 금지"
        - "core_satellite 병합 실패 상태에서 시트 다이어트 목적으로 삭제 금지"
  sheet_diet_policy:
    keep:
      canonical_required: ["data_feed", "sector_flow", "macro", "event_risk", "core_satellite"]
      support: ["settings", "account_snapshot", "sector_universe", "sector_flow_history", "etf_nav_manual", "universe", "monthly_history", "performance", "backdata_feature_bank", "event_calendar", "daily_history", "pa1_feedback", "alpha_history", "evaluation_dashboard", "trade_quality_history", "rebalance"]
      deprecated: ["positions", "chat_input", "etf_raw", "core_satellite_status", "orbit_gap", "asset_history"]
    delete:
      transient_after_complete: ["cs_chunk_N"]
    notes:
      - "orbit_gap·asset_history → monthly_history 통합 (Month당 1행, 16컬럼)."
      - "etf_raw → GAS in-memory map 전환. 시트 쓰기 제거."
      - "core_satellite_status → ScriptProperties 이전."
      - "sector_flow는 sector_flow_v2 역할을 대체하는 canonical 분석 시트다."

canonical_field_mapping:
  ticker: {sheet: ["data_feed", "core_satellite"], column: "Ticker"}
  name: {sheet: ["data_feed", "core_satellite"], column: "Name"}
  close_price: {sheet: ["data_feed", "core_satellite"], column: "Close"}
  open_price: {sheet: ["data_feed", "core_satellite"], column: "Open", fallback: "DATA_MISSING"}
  previous_close_price: {sheet: ["data_feed", "core_satellite"], column: "PrevClose", fallback: "DATA_MISSING"}
  volume: {sheet: ["data_feed", "core_satellite"], column: "Volume", fallback: "DATA_MISSING"}
  avg_volume_5d: {sheet: ["data_feed", "core_satellite"], column: "AvgVolume_5D", fallback: "DATA_MISSING"}
  ma20: {sheet: ["data_feed", "core_satellite"], column: "MA20", fallback: "DATA_MISSING"}
  ma60: {sheet: ["data_feed", "core_satellite"], column: "MA60", fallback: "DATA_MISSING"}
  atr20: {sheet: ["data_feed", "core_satellite"], column: "ATR20"}
  avg_trade_value_5d:
    sheet: ["data_feed", "core_satellite"]
    preferred_column: "AvgTradeValue_5D_KRW"
    legacy_column: "AvgTradeValue_5D_M"
    unit_rule: "preferred_column은 KRW. legacy_column은 million KRW로 해석해 ×1,000,000 적용."
  avg_trade_value_20d:
    sheet: ["data_feed", "core_satellite"]
    preferred_column: "AvgTradeValue_20D_KRW"
    legacy_column: "AvgTradeValue_20D_M"
    unit_rule: "preferred_column은 KRW. legacy_column은 million KRW로 해석해 ×1,000,000 적용."
  frg_5d_sh: {sheet: ["data_feed", "core_satellite"], column: "Frg_5D"}
  inst_5d_sh: {sheet: ["data_feed", "core_satellite"], column: "Inst_5D"}
  flow_ok: {sheet: ["data_feed", "core_satellite"], column: "Flow_OK"}
  flow_rows: {sheet: ["data_feed", "core_satellite"], column: "Flow_Rows"}
  vix_close: {sheet: "macro", row_mapping: "macro.row_based_mapping.vix_close"}
  kospi_close: {sheet: "macro", row_mapping: "macro.row_based_mapping.kospi_close"}
  usd_krw: {sheet: "macro", row_mapping: "macro.row_based_mapping.usd_krw"}
  # ── 밸류에이션 필드 매핑 (proposal_96 1단계 / 2026-05-16) ───────────────────────
  # data_feed 시트 recommended_columns에 이미 존재하나 canonical_field_mapping 누락이었음.
  forward_pe:
    sheet: ["data_feed"]
    column: "Forward_PE"
    fallback: "DATA_MISSING"
    note: "recommended_column — 미입력 시 PEG_SCORE_V1.fallback(sector_median 기준) 발동. 추정 생성 금지."
  pbr:
    sheet: ["data_feed"]
    column: "PBR"
    fallback: "DATA_MISSING"
    note: "recommended_column — 미입력 시 SS001_VAL_VALUATION valuation_score_zero 처리."
  eps_revision_status:
    sheet: ["data_feed"]
    column: "EPS_Revision_Status"
    fallback: "DATA_MISSING"
    allowed_values: ["UP", "FLAT", "DOWN"]
    note: "recommended_column — 미입력 시 SS001_E_EARNINGS_REVISION 0점 처리."
  # ── 신규 추가 필드 (2026-05-17) ──────────────────────────────────────────────
  dividend_yield:
    sheet: ["data_feed"]
    column: "DividendYield"
    unit: "percent"
    fallback: "DATA_MISSING"
    source_priority: ["Naver main _dvr", "Yahoo v7 trailingAnnualDividendYield"]
    note: "배당수익률(%). 은퇴포트폴리오 현금흐름 평가에 사용."
  beta:
    sheet: ["data_feed"]
    column: "Beta"
    unit: "ratio"
    fallback: "DATA_MISSING"
    source_priority: ["Yahoo v7 quote beta", "Yahoo quoteSummary defaultKeyStatistics.beta"]
    note: "1년 베타. 포트폴리오 전체 리스크 계산에 사용."
  high_52w:
    sheet: ["data_feed"]
    column: "High52W"
    unit: "KRW_per_share"
    fallback: "DATA_MISSING"
    source_priority: ["Naver main 52주최고", "Yahoo v7 fiftyTwoWeekHigh"]
    note: "52주 최고가. Pct_52W_High 계산의 기준값."
  low_52w:
    sheet: ["data_feed"]
    column: "Low52W"
    unit: "KRW_per_share"
    fallback: "DATA_MISSING"
    source_priority: ["Naver main 52주최저", "Yahoo v7 fiftyTwoWeekLow"]
  pct_52w_high:
    sheet: ["data_feed"]
    column: "Pct_52W_High"
    unit: "percent"
    fallback: "DATA_MISSING"
    expression: "(Close / High52W - 1) * 100"
    note: "현재가의 52주 고점 대비 위치(%). 음수=고점 아래. -20 이하 → 눌림 구간."
  target_price:
    sheet: ["data_feed"]
    column: "Target_Price"
    unit: "KRW_per_share"
    fallback: "DATA_MISSING"
    source_priority: ["Naver coinfo estimate 목표주가", "Yahoo quoteSummary financialData.targetMeanPrice"]
    note: "애널리스트 컨센서스 목표주가. Upside_Pct 계산 기준."
  upside_pct:
    sheet: ["data_feed"]
    column: "Upside_Pct"
    unit: "percent"
    fallback: "DATA_MISSING"
    expression: "(Target_Price / Close - 1) * 100"
    note: "목표주가 대비 상승여력(%). 포지션 진입/청산 판단 보조."
  # ── 2026-05-17 추가 필드 ──────────────────────────────────────────────────────
  sector_median_pe:
    sheet: ["sector_flow"]
    column: "Sector_Median_PE"
    unit: "ratio"
    fallback: "DATA_MISSING"
    note: "섹터 구성 3종목 PER 중앙값. SS001_VAL_VALUATION의 sector_median_forward_pe 대용."
  sector_median_pbr:
    sheet: ["sector_flow"]
    column: "Sector_Median_PBR"
    unit: "ratio"
    fallback: "DATA_MISSING"
    note: "섹터 구성 3종목 PBR 중앙값. SS001_VAL_VALUATION의 sector_median_pbr 대용."
  rs_rank_20d:
    sheet: ["core_satellite"]
    column: "RS_Rank_20D"
    unit: "rank_integer"
    fallback: "DATA_MISSING"
    note: "섹터 내 20D 수익률 기준 순위 (1=최상위). SS001_P price_strength 입력값."
  rs_pct_20d:
    sheet: ["core_satellite"]
    column: "RS_Pct_20D"
    unit: "percent"
    fallback: "DATA_MISSING"
    note: "섹터 내 20D 수익률 백분위 (100=최상위). relative_strength_1m_percentile 대용."
  earnings_date:
    sheet: ["data_feed"]
    column: "Earnings_Date"
    unit: "date_ISO8601"
    fallback: "DATA_MISSING"
    source_priority: ["Yahoo quoteSummary calendarEvents.earnings.earningsDate"]
    note: "다음 실적 발표 예정일. 발표 3일 이내 신규매수 자제 기준으로 활용."
  days_to_earnings:
    sheet: ["data_feed"]
    column: "Days_To_Earnings"
    unit: "integer_days"
    fallback: "DATA_MISSING"
    expression: "(Earnings_Date - AsOfDate).days"
    note: "실적 발표까지 잔여 영업일. 음수=이미 지남. event_risk 필터 입력값."
  # ── 2026-05-17 추가 필드 (3단계) ────────────────────────────────────────────
  eps_growth_1y_pct:
    sheet: ["data_feed"]
    column: "EPS_Growth_1Y_Pct"
    unit: "percent"
    fallback: "DATA_MISSING → SS001_VAL_KOSDAQ_PEG.fallback_per_only 발동"
    source: "Yahoo earningsTrend +1y earningsEstimate.growth (A2)"
    note: "KOSDAQ PEG = Forward_PE / EPS_Growth_1Y_Pct. 양수 성장률만 유효."
  dps:
    sheet: ["data_feed"]
    column: "DPS"
    unit: "KRW_per_share"
    fallback: "DATA_MISSING"
    source: "Yahoo defaultKeyStatistics.lastDividendValue (A4)"
    note: "주당 배당금. DividendYield와 함께 은퇴 현금흐름 평가."
  ex_dividend_date:
    sheet: ["data_feed"]
    column: "Ex_Dividend_Date"
    unit: "date_ISO8601"
    fallback: "DATA_MISSING"
    source: "Yahoo calendarEvents.exDividendDate (A4)"
    note: "배당락일. Days_To_Ex_Div 계산 기준."
  days_to_ex_div:
    sheet: ["data_feed"]
    column: "Days_To_Ex_Div"
    unit: "integer_days"
    fallback: "DATA_MISSING"
    expression: "(Ex_Dividend_Date - AsOfDate).days"
    note: "배당락일 잔여 일수. 음수=이미 지남."
  stop_price_est:
    sheet: ["data_feed"]
    column: "Stop_Price_Est"
    unit: "KRW_per_share"
    fallback: "DATA_MISSING"
    source_priority: ["account_snapshot stop_price", "average_cost - ATR20 × 1.5 추정"]
    note: "account_snapshot 실제 손절가 우선. ATR 추정은 참고값. Stop_Price_Source로 출처 구분."
  stop_price_source:
    sheet: ["data_feed"]
    column: "Stop_Price_Source"
    unit: "string"
    values: ["account_snapshot", "ATR추정"]
    note: "Stop_Price_Est의 데이터 출처."
  pos_size_qty:
    sheet: ["data_feed"]
    column: "Pos_Size_Qty"
    unit: "integer_shares"
    fallback: "DATA_MISSING (settings total_asset_krw 미입력)"
    expression: "min(floor(total_asset × risk_budget × bayesian / (ATR20 × 1.5)), floor(total_asset × 0.05 / Close))"
    note: "POSITION_SIZE_V1 간략 추정. cash/sector/유동성 한도 미적용 — account_snapshot 제공 시 정밀화 가능."

  # ── 2단계 보완 예정 필드 (현재 JSON 미포함) ──────────────────────────────────
  # 아래 필드는 spec에 정의되어 있으나 JSON 컬럼이 없어 DATA_MISSING으로만 처리됨.
  # 2단계 워크북 보완 완료 후 이 주석을 제거하고 매핑 활성화.
  # eps_growth_3y_cagr_pct:
  #   sheet: ["data_feed"]
  #   column: "EPS_Growth_3Y_CAGR_pct"   # 추가 예정 컬럼
  #   fallback: "DATA_MISSING → PEG_SCORE_V1.fallback"
  # sector_median_forward_pe:
  #   sheet: ["sector_flow"]
  #   column: "Sector_Median_PE"          # 추가 예정 컬럼
  #   fallback: "DATA_MISSING"
  # sector_median_pbr:
  #   sheet: ["sector_flow"]
  #   column: "Sector_Median_PBR"         # 추가 예정 컬럼
  #   fallback: "DATA_MISSING"
  # ── 재무 건전성 필드 (2026-05-18_FINANCIAL_HEALTH_V1) ──────────────────────
  roe_pct:
    sheet: ["data_feed"]
    column: "ROE_Pct"
    fallback: "DATA_MISSING"
    note: "자기자본이익률(%). FINANCIAL_HEALTH_SCORE_V1 수익성 축 입력."
  operating_margin_pct:
    sheet: ["data_feed"]
    column: "Operating_Margin_Pct"
    fallback: "DATA_MISSING"
    note: "영업이익률(%). 음수=영업적자 → HF007 하드필터."
  debt_to_equity:
    sheet: ["data_feed"]
    column: "Debt_To_Equity"
    fallback: "DATA_MISSING"
    note: "부채비율(D/E). 금융업(은행·보험·증권) 제외 적용. >400% → HF008."
  current_ratio:
    sheet: ["data_feed"]
    column: "Current_Ratio"
    fallback: "DATA_MISSING"
    note: "유동비율. <1.0이면 단기 유동성 위험."
  fcf_b:
    sheet: ["data_feed"]
    column: "FCF_B"
    fallback: "DATA_MISSING"
    note: "잉여현금흐름(억원). 양수=실제 현금 창출. 음수=현금 소각."
  revenue_growth_pct:
    sheet: ["data_feed"]
    column: "Revenue_Growth_Pct"
    fallback: "DATA_MISSING"
    note: "전년 대비 매출 성장률(%). 성장성 판단 보조."

prohibited_use:
  - "이 JSON/xlsx에서 보유수량·평단·현금·미체결 주문을 추정하지 않는다."
  - "시장 raw JSON 누락 필드를 LLM이 임의 생성하지 않는다."
  - "legacy AvgTradeValue_5D_M를 억원 단위로 해석하지 않는다. million KRW로 해석한다."
