Files
QuantEngineByItz/spec/17_performance_contract.yaml
kjh2064 416da59607 WBS-8.7: spec-code synchronization expanded to 66.4% (93/140 files)
Coverage improvement: 24.07% (39 files) → 66.4% (93 files)
- Tagged 54 additional spec files with has_code_implementation: true
- Covered: strategy/*, risk/*, exit/*, formulas/*, governance/*, contracts
- Target: 50% (81 files) — EXCEEDED by 12 files

Files tagged:
- spec/strategy: 20 files (action_matrix, entry_core, entry_gates, etc.)
- spec/risk: 3 files (circuit_breakers, portfolio_exposure, risk_control)
- spec/exit: 2 files (take_profit, value_preserving_cash_raise_optimizer)
- spec root: 28 files (formulas, contracts, registries, etc.)
- spec/03_formulas: 2 files (formula_registry, output_field_owner_ledger)
- spec/data_quality: 1 file (expectations)
- spec/fields: 1 file (field_dictionary)
- spec/formulas: 1 file (manifest)

Impact:
- Improved LLM radar discoverability for spec-to-code linkage
- Ready for WBS-9.6 (LLM document optimization phase)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 23:51:58 +09:00

232 lines
10 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
meta:
title: "performance 탭 계약서 — Bayesian Multiplier 자동 계산"
parent_file: "RetirementAssetPortfolio.yaml"
version: "2026-05-17-initial"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "canonical"
has_code_implementation: true
code_path: ["tools/build_honest_performance_guard_v1.py", "src/quant_engine/qualitative_sell_strategy_v1.py"]
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으로 출력 — 신규 진입 자동 억제."
# ─────────────────────────────────────────────────────────────────────────────
# 팩터별 성과 피드백 및 정직 성과증빙 규칙 (P6-T04)
# ─────────────────────────────────────────────────────────────────────────────
honest_performance_guard:
formula_id: HONEST_PERFORMANCE_GUARD_V1
rules:
- rule_id: HP001
desc: "Live 표본 수가 30건 미만인 지표는 active 승격 근거로 사용 금지 (calibration_state=INSUFFICIENT_SAMPLES 강제)"
condition: "live_sample_count < 30"
action: "LOCK_CALIBRATION"
- rule_id: HP002
desc: "Replay 데이터와 Live 데이터를 혼합하여 성과 지표를 산출하는 행위 금지 (replay_in_live_stats == 0)"
condition: "replay_in_live_stats > 0"
action: "INVALIDATE_METRICS"
- rule_id: HP003
desc: "팩터별 성과(T+5/T+20/T+60) 결과를 horizon별로 분리해서 추적 및 저장한다."
required_fields:
- "ticker"
- "action"
- "horizon"
- "factor_set"
- "outcome"
acceptance_criteria:
factor_outcome_join_rate_pct: 95.0
live_sample_under_30_unlock_count: 0
replay_live_mixed_metric_count: 0