Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [22.7.0-rc.1](https://github.com/sequential-parameter-optimization/spotforecast2-safe/compare/v22.6.0...v22.7.0-rc.1) (2026-06-14)


### Features

* **downloader:** add derive_download_window, extract_entsoe_hourly_forecast, download_side_tables ([8f68b38](https://github.com/sequential-parameter-optimization/spotforecast2-safe/commit/8f68b38184966eea029da37721138db7abff31d6))

## [22.6.0](https://github.com/sequential-parameter-optimization/spotforecast2-safe/compare/v22.5.0...v22.6.0) (2026-06-13)


Expand Down
8 changes: 4 additions & 4 deletions MODEL_CARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This card describes what spotforecast2-safe is, how to use it safely, the condit
| Field | Value |
| --- | --- |
| Name | spotforecast2-safe |
| Version | 22.6.0 |
| Version | 22.7.0-rc.1 |
| Type | Deterministic Python library for time series feature engineering and recursive multi-step forecasting. It performs no training of its own. |
| Developed by | Thomas Bartz-Beielstein, ORCID [0000-0002-5938-5158](https://orcid.org/0000-0002-5938-5158) |
| Distributed by | the `sequential-parameter-optimization` GitHub organization |
Expand All @@ -18,7 +18,7 @@ This card describes what spotforecast2-safe is, how to use it safely, the condit

The library depends only on numpy, pandas, scikit-learn, lightgbm, numba, pyarrow, requests, feature-engine, holidays, astral, and tqdm. It deliberately excludes plotly, matplotlib, spotoptim, optuna, torch, and tensorflow, so no plotting or automated-tuning code ships in this package.

Two Common Platform Enumeration (CPE) identifiers let vulnerability-tracking and software bill of materials (SBOM) tools recognize the package. The wildcard identifier `cpe:2.3:a:sequential_parameter_optimization:spotforecast2_safe:*:*:*:*:*:*:*:*` matches any release; the current release is `cpe:2.3:a:sequential_parameter_optimization:spotforecast2_safe:22.6.0:*:*:*:*:*:*:*`.
Two Common Platform Enumeration (CPE) identifiers let vulnerability-tracking and software bill of materials (SBOM) tools recognize the package. The wildcard identifier `cpe:2.3:a:sequential_parameter_optimization:spotforecast2_safe:*:*:*:*:*:*:*:*` matches any release; the current release is `cpe:2.3:a:sequential_parameter_optimization:spotforecast2_safe:22.7.0-rc.1:*:*:*:*:*:*:*`.

The library itself is a low-risk component: it is deterministic, its source is fully inspectable, and it fails safe on invalid input. It is built to support high-risk AI systems in the sense of the EU AI Act, but it is not itself such a system. When it is embedded in a high-risk deployment, the duties that attach to that system fall on the integrator, not on the library.

Expand All @@ -30,7 +30,7 @@ Responsibilities are divided as follows.
| Distribution | sequential-parameter-optimization on GitHub | repository issue tracker |
| Deployment, operation, and audit | the system integrator | defined per deployment |

The current release is 22.6.0, with a stable public interface pinned in `spotforecast2_safe.__init__.__all__`. The full version history, including release dates, is recorded in `CHANGELOG.md` and on the GitHub Releases page; it is maintained automatically by the release pipeline and is not repeated here.
The current release is 22.7.0-rc.1, with a stable public interface pinned in `spotforecast2_safe.__init__.__all__`. The full version history, including release dates, is recorded in `CHANGELOG.md` and on the GitHub Releases page; it is maintained automatically by the release pipeline and is not repeated here.

## 2. Intended Use and Scope

Expand Down Expand Up @@ -216,7 +216,7 @@ Maintainer: Thomas Bartz-Beielstein, ORCID [0000-0002-5938-5158](https://orcid.o
}
```

Or as a formatted reference: Bartz-Beielstein, T. (2026). *spotforecast2-safe: Safety-critical subset of spotforecast2* (Version 22.6.0) [Computer software]. https://github.com/sequential-parameter-optimization/spotforecast2-safe
Or as a formatted reference: Bartz-Beielstein, T. (2026). *spotforecast2-safe: Safety-critical subset of spotforecast2* (Version 22.7.0-rc.1) [Computer software]. https://github.com/sequential-parameter-optimization/spotforecast2-safe

The technical report (`bart26h/index.qmd`) is the long-form reference for design rationale, compliance mapping, and evaluation protocol.

Expand Down
9 changes: 9 additions & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,8 @@ website:
file: docs/reference/downloader.entsoe.download_renewable_forecast.qmd
- text: "download_day_ahead_price"
file: docs/reference/downloader.entsoe.download_day_ahead_price.qmd
- text: "download_side_tables"
file: docs/reference/downloader.entsoe.download_side_tables.qmd
- text: "merge_build_manual"
file: docs/reference/downloader.entsoe.merge_build_manual.qmd
- text: "repair_data_gaps"
Expand All @@ -457,6 +459,10 @@ website:
file: docs/reference/downloader.entsoe.ZoneResult.qmd
- text: "build_zone_qc_frame"
file: docs/reference/downloader.entsoe.build_zone_qc_frame.qmd
- text: "derive_download_window"
file: docs/reference/downloader.entsoe.derive_download_window.qmd
- text: "extract_entsoe_hourly_forecast"
file: docs/reference/downloader.entsoe.extract_entsoe_hourly_forecast.qmd

- text: "Exceptions"
file: docs/reference/exceptions.qmd
Expand Down Expand Up @@ -835,13 +841,16 @@ quartodoc:
- downloader.entsoe.download_new_data
- downloader.entsoe.download_renewable_forecast
- downloader.entsoe.download_day_ahead_price
- downloader.entsoe.download_side_tables
- downloader.entsoe.merge_build_manual
- downloader.entsoe.repair_data_gaps
- downloader.entsoe.find_missing_intervals
- downloader.entsoe.download_zone_loads
- downloader.entsoe.assemble_zone_loads
- downloader.entsoe.ZoneResult
- downloader.entsoe.build_zone_qc_frame
- downloader.entsoe.derive_download_window
- downloader.entsoe.extract_entsoe_hourly_forecast

# ── Exceptions ────────────────────────────────────────────────────────────
- title: "Exceptions"
Expand Down
63 changes: 63 additions & 0 deletions docs/reference/downloader.entsoe.derive_download_window.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# downloader.entsoe.derive_download_window { #spotforecast2_safe.downloader.entsoe.derive_download_window }

```python
downloader.entsoe.derive_download_window(
now,
*,
train_years,
start_margin_days=45,
data_end=None,
)
```

Derive the ``(start_dl, end_dl)`` string pair for an ENTSO-E download.

Computes the date window needed to cover *train_years* of history plus a
*start_margin_days* buffer on the left, and optionally a caller-supplied
upper bound on the right. All computation is pure (no network, no
filesystem access).

## Parameters {.doc-section .doc-section-parameters}

| Name | Type | Description | Default |
|-------------------|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|
| now | [pd](`pandas`).[Timestamp](`pandas.Timestamp`) | Reference point for the window, typically ``pd.Timestamp.now(tz="UTC")``. Must be a ``pd.Timestamp`` — coercion is intentionally refused to prevent silent mistakes with string arguments. | _required_ |
| train_years | [int](`int`) | Number of full calendar years of history required. Must be a positive ``int`` (``bool`` values are rejected even though ``bool`` is a subclass of ``int``). | _required_ |
| start_margin_days | [int](`int`) | Extra days prepended to the window so that the training data survives the downstream lag-feature creation step. Defaults to ``45``. Must be a non-negative ``int`` (bools rejected). | `45` |
| data_end | [str](`str`) \| None | Upper bound of the window in ``'YYYYMMDDHHMM'`` format, or ``None`` (the default) to set the end to *now* + 2 days (i.e. today + tomorrow + one additional day — the standard day-ahead horizon). When provided, the value is validated strictly; any deviation from the format raises ``ValueError``. | `None` |

## Returns {.doc-section .doc-section-returns}

| Name | Type | Description |
|--------|------------------------------------------------|--------------------------------------------------------------|
| | [str](`str`) | A two-tuple ``(start_dl, end_dl)`` where both strings are in |
| | [str](`str`) | ``'YYYYMMDDHHMM'`` format and can be passed directly to |
| | [tuple](`tuple`)\[[str](`str`), [str](`str`)\] | `download_new_data`, `download_renewable_forecast`, or |
| | [tuple](`tuple`)\[[str](`str`), [str](`str`)\] | `download_day_ahead_price`. |

## Raises {.doc-section .doc-section-raises}

| Name | Type | Description |
|--------|----------------------------|---------------------------------------------------------------------------------------------------------------------------|
| | [TypeError](`TypeError`) | If *now* is not a ``pd.Timestamp``, or is timezone-naive (the window must be anchored to an explicit timezone, e.g. UTC). |
| | [ValueError](`ValueError`) | If *train_years* is not a positive ``int`` or is a ``bool``. |
| | [ValueError](`ValueError`) | If *start_margin_days* is negative, not an ``int``, or is a ``bool``. |
| | [ValueError](`ValueError`) | If *data_end* is not ``None`` and does not conform to the ``'YYYYMMDDHHMM'`` format. |

## Examples {.doc-section .doc-section-examples}

```{python}
import pandas as pd
from spotforecast2_safe.downloader.entsoe import derive_download_window

now = pd.Timestamp("2026-06-14", tz="UTC")
start, end = derive_download_window(now, train_years=3)
print(start) # 202305010000
print(end) # 202606160000

# Explicit upper bound:
start2, end2 = derive_download_window(
now, train_years=3, data_end="202612312300"
)
print(end2) # 202612312300
```
80 changes: 80 additions & 0 deletions docs/reference/downloader.entsoe.download_side_tables.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# downloader.entsoe.download_side_tables { #spotforecast2_safe.downloader.entsoe.download_side_tables }

```python
downloader.entsoe.download_side_tables(
api_key,
*,
start=None,
end=None,
country_code='DE',
price_country_code='DE_LU',
force=False,
timeout=60.0,
on_provider_failure='raise',
)
```

Download both ENTSO-E day-ahead side tables in one call.

Calls `download_renewable_forecast` (with *country_code*) followed by
`download_day_ahead_price` (with *price_country_code*). Both providers
share the same *start*, *end*, *force*, and *timeout* arguments.

The library default is **fail-loud**: when *on_provider_failure* is
``"raise"`` (the default), the first provider exception propagates
immediately and the second provider is never attempted. Pass
``on_provider_failure="skip"`` to opt into degraded-but-continuing
behaviour: each provider failure is logged at WARNING level and the
next provider is still attempted. ``"skip"`` is intended for
opportunistic refreshes where a partial update is better than no
update; it must **not** be used in safety-critical pipelines where
missing a table is a hard error.

## Parameters {.doc-section .doc-section-parameters}

| Name | Type | Description | Default |
|---------------------|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|
| api_key | [str](`str`) | ENTSO-E Web API security token. | _required_ |
| start | [Optional](`typing.Optional`)\[[str](`str`)\] | Start in ``'YYYYMMDDHHMM'`` format, or ``None`` to use each downloader's own default (seven days ago). | `None` |
| end | [Optional](`typing.Optional`)\[[str](`str`)\] | End in ``'YYYYMMDDHHMM'`` format, or ``None`` to use each downloader's own default (start of tomorrow). | `None` |
| country_code | [str](`str`) | Bidding-zone or country code for the renewable forecast query. Defaults to ``"DE"``. | `'DE'` |
| price_country_code | [str](`str`) | Bidding-zone code for the day-ahead price query. Defaults to ``"DE_LU"``. | `'DE_LU'` |
| force | [bool](`bool`) | If ``True``, bypass the small-window cooldown check in each downloader. | `False` |
| timeout | [Optional](`typing.Optional`)\[[float](`float`)\] | Per-socket-operation read timeout in seconds passed to each downloader. ``None`` disables the timeout. Defaults to ``60.0``. | `60.0` |
| on_provider_failure | [str](`str`) | How to handle a provider-level exception. ``"raise"`` (default) — let the exception propagate immediately. ``"skip"`` — log a WARNING and continue to the next provider. Any other value raises ``ValueError`` before any download begins. | `'raise'` |

## Returns {.doc-section .doc-section-returns}

| Name | Type | Description |
|--------|--------|---------------|
| | None | None |

## Raises {.doc-section .doc-section-raises}

| Name | Type | Description |
|--------|--------------------------------|---------------------------------------------------------------------------------------------------------------------------|
| | [ValueError](`ValueError`) | If *on_provider_failure* is not ``"raise"`` or ``"skip"``. |
| | [ImportError](`ImportError`) | If ``entsoe-py`` is not installed (propagated from the individual downloaders when *on_provider_failure* is ``"raise"``). |
| | [RuntimeError](`RuntimeError`) | If a download fails after ``_MAX_RETRIES`` attempts and *on_provider_failure* is ``"raise"``. |

## Examples {.doc-section .doc-section-examples}

```{python}
#| eval: false
from spotforecast2_safe.downloader.entsoe import download_side_tables

# Fail-loud (default): any provider error propagates immediately.
download_side_tables(
api_key="YOUR_API_KEY",
start="202301010000",
end="202301050000",
force=True,
)

# Opt-in degradation: renewable failure is logged but price is still
# attempted. Use only in non-safety-critical refresh workflows.
download_side_tables(
api_key="YOUR_API_KEY",
on_provider_failure="skip",
)
```
Loading
Loading