meta:
  title: "데이터 갭 로드맵 — 단계별 보완 계획"
  version: "2026-05-17-initial"
  language: "ko-KR"
  purpose: >
    의사결정 파이프라인(spec/09_decision_flow.yaml)에서 식별된 데이터 공백을
    우선순위별로 정리하고, 단계별 구현 계획을 명시한다.
    GAS 수집 → spec 공식 → LLM 판단 순서로 각 갭의 의존성을 추적한다.

# ─────────────────────────────────────────────────────────────────────────────
# 1단계 완료 (2026-05-17 구현됨)
# ─────────────────────────────────────────────────────────────────────────────
phase_1_completed:
  G1_KOSPI_MA60:
    status: DONE
    implementation: "fetchYahooOhlcMetrics → macro 탭 MA60 컬럼"
    enables: "RISK_ON 판정 조건 KOSPI_MA20 >= KOSPI_MA60"
  G2_KOSPI_KOSDAQ_Ret10D:
    status: DONE
    implementation: "fetchYahooOhlcMetrics → macro 탭 Ret10D"
    enables: "daily_leader_scan C2: Ret10D_종목 - Ret10D_KOSPI"
  G3_ETF_Ret10D:
    status: DONE
    implementation: "fetchYahooPrice → sector_flow ETF_Ret10D"
    enables: "RW2 상대약세: Ret10D_종목 - Ret10D_주도섹터ETF"
  G4_USD_JPY_Ret2D:
    status: DONE
    implementation: "calcDerivedPriceMetrics.ret2D → macro 탭 Ret2D (USD_JPY행)"
    enables: "MRS usd_jpy_score: Ret2D <= -1 → +1점"
  G5_credit_stress_proxy:
    status: DONE
    implementation: "HYG ETF Ret5D → REGIME_PRELIM 행 credit_stress 필드"
    enables: "MRS credit_score 자동 계산"
    limitation: "HYG는 미국 HY proxy. 한국 신용스프레드 직접 수집은 3단계 과제."
  G6_Rotation_Rank:
    status: DONE
    implementation: "runSectorFlow 정렬 후 Rotation_Rank 컬럼"
    enables: "C5 Rotation_Score 순위 <= 3 판단, RW1 섹터 순위 변화 추적"
  S4_MRS_auto_compute:
    status: DONE
    implementation: "runMacro 내 MARKET_RISK_SCORE_V1 계산 → macro 탭 MRS_COMPUTED 행"
    enables: "LLM이 macro 탭 한 번 읽어 MRS 즉시 확인 가능"

# ─────────────────────────────────────────────────────────────────────────────
# 2단계 — 구조적 갭 (시트 신설 필요, 중간 우선순위)
# ─────────────────────────────────────────────────────────────────────────────
phase_2_structural:
  S1_trades_performance_sheet:
    priority: HIGH
    status: DONE
    purpose: >
      Bayesian multiplier (high/medium/low/no_bet) 및 net_expectancy 산출 기반.
      미구현 시 bayesian_confidence는 항상 medium_confidence(0.5×) 고정.
    required_columns:
      - "trade_id"
      - "ticker"
      - "sector"
      - "entry_date"
      - "entry_price"
      - "entry_stage"       # stage_1/2/3
      - "quantity"
      - "stop_price_at_entry"
      - "target_price_at_entry"
      - "exit_date"
      - "exit_price"
      - "exit_reason"       # stop_loss/take_profit/time_stop/rw_exit/manual
      - "pnl_pct"
      - "holding_days"
      - "entry_c1_score"    # daily_leader_scan 입장 시 C1~C5
      - "entry_c2_score"
      - "entry_c3_score"
      - "entry_c4_score"
      - "entry_c5_score"
      - "entry_mrs_score"
      - "fc_bucket"         # Y/N: explore_loss_budget 귀속 여부
    derived_outputs:
      - "win_rate (최근 30건)"
      - "avg_win_pct"
      - "avg_loss_pct"
      - "net_expectancy = (win_rate × avg_win_pct) - (loss_rate × avg_loss_pct)"
      - "bayesian_confidence_multiplier 자동 결정"
    implementation_plan:
      step_1: "Google Sheets에 'performance' 탭 수동 생성"
      step_2: "spec/17_performance_contract.yaml 계약서 작성"
      step_3: "GAS에 performance 탭 읽기 함수 추가 → Bayesian multiplier 자동 계산"
      step_4: "runDataFeed 내 bayesian_multiplier 필드 → data_feed 탭 추가"
    implementation_note: >
      2026-05-17 구현 완료. spec/17_performance_contract.yaml 신규 작성.
      readPerformanceSheet_() GAS 함수 추가. EE_Est 계산에 Bayesian multiplier 반영.
      macro 탭 BAYESIAN_COMPUTED 행으로 상태 출력.

  S2_stop_price_tracking:
    priority: HIGH
    status: DONE
    purpose: >
      TOTAL_HEAT_V1 계산 필수 입력값. 미구현 시 HF005(Total_Heat 10% 차단) 미작동.
      stop_price 없으면 포트폴리오 총 위험노출 계산 불가.
    required_fields_per_position:
      - "ticker"
      - "account"
      - "average_cost"
      - "stop_price"        # ATR 기준 계산값 또는 HTS 실제 설정값
      - "holding_quantity"
      - "last_updated"
    implementation_plan:
      step_1: "account_snapshot에 stop_price/highest_price_since_entry/last_updated 선택 컬럼 추가"
      step_2: "spec/15_account_snapshot_contract.yaml에 account_snapshot_position_state 추가"
      step_3: "GAS: account_snapshot 기반 TOTAL_HEAT 및 trailing stop 갱신"
      interim_workaround: >
        stop_price 미기록 시 STOP_PRICE_CORE_V1 공식으로 ATR 기준 추정:
        stop_price_est = average_cost - ATR20 × 1.5
        Total_Heat_est = sum((average_cost - stop_price_est) × holding_quantity) / total_asset × 100
    implementation_note: >
      2026-05-18 account_snapshot 통합 완료. positions_tab은 deprecated.
      readAccountSnapshotHeat_() GAS 함수 추가 (ATR 추정 폴백 포함).
      macro 탭 TOTAL_HEAT 행으로 HF005 상태 출력.

  S3_sector_flow_weekly_history:
    priority: MEDIUM
    status: DONE
    implementation: >
      option_B legacy interim 구현 완료 (2026-05-17).
      sector_flow 탭에 Prev_Rotation_Rank(W1), Prev_Rotation_Rank_W2 컬럼 추가.
      W1 = sector_flow 시트에서 직전 실행 결과 읽기.
      W2 = PropertiesService['sf_w2_ranks_json'] 캐시 (직전 실행 시 저장).
      RW1·RW3 컬럼이 sector_flow 탭에 자동 계산됨 → data_feed RW1·RW3 컬럼으로 전파.
    enables: >
      RW1 '2주 연속 섹터 순위 3순위 이상 하락' 자동 판단.
      RW3 '외국인+기관 5D 순매도 2주 연속' 자동 판단.
    note: >
      2회 이상 실행 후 W1/W2 데이터가 축적되어야 RW1·RW3가 작동한다.
      단, 이는 전회 실행 기준 legacy interim이며 주간 스냅샷 기반 sector_flow_history는 후속 Phase 4 과제다.

  S4_sector_flow:
    priority: HIGH
    status: DONE
    implementation: >
      2026-05-17 Phase 1-2 구현.
      sector_universe 원장 또는 DEFAULT_SECTOR_UNIVERSE_V2를 canonical source로 사용하고,
      구성종목 수급을 수량 단순합산이 아닌 원화금액×가중치로 sector_flow에 집계한다.
      Coverage_Weight, SmartMoney_5D_KRW, SmartMoney_5D_Norm, Flow_Breadth_5D,
      Data_Quality, Decision_Use를 추가했다.
      sector_universe 시트가 없으면 기본 템플릿을 생성하며, Is_ETF=Y 구성행은
      ETF 자체 수급 혼입을 막기 위해 섹터 smart money 집계에서 제외한다.
    limitation: >
      KRX/KIND 기반 NAV/괴리율/추적오차/AUM 수집은 아직 미구현이며 etf_raw에서
      ETF_NAV_Risk=NAV_DATA_MISSING으로 명시한다.

  S5_etf_raw_execution_quality:
    priority: HIGH
    status: DATA_GATED
    implementation: >
      2026-05-17 Phase 3 interim 구현.
      etf_raw 시트를 신설하고 ETF proxy/ETF 구성행에 대해 Close, Bid, Ask, Spread_Pct,
      AvgTradeValue_5D_KRW, AvgTradeValue_20D_KRW, ETF_Frg_5D_KRW, ETF_Inst_5D_KRW,
      ETF_Liquidity_Score, ETF_Liquidity_Status를 산출한다.
      NAV_DATA_MISSING 상태에서는 ETF_Execution_Use=WATCH_ONLY로 표시해 ETF 매매 실행 근거와 섹터 수급 근거를 분리한다.
      etf_nav_manual 시트가 있으면 NAV, iNAV, 괴리율, 추적오차, AUM을 etf_raw에 반영한다.
      tools/import_etf_nav_manual.py로 KRX/KIND/운용사 CSV/XLSX export를 etf_nav_manual로 변환할 수 있다.
    limitation: "NAV, iNAV, 괴리율, 추적오차, AUM 자동 수집은 KRX/KIND 수집 경로 확정 전까지 미구현."

  S6_sector_flow_history:
    priority: HIGH
    status: DONE
    implementation: >
      2026-05-17 Phase 4 interim 구현.
      sector_flow_history 시트에 Snapshot_Date+Sector 기준으로 sector_flow 누적 스냅샷을 upsert한다.
      sector_flow의 RW1/RW3 계산은 sector_flow_history를 우선 사용하고,
      이력이 부족할 때만 기존 sector_flow/PropertiesService 값을 fallback으로 사용한다.
      Snapshot_Date는 Apps Script Date 객체와 문자열 날짜를 모두 yyyy-MM-dd로 정규화한다.

# ─────────────────────────────────────────────────────────────────────────────
# 3단계 — 분석 품질 고도화 (낮은 우선순위)
# ─────────────────────────────────────────────────────────────────────────────
phase_3_enhancement:
  A1_daily_leader_scan_auto:
    priority: MEDIUM
    status: DONE
    implementation: >
      P1(2026-05-17) 구현 완료. runDataFeed 루프 내 C1~C5 자동 계산.
      data_feed 탭: C1_Price, C2_RelStr, C3_VolSurge, C4_Flow, C5_Sector, Leader_Scan_Total, Leader_Gate.
    output_columns: ["C1_Price","C2_RelStr","C3_VolSurge","C4_Flow","C5_Sector","Leader_Scan_Total","Leader_Gate"]

  A2_eps_growth_3y_cagr:
    priority: MEDIUM
    status: DONE
    implementation: >
      2026-05-17 구현 완료. fetchYahooConsensusEps() 확장.
      Yahoo earningsTrend +1y의 earningsEstimate.growth.raw 직접 추출.
      없으면 (+1y avg - 0y avg) / abs(0y avg) × 100 으로 계산.
      data_feed 탭 EPS_Growth_1Y_Pct 컬럼 추가 → LLM이 PEG = Forward_PE / EPS_Growth_1Y_Pct 계산 가능.
    accuracy_note: "1년 성장률 추정치 (3Y CAGR 완전 대체 불가). KOSDAQ PEG 게이트에서 fallback보다 정확."

  A3_pct_from_52w_low:
    priority: LOW
    status: DONE
    purpose: "Low52W 대비 현재가 반등률 — 저점 대비 위치 파악 (눌림 구간 분석)"
    expression: "(Close / Low52W - 1) × 100"
    implementation: "data_feed 탭 Pct_From_52W_Low 컬럼 (2026-05-17)"

  A4_ex_dividend_date:
    priority: LOW
    status: DONE
    implementation: >
      2026-05-17 구현 완료. fetchYahooTargetPrice() 확장 (추가 API 호출 없음).
      calendarEvents.exDividendDate → data_feed Ex_Dividend_Date, Days_To_Ex_Div 컬럼.
      defaultKeyStatistics.lastDividendValue → data_feed DPS 컬럼.

  A6_pos_size_qty:
    priority: HIGH
    status: DONE
    implementation: >
      2026-05-17 구현 완료.
      data_feed Pos_Size_Qty 컬럼: POSITION_SIZE_V1 간략 버전 자동 계산.
      atr_qty = floor(total_asset × 0.007 × bayesian_multiplier / (ATR20 × 1.5))
      weight_qty = floor(total_asset × 0.05 / Close)
      Pos_Size_Qty = min(atr_qty, weight_qty).
      requires: settings 탭 total_asset_krw 입력.
      limitation: "cash/sector/liquidity 한도는 account_snapshot 필요. 현재는 ATR+weight 한도만 적용."

  A7_stop_price_actual:
    priority: HIGH
    status: DONE
    implementation: >
      2026-05-17 구현 완료.
      data_feed Stop_Price_Est: account_snapshot 실제 stop_price 우선 → ATR 추정 폴백.
      Stop_Price_Source 컬럼: "account_snapshot" / "ATR추정" 표시.
      EE_Est가 실제 stop_price 기반으로 계산됨 (포지션 있는 종목).

  A8_settings_tab:
    priority: HIGH
    status: DONE
    implementation: >
      2026-05-17 구현 완료. readSettingsTab_() GAS 함수 추가.
      settings 탭: key-value 구조 (key, value, note 컬럼).
      total_asset_krw: Pos_Size_Qty, TOTAL_HEAT_V1, FC_BUDGET 계산에 사용.
      spec/18_settings_contract.yaml 계약서 작성.

  A9_fc_budget_tracking:
    priority: MEDIUM
    status: DONE
    implementation: >
      2026-05-17 구현 완료. calcFcBudget_() GAS 함수 추가.
      performance 탭 fc_bucket=Y 거래 중 당월 청산 손실 집계.
      macro 탭 FC_BUDGET 행 추가: used_pct / 2.5% 예산 상태 출력.
      exhausted 시 LLM이 신규 stage_1 탐색 자동 억제 가능.

  A5_kosdaq_vs_ma20:
    priority: LOW
    status: DONE
    purpose: "kosdaq_regime_supplement 규칙: KOSDAQ_Close < KOSDAQ_MA20 → MRS +1"
    implementation: "runMacro MRS 계산에 kosdaqSupp 로직 추가. macro 탭 KOSDAQ (^KQ11) 이미 수집 중 (2026-05-17)"

# ─────────────────────────────────────────────────────────────────────────────
# 분석 보완 제안 (GAS 미관여, LLM 분석 품질 향상)
# ─────────────────────────────────────────────────────────────────────────────
analysis_enhancements:
  B1_rw_signal_checklist:
    purpose: "RW1~RW5 자동 계산 및 출력"
    current_status:
      RW1: "✅ sector_flow RW1 컬럼 자동 계산 (2026-05-17). Prev_Rotation_Rank(W1)+W2 PropertiesService 캐시 기반. 2회 이상 실행 후 정확."
      RW2: "✅ data_feed RW2 컬럼 자동 계산 (2026-05-17). Ret10D - ETF_Ret10D <= -5%p"
      RW3: "✅ sector_flow RW3 컬럼 자동 계산 (2026-05-17). 외국인+기관 5D 순매도 2주 연속 (현재+W1 비교)."
      RW4: "✅ data_feed RW4 컬럼 자동 계산 (2026-05-17). AvgTradeValue_5D/20D <= 0.60"
      RW5: "✅ data_feed RW5 컬럼 자동 계산 (2026-05-17). Close < MA20 AND < MA60"
      RW_Partial: "✅ data_feed RW_Partial = RW1+RW2+RW3+RW4+RW5 합계 (0~5)"
    fully_automated: ["RW1 (2회 이상 실행 후)", "RW2", "RW3 (현재+W1 기준)", "RW4", "RW5"]
    note: "RW1·RW3의 '2주 연속' 조건은 2회 이상 실행 후 W1/W2 데이터 축적 시 완전히 정확해짐."

  B2_total_heat_estimation:
    purpose: "stop_price 미기록 시 ATR 기준 Total_Heat 추정"
    formula: >
      For each holding in account_snapshot:
        stop_price_est = entry_price - ATR20 × 1.5
        heat_contribution = (entry_price - stop_price_est) × quantity / total_asset × 100
      Total_Heat_est = sum(heat_contribution)
    requirement: "ATR20(data_feed), entry_price(account_snapshot), quantity(account_snapshot), total_asset(account_snapshot) 필요"
    note: "actual stop_price가 있으면 실제값 우선. 없으면 이 추정으로 HF005 체크 가능."

  B3_expected_edge_checklist:
    purpose: "EXPECTED_EDGE_V1 계산 전 필수 입력 확인"
    required_fields:
      - "target_price: data_feed Target_Price (Naver 또는 Yahoo) ✓"
      - "entry_price: limit_price (entry_core.yaml limit_price_formula) ✓"
      - "stop_price: stop_price_est (B2 또는 실제값) △"
      - "bayesian_confidence_multiplier: performance 시트 기반 (S1) — 미구현 시 0.5× 기본"
      - "execution_cost_rate: 0.003 고정 ✓"

  B4_c2_now_computable:
    purpose: "daily_leader_scan C2 바로 계산 가능"
    formula: "Ret10D_종목(data_feed) - Ret10D_KOSPI(macro) >= 3%p"
    status: "G2 완료로 계산 가능. LLM이 두 탭 조회 후 즉시 판단."

  B5_mrs_now_readable:
    purpose: "MRS 자동 계산 결과 macro 탭에서 직접 읽기"
    location: "macro 탭 Symbol='MRS_COMPUTED' 행 → Close=점수, Status='score=X/10 cash=Y%'"
    usage: "LLM이 macro 탭을 읽으면 MRS 재계산 없이 즉시 확인 가능"

  B6_flow_credit_v1_auto:
    purpose: "FLOW_CREDIT_V1 자동 계산 — data_feed Flow_Credit 컬럼"
    status: DONE
    implementation: >
      2026-05-17 구현 완료. runDataFeed 루프 내 자동 계산.
      fc_c1: close >= open OR close > prevClose → 0.30
      fc_c2: volume >= avgVolume5D × 1.20 → 0.30
      fc_c3: flow_ok AND (frg5 + inst5) > 0 → 0.40
      Hard override: C1=0 AND C2=0 → 0 (C3 단독 충족은 물량받기로 간주)
    output_column: "data_feed Flow_Credit (0.00~1.00)"

  B7_trailing_stop_price_auto:
    purpose: "TRAILING_STOP_PRICE_V1 자동 계산 — account_snapshot 최고가 기반"
    status: DONE
    implementation: >
      2026-05-18 account_snapshot highest_price_since_entry 읽기/쓰기 통합 완료.
      trailingStopPrice = highest_price_since_entry - ATR20 × 1.5
      account_snapshot에 highest_price_since_entry 미입력 시 공백.
    output_column: "data_feed Trailing_Stop_Price (KRW 정수)"

  B8_ss001_score_auto:
    purpose: "SS001 종목 점수 자동 계산 — 6개 축 + 총점 + 등급"
    status: DONE
    implementation: >
      2026-05-17 구현 완료. runDataFeed 루프 내 자동 계산.
      SS001_P: core_satellite RS_Pct_20D → percentile=100-RS_Pct_20D → ≤30=25pt/30~60=15pt/>60=0pt
      SS001_V: avgTradingValue5D/20D 비율 → ≥120%=15pt/80~120%=8pt/<80%=0pt
      SS001_F: Flow_Credit → ≥0.70=25pt/0.40~0.70=12pt/<0.40=0pt
      SS001_E: EPS_Revision_Status → UP=20pt/FLAT=10pt/DOWN=0pt
      SS001_M: REGIME_PRELIM(전회 macro 결과) → RISK_ON/LEADER_CONCENTRATION=10pt/NEUTRAL=5pt/기타=0pt
      SS001_VAL: KOSPI→PER/PBR vs 섹터중앙값(max 5pt), KOSDAQ→PEG 기반(max 12pt)
      SS001_Total: 6개 합산 (KOSPI max 100, KOSDAQ max 107)
      SS001_Grade: 100점 환산 ≥80=A / ≥65=B / ≥50=C / <50=D
    output_columns: ["SS001_P","SS001_V","SS001_F","SS001_E","SS001_M","SS001_VAL","SS001_Total","SS001_Grade"]
    data_dependency:
      SS001_P: "core_satellite 탭 RS_Pct_20D 컬럼 (runCoreSatelliteFinalize 필요)"
      SS001_M: "전회 runMacro 실행 결과 (초회 실행 시 0점)"
      SS001_VAL: "sector_flow Sector_Median_PE/PBR (runSectorFlow 필요)"
    limitation: "SS001_P는 core_satellite 탭이 비어있으면 0점 처리. 처음 사용 시 runCoreSatellite 먼저 실행."

  B9_peg_gate_auto:
    purpose: "PEG 게이트 자동 계산 (KOSDAQ 종목 전용)"
    status: DONE
    implementation: >
      2026-05-17 구현 완료. KOSDAQ_TICKERS Set 상수 추가 (현재 비어있음 — 보유 종목 모두 KOSPI).
      KOSDAQ 종목에서 EPS_Growth_1Y_Pct > 0 이면: PEG = Forward_PE / EPS_Growth_1Y_Pct
      PEG_Gate: PASS(≤1.5) / CAUTION(1.5~2.5) / REJECT(>2.5)
      EPS 성장률 미수집 시: Fallback → sector_median_PE 대비 Forward_PE 배수로 대체
    output_columns: ["PEG","PEG_Gate"]

  B10_full_market_regime:
    purpose: "MARKET_REGIME_V1 완전 판정 — macro+sector_flow 통합"
    status: DONE
    implementation: >
      2026-05-17 구현 완료. runMacro() 내에서 sector_flow 탭 읽기 추가.
      (runSectorFlow()가 sector_flow 기록 완료 후 runMacro()가 호출되므로 최신값 읽기 가능)
      판정 우선순위: RISK_OFF > SECULAR_LEADER_RISK_ON > LEADER_CONCENTRATION > RISK_ON > NEUTRAL > RISK_OFF_CANDIDATE
      RISK_OFF: MRS>=7 OR (VIX>=25 AND KOSPI<MA20)
      SECULAR_LEADER_RISK_ON: LEADER_CONCENTRATION + top1=반도체 + VIX<22 + KOSPI>MA20
      LEADER_CONCENTRATION: top2_sum>=100 AND top1_score>=55 AND top1_alertScore>=2 AND Tier1섹터 AND Ret20D>0 AND VIX<25
      RISK_ON: VIX<18 AND KOSPI>MA20 AND (MA20>=MA60 OR Ret20D>0) AND (sfFrg20>0 OR sfInst20>0 OR top2sum>=100)
      NEUTRAL: MRS<=5 그 외
      RISK_OFF_CANDIDATE: MRS 5~6
    output: "REGIME_PRELIM 행 Close 컬럼 (Symbol=REGIME_PRELIM 유지 — 하위 호환)"
    note: >
      이전 값이 'RISK_ON_CANDIDATE', 'RISK_OFF_CANDIDATE'이던 것이 'RISK_ON', 'RISK_OFF'로 변경됨.
      SS001_M 점수 계산이 이제 정상 작동 (이전엔 항상 0점이었음).

  B11_net_return_feedback:
    purpose: "net_return_feedback 상태 자동 계산 — RISK_BUDGET_CASCADE_V1 입력"
    status: DONE
    implementation: >
      2026-05-17 구현 완료. runMacro() 내 bayesianInfo(performance 탭) 재사용.
      규칙 (spec/05_position_sizing.yaml:net_return_feedback):
        trades < 20건: NORMAL (규칙 미적용)
        ne <= -2%: REDUCED — base_risk 0.007→0.003 삭감 권고
        ne <= 0%: CAUTION — high_confidence 금지, multiplier 0.5× 강제
        연속손실 ≥5건 (bayesian no_bet와 별개로): NORMAL→CAUTION 승격
        그 외: NORMAL
    output: "macro 탭 NET_RETURN_FEEDBACK 행 (Symbol=NET_RETURN_FEEDBACK, Close=상태값)"

  C1_orbit_gap_tracking:
    purpose: "orbit_gap 자동 계산 — 월별 목표궤도 vs 실제 자산 추이 추적"
    status: DONE
    implementation: >
      2026-05-17 구현 완료. spec/01_objective_profile.yaml:orbit_monthly_tracker 구현.
      calcOrbitGap_(settings) 함수: 기하평균 보간으로 목표누적수익률 계산.
      orbit_gap(%) = ((target/start)^(elapsed/total) - 1) - (current/start - 1)
      orbit_state: significantly_behind(>3%p) / mild_behind(1~3%p) / on_track / ahead_of_target(<-2%p)
      offensive_slot_adj: significantly_behind=+2, mild_behind=+1, on_track=0, ahead=0
      cash_floor_adj: significantly_behind=-2%p, mild_behind=-1%p, on_track=0, ahead=+1%p
      runOrbitGap(): orbit_gap 탭에 현재 월 행 추가/갱신 (월 1회 독립 트리거)
      runMacro(): ORBIT_GAP + ORBIT_STATE 2개 행 macro 탭에 자동 기록
    required_settings:
      - "orbit_start_asset_krw — 시작 자산 (1월 기준)"
      - "orbit_target_asset_krw — 목표 자산 (예: 5억)"
      - "orbit_start_yyyymm — 추적 시작 연월 (예: 2026-01)"
      - "orbit_end_yyyymm — 목표 달성 기한 (예: 2028-12)"
      - "total_asset_krw — 현재 자산 (기존 settings 항목 재사용)"
    output:
      macro_tab: "ORBIT_GAP 행(Close=gap %p), ORBIT_STATE 행(Close=상태, Status=slot/cash 조정값)"
      orbit_gap_tab: "Month/Start_Asset/Target_Asset/Actual_Asset/Target_Return_Pct/Actual_Return_Pct/Orbit_Gap_Pct/Orbit_State/Slot_Adj/Cash_Floor_Adj/Updated"
    api_exposure:
      getMacroJson: "orbit_gap_pct, orbit_state, orbit_slot_adj, orbit_cash_adj"
      getSummaryJson: "macro_snapshot.orbit_gap_pct, orbit_state, orbit_slot_adj"
    limitation: "settings 탭에 4개 orbit_* 파라미터 미입력 시 ORBIT_GAP=N/A 기록 (runOrbitGap 스킵)"

  C2_take_profit_ladder:
    purpose: "TAKE_PROFIT_LADDER_V1 자동 계산 — core/satellite 분리 익절 사다리"
    status: DONE
    implementation: >
      2026-05-18 account_snapshot average_cost + holding_quantity + position_type 기반으로 변경.
      core: TP1=+15%(25% 물량), TP2=+25%(잔여 40% 물량).
      satellite: TP1=+10%(50% 물량), TP2=+20%(잔여 50% 물량).
      Time_Stop: stage_1=60일, stage_2=30일 (entry_date 기준 만료일 + 잔여일수).
    output_columns:
      data_feed: ["TP1_Price","TP1_Qty","TP2_Price","TP2_Qty","Time_Stop_Date","Days_To_Time_Stop"]
    required_account_snapshot_fields: ["average_cost","holding_quantity","position_type","entry_date","entry_stage"]

  C3_position_monitoring:
    purpose: "포지션 모니터링 컬럼 자동 계산 — 비중/수익률/PnL/스테이지/밴드"
    status: DONE
    implementation: >
      2026-05-18 account_snapshot + 당일 종가 기반으로 변경.
      Weight_Pct: close × quantity / total_asset_krw × 100.
      Profit_Pct: (close - entry_price) / entry_price × 100.
      Unrealized_PnL: (close - entry_price) × quantity (KRW 정수).
      Stage2_Gate: stage_1 포지션에서 가격 +1.5% 이상 시 PASS, 아니면 PENDING.
      Band_Status: satellite 단일종목 7% 상한. OVERWEIGHT/IN_BAND/UNDERWEIGHT.
                   core는 CORE_HIGH(>10%)/CORE_MID(3~10%)/CORE_LOW(<3%).
    output_columns:
      data_feed: ["Weight_Pct","Profit_Pct","Unrealized_PnL","Stage2_Gate","Band_Status"]

  D1_bucket_allocation_status:
    purpose: "포트폴리오 버킷 할당 상태 자동 계산 — core/satellite/cash 합계 vs 목표 범위"
    status: DONE
    implementation: >
      2026-05-17 구현 완료. runDataFeed 루프에서 버킷별 Weight_Pct 누산.
      calcBucketStatus_() 함수: _bucketSnapshot_ 기반 bucket-level 집계.
      목표 범위 (spec/risk): core 60-72%, satellite 10-25%, cash 10-22%.
      cash_pct = max(0, 100 - core_pct - satellite_pct) (추정값 — account_snapshot 미연동).
      overall: BALANCED / core_UNDERWEIGHT | sat_OVERWEIGHT 등 파이프 구분.
    output:
      macro_tab: "BUCKET_STATUS 행(Close=overall, Status=core/sat/cash 상세)"
    api_exposure:
      getMacroJson: "bucket_status, bucket_detail"
      getSummaryJson: "macro_snapshot.bucket_status, bucket_detail"
    limitation: >
      cash_pct는 account_snapshot 마켓밸류를 total_asset에서 뺀 추정값.
      실제 현금은 account_snapshot 없이 확인 불가.
      runDataFeed 실행 없이 runMacro만 실행하면 N/A.

phase_4_backdata_collection:
  B1_gas_backdata_feature_bank:
    priority: HIGH
    status: DONE
    purpose: >
      매수/매도 뒷북과 설거지 패턴을 줄이기 위한 진입-청산 백데이터 원장.
      사람 입력보다 GAS 자동 수집을 1순위로 두고, 사람이 입력한 performance 기록은
      보조 검증용 fallback으로만 사용한다.
    collection_priority:
      1: "GAS가 생성한 backdata_feature_bank 시트/JSON"
      2: "performance 시트의 청산 완료 거래"
      3: "수동 보정값"
    required_signals:
      - "entry_stage"
      - "entry_leader_scan_total"
      - "entry_c1_score"
      - "entry_c2_score"
      - "entry_c3_score"
      - "entry_c4_score"
      - "entry_c5_score"
      - "entry_mrs_score"
      - "entry_close_vs_ma20_pct"
      - "entry_volume_ratio_5d"
      - "entry_flow_credit"
      - "entry_breakout_score"
      - "entry_late_chase_risk_score"
      - "entry_follow_through_score"
      - "pnl_pct"
      - "holding_days"
      - "max_adverse_excursion_pct"
      - "max_favorable_excursion_pct"
    implementation_plan:
      step_1:
        action: "GAS에서 daily setup snapshot을 backdata_feature_bank 시트로 자동 upsert"
        status: DONE
        note: "syncBackdataFeatureBank_() — gdc_01_fetch_fundamentals.gs:1128, gdf_02_harness_assembly.gs:146"
      step_2:
        action: "performance 시트 청산 결과와 join해 outcome label을 자동 부여"
        status: DONE
        note: "buildBackdataFeatureBankRowsV1_() — mapLatestPerformanceByTicker_ 기반 join"
      step_3:
        action: "convert_xlsx_to_json.py에서 backdata_feature_bank_json 우선 수집"
        status: DONE
        note: "src/quant_engine/convert_xlsx_to_json.py:synthesize_backdata_feature_bank() 구현"
      step_4:
        action: "backdata_feature_bank_table을 report/validation에 반영"
        status: DONE
        note: "render_operational_report.py:_backdata_feature_bank_table(), validate_harness_context.py 검증"
    fallback_policy:
      manual_input: "performance 시트 또는 수동 보정은 fallback일 뿐 primary source가 아니다."
      missing_action: "GAS 생성본이 없으면 fallback 기록과 함께 source_origin=FALLBACK_SYNTH로 남긴다."
    implementation_note: >
      2026-06-14 구현 완료 확인. GAS(syncBackdataFeatureBank_) + Python(synthesize_backdata_feature_bank)
      모두 구현됨. T+20 데이터 누적 후 ML 패턴 학습 품질 향상 예정.

# 2026-05-30 구현 현황
# - S5_etf_raw: PARTIAL_DONE 유지 (수동 NAV 병행)
# - Stage2_Gate PENDING: T+20 표본 누적 후 자동 평가
# - 주요 지표: outcome_quality=85.23(PASS) guidance_proof=99.26(PASS)
# - 미수집 펀더멘털(ROE/OPM/FCF/Revenue): CHECK_58/59 해결 시 자동 개선
