meta: title: "performance 탭 계약서 — Bayesian Multiplier 자동 계산" parent_file: "RetirementAssetPortfolio.yaml" version: "2026-05-17-initial" language: "ko-KR" timezone: "Asia/Seoul" role: "canonical" purpose: > Google Sheets 'performance' 탭의 구조·입력 규칙을 정의하고, GAS가 이 데이터를 읽어 Bayesian multiplier를 자동 계산하는 계약을 명시한다. S1_trades_performance_sheet(spec/16_data_gaps_roadmap.yaml) 구현 사양. # ───────────────────────────────────────────────────────────────────────────── # 시트 구조 # ───────────────────────────────────────────────────────────────────────────── sheet: name: "performance" header_row: 2 # row1=updated 메타, row2=헤더 data_start_row: 3 sort_order: "entry_date 내림차순 (최신 거래 위)" max_lookback_trades: 30 # Bayesian 계산에 사용하는 최근 거래 수 required_columns: - name: "trade_id" type: "string" format: "T-YYYYMMDD-NNN (예: T-20260517-001)" note: "중복 방지용 고유 ID. 수동 입력." - name: "ticker" type: "string" note: "종목코드 6자리." - name: "name" type: "string" - name: "sector" type: "string" note: "TICKER_SECTOR_MAP 기준 섹터명." - name: "account" type: "string" allowed_values: ["일반계좌", "ISA", "연금저축"] - name: "entry_date" type: "date_ISO8601" example: "2026-05-17" - name: "entry_price" type: "number" unit: "KRW_per_share" - name: "entry_stage" type: "string" allowed_values: ["stage_1", "stage_2", "stage_3"] note: "staged_entry_v2 기준 진입 단계." - name: "quantity" type: "integer" unit: "shares" - name: "stop_price_at_entry" type: "number" unit: "KRW_per_share" note: "진입 당시 설정한 손절가. ATR 기반 또는 HTS 실제 설정값." - name: "target_price_at_entry" type: "number" unit: "KRW_per_share" note: "진입 당시 컨센서스 목표주가." - name: "exit_date" type: "date_ISO8601" note: "미청산 포지션은 공백 — 진행 중 거래 제외 후 계산." - name: "exit_price" type: "number" unit: "KRW_per_share" note: "미청산 시 공백." - name: "exit_reason" type: "string" allowed_values: ["stop_loss", "take_profit", "time_stop", "rw_exit", "manual", "partial_exit"] note: "exit_date 공백이면 이 필드도 공백." - name: "pnl_pct" type: "number" unit: "percent" expression: "(exit_price / entry_price - 1) * 100" note: "수동 입력 또는 시트 수식. 미청산 시 공백." - name: "holding_days" type: "integer" expression: "exit_date - entry_date (영업일 기준 권장, 달력일도 허용)" note: "미청산 시 공백." - name: "entry_c1_score" type: "number" note: "진입 당시 C1 값 (0 or 1). data_feed 탭에서 복사." - name: "entry_c2_score" type: "number" - name: "entry_c3_score" type: "number" - name: "entry_c4_score" type: "number" - name: "entry_c5_score" type: "number" - name: "entry_mrs_score" type: "number" note: "진입 당시 MRS 점수 (0~10). macro 탭에서 복사." - name: "entry_leader_scan_total" type: "number" note: "진입 당시 Leader_Scan_Total. 자동 계산 = sum(C1~C5)." - name: "fc_bucket" type: "string" allowed_values: ["Y", "N"] note: "Y=stage_1 탐색 손실 → explore_loss_budget 귀속. N=본계좌 PnL." # ───────────────────────────────────────────────────────────────────────────── # Bayesian Multiplier 계산 규칙 # ───────────────────────────────────────────────────────────────────────────── bayesian_multiplier: formula_ref: "spec/13_formula_registry.yaml:formula_registry.formulas.RISK_BUDGET_CASCADE_V1" lookback: 30 # 최근 N건 청산 완료 거래만 사용 (exit_date 공백 제외) minimum_trades: 5 # 5건 미만 시 not_enough_data → medium_confidence(0.5×) 기본 derived_metrics: win_rate: expression: "count(pnl_pct > 0) / count(pnl_pct is not null)" note: "청산 완료 거래 중 수익 비율." avg_win_pct: expression: "mean(pnl_pct where pnl_pct > 0)" avg_loss_pct: expression: "mean(abs(pnl_pct) where pnl_pct <= 0)" net_expectancy: expression: "(win_rate * avg_win_pct) - ((1 - win_rate) * avg_loss_pct)" note: "양수=시스템 양기대치. 음수=시스템 개선 필요." multiplier_rules: high_confidence: condition: "win_rate >= 0.60 AND net_expectancy >= 3.0" multiplier: 1.0 label: "high_bet" medium_confidence: condition: "win_rate >= 0.45 AND net_expectancy >= 0" multiplier: 0.5 label: "medium_bet" low_confidence: condition: "win_rate < 0.45 OR net_expectancy < 0" multiplier: 0.25 label: "low_bet" no_bet: condition: "연속 5회 손절 (최근 5건 모두 pnl_pct <= 0)" multiplier: 0.0 label: "no_bet" note: "시스템 재검토 기간. performance_brake와 연동." not_enough_data: condition: "청산 완료 거래 < minimum_trades" multiplier: 0.5 label: "medium_confidence (데이터 부족 기본값)" output_fields: bayesian_multiplier: type: "number" values: [0.0, 0.25, 0.5, 1.0] bayesian_label: type: "string" values: ["high_bet", "medium_bet", "low_bet", "no_bet", "medium_confidence"] win_rate_30: type: "number" note: "최근 30건 승률." net_expectancy_30: type: "number" note: "최근 30건 기대수익률(%)." consecutive_losses: type: "integer" note: "최근 연속 손절 횟수." trades_used: type: "integer" note: "계산에 사용된 거래 수." # ───────────────────────────────────────────────────────────────────────────── # GAS 통합 계획 # ───────────────────────────────────────────────────────────────────────────── gas_integration: function_name: "readPerformanceSheet_" return_type: "object" return_fields: - "bayesian_multiplier: number (0.0/0.25/0.5/1.0)" - "bayesian_label: string" - "win_rate_30: number | null" - "net_expectancy_30: number | null" - "consecutive_losses: integer" - "trades_used: integer" fallback: condition: "performance 탭 없음 OR 청산 완료 거래 < 5건" return: "{ bayesian_multiplier: 0.5, bayesian_label: 'medium_confidence', trades_used: 0 }" integration_point: sheet: "data_feed" field: "EE_Est" current_formula: "(target-entry)/(entry-stop) × 0.5 - 0.003" updated_formula: "(target-entry)/(entry-stop) × bayesian_multiplier - 0.003" note: "runDataFeed 시작 시 1회 호출 → 루프 전체에 동일 multiplier 적용." additional_output: sheet: "macro" row: "BAYESIAN_COMPUTED" fields: ["Close=multiplier", "Status=label + win_rate + net_expectancy"] note: "runMacro 또는 runDataFeed 마지막에 macro 탭에 Bayesian 상태 추가 행으로 기록." # ───────────────────────────────────────────────────────────────────────────── # 운영 지침 # ───────────────────────────────────────────────────────────────────────────── operational_rules: - "포지션 청산 당일 performance 탭에 수동 기록한다." - "entry_c1~c5는 진입 당일 data_feed 탭에서 복사 — 사후 재계산 금지." - "entry_mrs_score는 진입 당일 macro 탭 MRS_COMPUTED 행의 Close 값." - "fc_bucket=Y인 거래는 explore_loss_budget 누적에 포함. 월말 집계." - "연속 5회 손절(no_bet) 발동 시 runDataFeed에서 EE_Est=0으로 출력 — 신규 진입 자동 억제."