{
  "$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"
        }
      }
    }
  }
}
