{
  "openapi": "3.0.3",
  "info": {
    "title": "CZ DAM Price Forecast API",
    "description": "Public API for accessing Czech Day-Ahead Market electricity price forecasts.\n\n## Overview\n\nThis API provides electricity price forecasts for the Czech Day-Ahead Market (DAM). Forecasts are generated daily after the D-1 gate using machine learning models trained on historical price data, weather forecasts, and renewable generation data.\n\n## Two model families\n\n| Family | Endpoint prefix | Model | Output | Uncertainty band | Calibration |\n|--------|-----------------|-------|--------|------------------|-------------|\n| **v1** | `/api/v1` | `cz_dam_ensemble_v11` quantile ensemble | calibrated point + P10/P90 | yes | optional hour/weekend bias |\n| **v4** | `/api/v4` | `cz_epex_v4` \"EPEX-CZ Enhanced\" LightGBM | raw point | no | none (served raw) |\n\nPick **v1** if you want a calibrated forecast with an uncertainty band for production use. Pick **v4** if you want the raw EPEX-CZ-enhanced point forecast (57 features, retrained daily on a rolling 120-day window) — it is intentionally uncalibrated so its head-to-head accuracy stays honest.\n\n## Resolutions\n\nEach family exposes two resolutions with identical query parameters:\n\n- `GET /{family}/forecast` returns **15-minute** resolution (96 quarter-hour points, `PT15M`).\n- `GET /{family}/forecast/hourly` returns **hourly** resolution (24 points, `PT60M`).\n\n## Calibration (v1 only)\n\nWhen calibration is active on the server (`CALIBRATION_ACTIVE=1`), an hour-of-day + weekday/weekend bias correction is applied to v1 so the published values match the internal dashboard. The `meta.calibration` block reports whether it was applied. v4 is always served raw (`calibration.applied = false`).\n\n## Authentication\n\nThis API is currently public and does not require authentication. Rate limiting may apply.\n\n## Forecast Schedule\n\n- **v1 generation**: Daily at 10:00 CET for D+1\n- **v4 generation**: Daily after the D-1 12:00 CET gate (available ~13:00 CET)\n- **Actuals update**: After DAM auction results (~12:42 CET)",
    "version": "1.4.0",
    "contact": {
      "name": "API Support",
      "email": "support@example.com"
    }
  },
  "servers": [
    {
      "url": "/api",
      "description": "Production API"
    }
  ],
  "tags": [
    {
      "name": "v1 — Ensemble (v11)",
      "description": "Calibrated quantile ensemble with a P10/P90 uncertainty band. Recommended for production."
    },
    {
      "name": "v4 — EPEX-CZ Enhanced",
      "description": "Raw (uncalibrated) LightGBM point forecast, 57 CZ-enhanced features, retrained daily on a rolling 120-day window. No uncertainty band."
    },
    {
      "name": "v5 — Ensemble (v1 x v4)",
      "description": "Computed walk-forward, accuracy-weighted (inverse-MAE) blend of the v1 ensemble median and the v4 point forecast. No trained model and nothing stored. Each point exposes both component legs and the per-day blend weight. No uncertainty band."
    },
    {
      "name": "v6 — Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)",
      "description": "Walk-forward, accuracy-weighted (inverse-MAE) blend of four legs: the v1 ensemble median, the v4 point forecast, a global CatBoost GBM and a per-15-min-slot LEAR (LASSO) model. The CatBoost and LEAR legs are trained walk-forward and stored; v1/v4 are reused as forecast. Each point exposes all four component legs and the four per-day blend weights. No uncertainty band."
    }
  ],
  "paths": {
    "/v1/forecast": {
      "get": {
        "summary": "v1: Get 15-minute price forecast",
        "description": "Retrieves quarter-hourly (PT15M) electricity price forecasts for a specific delivery date, including the P10/P90 band. Returns 96 points for the latest completed forecast. Values are calibrated when calibration is active on the server.",
        "operationId": "getForecast",
        "tags": ["v1 — Ensemble (v11)"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with 15-minute forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/QuarterForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "1.1",
                    "generated_at": "2026-06-02T08:00:00.000Z",
                    "model": { "name": "cz_dam_ensemble_v11", "version": "1.0" },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT15M",
                    "points_available": 96,
                    "points_with_actuals": 0,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": {
                      "active": true,
                      "applied": true,
                      "method": "hour_weekend_bias",
                      "train_days": 28
                    }
                  },
                  "summary": {
                    "min_price": 76.23,
                    "max_price": 192.04,
                    "mean_price": 118.07,
                    "mae_eur": null,
                    "rmse_eur": null,
                    "price_bands": { "negative": 0, "low": 0, "normal": 30, "high": 66 }
                  },
                  "quarter_hourly_forecasts": [
                    {
                      "hour": 0,
                      "quarter": 0,
                      "interval": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 135.45,
                      "forecast_p10_eur": 112.13,
                      "forecast_p90_eur": 157.17,
                      "actual_price_eur": null,
                      "error_eur": null,
                      "price_band": "high",
                      "regime_probability": null
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,quarter,timestamp_utc,forecast_price_eur,forecast_p10_eur,forecast_p90_eur,actual_price_eur,error_eur,price_band\n0,0,2026-06-02T22:00:00.000Z,135.45,112.13,157.17,,,high"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v1/forecast/hourly": {
      "get": {
        "summary": "v1: Get hourly price forecast",
        "description": "Retrieves hourly (PT60M) electricity price forecasts for a specific delivery date, including the P10/P90 band. Returns 24 points for the latest completed forecast. Values are calibrated when calibration is active on the server.",
        "operationId": "getForecastHourly",
        "tags": ["v1 — Ensemble (v11)"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with hourly forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HourlyForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "1.1",
                    "generated_at": "2026-06-02T08:00:00.000Z",
                    "model": { "name": "cz_dam_ensemble_v11", "version": "1.0" },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT60M",
                    "points_available": 24,
                    "points_with_actuals": 0,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": {
                      "active": true,
                      "applied": true,
                      "method": "hour_weekend_bias",
                      "train_days": 28
                    }
                  },
                  "summary": {
                    "min_price": 89.5,
                    "max_price": 169.1,
                    "mean_price": 119.47,
                    "mae_eur": null,
                    "rmse_eur": null,
                    "price_bands": { "negative": 0, "low": 0, "normal": 9, "high": 15 }
                  },
                  "hourly_forecasts": [
                    {
                      "hour": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 127.15,
                      "forecast_p10_eur": 106.06,
                      "forecast_p90_eur": 151.56,
                      "actual_price_eur": null,
                      "error_eur": null,
                      "price_band": "high",
                      "regime_probability": null
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,timestamp_utc,forecast_price_eur,forecast_p10_eur,forecast_p90_eur,actual_price_eur,error_eur,price_band\n0,2026-06-02T22:00:00.000Z,127.15,106.06,151.56,,,high"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v4/forecast": {
      "get": {
        "summary": "v4: Get 15-minute price forecast",
        "description": "Retrieves quarter-hourly (PT15M) forecasts from the v4 \"EPEX-CZ Enhanced\" point model for a delivery date. Returns 96 points. v4 is a single-value point forecast (no P10/P90 band) and is served RAW (uncalibrated) — `meta.calibration.applied` is always `false`.",
        "operationId": "getForecastV4",
        "tags": ["v4 — EPEX-CZ Enhanced"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with 15-minute v4 forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/V4QuarterForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "4.0",
                    "generated_at": "2026-06-02T10:02:12.364Z",
                    "model": {
                      "name": "EPEX-CZ Enhanced",
                      "version": "cz_epex_v4",
                      "type": "point",
                      "n_features": 57,
                      "train_window_days": 120
                    },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT15M",
                    "points_available": 96,
                    "points_with_actuals": 0,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": { "active": false, "applied": false, "note": "v4 served raw (uncalibrated)" }
                  },
                  "summary": { "min_price": 67.29, "max_price": 190.66, "mean_price": 123.2, "mae_eur": null, "rmse_eur": null },
                  "quarter_hourly_forecasts": [
                    {
                      "hour": 0,
                      "quarter": 0,
                      "interval": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 146.42,
                      "actual_price_eur": null,
                      "error_eur": null
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,quarter,timestamp_utc,forecast_price_eur,actual_price_eur,error_eur\n0,0,2026-06-02T22:00:00.000Z,146.42,,"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v4/forecast/hourly": {
      "get": {
        "summary": "v4: Get hourly price forecast",
        "description": "Retrieves hourly (PT60M) forecasts from the v4 \"EPEX-CZ Enhanced\" point model for a delivery date — the hourly means of the 15-min v4 forecast. Returns 24 points. v4 is a point forecast (no P10/P90 band) and is served RAW (uncalibrated).",
        "operationId": "getForecastV4Hourly",
        "tags": ["v4 — EPEX-CZ Enhanced"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with hourly v4 forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/V4HourlyForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "4.0",
                    "generated_at": "2026-06-02T10:02:29.089Z",
                    "model": {
                      "name": "EPEX-CZ Enhanced",
                      "version": "cz_epex_v4",
                      "type": "point",
                      "n_features": 57,
                      "train_window_days": 120
                    },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT60M",
                    "points_available": 24,
                    "points_with_actuals": 0,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": { "active": false, "applied": false, "note": "v4 served raw (uncalibrated)" }
                  },
                  "summary": { "min_price": 101.31, "max_price": 171.6, "mean_price": 127.49, "mae_eur": null, "rmse_eur": null },
                  "hourly_forecasts": [
                    {
                      "hour": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 112.83,
                      "actual_price_eur": null,
                      "error_eur": null
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,timestamp_utc,forecast_price_eur,actual_price_eur,error_eur\n0,2026-06-02T22:00:00.000Z,112.83,,"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v5/forecast": {
      "get": {
        "summary": "v5: Get 15-minute price forecast",
        "description": "Retrieves quarter-hourly (PT15M) forecasts from the v5 \"Ensemble (v1 x v4)\" model for a delivery date. Returns 96 points. v5 is a COMPUTED walk-forward, accuracy-weighted (inverse-MAE) blend of the v1 ensemble median and the v4 point forecast — no trained model and nothing stored. Each point also exposes the two component legs (`forecast_v1_eur`, `forecast_v4_eur`) and the per-day blend `weight_v1`. No P10/P90 band.",
        "operationId": "getForecastV5",
        "tags": ["v5 — Ensemble (v1 x v4)"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with 15-minute v5 forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/V5QuarterForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "5.0",
                    "generated_at": "2026-06-02T12:24:46.741Z",
                    "model": {
                      "name": "Ensemble (v1 x v4)",
                      "version": "cz_ensemble_v5",
                      "type": "ensemble",
                      "method": "walk-forward accuracy-weighted (inverse-MAE) blend of v1 and v4",
                      "components": ["v1 (cz_dam_ensemble_v11 median)", "v4 (cz_epex_v4 point)"]
                    },
                    "blend": { "weight_v1": 0.5253, "weight_v4": 0.4747 },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT15M",
                    "points_available": 96,
                    "points_with_actuals": 96,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": { "active": false, "applied": false, "note": "v5 inherits each leg as stored; the blend weight is the only post-processing" }
                  },
                  "summary": { "min_price": 93.99, "max_price": 175.26, "mean_price": 123.28, "mae_eur": 11.06, "rmse_eur": 14.51 },
                  "quarter_hourly_forecasts": [
                    {
                      "hour": 0,
                      "quarter": 0,
                      "interval": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 124.73,
                      "forecast_v1_eur": 125.21,
                      "forecast_v4_eur": 124.2,
                      "weight_v1": 0.5253,
                      "actual_price_eur": 120.11,
                      "error_eur": 4.62
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,quarter,timestamp_utc,forecast_price_eur,forecast_v1_eur,forecast_v4_eur,weight_v1,actual_price_eur,error_eur\n0,0,2026-06-02T22:00:00.000Z,124.73,125.21,124.20,0.5253,120.11,4.62"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/v5/forecast/hourly": {
      "get": {
        "summary": "v5: Get hourly price forecast",
        "description": "Retrieves hourly (PT60M) forecasts from the v5 \"Ensemble (v1 x v4)\" model for a delivery date — the hourly means of the 15-min v5 blend. Returns 24 points. Each point also exposes the two component legs and the per-day blend weight. No P10/P90 band.",
        "operationId": "getForecastV5Hourly",
        "tags": ["v5 — Ensemble (v1 x v4)"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with hourly v5 forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/V5HourlyForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "5.0",
                    "generated_at": "2026-06-02T12:24:47.518Z",
                    "model": {
                      "name": "Ensemble (v1 x v4)",
                      "version": "cz_ensemble_v5",
                      "type": "ensemble",
                      "method": "walk-forward accuracy-weighted (inverse-MAE) blend of v1 and v4",
                      "components": ["v1 (cz_dam_ensemble_v11 median)", "v4 (cz_epex_v4 point)"]
                    },
                    "blend": { "weight_v1": 0.5253, "weight_v4": 0.4747 },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT60M",
                    "points_available": 24,
                    "points_with_actuals": 24,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": { "active": false, "applied": false, "note": "v5 inherits each leg as stored; the blend weight is the only post-processing" }
                  },
                  "summary": { "min_price": 96.04, "max_price": 170.29, "mean_price": 123.28, "mae_eur": 10.37, "rmse_eur": 13.74 },
                  "hourly_forecasts": [
                    {
                      "hour": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 119.33,
                      "forecast_v1_eur": 125.21,
                      "forecast_v4_eur": 112.81,
                      "weight_v1": 0.5253,
                      "actual_price_eur": null,
                      "error_eur": null
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,timestamp_utc,forecast_price_eur,forecast_v1_eur,forecast_v4_eur,weight_v1,actual_price_eur,error_eur\n0,2026-06-02T22:00:00.000Z,119.33,125.21,112.81,0.5253,,"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/v6/forecast": {
      "get": {
        "summary": "v6: Get 15-minute price forecast",
        "description": "Retrieves quarter-hourly (PT15M) forecasts from the v6 \"Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)\" model for a delivery date. Returns 96 points. v6 is a walk-forward, accuracy-weighted (inverse-MAE) blend of four legs: the v1 ensemble median, the v4 point forecast, a global CatBoost GBM and a per-15-min-slot LEAR (LASSO) model. Each point also exposes the four component legs (`forecast_v1_eur`, `forecast_v4_eur`, `forecast_cat_eur`, `forecast_lear_eur`) and the four per-day blend weights. No P10/P90 band. A v6 forecast only exists for delivery days where all four legs have been generated.",
        "operationId": "getForecastV6",
        "tags": ["v6 — Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with 15-minute v6 forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/V6QuarterForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "6.0",
                    "generated_at": "2026-06-02T12:24:46.741Z",
                    "model": {
                      "name": "Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)",
                      "version": "cz_ensemble_v6",
                      "type": "ensemble",
                      "method": "walk-forward accuracy-weighted (inverse-MAE) blend of v1, v4, CatBoost and LEAR",
                      "components": ["v1 (cz_dam_ensemble_v11 median)", "v4 (cz_epex_v4 point)", "CatBoost (global GBM)", "LEAR (per-15-min-slot LASSO)"]
                    },
                    "blend": { "weight_v1": 0.2877, "weight_v4": 0.2581, "weight_cat": 0.2666, "weight_lear": 0.1875 },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT15M",
                    "points_available": 96,
                    "points_with_actuals": 96,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": { "active": false, "applied": false, "note": "v6 inherits each leg as stored; the four blend weights are the only post-processing" }
                  },
                  "summary": { "min_price": 95.2, "max_price": 172.4, "mean_price": 123.9, "mae_eur": 12.73, "rmse_eur": 16.22 },
                  "quarter_hourly_forecasts": [
                    {
                      "hour": 0,
                      "quarter": 0,
                      "interval": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 123.45,
                      "forecast_v1_eur": 125.21,
                      "forecast_v4_eur": 124.2,
                      "forecast_cat_eur": 121.8,
                      "forecast_lear_eur": 119.6,
                      "weight_v1": 0.2877,
                      "weight_v4": 0.2581,
                      "weight_cat": 0.2666,
                      "weight_lear": 0.1875,
                      "actual_price_eur": 120.11,
                      "error_eur": 3.34
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,quarter,timestamp_utc,forecast_price_eur,forecast_v1_eur,forecast_v4_eur,forecast_cat_eur,forecast_lear_eur,weight_v1,weight_v4,weight_cat,weight_lear,actual_price_eur,error_eur\n0,0,2026-06-02T22:00:00.000Z,123.45,125.21,124.20,121.80,119.60,0.2877,0.2581,0.2666,0.1875,120.11,3.34"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/v6/forecast/hourly": {
      "get": {
        "summary": "v6: Get hourly price forecast",
        "description": "Retrieves hourly (PT60M) forecasts from the v6 \"Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)\" model for a delivery date — the hourly means of the 15-min v6 blend. Returns 24 points. Each point also exposes the four component legs and the four per-day blend weights. No P10/P90 band.",
        "operationId": "getForecastV6Hourly",
        "tags": ["v6 — Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)"],
        "parameters": [
          { "$ref": "#/components/parameters/DateParam" },
          { "$ref": "#/components/parameters/FormatParam" }
        ],
        "responses": {
          "200": {
            "description": "Successful response with hourly v6 forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/V6HourlyForecastResponse" },
                "example": {
                  "meta": {
                    "api_version": "6.0",
                    "generated_at": "2026-06-02T12:24:47.518Z",
                    "model": {
                      "name": "Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)",
                      "version": "cz_ensemble_v6",
                      "type": "ensemble",
                      "method": "walk-forward accuracy-weighted (inverse-MAE) blend of v1, v4, CatBoost and LEAR",
                      "components": ["v1 (cz_dam_ensemble_v11 median)", "v4 (cz_epex_v4 point)", "CatBoost (global GBM)", "LEAR (per-15-min-slot LASSO)"]
                    },
                    "blend": { "weight_v1": 0.2877, "weight_v4": 0.2581, "weight_cat": 0.2666, "weight_lear": 0.1875 },
                    "forecast_date": "2026-06-03",
                    "status": "completed",
                    "resolution": "PT60M",
                    "points_available": 24,
                    "points_with_actuals": 24,
                    "currency": "EUR",
                    "unit": "MWh",
                    "timezone": "UTC",
                    "calibration": { "active": false, "applied": false, "note": "v6 inherits each leg as stored; the four blend weights are the only post-processing" }
                  },
                  "summary": { "min_price": 97.1, "max_price": 168.0, "mean_price": 123.9, "mae_eur": 12.1, "rmse_eur": 15.4 },
                  "hourly_forecasts": [
                    {
                      "hour": 0,
                      "timestamp_utc": "2026-06-02T22:00:00.000Z",
                      "forecast_price_eur": 121.0,
                      "forecast_v1_eur": 125.21,
                      "forecast_v4_eur": 112.81,
                      "forecast_cat_eur": 121.8,
                      "forecast_lear_eur": 119.6,
                      "weight_v1": 0.2877,
                      "weight_v4": 0.2581,
                      "weight_cat": 0.2666,
                      "weight_lear": 0.1875,
                      "actual_price_eur": null,
                      "error_eur": null
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": { "type": "string" },
                "example": "hour,timestamp_utc,forecast_price_eur,forecast_v1_eur,forecast_v4_eur,forecast_cat_eur,forecast_lear_eur,weight_v1,weight_v4,weight_cat,weight_lear,actual_price_eur,error_eur\n0,2026-06-02T22:00:00.000Z,121.00,125.21,112.81,121.80,119.60,0.2877,0.2581,0.2666,0.1875,,"
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "425": { "$ref": "#/components/responses/TooEarly" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "DateParam": {
        "name": "date",
        "in": "query",
        "description": "Forecast delivery date in YYYY-MM-DD format. Defaults to tomorrow if not specified.",
        "required": false,
        "schema": { "type": "string", "format": "date", "example": "2026-06-03" }
      },
      "FormatParam": {
        "name": "format",
        "in": "query",
        "description": "Response format. Use 'csv' for a downloadable CSV file.",
        "required": false,
        "schema": { "type": "string", "enum": ["json", "csv"], "default": "json" }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request parameters",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "examples": {
              "invalid_date_format": {
                "summary": "Invalid date format",
                "value": {
                  "error": "invalid_request",
                  "code": "INVALID_DATE_FORMAT",
                  "message": "Date must be in YYYY-MM-DD format",
                  "example": "2026-06-03",
                  "provided": "03-06-2026"
                }
              },
              "date_too_far": {
                "summary": "Date too far in future",
                "value": {
                  "error": "forecast_not_available",
                  "code": "DATE_TOO_FAR_AHEAD",
                  "message": "Forecasts are only available for today and tomorrow.",
                  "requested_date": "2026-06-15",
                  "max_available_date": "2026-06-03"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Forecast not found for the requested date",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "forecast_not_found",
              "code": "NO_FORECAST_DATA",
              "message": "No forecast available for 2026-04-01. This date may be too old.",
              "requested_date": "2026-04-01",
              "hint": "Forecasts are typically available for the past 30 days and tomorrow."
            }
          }
        }
      },
      "TooEarly": {
        "description": "Forecast not ready yet (Too Early)",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "examples": {
              "pending": {
                "summary": "Forecast pending (before 10am)",
                "value": {
                  "error": "forecast_not_ready",
                  "code": "FORECAST_PENDING",
                  "message": "Forecast for 2026-06-03 is not ready yet. Forecasts are generated daily at 10:00 CET.",
                  "requested_date": "2026-06-03",
                  "expected_availability": "10:00 CET",
                  "retry_after_seconds": 3600
                }
              },
              "in_progress": {
                "summary": "Forecast generation in progress",
                "value": {
                  "error": "forecast_in_progress",
                  "code": "GENERATION_IN_PROGRESS",
                  "message": "Forecast for 2026-06-03 is currently being generated. Please retry in a few minutes.",
                  "requested_date": "2026-06-03",
                  "status": "running",
                  "retry_after_seconds": 120
                }
              }
            }
          }
        }
      },
      "ServerError": {
        "description": "Internal server error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "internal_error",
              "code": "UNEXPECTED_ERROR",
              "message": "An unexpected error occurred while fetching the forecast."
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Service unavailable",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "examples": {
              "incomplete": {
                "summary": "Forecast data incomplete",
                "value": {
                  "error": "forecast_incomplete",
                  "code": "NO_QUARTER_HOURLY_DATA",
                  "message": "Forecast run exists but 15-minute data is missing for 2026-06-03.",
                  "requested_date": "2026-06-03"
                }
              },
              "generation_failed": {
                "summary": "Forecast generation failed",
                "value": {
                  "error": "forecast_generation_failed",
                  "code": "GENERATION_ERROR",
                  "message": "Forecast generation for 2026-06-03 failed. Our team has been notified.",
                  "requested_date": "2026-06-03",
                  "failure_reason": "Model inference error"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "QuarterForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/Meta" },
          "summary": { "$ref": "#/components/schemas/Summary" },
          "quarter_hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ForecastPoint" }
          }
        }
      },
      "HourlyForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/Meta" },
          "summary": { "$ref": "#/components/schemas/Summary" },
          "hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ForecastPoint" }
          }
        }
      },
      "V4QuarterForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/V4Meta" },
          "summary": { "$ref": "#/components/schemas/V4Summary" },
          "quarter_hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/V4ForecastPoint" }
          }
        }
      },
      "V4HourlyForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/V4Meta" },
          "summary": { "$ref": "#/components/schemas/V4Summary" },
          "hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/V4ForecastPoint" }
          }
        }
      },
      "V4Meta": {
        "type": "object",
        "description": "Metadata for v4 responses. Unlike v1, the model block reports point-model details and calibration is always inactive.",
        "properties": {
          "api_version": { "type": "string", "example": "4.0" },
          "generated_at": { "type": "string", "format": "date-time", "description": "Timestamp when the v4 run was generated" },
          "model": {
            "type": "object",
            "properties": {
              "name": { "type": "string", "example": "EPEX-CZ Enhanced" },
              "version": { "type": "string", "example": "cz_epex_v4" },
              "type": { "type": "string", "enum": ["point"], "description": "v4 is a point forecast (no quantile band)" },
              "n_features": { "type": "integer", "description": "Number of features used by the model", "example": 57 },
              "train_window_days": { "type": "integer", "description": "Rolling training window length in days", "example": 120 }
            }
          },
          "forecast_date": { "type": "string", "format": "date" },
          "status": { "type": "string", "enum": ["pending", "completed", "failed"] },
          "resolution": { "type": "string", "enum": ["PT15M", "PT60M"] },
          "points_available": { "type": "integer", "description": "96 for PT15M, 24 for PT60M" },
          "points_with_actuals": { "type": "integer" },
          "currency": { "type": "string", "example": "EUR" },
          "unit": { "type": "string", "example": "MWh" },
          "timezone": { "type": "string", "example": "UTC" },
          "calibration": { "$ref": "#/components/schemas/V4Calibration" }
        }
      },
      "V4Calibration": {
        "type": "object",
        "description": "v4 is always served raw, so calibration is never applied.",
        "properties": {
          "active": { "type": "boolean", "enum": [false] },
          "applied": { "type": "boolean", "enum": [false] },
          "note": { "type": "string", "example": "v4 served raw (uncalibrated)" }
        }
      },
      "V4Summary": {
        "type": "object",
        "description": "Statistical summary for v4. No price_bands block (point model).",
        "properties": {
          "min_price": { "type": "number", "nullable": true, "description": "Minimum forecasted price (EUR/MWh)" },
          "max_price": { "type": "number", "nullable": true, "description": "Maximum forecasted price (EUR/MWh)" },
          "mean_price": { "type": "number", "nullable": true, "description": "Mean forecasted price (EUR/MWh)" },
          "mae_eur": { "type": "number", "nullable": true, "description": "Mean Absolute Error vs actuals (EUR/MWh), null until actuals clear" },
          "rmse_eur": { "type": "number", "nullable": true, "description": "Root Mean Squared Error vs actuals (EUR/MWh), null until actuals clear" }
        }
      },
      "V4ForecastPoint": {
        "type": "object",
        "description": "A single v4 point forecast. No P10/P90, price_band, or regime_probability fields.",
        "properties": {
          "hour": { "type": "integer", "minimum": 0, "maximum": 23, "description": "Hour of day (0-23) in CET/CEST" },
          "quarter": { "type": "integer", "minimum": 0, "maximum": 3, "nullable": true, "description": "Quarter index within the hour (0-3). Only present for 15-minute resolution." },
          "interval": { "type": "integer", "minimum": 0, "maximum": 95, "description": "Absolute quarter-hour index of the day (0-95). Only present for 15-minute resolution." },
          "timestamp_utc": { "type": "string", "format": "date-time", "description": "Exact timestamp in UTC (start of the interval)" },
          "forecast_price_eur": { "type": "number", "nullable": true, "description": "Forecasted raw point price (EUR/MWh)" },
          "actual_price_eur": { "type": "number", "nullable": true, "description": "Actual DAM price (EUR/MWh), null if not yet published" },
          "error_eur": { "type": "number", "nullable": true, "description": "Forecast error (forecast - actual), null if no actual available" }
        }
      },
      "V5QuarterForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/V5Meta" },
          "summary": { "$ref": "#/components/schemas/V4Summary" },
          "quarter_hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/V5ForecastPoint" }
          }
        }
      },
      "V5HourlyForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/V5Meta" },
          "summary": { "$ref": "#/components/schemas/V4Summary" },
          "hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/V5ForecastPoint" }
          }
        }
      },
      "V5Meta": {
        "type": "object",
        "description": "Metadata for v5 responses. The model block describes the ensemble, and a blend block reports the per-day walk-forward weights.",
        "properties": {
          "api_version": { "type": "string", "example": "5.0" },
          "generated_at": { "type": "string", "format": "date-time", "description": "Timestamp when the v5 response was computed" },
          "model": {
            "type": "object",
            "properties": {
              "name": { "type": "string", "example": "Ensemble (v1 x v4)" },
              "version": { "type": "string", "example": "cz_ensemble_v5" },
              "type": { "type": "string", "enum": ["ensemble"], "description": "v5 is a computed blend (no quantile band)" },
              "method": { "type": "string", "example": "walk-forward accuracy-weighted (inverse-MAE) blend of v1 and v4" },
              "components": { "type": "array", "items": { "type": "string" }, "description": "The two component legs feeding the blend" }
            }
          },
          "blend": { "$ref": "#/components/schemas/V5Blend" },
          "forecast_date": { "type": "string", "format": "date" },
          "status": { "type": "string", "enum": ["completed"] },
          "resolution": { "type": "string", "enum": ["PT15M", "PT60M"] },
          "points_available": { "type": "integer", "description": "96 for PT15M, 24 for PT60M" },
          "points_with_actuals": { "type": "integer" },
          "currency": { "type": "string", "example": "EUR" },
          "unit": { "type": "string", "example": "MWh" },
          "timezone": { "type": "string", "example": "UTC" },
          "calibration": { "$ref": "#/components/schemas/V4Calibration" }
        }
      },
      "V5Blend": {
        "type": "object",
        "description": "Walk-forward blend weights, learned only from days before the delivery date (leakage-free). The two weights sum to 1.",
        "properties": {
          "weight_v1": { "type": "number", "nullable": true, "minimum": 0, "maximum": 1, "description": "Weight applied to the v1 leg", "example": 0.5253 },
          "weight_v4": { "type": "number", "nullable": true, "minimum": 0, "maximum": 1, "description": "Weight applied to the v4 leg", "example": 0.4747 }
        }
      },
      "V5ForecastPoint": {
        "type": "object",
        "description": "A single v5 ensemble point. forecast_price_eur is the blended value; the two component legs and the blend weight are included for transparency. No P10/P90, price_band, or regime_probability fields.",
        "properties": {
          "hour": { "type": "integer", "minimum": 0, "maximum": 23, "description": "Hour of day (0-23) in CET/CEST" },
          "quarter": { "type": "integer", "minimum": 0, "maximum": 3, "nullable": true, "description": "Quarter index within the hour (0-3). Only present for 15-minute resolution." },
          "interval": { "type": "integer", "minimum": 0, "maximum": 95, "description": "Absolute quarter-hour index of the day (0-95). Only present for 15-minute resolution." },
          "timestamp_utc": { "type": "string", "format": "date-time", "description": "Exact timestamp in UTC (start of the interval)" },
          "forecast_price_eur": { "type": "number", "nullable": true, "description": "Blended ensemble price (EUR/MWh)" },
          "forecast_v1_eur": { "type": "number", "nullable": true, "description": "The v1 component value going into the blend (EUR/MWh)" },
          "forecast_v4_eur": { "type": "number", "nullable": true, "description": "The v4 component value going into the blend (EUR/MWh)" },
          "weight_v1": { "type": "number", "nullable": true, "description": "Blend weight on the v1 leg (constant per day)" },
          "actual_price_eur": { "type": "number", "nullable": true, "description": "Actual DAM price (EUR/MWh), null if not yet published" },
          "error_eur": { "type": "number", "nullable": true, "description": "Forecast error (forecast - actual), null if no actual available" }
        }
      },
      "V6QuarterForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/V6Meta" },
          "summary": { "$ref": "#/components/schemas/V4Summary" },
          "quarter_hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/V6ForecastPoint" }
          }
        }
      },
      "V6HourlyForecastResponse": {
        "type": "object",
        "properties": {
          "meta": { "$ref": "#/components/schemas/V6Meta" },
          "summary": { "$ref": "#/components/schemas/V4Summary" },
          "hourly_forecasts": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/V6ForecastPoint" }
          }
        }
      },
      "V6Meta": {
        "type": "object",
        "description": "Metadata for v6 responses. The model block describes the four-leg ensemble, and a blend block reports the per-day walk-forward weights for all four legs.",
        "properties": {
          "api_version": { "type": "string", "example": "6.0" },
          "generated_at": { "type": "string", "format": "date-time", "description": "Timestamp when the v6 response was computed" },
          "model": {
            "type": "object",
            "properties": {
              "name": { "type": "string", "example": "Four-Leg Ensemble (v1 x v4 x CatBoost x LEAR)" },
              "version": { "type": "string", "example": "cz_ensemble_v6" },
              "type": { "type": "string", "enum": ["ensemble"], "description": "v6 is a computed blend (no quantile band)" },
              "method": { "type": "string", "example": "walk-forward accuracy-weighted (inverse-MAE) blend of v1, v4, CatBoost and LEAR" },
              "components": { "type": "array", "items": { "type": "string" }, "description": "The four component legs feeding the blend" }
            }
          },
          "blend": { "$ref": "#/components/schemas/V6Blend" },
          "forecast_date": { "type": "string", "format": "date" },
          "status": { "type": "string", "enum": ["completed"] },
          "resolution": { "type": "string", "enum": ["PT15M", "PT60M"] },
          "points_available": { "type": "integer", "description": "96 for PT15M, 24 for PT60M" },
          "points_with_actuals": { "type": "integer" },
          "currency": { "type": "string", "example": "EUR" },
          "unit": { "type": "string", "example": "MWh" },
          "timezone": { "type": "string", "example": "UTC" },
          "calibration": { "$ref": "#/components/schemas/V4Calibration" }
        }
      },
      "V6Blend": {
        "type": "object",
        "description": "Walk-forward blend weights, learned only from scored days before the delivery date (leakage-free). The four weights sum to 1.",
        "properties": {
          "weight_v1": { "type": "number", "nullable": true, "minimum": 0, "maximum": 1, "description": "Weight applied to the v1 leg", "example": 0.2877 },
          "weight_v4": { "type": "number", "nullable": true, "minimum": 0, "maximum": 1, "description": "Weight applied to the v4 leg", "example": 0.2581 },
          "weight_cat": { "type": "number", "nullable": true, "minimum": 0, "maximum": 1, "description": "Weight applied to the CatBoost leg", "example": 0.2666 },
          "weight_lear": { "type": "number", "nullable": true, "minimum": 0, "maximum": 1, "description": "Weight applied to the LEAR leg", "example": 0.1875 }
        }
      },
      "V6ForecastPoint": {
        "type": "object",
        "description": "A single v6 ensemble point. forecast_price_eur is the blended value; the four component legs and the four blend weights are included for transparency. No P10/P90, price_band, or regime_probability fields.",
        "properties": {
          "hour": { "type": "integer", "minimum": 0, "maximum": 23, "description": "Hour of day (0-23) in CET/CEST" },
          "quarter": { "type": "integer", "minimum": 0, "maximum": 3, "nullable": true, "description": "Quarter index within the hour (0-3). Only present for 15-minute resolution." },
          "interval": { "type": "integer", "minimum": 0, "maximum": 95, "description": "Absolute quarter-hour index of the day (0-95). Only present for 15-minute resolution." },
          "timestamp_utc": { "type": "string", "format": "date-time", "description": "Exact timestamp in UTC (start of the interval)" },
          "forecast_price_eur": { "type": "number", "nullable": true, "description": "Blended ensemble price (EUR/MWh)" },
          "forecast_v1_eur": { "type": "number", "nullable": true, "description": "The v1 component value going into the blend (EUR/MWh)" },
          "forecast_v4_eur": { "type": "number", "nullable": true, "description": "The v4 component value going into the blend (EUR/MWh)" },
          "forecast_cat_eur": { "type": "number", "nullable": true, "description": "The CatBoost component value going into the blend (EUR/MWh)" },
          "forecast_lear_eur": { "type": "number", "nullable": true, "description": "The LEAR component value going into the blend (EUR/MWh)" },
          "weight_v1": { "type": "number", "nullable": true, "description": "Blend weight on the v1 leg (constant per day)" },
          "weight_v4": { "type": "number", "nullable": true, "description": "Blend weight on the v4 leg (constant per day)" },
          "weight_cat": { "type": "number", "nullable": true, "description": "Blend weight on the CatBoost leg (constant per day)" },
          "weight_lear": { "type": "number", "nullable": true, "description": "Blend weight on the LEAR leg (constant per day)" },
          "actual_price_eur": { "type": "number", "nullable": true, "description": "Actual DAM price (EUR/MWh), null if not yet published" },
          "error_eur": { "type": "number", "nullable": true, "description": "Forecast error (forecast - actual), null if no actual available" }
        }
      },
      "Meta": {
        "type": "object",
        "properties": {
          "api_version": { "type": "string", "description": "API version" },
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when forecast was generated"
          },
          "model": {
            "type": "object",
            "properties": {
              "name": { "type": "string", "description": "Model name" },
              "version": { "type": "string", "description": "Model version" }
            }
          },
          "forecast_date": {
            "type": "string",
            "format": "date",
            "description": "Delivery date for the forecast"
          },
          "status": {
            "type": "string",
            "enum": ["pending", "completed", "failed"],
            "description": "Forecast generation status"
          },
          "resolution": {
            "type": "string",
            "enum": ["PT15M", "PT60M"],
            "description": "ISO-8601 duration of each forecast point (15-minute or hourly)"
          },
          "points_available": {
            "type": "integer",
            "description": "Number of forecast points returned (96 for PT15M, 24 for PT60M)"
          },
          "points_with_actuals": {
            "type": "integer",
            "description": "Number of points with actual prices available"
          },
          "currency": { "type": "string", "description": "Price currency (EUR)" },
          "unit": { "type": "string", "description": "Price unit (MWh)" },
          "timezone": { "type": "string", "description": "Timestamp timezone (UTC)" },
          "calibration": { "$ref": "#/components/schemas/Calibration" }
        }
      },
      "Calibration": {
        "type": "object",
        "description": "Bias-calibration status applied to the returned values.",
        "properties": {
          "active": {
            "type": "boolean",
            "description": "Whether calibration is enabled on the server (CALIBRATION_ACTIVE=1)"
          },
          "applied": {
            "type": "boolean",
            "description": "Whether calibration was actually applied to these values (requires enough training history)"
          },
          "method": {
            "type": "string",
            "nullable": true,
            "description": "Calibration method, e.g. 'hour_weekend_bias', or null when not applied"
          },
          "train_days": {
            "type": "integer",
            "description": "Number of realized delivery days the bias offsets were learned from"
          }
        }
      },
      "Summary": {
        "type": "object",
        "properties": {
          "min_price": { "type": "number", "nullable": true, "description": "Minimum forecasted price (EUR/MWh)" },
          "max_price": { "type": "number", "nullable": true, "description": "Maximum forecasted price (EUR/MWh)" },
          "mean_price": { "type": "number", "nullable": true, "description": "Mean forecasted price (EUR/MWh)" },
          "mae_eur": {
            "type": "number",
            "nullable": true,
            "description": "Mean Absolute Error vs actuals (EUR/MWh), null if no actuals available"
          },
          "rmse_eur": {
            "type": "number",
            "nullable": true,
            "description": "Root Mean Squared Error vs actuals (EUR/MWh), null if no actuals available"
          },
          "price_bands": {
            "type": "object",
            "description": "Count of points in each price band",
            "properties": {
              "negative": { "type": "integer", "description": "Points with negative prices" },
              "low": { "type": "integer", "description": "Points with low prices (0-40 EUR)" },
              "normal": { "type": "integer", "description": "Points with normal prices (40-100 EUR)" },
              "high": { "type": "integer", "description": "Points with high prices (>100 EUR)" }
            }
          }
        }
      },
      "ForecastPoint": {
        "type": "object",
        "properties": {
          "hour": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Hour of day (0-23) in CET/CEST"
          },
          "quarter": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3,
            "nullable": true,
            "description": "Quarter index within the hour (0-3). Only present for 15-minute resolution."
          },
          "interval": {
            "type": "integer",
            "minimum": 0,
            "maximum": 95,
            "description": "Absolute quarter-hour index of the day (0-95). Only present for 15-minute resolution."
          },
          "timestamp_utc": {
            "type": "string",
            "format": "date-time",
            "description": "Exact timestamp in UTC (start of the interval)"
          },
          "forecast_price_eur": { "type": "number", "nullable": true, "description": "Forecasted price (EUR/MWh)" },
          "forecast_p10_eur": {
            "type": "number",
            "nullable": true,
            "description": "Lower bound of the 80% prediction interval (10th percentile, EUR/MWh)"
          },
          "forecast_p90_eur": {
            "type": "number",
            "nullable": true,
            "description": "Upper bound of the 80% prediction interval (90th percentile, EUR/MWh)"
          },
          "actual_price_eur": {
            "type": "number",
            "nullable": true,
            "description": "Actual DAM price (EUR/MWh), null if not yet published"
          },
          "error_eur": {
            "type": "number",
            "nullable": true,
            "description": "Forecast error (forecast - actual), null if no actual available"
          },
          "price_band": {
            "type": "string",
            "enum": ["negative", "low", "normal", "high"],
            "nullable": true,
            "description": "Classified price band"
          },
          "regime_probability": {
            "type": "number",
            "nullable": true,
            "description": "Model confidence for the predicted price regime"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "description": "Error type" },
          "code": { "type": "string", "description": "Machine-readable error code" },
          "message": { "type": "string", "description": "Human-readable error message" }
        }
      }
    }
  }
}
