> For the complete documentation index, see [llms.txt](https://docs.jua.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.jua.ai/python-sdk/power-forecast.md).

# Power Forecast

Jua's [Power Forecast](/models-and-products/power-forecast.md) is an end-to-end model trained directly on actual generation data, delivering renewable energy generation forecasts in MW for power trading and grid management.

Power forecasts can be queried directly through the Jua Python SDK using the `client.power_forecast` interface. Forecasts are returned as an `xarray.Dataset` with dimensions `(zone_key, psr_type, time)` and a `value` data variable in MW.

{% hint style="info" %}
Power Forecast support requires `jua >= 0.22.0`. See [Installation](/python-sdk/installation.md) to upgrade.
{% endhint %}

## Example

As an introduction to power forecasts in the SDK, see this simple example. For more examples, please see [our GitHub repository](https://github.com/juaAI/jua-python-sdk/tree/main/examples/power_forecast).

```python
import matplotlib.pyplot as plt
from jua import JuaClient

client = JuaClient()
pf = client.power_forecast

# Fetch the latest German Solar forecast, up to 48h ahead (2880 minutes)
ds = pf.get_data(
    zone_keys=["DE"],
    psr_types=["Solar"],
    init_time="latest",
    max_prediction_timedelta=2880,
    debias=True,
)

# The result is an xarray.Dataset; select the single zone/PSR slice and plot it
series = ds["value"].sel(zone_key="DE", psr_type="Solar")

fig, ax = plt.subplots(figsize=(15, 5))
series.plot(ax=ax, x="time")
ax.set_title("Germany — Solar Power Forecast (latest run)")
ax.set_ylabel("Power [MW]")
plt.show()
```

## Documentation

The `PowerForecast` interface is available as `client.power_forecast`. It exposes metadata helpers (`get_zones`, `get_psr_types`, `get_init_times`), the main `get_data` query method, and a `get_day_ahead_timeseries` helper for stitching a continuous day-ahead series across runs.

### Zones and PSR Types

Power Forecast covers a set of market zones, each serving one or more **PSR types** (Production Source types, e.g. `Solar`, `Wind Onshore`). The available zones and PSR types differ per zone, so query them before building requests.

{% tabs %}
{% tab title="List zones and PSR types" %}

```python
from jua import JuaClient

client = JuaClient()
pf = client.power_forecast

# All available zones
zones = pf.get_zones()
print("Zones:", zones)

# All PSR types across all zones
print("All PSR types:", pf.get_psr_types())

# PSR types served for a specific zone
print("DE PSR types:", pf.get_psr_types(zone_key="DE"))
```

{% endtab %}

{% tab title="Output" %}

```
Zones: ['BE', 'DE', 'FR', 'GB', 'NL']
All PSR types: ['Load', 'Solar', 'Wind', 'Wind Embedded', 'Wind Offshore', 'Wind Onshore', 'Wind Transmission']
DE PSR types: ['Load', 'Solar', 'Wind Offshore', 'Wind Onshore']
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Requesting a PSR type that a zone does not serve raises a `ValueError` listing the available types. For predicted demand in zones without a fitted `Load` model, use [Market Aggregates](/python-sdk/market-aggregates.md) with `weighting=population` and `unit=mw`, which returns a `load_mw` column.
{% endhint %}

### Init Times

`get_init_times` lists the available forecast runs (init times), newest first. Use **count mode** for the most recent runs, or **time-window mode** to retrieve every run in a date range. Pass `version` and `regime` to list runs for a specific serving selection (same semantics as `get_data`).

```python
from datetime import datetime, timezone

# Count mode: the 5 most recent German runs
for it in pf.get_init_times(zone_key="DE", limit=5):
    print(it.init_time, "max horizon (min):", it.max_prediction_timedelta)

# Time-window mode: every run in January 2025 (can exceed 1000)
init_times = pf.get_init_times(
    zone_key="DE",
    start_time=datetime(2025, 1, 1, tzinfo=timezone.utc),
    end_time=datetime(2025, 2, 1, tzinfo=timezone.utc),
)
print(f"{len(init_times)} runs in January 2025")
```

Each entry is an `InitTimeInfo` with an `init_time` (timezone-aware `datetime`) and `max_prediction_timedelta` (forecast horizon in minutes).

### Querying Data

`get_data` supports two mutually exclusive query modes.

**Horizon mode** is init-time-centric. Specify `init_time` as a `datetime`, an integer offset (`0` = latest), or a relative token (`"latest"`, `"latest-N"`), and optionally cap the horizon with `max_prediction_timedelta` (in minutes). Relative tokens resolve to the most recent run where *all* requested zone/PSR-type combinations have data.

```python
# Latest run, 48h horizon, German Solar
ds = pf.get_data(
    zone_keys=["DE"],
    psr_types=["Solar"],
    init_time="latest",
    max_prediction_timedelta=2880,
    debias=True,
)

# The two most recent runs for several zones, all PSR types, localized times
ds = pf.get_data(
    zone_keys=["DE", "FR"],
    init_time=[0, 1],
    max_prediction_timedelta=1440,
    time_zone="Europe/Berlin",
    debias=True,
)
```

**Time range mode** is valid-time-centric. Specify `start_time` and/or `end_time` to filter by the forecast's valid time instead of by run.

```python
from datetime import datetime

ds = pf.get_data(
    zone_keys=["DE"],
    psr_types=["Solar", "Wind Onshore"],
    start_time=datetime(2025, 12, 1),
    end_time=datetime(2025, 12, 3),
    debias=True,
)
```

The two modes cannot be combined; passing both horizon and time-range parameters raises a `ValueError`.

Examples pass `debias=True` to apply the same [walk-forward MW bias correction](/models-and-products/power-forecast.md#optional-walk-forward-debiasing) as the HTTP API. Wind uses eight weeks of history and solar and load use four weeks to estimate the bias for each init time and lead; that bias is subtracted from the current forecast, then the window moves ahead. The API default is still raw: omit `debias` or pass `debias=False` only when you need the unadjusted forecast. `debias=True` cannot be combined with `regime="uncurtailed"` — uncurtailed queries stay raw.

### Model version and regime

`version` and `regime` are independent. Omit `version` (or pass `"stable"`) to follow Jua's stable model; `"latest"` follows the newest model; a version hash from `get_versions()` keeps using that exact model after Jua updates. `regime` selects which product `"stable"` and `"latest"` refer to: `"curtailed"` (actual production, default) or `"uncurtailed"` (potential). A version hash ignores `regime`.

```python
# Uncurtailed German Solar (potential). Debias is not available.
ds = pf.get_data(
    zone_keys=["DE"],
    psr_types=["Solar"],
    init_time="latest",
    max_prediction_timedelta=2880,
    regime="uncurtailed",
)

# Freeze today's uncurtailed stable run
versions = pf.get_versions(zone_key="DE", psr_type="Solar")
uncurtailed = next(v for v in versions if v.is_stable_uncurtailed)
ds = pf.get_data(
    zone_keys=["DE"],
    psr_types=["Solar"],
    init_time="latest",
    version=uncurtailed.model_version,
)
```

Uncurtailed currently covers Germany Solar / Wind Onshore / Wind Offshore and Great Britain Wind Transmission. Other zone and generation-type combinations raise rather than falling back to the curtailed product.

```
Attributes:
    zone_keys: list[str] | None
        Zone codes to query (e.g. ["DE", "FR"]).
    psr_types: list[str] | None
        PSR types to query (e.g. ["Solar", "Wind Onshore"]).
        If None, returns all available types.
    init_time: str | int | datetime | list | None
        Horizon-mode run selection: "latest", "latest-N", a datetime,
        an integer offset (0 = latest), or a list of these.
    max_prediction_timedelta: int | None
        Horizon-mode maximum forecast horizon in minutes.
    start_time: datetime | None
        Time-range-mode lower bound on valid time (inclusive).
    end_time: datetime | None
        Time-range-mode upper bound on valid time (exclusive).
    time_zone: str | None
        IANA time zone name for time formatting (e.g. "Europe/Berlin").
    version: str | None
        `"stable"`, `"latest"`, or a version hash.
        `"stable"` and `"latest"` are looked up for the
        chosen regime.
    regime: "curtailed" | "uncurtailed" | None
        Product `"stable"` / `"latest"` refer to. Default
        (omitted) is curtailed / actual production.
        Uncurtailed is potential and only available for
        some zones and generation types. A version hash
        ignores regime.
    version_pins: list[dict] | None
        Per-(zone_key, psr_type) overrides. Each mapping needs
        zone_key, psr_type, and version; optional regime inherits
        the request regime when omitted.
    debias: bool
        Apply walk-forward additive MW debiasing. Wind uses eight weeks
        and solar and load use four weeks to estimate the bias, then the
        window moves ahead. Defaults to False. Not available with
        regime="uncurtailed". See the Power Forecast product page.

Returns:
    xarray.Dataset with dimensions (zone_key, psr_type, time) and the
    data variable `value` in MW.
```

### Day-Ahead Time Series

`get_day_ahead_timeseries` stitches a continuous day-ahead series from the runs whose local init-time hour matches `init_hour`. For each matching run it takes the day-ahead window and concatenates the results onto a single `time` axis — useful for plotting or backtesting a consistent day-ahead product over a long history.

```python
from datetime import datetime, timedelta, timezone

# Last year of German Solar day-ahead generation from the 09:00 UTC run
end = datetime.now(timezone.utc)
start = end - timedelta(days=365)

ds = pf.get_day_ahead_timeseries(
    zone_keys=["DE"],
    psr_types=["Solar"],
    init_hour=9,
    time_zone="UTC",
    start_date=start,
    end_date=end,
    debias=True,
)

# Plot the stitched series
df = ds.to_dataframe().reset_index().dropna(subset=["value"])
df.plot(x="time", y="value", figsize=(16, 6), title="DE Solar — day-ahead (09:00 UTC run)")
```

When `start_date`/`end_date` are given (date-range mode), one init run per day is constructed over the range, bypassing the init-times listing limit so arbitrarily long histories can be stitched. When no dates are given (latest mode), the most recent matching runs are discovered automatically (bounded by `max_init_times`). `version`, `regime`, and `version_pins` are forwarded to every fetched run. The optional `debias` flag defaults to `False` and uses the same walk-forward windows as `get_data`; it cannot be combined with `regime="uncurtailed"`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.jua.ai/python-sdk/power-forecast.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
