schema_version: formula_domain.v1 source: C:\Temp\data_feed\spec\13_formula_registry.yaml domain: simulation meta: note: > governance/todo/v8_9_p0_adoption_plan.yaml P0-3.1. source: suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml:forecast_and_simulation_engine_v8_9 배경: spec/29_backtest_harness_contract.yaml가 이미 "T+20 실현 표본 0건, walk_forward=insufficient_data"를 명시한다(AGENTS.md §6b). 이 도메인 파일은 분포를 날조하지 않고, 표본이 minimum_sample_rules를 충족할 때만 실제 bootstrap 계산 경로를 열고 미달이면 DATA_MISSING/WATCH_ONLY를 결정론적으로 반환하는 계약이다. formulas: FORECAST_SIMULATION_ENGINE_V1: purpose: > 개별 종목의 점 추정 기대수익률이 아니라 레짐별 손익분포에서 CE70(30%분위)·CE90(10%분위)· CVaR95(95% 신뢰구간 꼬리손실 평균)를 산출한다. 표본 부족 시 가짜 분포를 만들지 않고 WATCH_ONLY 또는 DATA_MISSING으로 정직하게 반환한다. applicable: PORTFOLIO_TRANSITION_UTILITY_V1의 ce70_net_profit_krw 입력 직전. inputs: - field: net_profit_distribution_after_tax_fee_slippage source: spec/29_backtest_harness_contract.yaml:current_metrics unit: list_of_KRW note: 세후·비용 차감 손익 표본. 현재 t20_op_rate.n_sample=0 (insufficient_data). - field: sample_count_total unit: count - field: sample_count_same_regime unit: count - field: execution_mode unit: enum note: AUDIT_ONLY | SHADOW | PILOT | LIVE_LIMITED | LIVE_FULL minimum_sample_rules: AUDIT_ONLY: sample_count_total_min: 0 sample_count_same_regime_min: 0 note: no minimum; report missing samples only (documentation only, no live order) SHADOW: sample_count_total_min: 30 sample_count_same_regime_min: 10 PILOT: sample_count_total_min: 80 sample_count_same_regime_min: 20 LIVE_LIMITED: sample_count_total_min: 150 sample_count_same_regime_min: 30 LIVE_FULL: sample_count_total_min: 300 sample_count_same_regime_min: 50 agents_md_cross_check: "AGENTS.md §6b: Live T+20 표본 30건 미만이면 active/PASS_100 승격 금지" gate_logic: - if: sample_count_total < minimum_sample_rules[execution_mode].sample_count_total_min action: WATCH_ONLY output: "ce70_net_profit_krw=null, ce90_net_profit_krw=null, cvar95_loss_krw=null" - if: sample_count_same_regime < minimum_sample_rules[execution_mode].sample_count_same_regime_min action: WATCH_ONLY output: "ce70_net_profit_krw=null, ce90_net_profit_krw=null, cvar95_loss_krw=null" - if: sample_count_total >= minimum AND sample_count_same_regime >= minimum action: COMPUTE expression: ce70_net_profit_krw: quantile(net_profit_distribution_after_tax_fee_slippage, 0.30) ce90_net_profit_krw: quantile(net_profit_distribution_after_tax_fee_slippage, 0.10) cvar95_loss_krw: mean(losses beyond 95th percentile loss threshold in net_profit_distribution_after_tax_fee_slippage) estimator_allowlist: - walk_forward_bootstrap - regime_matched_bootstrap forbidden_estimators: - LLM_guess_return - single_point_target_price_without_distribution - backtest_without_cost_or_leakage_test output: field: ce70_net_profit_krw unit: KRW_or_null additional_outputs: - ce90_net_profit_krw - cvar95_loss_krw - sample_count_total - sample_count_same_regime - gate missing_policy: net_profit_distribution_after_tax_fee_slippage: 표본 없으면 모든 출력 null + gate=WATCH_ONLY. 0으로 대체 금지. canonical_ref: spec/29_backtest_harness_contract.yaml:current_metrics.walk_forward implementation: tools/build_forecast_simulation_engine_v1.py owner: quant_team lifecycle_state: shadow input_fields: - net_profit_distribution_after_tax_fee_slippage - sample_count_total - sample_count_same_regime - execution_mode output_fields: - ce70_net_profit_krw - ce90_net_profit_krw - cvar95_loss_krw - sample_count_total - sample_count_same_regime - gate golden_cases: - V89_013_missing_CVaR - V89_014_same_regime_sample_low activation_threshold: min_t20_sample: 30 retirement_condition: performance_degradation SCENARIO_SHOCK_MATRIX_V1: purpose: > base_case 분포 하나만으로 위기 취약성을 가리지 않도록, FORECAST_SIMULATION_ENGINE_V1의 net_profit_distribution_after_tax_fee_slippage에 5개 스트레스 시나리오를 결정론적으로 적용해 각 시나리오별 CE70/CVaR95를 산출한다. 거짓 분포 생성을 금지하며, base distribution이 없으면 모든 시나리오가 DATA_MISSING이다. (governance/todo/v8_9_p2_adoption_plan.yaml P2-A, source: suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml:forecast_and_simulation_engine_v8_9.simulation_grid) applicable: FORECAST_SIMULATION_ENGINE_V1의 gate=PASS(분포 산출 성공) 직후. scenario_definitions: base_case: {shock_multiplier: 1.0, note: current_regime_probability_weighted, source_distribution: as-is} adverse_case: {shock_multiplier: 1.5, note: volatility_up_and_breadth_down — 손실 구간 증폭} liquidity_drought_case: {shock_multiplier: 1.3, capacity_derate_pct: 40, note: spread_widening_and_capacity_down} crisis_case: {shock_multiplier: 2.0, correlation_to_one: true, note: correlation_to_one_and_gap_down} fx_shock_case: {shock_multiplier: 1.2, applies_only_to: foreign_assets, note: USDKRW adverse movement} tax_cost_case: {shock_multiplier: 1.0, additional_cost_pct: 5, note: realized_gain_tax_and_reentry_cost_stress} inputs: - field: net_profit_distribution_after_tax_fee_slippage unit: list_of_KRW - field: scenario_id unit: 'enum: base_case | adverse_case | liquidity_drought_case | crisis_case | fx_shock_case | tax_cost_case' expression: > shocked_distribution = [v * scenario.shock_multiplier if v < 0 else v / scenario.shock_multiplier for v in net_profit_distribution_after_tax_fee_slippage] (손실만 증폭, 이익은 보수적으로 축소) scenario_ce70_krw = quantile(shocked_distribution, 0.30) scenario_cvar95_krw = mean(losses beyond 95th percentile loss threshold in shocked_distribution) output: field: scenario_results unit: 'list_of_{scenario_id, scenario_ce70_krw, scenario_cvar95_krw}' missing_policy: net_profit_distribution_after_tax_fee_slippage가 없으면 전체 scenario_results가 DATA_MISSING. 시나리오별로 임의 분포 생성 금지. canonical_ref: spec/formulas/domains/simulation.yaml:FORECAST_SIMULATION_ENGINE_V1 implementation: tools/build_scenario_shock_matrix_v1.py owner: quant_team lifecycle_state: shadow input_fields: - net_profit_distribution_after_tax_fee_slippage - scenario_id output_fields: - scenario_results golden_cases: - V89_010_candidate_good_portfolio_bad activation_threshold: min_t20_sample: 30 retirement_condition: performance_degradation WALK_FORWARD_BOOTSTRAP_V1: purpose: > FORECAST_SIMULATION_ENGINE_V1이 입력으로 받는 net_profit_distribution_after_tax_fee_slippage를 실제 historical_returns 표본에서 walk-forward(시간순 비복원, in-sample/out-of-sample 분리) 및 regime-matched(현재 레짐과 동일한 구간만 필터) 리샘플링으로 생성한다. 가짜 분포 생성을 금지하며 historical_returns가 없거나 표본이 1건뿐이면 DATA_MISSING. (governance/todo/v8_9_p3_adoption_plan.yaml P3-B, source: suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml:forecast_and_simulation_engine_v8_9.allowed_estimators) applicable: FORECAST_SIMULATION_ENGINE_V1 직전 — 이 엔진의 출력이 FORECAST_SIMULATION_ENGINE_V1의 입력이 된다. inputs: - field: historical_returns unit: list_of_object note: 'spec/29_backtest_harness_contract.yaml 연동. [{date, regime_state, net_return_after_cost_pct}]. 현재 t20 n_sample=0.' - field: current_regime_state unit: string - field: bootstrap_method unit: 'enum: walk_forward | regime_matched' - field: resample_count unit: count default: 1000 note: 리샘플링 반복 횟수. historical_returns 표본이 적으면 resample_count와 무관하게 sample_count_total은 historical_returns 길이로 결정. estimator_rule: walk_forward: > historical_returns를 시간순으로 정렬해 첫 70%를 in-sample, 나머지 30%를 out-of-sample로 고정 분리한다. out-of-sample 구간에서만 비복원 블록 리샘플링(block size=5)으로 net_profit_distribution을 생성한다. in-sample 재사용 금지(leakage 방지). regime_matched: > historical_returns 중 regime_state == current_regime_state인 행만 필터링 후 복원추출(bootstrap with replacement)로 net_profit_distribution을 생성한다. 필터 결과가 비어 있으면 DATA_MISSING(다른 레짐으로 대체 금지). leakage_controls: - asof_join_required - future_constituent_exclusion - calendar_alignment output: field: net_profit_distribution_after_tax_fee_slippage unit: list_of_KRW_or_null additional_outputs: - sample_count_total - sample_count_same_regime - leakage_check_status missing_policy: historical_returns 결측 또는 표본 1건 이하면 net_profit_distribution=null + gate=DATA_MISSING. 보간·추정 금지. canonical_ref: spec/29_backtest_harness_contract.yaml:current_metrics.walk_forward implementation: tools/build_walk_forward_bootstrap_v1.py owner: quant_team lifecycle_state: shadow input_fields: - historical_returns - current_regime_state - bootstrap_method - resample_count output_fields: - net_profit_distribution_after_tax_fee_slippage - sample_count_total - sample_count_same_regime golden_cases: - V89_014_same_regime_sample_low - V89_048_solver_failure activation_threshold: min_t20_sample: 30 retirement_condition: performance_degradation