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>
This commit is contained in:
2026-06-13 13:20:14 +09:00
commit ee3e799de1
1474 changed files with 176087 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
case_id: EX_AVOID_001
type: AVOID
input_summary:
flow_ok: "N"
atr20_status: "DATA_MISSING"
total_score: 88
expected_output:
investment_action: "AVOID"
grade: "D"
reason: "Risk/data hard filters override high score."
triggered_rules:
- "Flow_OK=N이면 신규매수 금지"
- "ATR20 미확인 시 정수 매수수량 산출 금지"
+18
View File
@@ -0,0 +1,18 @@
case_id: EX_BUY_001
type: BUY
input_summary:
data_status: "all_required_fields_ok"
hard_filters: "pass"
flow_rows: 20
atr20_status: "OK"
total_heat_pct: 5.5
expected_edge: 2.1
expected_output:
investment_action: "BUY"
grade: "A"
required_order_fields: ["account", "ticker", "limit_price", "quantity", "stop_price", "stop_quantity", "take_profit_price", "take_profit_quantity"]
rule_ids_used:
- "risk_policy.master_prohibitions"
- "data_contract.data_completeness_gate"
- "position_sizing.volatility_targeting"
- "output_schema.buy_proposal_template.validation"
+4
View File
@@ -0,0 +1,4 @@
{"case_id":"EX_BUY_001","type":"BUY","expected_action":"BUY","required_rules":["HF001_DATA_MATRIX_REQUIRED","HF002_ATR20_REQUIRED_FOR_QUANTITY","HF006_BUY_ORDER_SET_REQUIRED"]}
{"case_id":"EX_SELL_001","type":"SELL","expected_action":"SELL","required_rules":["HF003_HOLDINGS_REQUIRED_FOR_SELL_QTY","spec/exit/stop_loss.yaml:stop_loss.relative_weakness_exit"]}
{"case_id":"EX_REJECT_001","type":"REJECT","expected_action":"AVOID","required_rules":["HF005_TOTAL_HEAT_HARD_BLOCK"]}
{"case_id":"EX_INSUFFICIENT_DATA_001","type":"INSUFFICIENT_DATA","expected_action":"INSUFFICIENT_DATA","required_rules":["HF001_DATA_MATRIX_REQUIRED","HF002_ATR20_REQUIRED_FOR_QUANTITY"]}
+143
View File
@@ -0,0 +1,143 @@
{
"schema_version": "2026-05-15-F6-compat-output",
"analysis_date": "2026-05-15",
"analysis_scope": {
"scope_type": "SINGLE_TICKER",
"tickers": ["005930"],
"accounts": ["일반계좌"]
},
"data_basis": {
"as_of": "2026-05-15 16:30 KST",
"timezone": "Asia/Seoul",
"sources": [
{
"name": "sample",
"type": "CALCULATED",
"status": "OK",
"note": "schema validation sample"
}
]
},
"capture_read_ledger": [
{
"source": "sample",
"account": "일반계좌",
"screen_type": "unknown",
"read_values": {},
"confidence": 0,
"applied_to_orders": false,
"read_status": "NOT_PROVIDED",
"next_source_to_check": "HTS 보유종목/현금 화면 캡처"
}
],
"portfolio_decision": {
"final_action": "WATCH",
"grade": "C",
"confidence_score": 50,
"rationale": "검증용 샘플. 실제 투자 판단 아님."
},
"scores": {
"quality_score": null,
"valuation_score": null,
"momentum_score": null,
"risk_score": 50,
"strategy_score": null,
"portfolio_fit_score": null,
"total_score": null,
"score_formula": "not_calculated",
"score_notes": ["검증 샘플이므로 점수 산출 생략"]
},
"position_sizing": {
"status": "NOT_APPLICABLE",
"recommended_position_size_pct": null,
"max_allowed_position_size_pct": null,
"risk_budget": null,
"atr20": null,
"calculated_quantity": null,
"final_quantity": null,
"reason": "WATCH 샘플로 수량 산출 대상 아님"
},
"risk_gate": {
"status": "CAUTION",
"cash_floor_status": "UNKNOWN",
"total_heat_pct": null,
"hard_stop_triggered": false,
"triggered_rules": []
},
"data_completeness_matrix": [
{
"ticker": "005930",
"name": "삼성전자",
"price_status": "OK",
"flow5d_status": "PARTIAL",
"flow20d_status": "DATA_MISSING",
"atr20_status": "DATA_MISSING",
"dart_status": "NOT_APPLICABLE",
"missing_fields": ["ATR20", "Flow20D"],
"next_source_to_check": "KRX/Naver/Yahoo 21거래일 OHLC 및 수급",
"allowed_action": "WATCH_ONLY"
}
],
"decision_trace": [
{
"state": "DATA_COMPLETENESS_CHECK",
"check_id": "DCC_SAMPLE_001",
"rule_ref": "spec/09_decision_flow.yaml:decision_flow.states.DATA_COMPLETENESS_CHECK",
"inputs_used": ["ticker", "ATR20", "Flow20D"],
"result": "INSUFFICIENT_DATA",
"selected_action": "WATCH",
"blocked_actions": ["BUY"],
"missing_inputs": ["ATR20", "Flow20D"],
"tie_breaker_applied": "5: 보수적 행동"
}
],
"orders": [],
"prohibited_calculations": [
{
"item": "buy_quantity",
"reason": "ATR20 DATA_MISSING",
"next_source_to_check": "21거래일 OHLC"
}
],
"triggered_rules": [
{
"file": "spec/00_execution_contract.yaml",
"path": "master_prohibitions.P2_no_atr_extrapolation",
"result": "BLOCK",
"explanation": "ATR20 미확인으로 정수 매수수량 산출 금지"
}
],
"missing_data": [
{
"field": "ATR20",
"status": "DATA_MISSING",
"next_source_to_check": "Yahoo/Naver OHLC"
}
],
"invalidation_conditions": [
{
"condition": "ATR20 remains DATA_MISSING",
"action": "NO_BUY_QUANTITY",
"rule_ref": "spec/00_execution_contract.yaml:master_prohibitions.P2_no_atr_extrapolation"
}
],
"evidence": [
{
"field": "ticker",
"value": "005930",
"source": "sample",
"as_of": "2026-05-15",
"data_tag": "계산값"
}
],
"rule_ids_used": ["P2_no_atr_extrapolation"],
"rules_used": [
{
"file": "spec/00_execution_contract.yaml",
"path": "master_prohibitions.P2_no_atr_extrapolation",
"result": "USED",
"explanation": "정수 매수수량 산출 금지 규칙 확인"
}
],
"summary": "검증용 WATCH 샘플. 실제 투자 판단이 아니다."
}
+11
View File
@@ -0,0 +1,11 @@
case_id: EX_HOLD_001
type: HOLD
input_summary:
current_holding_confirmed: true
grade: "B"
current_weight_within_band: true
expected_edge: 1.8
expected_output:
investment_action: "HOLD"
report_section: "current_holdings_analysis_report_template"
reason: "보유수량·평단·현재가 확인 후 유지 조건 충족."
+15
View File
@@ -0,0 +1,15 @@
case_id: EX_INSUFFICIENT_DATA_001
type: INSUFFICIENT_DATA
input_summary:
holdings_screen: "NOT_PROVIDED"
cash_available: "DATA_MISSING"
atr20_status: "DATA_MISSING"
expected_output:
investment_action: "INSUFFICIENT_DATA"
prohibited_outputs:
- "매도수량 숫자"
- "매수수량 숫자"
- "A등급"
next_source_to_check:
- "계좌 잔고 원장"
- "21거래일 OHLC for ATR20"
+14
View File
@@ -0,0 +1,14 @@
case_id: EX_REJECT_001
type: REJECT
input_summary:
strategy_score: 91
total_heat_pct: 10.4
cash_floor_status: "BLOCK"
atr20_status: "OK"
expected_output:
investment_action: "AVOID"
grade: "D"
reason: "Risk hard block overrides high strategy score."
triggered_rules:
- "HF005_TOTAL_HEAT_HARD_BLOCK"
- "spec/risk/aggregate_risk.yaml:risk_control.aggregate_risk_cap.threshold.hard_block"
+21
View File
@@ -0,0 +1,21 @@
case_id: EX_SELL_001
type: SELL
input_summary:
confirmed_holding_quantity: 120
price_status: "OK"
flow20d_status: "OK"
trend_status: "20일선 이탈"
risk_trigger: "relative_weakness_exit"
expected_output:
investment_action: "SELL"
required_fields:
- "account"
- "ticker"
- "limit_price_krw"
- "quantity"
- "rationale"
prohibited_if_missing:
- "confirmed_holding_quantity"
rule_ids_used:
- "HF003_HOLDINGS_REQUIRED_FOR_SELL_QTY"
- "spec/exit/stop_loss.yaml:stop_loss.relative_weakness_exit"