Files
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

712 lines
18 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://local.retirement-portfolio/schemas/output_schema.json",
"title": "Retirement Portfolio LLM Output Schema",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"analysis_date",
"analysis_scope",
"data_basis",
"capture_read_ledger",
"portfolio_decision",
"scores",
"position_sizing",
"risk_gate",
"data_completeness_matrix",
"decision_trace",
"orders",
"prohibited_calculations",
"triggered_rules",
"missing_data",
"invalidation_conditions",
"evidence",
"rule_ids_used",
"rules_used",
"summary"
],
"properties": {
"schema_version": {
"type": "string",
"const": "2026-05-15-F6-compat-output"
},
"analysis_date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"analysis_scope": {
"type": "object",
"additionalProperties": false,
"required": ["scope_type", "tickers", "accounts"],
"properties": {
"scope_type": {
"type": "string",
"enum": ["SINGLE_TICKER", "PORTFOLIO", "WATCHLIST", "REPORT_ONLY"]
},
"tickers": {
"type": "array",
"items": {
"type": "string",
"pattern": "^\\d{6}$"
}
},
"accounts": {
"type": "array",
"items": {
"type": "string",
"enum": ["일반계좌", "ISA", "연금저축"]
}
}
}
},
"data_basis": {
"type": "object",
"additionalProperties": false,
"required": ["as_of", "timezone", "sources"],
"properties": {
"as_of": {
"type": "string",
"minLength": 1
},
"timezone": {
"type": "string",
"const": "Asia/Seoul"
},
"sources": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name", "type", "status"],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"enum": ["USER_INPUT", "JSON", "XLSX", "GOOGLE_SHEETS", "PUBLIC_WEB", "CALCULATED", "MISSING"]
},
"status": {
"type": "string",
"enum": ["OK", "PARTIAL", "DATA_MISSING", "DATA_CONFLICT", "DATA_STALE", "NOT_APPLICABLE"]
},
"note": {
"type": "string"
}
}
}
}
}
},
"capture_read_ledger": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"source",
"account",
"screen_type",
"read_values",
"confidence",
"applied_to_orders",
"read_status"
],
"properties": {
"source": {
"type": "string",
"minLength": 1
},
"account": {
"type": "string",
"enum": ["일반계좌", "ISA", "연금저축", "UNKNOWN"]
},
"screen_type": {
"type": "string",
"enum": ["holdings_screen", "cash_screen", "open_orders_screen", "auto_investment_screen", "account_summary", "unknown"]
},
"read_values": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "integer", "boolean", "null"]
}
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"applied_to_orders": {
"type": "boolean"
},
"read_status": {
"type": "string",
"enum": ["CAPTURE_READ_OK", "CAPTURE_PROVIDED_BUT_NOT_HOLDINGS", "CAPTURE_READ_FAILED", "NOT_PROVIDED"]
},
"next_source_to_check": {
"type": "string"
}
}
}
},
"portfolio_decision": {
"type": "object",
"additionalProperties": false,
"required": ["final_action", "grade", "confidence_score", "rationale"],
"properties": {
"final_action": {
"type": "string",
"enum": ["BUY", "HOLD", "SELL", "TRIM", "ROTATE", "AVOID", "WATCH", "INSUFFICIENT_DATA"]
},
"grade": {
"type": "string",
"enum": ["A", "B", "C", "D", "NONE"]
},
"confidence_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"rationale": {
"type": "string",
"minLength": 1
}
}
},
"scores": {
"type": "object",
"additionalProperties": false,
"required": [
"quality_score",
"valuation_score",
"momentum_score",
"risk_score",
"strategy_score",
"portfolio_fit_score",
"total_score"
],
"properties": {
"quality_score": {
"$ref": "#/$defs/nullable_score"
},
"valuation_score": {
"$ref": "#/$defs/nullable_score"
},
"momentum_score": {
"$ref": "#/$defs/nullable_score"
},
"risk_score": {
"$ref": "#/$defs/nullable_score"
},
"strategy_score": {
"$ref": "#/$defs/nullable_score"
},
"portfolio_fit_score": {
"$ref": "#/$defs/nullable_score"
},
"total_score": {
"$ref": "#/$defs/nullable_score"
},
"score_formula": {
"type": "string"
},
"score_notes": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"position_sizing": {
"type": "object",
"additionalProperties": false,
"required": [
"status",
"recommended_position_size_pct",
"max_allowed_position_size_pct",
"risk_budget",
"atr20",
"calculated_quantity",
"final_quantity",
"reason"
],
"properties": {
"status": {
"type": "string",
"enum": ["CALCULATED", "NO_QUANTITY", "BLOCKED", "INSUFFICIENT_DATA", "NOT_APPLICABLE"]
},
"recommended_position_size_pct": {
"type": ["number", "null"],
"minimum": 0
},
"max_allowed_position_size_pct": {
"type": ["number", "null"],
"minimum": 0
},
"risk_budget": {
"type": ["number", "null"],
"minimum": 0
},
"atr20": {
"type": ["number", "null"],
"minimum": 0
},
"calculated_quantity": {
"type": ["integer", "null"],
"minimum": 0
},
"final_quantity": {
"type": ["integer", "null"],
"minimum": 0
},
"reason": {
"type": "string",
"minLength": 1
}
}
},
"risk_gate": {
"type": "object",
"additionalProperties": false,
"required": ["status", "cash_floor_status", "total_heat_pct", "hard_stop_triggered", "triggered_rules"],
"properties": {
"status": {
"type": "string",
"enum": ["PASS", "CAUTION", "BLOCK"]
},
"cash_floor_status": {
"type": "string",
"enum": ["PASS", "CAUTION", "BLOCK", "UNKNOWN"]
},
"total_heat_pct": {
"type": ["number", "null"],
"minimum": 0
},
"hard_stop_triggered": {
"type": "boolean"
},
"triggered_rules": {
"type": "array",
"items": {
"$ref": "#/$defs/rule_reference"
}
}
}
},
"data_completeness_matrix": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"ticker",
"name",
"price_status",
"flow5d_status",
"flow20d_status",
"atr20_status",
"dart_status",
"missing_fields",
"allowed_action"
],
"properties": {
"ticker": {
"type": "string",
"pattern": "^\\d{6}$"
},
"name": {
"type": "string",
"minLength": 1
},
"price_status": {
"$ref": "#/$defs/data_status"
},
"flow5d_status": {
"$ref": "#/$defs/data_status"
},
"flow20d_status": {
"$ref": "#/$defs/data_status"
},
"atr20_status": {
"$ref": "#/$defs/data_status"
},
"dart_status": {
"$ref": "#/$defs/data_status"
},
"missing_fields": {
"type": "array",
"items": {
"type": "string"
}
},
"next_source_to_check": {
"type": "string"
},
"allowed_action": {
"type": "string",
"enum": ["BUY_ALLOWED", "SELL_ALLOWED", "HOLD_ALLOWED", "WATCH_ONLY", "NO_QUANTITY", "BLOCKED"]
}
}
}
},
"decision_trace": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"state",
"check_id",
"rule_ref",
"inputs_used",
"result",
"selected_action",
"blocked_actions",
"missing_inputs",
"tie_breaker_applied"
],
"properties": {
"state": {
"type": "string",
"minLength": 1
},
"check_id": {
"type": "string",
"minLength": 1
},
"rule_ref": {
"type": "string",
"minLength": 1
},
"inputs_used": {
"type": "array",
"items": {
"type": "string"
}
},
"result": {
"type": "string",
"enum": ["PASS", "FAIL", "BLOCK", "WARNING", "SELECTED", "SKIPPED", "INSUFFICIENT_DATA"]
},
"selected_action": {
"type": ["string", "null"]
},
"blocked_actions": {
"type": "array",
"items": {
"type": "string"
}
},
"missing_inputs": {
"type": "array",
"items": {
"type": "string"
}
},
"tie_breaker_applied": {
"type": ["string", "null"]
}
}
}
},
"orders": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"account",
"ticker",
"name",
"current_holding_quantity",
"average_cost_krw",
"current_price_krw",
"order_type",
"mode",
"limit_price_krw",
"quantity",
"stop_price_krw",
"stop_quantity",
"take_profit_price_krw",
"take_profit_quantity",
"order_amount_krw",
"validation_status",
"rationale"
],
"properties": {
"account": {
"type": "string",
"enum": ["일반계좌", "ISA", "연금저축"]
},
"ticker": {
"type": "string",
"pattern": "^\\d{6}$"
},
"name": {
"type": "string",
"minLength": 1
},
"current_holding_quantity": {
"type": ["integer", "null"],
"minimum": 0
},
"average_cost_krw": {
"type": ["integer", "null"],
"minimum": 0
},
"current_price_krw": {
"type": ["integer", "null"],
"minimum": 0
},
"order_type": {
"type": "string",
"enum": ["BUY", "SELL", "STOP_LOSS", "TAKE_PROFIT", "TRAILING_STOP", "HOLD", "WATCH"]
},
"mode": {
"type": "string",
"enum": ["lead", "lag", "hybrid", "none"]
},
"limit_price_krw": {
"type": ["integer", "null"],
"minimum": 0
},
"quantity": {
"type": ["integer", "null"],
"minimum": 0
},
"stop_price_krw": {
"type": ["integer", "null"],
"minimum": 0
},
"stop_quantity": {
"type": ["integer", "null"],
"minimum": 0
},
"take_profit_price_krw": {
"type": ["integer", "null"],
"minimum": 0
},
"take_profit_quantity": {
"type": ["integer", "null"],
"minimum": 0
},
"order_amount_krw": {
"type": ["integer", "null"],
"minimum": 0
},
"validation_status": {
"type": "string",
"enum": ["PASS", "BLOCKED", "INSUFFICIENT_DATA", "MANUAL_CHECK_REQUIRED"]
},
"rationale": {
"type": "string"
}
},
"allOf": [
{
"if": {
"properties": {
"order_type": {
"const": "BUY"
},
"validation_status": {
"const": "PASS"
}
},
"required": ["order_type", "validation_status"]
},
"then": {
"required": [
"limit_price_krw",
"quantity",
"stop_price_krw",
"stop_quantity",
"take_profit_price_krw",
"take_profit_quantity"
],
"properties": {
"limit_price_krw": {
"type": "integer",
"minimum": 1
},
"quantity": {
"type": "integer",
"minimum": 1
},
"stop_price_krw": {
"type": "integer",
"minimum": 1
},
"stop_quantity": {
"type": "integer",
"minimum": 1
},
"take_profit_price_krw": {
"type": "integer",
"minimum": 1
},
"take_profit_quantity": {
"type": "integer",
"minimum": 1
}
}
}
}
]
}
},
"prohibited_calculations": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["item", "reason", "next_source_to_check"],
"properties": {
"item": {
"type": "string"
},
"reason": {
"type": "string"
},
"next_source_to_check": {
"type": "string"
}
}
}
},
"triggered_rules": {
"type": "array",
"items": {
"$ref": "#/$defs/rule_reference"
}
},
"missing_data": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["field", "status", "next_source_to_check"],
"properties": {
"field": {
"type": "string",
"minLength": 1
},
"status": {
"$ref": "#/$defs/data_status"
},
"next_source_to_check": {
"type": "string",
"minLength": 1
}
}
}
},
"invalidation_conditions": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["condition", "action"],
"properties": {
"condition": {
"type": "string",
"minLength": 1
},
"action": {
"type": "string",
"minLength": 1
},
"rule_ref": {
"type": "string"
}
}
}
},
"evidence": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["field", "value", "source", "as_of"],
"properties": {
"field": {
"type": "string",
"minLength": 1
},
"value": {
"type": ["string", "number", "integer", "boolean", "null"]
},
"source": {
"type": "string",
"minLength": 1
},
"as_of": {
"type": "string",
"minLength": 1
},
"data_tag": {
"type": "string",
"enum": ["사용자입력", "웹확인", "판독값", "계산값", "데이터누락"]
}
}
}
},
"rule_ids_used": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"rules_used": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/rule_reference"
}
},
"summary": {
"type": "string",
"minLength": 1
}
},
"$defs": {
"nullable_score": {
"type": ["number", "null"],
"minimum": 0,
"maximum": 100
},
"data_status": {
"type": "string",
"enum": ["OK", "PARTIAL", "DATA_MISSING", "DATA_CONFLICT", "DATA_STALE", "NOT_APPLICABLE"]
},
"rule_reference": {
"type": "object",
"additionalProperties": false,
"required": ["file", "path", "result"],
"properties": {
"file": {
"type": "string",
"minLength": 1
},
"path": {
"type": "string",
"minLength": 1
},
"result": {
"type": "string",
"enum": ["PASS", "FAIL", "WARNING", "BLOCK", "USED"]
},
"explanation": {
"type": "string"
}
}
}
}
}