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:
@@ -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 미확인 시 정수 매수수량 산출 금지"
|
||||
@@ -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"
|
||||
@@ -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"]}
|
||||
@@ -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 샘플. 실제 투자 판단이 아니다."
|
||||
}
|
||||
@@ -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: "보유수량·평단·현재가 확인 후 유지 조건 충족."
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user