ee3e799de1
주요 변경: - 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>
712 lines
18 KiB
JSON
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"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|