aedabdd37b
suggest/quant_investment_engine_v8_9_portfolio_optimizer_canonical_refactored.yaml의
implementation_todo_v8_9(P0~P4) 전체를 spec/tool/golden case 레벨로 구현.
- P0: PORTFOLIO_TRANSITION_UTILITY_V1, SELL_LOT_PARETO_SELECTOR_V1, FORECAST_SIMULATION_ENGINE_V1
- P1: SECTOR_EXPOSURE_GRAPH_V1/LEADER_LIFECYCLE_GATE_V1, EXECUTION_CAPACITY_LADDER_V1, MODEL_GOVERNANCE_KILL_SWITCH_V1
- P2: SCENARIO_SHOCK_MATRIX_V1, TRANSITION_SET_ENUMERATOR_V1, IMMUTABLE_DECISION_LEDGER_V1, EXECUTION_PLAN_COMPILER_V1
- P3: STATE_VECTOR_CONSTRUCTOR_V1, WALK_FORWARD_BOOTSTRAP_V1, TRANSITION_SET_ENUMERATOR_V1(MRC/CVaR 확장),
REBALANCE_CADENCE_GATE_V1, WEEKLY_LEGACY_TRANSFER_PLAN_V1
기존 regime/cluster 연동 정책 수치(현금방어선, 반도체 cap)는 그대로 유지하고 신규 cap 필드만 추가.
spec/09_decision_flow.yaml과 runtime/active_artifact_manifest.yaml에 전 엔진 배선 완료.
governance/todo/v8_9_p{0,1,2,3}_adoption_plan.yaml에 각 단계 작업 추적 기록.
검증: validate_specs/validate_golden_coverage_100(100%)/validate_calibration_registry_v1/
validate_schema_model_generation_v1/validate_agents_shrink_v1 전부 PASS. golden test 53/53 PASS.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
206 lines
10 KiB
YAML
206 lines
10 KiB
YAML
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
|