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>
371 lines
22 KiB
YAML
371 lines
22 KiB
YAML
meta:
|
||
title: "은퇴자산포트폴리오 — 의사결정 상태 머신"
|
||
parent_file: "RetirementAssetPortfolio.yaml"
|
||
version: "2026-05-15-F3_decision_flow"
|
||
language: "ko-KR"
|
||
timezone: "Asia/Seoul"
|
||
purpose: >
|
||
LLM이 투자 판단을 임의 순서로 수행하지 않도록 상태 머신으로 절차를 고정한다.
|
||
각 상태는 통과 조건, 실패 시 행동, 참조 파일을 가진다.
|
||
|
||
decision_flow:
|
||
initial_state: "MODEL_GOVERNANCE_GATE"
|
||
terminal_states: ["FINAL_DECISION", "INSUFFICIENT_DATA", "BLOCKED"]
|
||
deterministic_execution_control:
|
||
purpose: "텍스트 해석 차이로 매번 다른 결론이 나오는 것을 줄이기 위한 결정 추적·동률 처리·first-match 규칙."
|
||
rule: "모든 상태는 ordered_checks를 위에서 아래로 평가하고, 첫 번째 BLOCK/PASS/SELECT 결과를 decision_trace에 기록한다."
|
||
no_freeform_override:
|
||
- "상태 머신 밖의 산문 판단으로 final_action, grade, quantity, sell_priority를 변경 금지"
|
||
- "동일 입력·동일 기준시각이면 동일 decision_trace와 동일 final_action이 나와야 한다."
|
||
- "규칙 충돌 시 자연어로 절충하지 말고 tie_breaker 또는 INSUFFICIENT_DATA/BLOCKED로 종료한다."
|
||
trace_required_fields:
|
||
- "state"
|
||
- "check_id"
|
||
- "rule_ref"
|
||
- "inputs_used"
|
||
- "result"
|
||
- "selected_action"
|
||
- "blocked_actions"
|
||
- "missing_inputs"
|
||
- "tie_breaker_applied"
|
||
tie_breaker_order:
|
||
1: "source_of_truth_order 상위 파일"
|
||
2: "risk hard stop 또는 master_prohibitions"
|
||
3: "데이터 완성도 OK > PARTIAL > DATA_MISSING"
|
||
4: "계산 가능한 공식 ID가 있는 규칙"
|
||
5: "보수적 행동: BLOCKED/INSUFFICIENT_DATA/WATCH"
|
||
null_propagation_rule: "필수 입력이 null이면 해당 계산은 null로 유지하고 prohibited_calculations에 사유를 남긴다. null을 0으로 대체 금지."
|
||
output_requirement: "OUTPUT_VALIDATION에서 decision_trace 누락 시 schema_validation_status=FAIL."
|
||
states:
|
||
MODEL_GOVERNANCE_GATE: # [governance/todo/v8_9_p1_adoption_plan.yaml P1-5]
|
||
purpose: >
|
||
매 의사결정 사이클 시작 전 kill switch(data_quarantine_rate, implementation_shortfall,
|
||
T5_hit_rate, calibration_error, drawdown)를 평가해 execution_mode를 확정한다.
|
||
이후 모든 단계는 이 execution_mode를 기준으로 동작한다.
|
||
required_refs:
|
||
- "spec/formulas/domains/governance.yaml:MODEL_GOVERNANCE_KILL_SWITCH_V1"
|
||
- "tools/build_model_governance_kill_switch_v1.py"
|
||
required_inputs: ["data_quarantine_rate_pct", "implementation_shortfall_ratio", "t5_hit_rate_pct", "t5_sample_count", "calibration_error", "account_mdd_pct"]
|
||
computed_outputs: ["execution_mode", "kill_switch_triggered", "kill_switch_reason_codes"]
|
||
pass_condition: "execution_mode 확정(kill switch 평가 완료 또는 DATA_MISSING)"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
INPUT_VALIDATION:
|
||
purpose: "요청, 기준일, 계좌, 보유수량, 가격/수급/ATR 입력 존재 여부 확인"
|
||
required_refs:
|
||
- "spec/01_objective_profile.yaml:input_required"
|
||
- "spec/12_field_dictionary.yaml:field_dictionary"
|
||
- "spec/02_data_contract.yaml:quant_feed_contract"
|
||
required_inputs: ["request_scope", "analysis_date", "account_scope"]
|
||
computed_outputs: ["normalized_request", "field_alias_resolution_log"]
|
||
pass_condition: "minimum request context exists"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
DATA_COMPLETENESS_CHECK:
|
||
purpose: "데이터 상태 OK/PARTIAL/DATA_MISSING/DATA_CONFLICT/DATA_STALE 판정"
|
||
required_refs:
|
||
- "spec/02_data_contract.yaml:data_rule"
|
||
- "spec/02_data_contract.yaml:quant_feed_contract.data_completeness_gate"
|
||
- "spec/12_field_dictionary.yaml:field_dictionary.policy"
|
||
required_inputs: ["normalized_request", "raw_data_sources"]
|
||
computed_outputs: ["data_completeness_matrix", "missing_fields", "field_unit_conflicts"]
|
||
pass_condition: "data_completeness_matrix produced"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
STATE_VECTOR_CONSTRUCTION: # [governance/todo/v8_9_p3_adoption_plan.yaml P3-F]
|
||
purpose: >
|
||
holdings, cash, tax_lots, sector_graph, factor_exposures, macro_regime_probabilities를
|
||
단일 state_vector로 통합한다. cash_ladder 구성 시 WEEKLY_LEGACY_TRANSFER_PLAN_V1을
|
||
통해 미확정 레거시 이전계획을 deployable_cash에서 제외한다.
|
||
required_refs:
|
||
- "spec/formulas/domains/portfolio.yaml:STATE_VECTOR_CONSTRUCTOR_V1"
|
||
- "spec/formulas/domains/cash.yaml:WEEKLY_LEGACY_TRANSFER_PLAN_V1"
|
||
- "tools/build_state_vector_constructor_v1.py"
|
||
- "tools/build_weekly_legacy_transfer_plan_v1.py"
|
||
required_inputs: ["data_completeness_matrix", "account_snapshot"]
|
||
computed_outputs: ["state_vector", "state_vector_completeness_pct", "missing_components", "deployable_cash_contribution_krw"]
|
||
pass_condition: "state_vector 산출(결측 component 포함 가능)"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
HARD_FILTER_CHECK:
|
||
purpose: "하드 필터를 점수보다 먼저 적용"
|
||
required_refs:
|
||
- "spec/08_scoring_rules.yaml:hard_filters"
|
||
- "spec/risk/aggregate_risk.yaml:risk_control.aggregate_risk_cap"
|
||
- "spec/risk/circuit_breakers.yaml:risk_control.weekly_circuit_breaker"
|
||
- "spec/13_formula_registry.yaml:formula_registry.formulas.TOTAL_HEAT_V1"
|
||
required_inputs: ["data_completeness_matrix", "account_snapshot", "total_asset"]
|
||
computed_outputs: ["hard_filter_results", "total_heat_pct", "blocked_actions"]
|
||
pass_condition: "no blocking hard filter for requested action"
|
||
fail_state: "BLOCKED"
|
||
MARKET_REGIME_CHECK:
|
||
purpose: "Risk-On/Neutral/Risk-Off 및 현금 목표 확인"
|
||
required_refs:
|
||
- "spec/risk/market_risk_cash.yaml:risk_control.market_risk_score_based_cash"
|
||
- "spec/strategy/entry_core.yaml:entry_timing_guardrails.regime_based_entry"
|
||
- "spec/13_formula_registry.yaml:formula_registry.formulas.MARKET_RISK_SCORE_V1"
|
||
- "spec/13_formula_registry.yaml:formula_registry.formulas.TARGET_CASH_PCT_V1"
|
||
required_inputs: ["vix_close", "kospi_close", "kospi_ma20", "usd_krw", "usd_jpy_2d_change_pct", "credit_stress_status"]
|
||
computed_outputs: ["market_risk_score", "target_cash_pct", "market_regime_state"]
|
||
pass_condition: "market regime classified or marked UNKNOWN"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
STRATEGY_SCORING:
|
||
purpose: "섹터/종목/수급/유동성/실적/밸류 점수 산출"
|
||
required_refs:
|
||
- "spec/08_scoring_rules.yaml:strategy_score"
|
||
- "spec/strategy/sector_model.yaml:sector_model"
|
||
- "spec/13_formula_registry.yaml:formula_registry.formulas.FLOW_CREDIT_V1"
|
||
required_inputs: ["close_price", "ma20", "flow_ok", "flow_rows", "frg_5d_sh", "inst_5d_sh", "avg_trade_value_5d"]
|
||
computed_outputs: ["flow_credit", "strategy_score", "component_scores", "grade_candidate"]
|
||
pass_condition: "strategy_score calculated or missing fields listed"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
PORTFOLIO_CONSTRAINT_CHECK:
|
||
purpose: "계좌·비중·중복노출·현금·납입한도 확인"
|
||
required_refs:
|
||
- "spec/10_portfolio_rules.yaml"
|
||
- "spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework"
|
||
required_inputs: ["target_cash_pct", "available_cash", "total_asset", "current_exposures", "account_scope"]
|
||
computed_outputs: ["portfolio_fit_score", "cash_floor_status", "exposure_limit_amounts"]
|
||
pass_condition: "portfolio constraints pass or downgrade action selected"
|
||
fail_state: "BLOCKED"
|
||
cash_shortfall_resolution: # [2026-05-19_HARNESS_AUDIT_V1] H2
|
||
rule_id: "CSR001"
|
||
purpose: >
|
||
현금 < target_cash_pct 확인 시 BUY를 결정론적으로 차단하고
|
||
SELL_PRIORITY 엔진으로 TRIM 수량을 자동 배정한다. LLM 임의 배정 금지.
|
||
trigger_condition: "CASH_RATIOS_V1.current_cash_pct < TARGET_CASH_PCT_V1.target_cash_pct"
|
||
steps:
|
||
step_1_block_buy:
|
||
action: "모든 BUY 신호 → IGNORE 전환. 이유 불문, LLM 재해석 금지."
|
||
output_key: "buy_gate_status = CASH_BLOCKED"
|
||
step_2_calc_shortfall:
|
||
formula: "cash_shortfall_krw = max(0, (target_cash_pct/100 - current_cash_pct/100) × total_asset)"
|
||
source: "CASH_RATIOS_V1 결과 기반 — LLM 직접 계산 금지"
|
||
output_key: "cash_shortfall_krw"
|
||
step_3_assign_trim:
|
||
source: "sell_priority_engine (spec/risk/portfolio_exposure.yaml)"
|
||
algorithm: >
|
||
SELL_PRIORITY 1순위 종목부터 계단식 TRIM 수량 배정.
|
||
잔여 부족액이 남으면 2순위로 이동하며 반복.
|
||
formula: "trim_qty = min(floor(remaining_shortfall_krw / current_price), holding_quantity)"
|
||
cascade: "remaining_shortfall_krw -= trim_qty × current_price → 다음 순위"
|
||
output_tag: "[CASH_RAISE_AUTO: {종목} {qty}주 TRIM → 예상확보 {proceeds}원]"
|
||
step_4_output_required:
|
||
fields:
|
||
- "cash_shortfall_krw (부족액)"
|
||
- "trim_assignments: [{종목, qty, expected_proceeds}] (배정 목록)"
|
||
- "post_trim_cash_pct (TRIM 후 예상 현금비중)"
|
||
enforcement:
|
||
- "수동 배정·서사적 배정 금지 — 공식 결과만 기재"
|
||
- "trim_assignments 없이 현금 부족 해소 주장 금지"
|
||
- "QEH_AUDIT_BLOCK.SELL_PRIORITY_V1 행에 배정 결과 요약 필수"
|
||
- "1순위 소진 전 2순위 배정 금지 (sell_priority_engine 순서 준수)"
|
||
SECTOR_EXPOSURE_REVIEW: # [governance/todo/v8_9_p1_adoption_plan.yaml P1-5]
|
||
purpose: >
|
||
섹터를 canonical ID로 분류하고 ETF lookthrough·factor beta residualization을 적용해
|
||
실질노출을 산출하며, 리더 생명주기(CAPTAIN~DISTRIBUTION_RISK)를 평가한다.
|
||
required_refs:
|
||
- "spec/formulas/domains/sector.yaml:SECTOR_EXPOSURE_GRAPH_V1"
|
||
- "spec/formulas/domains/sector.yaml:LEADER_LIFECYCLE_GATE_V1"
|
||
- "tools/build_sector_exposure_graph_v1.py"
|
||
required_inputs: ["exposure_limit_amounts", "current_exposures", "etf_constituents_json"]
|
||
computed_outputs: ["sector_family_total_pct", "lookthrough_etf_weight_pct", "leader_role"]
|
||
pass_condition: "섹터 노출 산출 또는 ETF_BUY_BLOCKED/DATA_MISSING 명시"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
POSITION_SIZING:
|
||
purpose: "ATR20·현금·목표비중·유동성으로 정수 수량 산출"
|
||
required_refs:
|
||
- "spec/05_position_sizing.yaml:position_sizing"
|
||
- "spec/13_formula_registry.yaml:formula_registry.formulas.RISK_BUDGET_CASCADE_V1"
|
||
- "spec/13_formula_registry.yaml:formula_registry.formulas.POSITION_SIZE_V1"
|
||
required_inputs: ["total_asset", "atr20", "entry_price", "available_cash", "exposure_limit_amounts", "bayesian_confidence_multiplier"]
|
||
computed_outputs: ["final_risk_budget", "atr_quantity", "cash_limit_quantity", "target_weight_limit_quantity", "sector_limit_quantity", "liquidity_limit_quantity", "final_quantity"]
|
||
pass_condition: "integer quantity calculated, or NO_QUANTITY reason emitted"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
EXIT_POLICY_CHECK:
|
||
purpose: "손절/익절/trailing/보유주 점검 규칙 적용"
|
||
required_refs:
|
||
- "spec/exit/stop_loss.yaml:stop_loss"
|
||
- "spec/exit/take_profit.yaml:take_profit"
|
||
- "spec/exit/position_review.yaml:position_review_cycle"
|
||
- "spec/13_formula_registry.yaml:formula_registry.formulas.EXPECTED_EDGE_V1"
|
||
required_inputs: ["entry_price", "stop_price", "target_price", "final_quantity"]
|
||
computed_outputs: ["expected_edge", "stop_order", "take_profit_order", "invalidation_conditions"]
|
||
pass_condition: "exit or hold policy evaluated"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
proactive_exit_radar_check: # [2026-05-19_PROACTIVE_RADAR_V1]
|
||
purpose: >
|
||
보유 포지션 분석 시 항상 실행. 가격이 멀쩡할 때도 수급·유동성·섹터
|
||
신호로 선제 매도 적기를 감지한다. '매도가 예술이다' 원칙 구현.
|
||
required_refs:
|
||
- "spec/exit/proactive_exit_radar.yaml"
|
||
- "spec/13_formula_registry.yaml:DIVERGENCE_SCORE_V1"
|
||
- "spec/13_formula_registry.yaml:OVERHANG_PRESSURE_V1"
|
||
- "spec/13_formula_registry.yaml:SECTOR_ROTATION_RADAR_V1"
|
||
required_inputs:
|
||
- "frg_5d_sh, inst_5d_sh, flow_credit (W1)"
|
||
- "frg_5d_sh, frg_20d_sh, volume, avg_volume_5d (W2)"
|
||
- "sector_flow.SmartMoney_5D_Norm_Score, sector_flow.Rank (W3)"
|
||
computed_outputs:
|
||
- "divergence_score + W1_status"
|
||
- "overhang_score + W2_status"
|
||
- "W3_status"
|
||
- "radar_composite_status (PASS/CAUTION/ALERT/CRITICAL_ALERT)"
|
||
- "PROACTIVE_RADAR_BLOCK (보고서 출력용)"
|
||
hard_rules:
|
||
- "ALERT 이상 발화 시 sell_priority_engine 실행 필수"
|
||
- "CRITICAL_ALERT 시 코어 포지션 포함 전면 재검토 강제"
|
||
- "LLM이 레이더 결과를 완화하는 서사 출력 금지 (Section B 해설만 허용)"
|
||
- "RADAR_MISSING(데이터 부족) 시 soft-block: 보유 포지션 수동 점검 권고 문구 출력"
|
||
PORTFOLIO_TRANSITION_REVIEW: # [governance/todo/v8_9_p0_adoption_plan.yaml P0-1.6, v8_9_p2_adoption_plan.yaml P2-E]
|
||
purpose: >
|
||
개별 종목 단위 결정을 포트폴리오 전체 전환 효용으로 재평가한다.
|
||
EXIT_POLICY_CHECK에서 나온 sell 후보들을 단일 전환 패킷으로 비교해
|
||
selected_transition_set 또는 NO_TRADE를 결정론적으로 확정한다. CE70/CVaR95 분포는
|
||
WALK_FORWARD_BOOTSTRAP_V1으로 생성하고 SCENARIO_SHOCK_MATRIX_V1의 5개 스트레스
|
||
시나리오를 반영하며, candidate 1건이 아니라 TRANSITION_SET_ENUMERATOR_V1으로 조합 단위
|
||
hard_constraint(MRC/CVaR95 포함)를 재검증한다. 실제 실행은 REBALANCE_CADENCE_GATE_V1의
|
||
rebalance_execution_allowed=true일 때만 허용된다.
|
||
required_refs:
|
||
- "spec/formulas/domains/portfolio.yaml:PORTFOLIO_TRANSITION_UTILITY_V1"
|
||
- "spec/formulas/domains/portfolio.yaml:TRANSITION_SET_ENUMERATOR_V1"
|
||
- "spec/formulas/domains/portfolio.yaml:REBALANCE_CADENCE_GATE_V1"
|
||
- "spec/formulas/domains/simulation.yaml:SCENARIO_SHOCK_MATRIX_V1"
|
||
- "spec/formulas/domains/simulation.yaml:WALK_FORWARD_BOOTSTRAP_V1"
|
||
- "tools/build_portfolio_transition_optimizer_v1.py"
|
||
- "tools/build_transition_set_enumerator_v1.py"
|
||
- "tools/build_scenario_shock_matrix_v1.py"
|
||
- "tools/build_walk_forward_bootstrap_v1.py"
|
||
- "tools/build_rebalance_cadence_gate_v1.py"
|
||
required_inputs: ["stop_order", "take_profit_order", "sell_waterfall_rows", "cash_repair_benefit_krw", "rebalance_execution_allowed"]
|
||
computed_outputs: ["transition_utility_krw", "acceptance_margin_krw", "selected_transition", "portfolio_transition_final_action", "selected_transition_set", "scenario_results", "post_trade_mrc", "post_trade_cvar95_krw"]
|
||
pass_condition: "selected_transition_set 결정(rebalance_execution_allowed=true 필요) 또는 default_action=NO_TRADE 명시"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
EXECUTION_CAPACITY_CHECK: # [governance/todo/v8_9_p1_adoption_plan.yaml P1-5, v8_9_p2_adoption_plan.yaml P2-E]
|
||
purpose: >
|
||
selected_transition_set의 주문금액이 실제 체결 가능 용량(20일 평균거래대금, 당일 거래대금,
|
||
호가창 깊이)을 초과하지 않는지 확인하고, broker_microstructure_packet 결측 시 차단한다.
|
||
용량이 확인되면 EXECUTION_PLAN_COMPILER_V1으로 30/30/40 LIMIT_SPLIT 슬라이스를 컴파일한다.
|
||
required_refs:
|
||
- "spec/formulas/domains/execution.yaml:EXECUTION_CAPACITY_LADDER_V1"
|
||
- "spec/formulas/domains/execution.yaml:EXECUTION_PLAN_COMPILER_V1"
|
||
- "tools/build_execution_capacity_ladder_v1.py"
|
||
- "tools/build_execution_plan_compiler_v1.py"
|
||
required_inputs: ["selected_transition_set", "avg_trade_value_20d_krw", "intraday_trade_value_krw", "orderbook_top3_depth_krw", "spread_bps"]
|
||
computed_outputs: ["order_capacity_krw", "execution_plan_status", "compiled_slices"]
|
||
pass_condition: "order_capacity_krw 산출 또는 EXECUTION_PLAN_BLOCKED 명시"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
OUTPUT_VALIDATION:
|
||
purpose: "JSON Schema와 HTS 표 필드 검증"
|
||
required_refs:
|
||
- "schemas/output_schema.json"
|
||
- "spec/07_output_schema.yaml"
|
||
required_inputs: ["final_action", "orders", "scores", "position_sizing", "triggered_rules", "missing_data"]
|
||
computed_outputs: ["schema_validation_status", "prohibited_calculations"]
|
||
pass_condition: "all required output fields populated or prohibited_calculations filled"
|
||
fail_state: "INSUFFICIENT_DATA"
|
||
FINAL_DECISION:
|
||
purpose: "BUY/HOLD/SELL/TRIM/ROTATE/AVOID/WATCH/INSUFFICIENT_DATA 중 하나로 결론"
|
||
required_refs:
|
||
- "spec/07_output_schema.yaml:recommendation_grade"
|
||
- "spec/formulas/domains/governance.yaml:IMMUTABLE_DECISION_LEDGER_V1"
|
||
output_required:
|
||
- "final_action"
|
||
- "grade"
|
||
- "orders or prohibited_calculations"
|
||
- "rules_used"
|
||
- "ledger_append_status"
|
||
post_state_action: "tools/build_immutable_decision_ledger_v1.py 호출 — decision_id 재기록 시도는 DUPLICATE_DECISION_ID로 거부."
|
||
INSUFFICIENT_DATA:
|
||
purpose: "데이터 부족으로 산출 불가. 다음 확인 출처를 제시."
|
||
output_required:
|
||
- "missing_fields"
|
||
- "prohibited_calculations"
|
||
- "next_source_to_check"
|
||
- "ledger_append_status"
|
||
post_state_action: "INSUFFICIENT_DATA로 종료해도 IMMUTABLE_DECISION_LEDGER_V1에 기록한다(결정 없음도 기록 대상)."
|
||
BLOCKED:
|
||
purpose: "하드 필터 또는 리스크 정책으로 행동 차단."
|
||
output_required:
|
||
- "triggered_rules"
|
||
- "blocked_action"
|
||
- "allowed_alternative"
|
||
- "ledger_append_status"
|
||
post_state_action: "BLOCKED로 종료해도 IMMUTABLE_DECISION_LEDGER_V1에 기록한다."
|
||
|
||
transitions:
|
||
- from: "MODEL_GOVERNANCE_GATE"
|
||
to: "INPUT_VALIDATION"
|
||
condition: "execution_mode 확정(kill switch 평가 완료 또는 DATA_MISSING)"
|
||
- from: "MODEL_GOVERNANCE_GATE"
|
||
to: "INSUFFICIENT_DATA"
|
||
condition: "kill switch 평가에 필요한 모든 지표가 결측"
|
||
- from: "INPUT_VALIDATION"
|
||
to: "DATA_COMPLETENESS_CHECK"
|
||
condition: "minimum request context exists"
|
||
- from: "INPUT_VALIDATION"
|
||
to: "INSUFFICIENT_DATA"
|
||
condition: "account/request context missing and cannot be inferred"
|
||
- from: "DATA_COMPLETENESS_CHECK"
|
||
to: "STATE_VECTOR_CONSTRUCTION"
|
||
condition: "data_completeness_matrix produced"
|
||
- from: "DATA_COMPLETENESS_CHECK"
|
||
to: "INSUFFICIENT_DATA"
|
||
condition: "matrix cannot be produced"
|
||
- from: "STATE_VECTOR_CONSTRUCTION"
|
||
to: "HARD_FILTER_CHECK"
|
||
condition: "state_vector 산출(결측 component 포함 가능)"
|
||
- from: "HARD_FILTER_CHECK"
|
||
to: "BLOCKED"
|
||
condition: "blocking hard_filter failed"
|
||
- from: "HARD_FILTER_CHECK"
|
||
to: "MARKET_REGIME_CHECK"
|
||
condition: "no blocking hard_filter failed"
|
||
- from: "MARKET_REGIME_CHECK"
|
||
to: "STRATEGY_SCORING"
|
||
condition: "regime classified or UNKNOWN with caution"
|
||
- from: "STRATEGY_SCORING"
|
||
to: "PORTFOLIO_CONSTRAINT_CHECK"
|
||
condition: "strategy score calculated or downgrade reason emitted"
|
||
- from: "PORTFOLIO_CONSTRAINT_CHECK"
|
||
to: "BLOCKED"
|
||
condition: "cash_floor, duplicate exposure, account limit, or Total_Heat blocks requested action"
|
||
- from: "PORTFOLIO_CONSTRAINT_CHECK"
|
||
to: "SECTOR_EXPOSURE_REVIEW"
|
||
condition: "requested action requires quantity or affects sector exposure"
|
||
- from: "PORTFOLIO_CONSTRAINT_CHECK"
|
||
to: "EXIT_POLICY_CHECK"
|
||
condition: "requested action is hold/trim/sell review"
|
||
- from: "SECTOR_EXPOSURE_REVIEW"
|
||
to: "POSITION_SIZING"
|
||
condition: "섹터 노출 산출 또는 ETF_BUY_BLOCKED/DATA_MISSING 명시"
|
||
- from: "SECTOR_EXPOSURE_REVIEW"
|
||
to: "INSUFFICIENT_DATA"
|
||
condition: "sector exposure 계산에 필요한 입력 누락"
|
||
- from: "POSITION_SIZING"
|
||
to: "EXIT_POLICY_CHECK"
|
||
condition: "quantity calculated or NO_QUANTITY reason emitted"
|
||
- from: "EXIT_POLICY_CHECK"
|
||
to: "PORTFOLIO_TRANSITION_REVIEW"
|
||
condition: "order/hold/watch decision prepared"
|
||
- from: "PORTFOLIO_TRANSITION_REVIEW"
|
||
to: "EXECUTION_CAPACITY_CHECK"
|
||
condition: "selected_transition 결정 또는 NO_TRADE 명시"
|
||
- from: "PORTFOLIO_TRANSITION_REVIEW"
|
||
to: "INSUFFICIENT_DATA"
|
||
condition: "transition utility 계산에 필요한 입력 누락"
|
||
- from: "EXECUTION_CAPACITY_CHECK"
|
||
to: "OUTPUT_VALIDATION"
|
||
condition: "order_capacity_krw 산출 또는 EXECUTION_PLAN_BLOCKED 명시 (NO_TRADE인 경우 즉시 통과)"
|
||
- from: "EXECUTION_CAPACITY_CHECK"
|
||
to: "INSUFFICIENT_DATA"
|
||
condition: "broker_microstructure_packet 필드 누락"
|
||
- from: "OUTPUT_VALIDATION"
|
||
to: "FINAL_DECISION"
|
||
condition: "schema and required output fields valid"
|
||
- from: "OUTPUT_VALIDATION"
|
||
to: "INSUFFICIENT_DATA"
|
||
condition: "required output fields missing without prohibited_calculations reason"
|
||
|
||
global_prohibitions:
|
||
- "HARD_FILTER_CHECK 이전에 BUY 결론 출력 금지"
|
||
- "POSITION_SIZING 이전에 정수 주문수량 출력 금지"
|
||
- "OUTPUT_VALIDATION 실패 상태에서 즉시 실행 플레이북 출력 금지"
|
||
- "BLOCKED 상태를 WATCH로 미화 금지. 차단 사유를 명시한다."
|