Files
QuantEngineByItz/spec/formulas/domains/portfolio.yaml
T
kjh2064 ee3e799de1 feat: 리밸런싱 엔진 V1 + GAS 버그 수정 (2026-06-13)
주요 변경:
- tools/build_rebalance_engine_v1.py: REBALANCE_ENGINE_V1 신규
  * account_snapshot 직접 합산(_build_snap_position_map) → 소수주 분리 행 병합
  * 레짐 소스 macro.REGIME_PRELIM 최우선 (GAS 와 동일)
- src/gas_adapter_parts/gdf_06_rebalance.gs: runRebalanceSheet_() 신규
  * Logger.log / getSpreadsheet_() 로 run_all 연동 수정
- src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs
  * _mergePositionRecord_(): 소수주 중복 행 합산 신규
  * parseInt → parseFloat (qty, availQty)
- src/gas_adapter_parts/gdf_01_price_metrics.gs
  * 미보유 종목 SELL_READY → WATCH_EXIT_SIGNAL
- spec/41_release_dag.yaml: build_rebalance_sheet 노드 추가 (step_count 63)
- spec/51_formula_lifecycle_registry.yaml: REBALANCE_ENGINE_V1 등록

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 13:20:14 +09:00

671 lines
21 KiB
YAML
Raw 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.
schema_version: formula_domain.v1
source: C:\Temp\data_feed\spec\13_formula_registry.yaml
domain: portfolio
formulas:
TOTAL_HEAT_V1:
purpose: 손절 기준 총 위험노출 계산
inputs:
- field: average_cost
source: account_snapshot
unit: KRW_per_share
- field: stop_price
source: account_snapshot
unit: KRW_per_share
- field: quantity
source: account_snapshot.holding_quantity
unit: shares
- field: total_asset
unit: KRW
expression: sum((average_cost - stop_price) * quantity for each confirmed account_snapshot
holding) / total_asset * 100
output:
field: total_heat_pct
unit: percent
missing_policy:
stop_price: if atr20 exists use entry_price - atr20*2.0 else assume portfolio
heat contribution cap breach
quantity: NO_TOTAL_HEAT
total_asset: NO_TOTAL_HEAT
gates:
- if: total_heat_pct >= 10
action: BLOCK_NEW_BUY
- if: 7 <= total_heat_pct < 10
action: HALVE_NEW_BUY_QUANTITY
- if: total_heat_pct < 7
action: ALLOW_CONTINUE
canonical_ref: spec/risk/aggregate_risk.yaml:risk_control.aggregate_risk_cap
owner: quant_team
lifecycle_state: active
input_fields:
- average_cost
- stop_price
- quantity
- total_asset
output_fields:
- total_heat_pct
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
RISK_BUDGET_CASCADE_V1:
purpose: base risk budget에 Bayesian, 성과, 국면, Kelly 감액을 순서대로 적용
inputs:
- field: base_risk_budget
unit: ratio
default: 0.007
- field: net_return_feedback_multiplier
unit: ratio
default: 1.0
- field: performance_brake_multiplier
unit: ratio
default: 1.0
- field: regime_reset_multiplier
unit: ratio
default: 1.0
- field: bayesian_confidence_multiplier
unit: ratio
- field: kelly_brake_multiplier
unit: ratio
default: 1.0
expression: base_risk_budget * net_return_feedback_multiplier * performance_brake_multiplier
* regime_reset_multiplier * bayesian_confidence_multiplier * kelly_brake_multiplier
output:
field: final_risk_budget
unit: ratio
floor_rule:
if: final_risk_budget < 0.001
action: NO_BET
canonical_ref: spec/05_position_sizing.yaml:position_sizing.cascade_risk_budget_rule
owner: quant_team
lifecycle_state: active
input_fields:
- base_risk_budget
- net_return_feedback_multiplier
- performance_brake_multiplier
- regime_reset_multiplier
- bayesian_confidence_multiplier
- kelly_brake_multiplier
output_fields:
- final_risk_budget
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
POSITION_SIZE_V1:
purpose: 최종 정수 매수수량 산출
inputs:
- field: total_asset
unit: KRW
- field: final_risk_budget
unit: ratio
- field: atr20
unit: KRW_per_share
- field: atr_multiplier
unit: ratio
default: 1.5
- field: available_cash
unit: KRW
- field: entry_price
unit: KRW_per_share
- field: target_weight_limit_amount
unit: KRW
- field: sector_limit_amount
unit: KRW
- field: liquidity_limit_amount
unit: KRW
intermediate_outputs:
atr_quantity: floor((total_asset * final_risk_budget) / (atr20 * atr_multiplier))
cash_limit_quantity: floor(available_cash / entry_price)
target_weight_limit_quantity: floor(target_weight_limit_amount / entry_price)
sector_limit_quantity: floor(sector_limit_amount / entry_price)
liquidity_limit_quantity: floor(liquidity_limit_amount / entry_price)
expression: min(atr_quantity, cash_limit_quantity, target_weight_limit_quantity,
sector_limit_quantity, liquidity_limit_quantity)
output:
field: final_quantity
unit: shares_integer
missing_policy:
atr20: NO_BUY_QUANTITY
total_asset: NO_BUY_QUANTITY
available_cash: NO_BUY_QUANTITY
entry_price: NO_BUY_QUANTITY
target_weight_limit_amount: use very large number only if portfolio rule says
NOT_APPLICABLE
sector_limit_amount: use very large number only if sector cap says NOT_APPLICABLE
liquidity_limit_amount: allow PARTIAL only for report; BUY validation_status
cannot PASS
canonical_ref: spec/05_position_sizing.yaml:position_sizing.volatility_targeting
owner: quant_team
lifecycle_state: active
input_fields:
- total_asset
- final_risk_budget
- atr20
- atr_multiplier
- available_cash
- entry_price
- target_weight_limit_amount
- sector_limit_amount
- liquidity_limit_amount
output_fields:
- final_quantity
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
PORTFOLIO_BAND_STATUS_V1:
purpose: 현재 비중이 목표 밴드보다 낮은지, 정상인지, 초과인지 판정
inputs:
- field: current_weight_pct
unit: percent
- field: target_band_min_pct
unit: percent
- field: target_band_max_pct
unit: percent
rules:
- if: current_weight_pct < target_band_min_pct
status: UNDERWEIGHT
action: ADD_ALLOWED_IF_ALL_GATES_PASS
- if: target_band_min_pct <= current_weight_pct <= target_band_max_pct
status: IN_BAND
action: HOLD_OR_SELECTIVE_ADD
- if: current_weight_pct > target_band_max_pct
status: OVERWEIGHT
action: TRIM_REVIEW
output:
field: portfolio_band_status
unit: enum
missing_policy: DATA_MISSING. add/trim 결론 보류.
canonical_ref: spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework.target_allocation_structure
owner: quant_team
lifecycle_state: active
input_fields:
- current_weight_pct
- target_band_min_pct
- target_band_max_pct
output_fields:
- portfolio_band_status
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
PORTFOLIO_BETA_V1:
purpose: 보유 포지션의 시가기준 가중평균 베타를 산출하여 팩터 과집중 판단에 사용
inputs:
- field: beta_i
source: data_feed.Beta for each holding i
unit: ratio
- field: market_value_i
source: account_snapshot.holding_quantity × close_price
unit: KRW
- field: total_equity_value
source: sum(market_value_i)
unit: KRW
expression: sum(beta_i × market_value_i / total_equity_value) for each holding
with known beta
output:
field: portfolio_beta
unit: ratio
missing_policy:
beta_i_missing_single: '해당 종목 제외 후 부분 산출. 제외 종목 시가 비중이 30% 초과 시 결과에 "(PARTIAL
— Beta 미확인 {N}개 종목 제외)" 표기.
'
beta_i_missing_all: NO_PORTFOLIO_BETA. 팩터 리스크 점검 PARTIAL 표기.
total_equity_value_zero: NO_PORTFOLIO_BETA
example:
holdings:
- name: 삼성전자
market_value: 100000000
beta: 1.1
- name: SK하이닉스
market_value: 80000000
beta: 1.3
- name: 한화에어로스페이스
market_value: 40000000
beta: 1.6
total_equity: 220000000
result: (1.1×100 + 1.3×80 + 1.6×40) / 220 = (110 + 104 + 64) / 220 = 278/220
≈ 1.26
canonical_ref: spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework.factor_risk_limit
version: 2026-05-18_ROUTING_OPTIMIZATION_V1
owner: quant_team
lifecycle_state: active
input_fields:
- beta_i
- market_value_i
- total_equity_value
output_fields:
- portfolio_beta
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
PORTFOLIO_CORRELATION_GATE_V1:
purpose: '위성 포지션들 간 20D 수익률 Pearson 상관관계를 계산해 동일 방향 클러스터가 포트폴리오 하락 리스크를 증폭시키는지
감지한다. 개별 Beta x 상관관계 조정으로 실질 포트폴리오 Beta(satellite_cluster_beta) 산출.
'
applicable: calcApexExecutionHarness_ 포트폴리오 집계 단계. SAPG_V1 이후 실행.
inputs:
- field: ticker
- field: price.ret20D
- field: beta_proxy
- field: weight_pct
computed:
correlation_matrix: 각 위성 쌍 (i,j) Pearson 상관계수. 데이터 부족 시 ret20D/globalKospiRet20D_
프록시.
satellite_cluster_beta: sum(weight_i * weight_j * beta_i * beta_j * corr_ij)
for all i,j pairs
effective_portfolio_beta: (core_weight * core_beta) + satellite_cluster_beta
gate_status:
CORRELATION_BLOCK:
condition: satellite_cluster_beta > 1.5 AND corr >= 0.70인 위성 쌍이 2쌍 이상
action: 고상관 약한 위성 ADD 금지, REVIEW 위성 우선 정리, 실질 beta 보고서 표기 의무
CORRELATION_WARN:
condition: satellite_cluster_beta > 1.2 OR corr >= 0.70인 위성 쌍이 1쌍
action: 신규 위성 편입 시 저상관 후보 우선
CORRELATION_PASS:
condition: satellite_cluster_beta <= 1.2
action: 정상. M2 독립 적용.
output_fields:
- field: satellite_cluster_beta
- field: effective_portfolio_beta
- field: high_corr_pairs
unit: list [{ticker1,ticker2,corr_coef}]
- field: correlation_gate_status
unit: enum [CORRELATION_PASS,CORRELATION_WARN,CORRELATION_BLOCK]
ground_truth: harness
llm_allowed: cite_only
prohibition:
- LLM이 상관행렬 직접 계산 금지
- 개별 beta 낮아도 satellite_cluster_beta 높으면 분산 됐다 서술 금지
output:
field: satellite_cluster_beta
additional_fields:
- effective_portfolio_beta
- high_corr_pairs
- correlation_gate_status
version: 2026-05-21_PCG_V1
owner: quant_team
lifecycle_state: active
input_fields:
- ticker
- price.ret20D
- beta_proxy
- weight_pct
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
DYNAMIC_HEAT_GATE_V1:
purpose: '국면별 총 위험노출 임계값을 산출해 신규 매수 차단 여부를 결정한다.
'
inputs:
- field: market_regime
unit: enum
- field: total_heat_pct
unit: pct
output:
field: heat_gate_status
input_fields:
- market_regime
- total_heat_pct
expected_outputs:
- heat_gate_status
- heat_gate_threshold_pct
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- heat_gate_status
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
POSITION_SIZE_REGIME_SCALE_V1:
purpose: '국면별 포지션 크기 스케일을 결정론적으로 산출한다.
'
inputs:
- field: market_regime
unit: enum
output:
field: regime_size_scale
input_fields:
- market_regime
expected_outputs:
- regime_size_scale
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- regime_size_scale
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
DRAWDOWN_GUARD_V1:
purpose: '연속 손절/성과 악화 구간에서 신규 매수 수량을 자동 축소하거나 차단한다.
'
inputs:
- field: win_loss_streak_state
unit: enum
- field: win_loss_streak_buy_scale
unit: multiplier
output:
field: drawdown_guard_state
input_fields:
- consecutive_loss_count
- recent_win_loss_state
expected_outputs:
- drawdown_guard_state
- drawdown_buy_scale
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- drawdown_guard_state
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
POSITION_COUNT_LIMIT_V1:
purpose: '동시 보유 종목 수 상한과 초과 여부를 판단한다.
'
inputs:
- field: position_count
unit: integer
- field: market_regime
unit: enum
output:
field: position_count_gate
input_fields:
- position_count
- market_regime
expected_outputs:
- position_count_gate
- position_count_max
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- position_count_gate
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
SINGLE_POSITION_WEIGHT_CAP_V1:
purpose: '단일 종목 비중 상한과 초과 TRIM 필요 여부를 판단한다.
'
inputs:
- field: single_position_weight_json
unit: json
- field: market_regime
unit: enum
output:
field: single_position_weight_gate
input_fields:
- position_weight_pct
- market_regime
expected_outputs:
- single_position_weight_gate
- weight_cap_pct
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- single_position_weight_gate
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
REGIME_TRIM_GUIDANCE_V1:
purpose: '국면별 현금확보용 TRIM 우선순위를 결정한다.
'
inputs:
- field: regime_adjusted_sell_priority_json
unit: json
- field: market_regime
unit: enum
output:
field: regime_trim_guidance
input_fields:
- market_regime
- sector_rank
expected_outputs:
- regime_trim_guidance
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- regime_trim_guidance
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
HEAT_CONCENTRATION_ALERT_V1:
purpose: '단일 종목이 총 Heat의 과도한 비중을 차지하는지 경보를 낸다.
'
inputs:
- field: heat_share_pct
unit: pct
output:
field: heat_concentration_gate
input_fields:
- heat_share_pct
expected_outputs:
- heat_concentration_gate
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- heat_concentration_gate
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
SECTOR_CONCENTRATION_LIMIT_V1:
purpose: '섹터 편중 한도와 신규 BUY 차단 여부를 판단한다.
'
inputs:
- field: sector_concentration_json
unit: json
- field: market_regime
unit: enum
output:
field: sector_concentration_gate
input_fields:
- sector_concentration_pct
- market_regime
expected_outputs:
- sector_concentration_gate
- sector_concentration_limit_pct
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- sector_concentration_gate
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
PORTFOLIO_DRAWDOWN_GATE_V1:
purpose: '포트폴리오 고점 대비 낙폭을 산출해 신규 BUY 차단 여부를 판단한다.
'
inputs:
- field: portfolio_peak_krw
unit: KRW
- field: total_asset_krw
unit: KRW
output:
field: portfolio_drawdown_gate
input_fields:
- portfolio_peak_krw
- total_asset_krw
expected_outputs:
- portfolio_drawdown_gate
- portfolio_drawdown_pct
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- portfolio_drawdown_gate
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
SECTOR_ROTATION_MOMENTUM_V1:
purpose: '섹터 로테이션 모멘텀 상태와 신규 매수 적합성을 판정한다.
'
inputs:
- field: sector
unit: string
- field: momentum_state
unit: enum
output:
field: sector_rotation_momentum_json
input_fields:
- sector
- momentum_state
expected_outputs:
- sector_rotation_momentum_json
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- sector_rotation_momentum_json
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
MARKET_WEIGHT_AWARE_CLUSTER_GATE_V1:
purpose: '시장 반도체 비중을 반영한 동적 클러스터 차단/경고 임계값을 산출한다.
'
inputs:
- field: semiconductor_cluster_json
unit: json
- field: market_regime
unit: enum
output:
field: semiconductor_cluster_gate
input_fields:
- kospi_semi_weight_pct
- combined_pct
- market_regime
expected_outputs:
- cluster_gate
- cap_pct
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- semiconductor_cluster_gate
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
LEADER_POSITION_WEIGHT_CAP_V1:
purpose: '주도주 종목별 차등 비중 상한과 초과 TRIM 필요 여부를 산출한다.
'
inputs:
- field: single_position_weight_json
unit: json
- field: market_regime
unit: enum
output:
field: single_position_weight_gate
input_fields:
- ticker
- position_weight_pct
- market_regime
expected_outputs:
- leader_position_weight_gate
- weight_cap_pct
llm_allowed: cite_only
version: 2026-05-30_PHASE8
owner: quant_team
lifecycle_state: active
output_fields:
- single_position_weight_gate
missing_policy: DATA_MISSING. 계산 결과를 추정하지 않는다.
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation
REGIME_CONDITIONAL_MACRO_FACTOR_V1:
purpose: 거시팩터 종목별 FX 민감도 베타 적용 — 단일팩터 전 종목 균일 지배 차단 (Direction SFP1)
agents_md_ref: 'Direction SFP1: SINGLE_FACTOR_DOMINANCE_CAP_V1'
inputs:
- field: base_macro_score
unit: ratio_0_1
- field: ticker
unit: string
- field: ticker_type
unit: 'enum: export | domestic | neutral'
expression: base_macro_score x fx_sensitivity_beta(ticker_type)
components:
fx_sensitivity_beta:
export: 1.2
domestic: 0.7
neutral: 1.0
note: '수출주(삼성전자·SK하이닉스 등): FX 민감도 20% 가중. 내수주: 30% 축소.'
output:
field: macro_factor_applied
unit: ratio_0_1
gate:
condition: single_factor_max_share_pct > 50
result: SINGLE_FACTOR_DEGENERATE
action: WARN — synthesis_verdict 다양성 확보 실패, 보고서 첫 줄 경고 의무
missing_policy: ticker_type 미확인 시 fx_beta=1.0(neutral) 적용
implementation: tools/build_predictive_alpha_dialectic_engine_v2.py:NF1
calibration_ref: spec/calibration_registry.yaml:NF1 (EXPERT_PRIOR)
version: 2026-06-04_NF1
owner: quant_team
lifecycle_state: active
input_fields:
- base_macro_score
- ticker
- ticker_type
output_fields:
- macro_factor_applied
golden_cases: []
activation_threshold:
min_t20_sample: 30
retirement_condition: performance_degradation