Metadata-Version: 2.4
Name: volatile-forecast-sdk
Version: 0.3.0
Summary: Python client for Volatile forecast data via the public GraphQL API.
Author: Volatile
License: Apache-2.0
Project-URL: Homepage, https://api.volatile.de
Keywords: forecast,graphql,energy,volatile
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tzdata>=2024.1
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# Volatile Forecast SDK

Python client for authenticated access to forecast **dataset queries** on the Volatile public GraphQL API. It focuses on a small, typed surface (`execute_dataset_query`, `my_datasets`, `me`, token lifecycle) with **client-side validation** so integrators pick only supported bidding zones, IANA time zones, percentile fields, and aggregation modes.

## About Volatile

[Volatile](https://www.volatile.de/) is an energy intelligence company focused on making European power-market data and forecasting workflows easier to consume and operationalize.  
This SDK is one of the tools provided to help customers integrate Volatile forecast products directly into Python-based analytics, trading, and automation pipelines.

## Install

From PyPI (recommended):

```bash
pip install volatile-forecast-sdk
```


Runtime dependency: **`tzdata`** (IANA zone database for `zoneinfo` on all platforms).

Usage examples (login, dataset listing, query execution, and token-only auth) are available in **`examples/`**.

## License and access model

This SDK is open source under the **Apache-2.0** license and is publicly installable.

API usage still requires a valid Volatile customer account and API credentials. The license for this SDK does not grant free API access, and API use is governed by your contract and applicable API Terms of Service.

## API endpoint

`VolatileForecastClient()` targets production by default (**`https://api.volatile.de`**). You can override `base_url` when needed:

```python
from volatile_forecast_sdk import VolatileForecastClient, DEFAULT_BASE_URL

client = VolatileForecastClient()  # same as VolatileForecastClient(DEFAULT_BASE_URL)
client = VolatileForecastClient("https://api.staging.example.com")
```

Existing access token, same default:

```python
client = VolatileForecastClient.from_token("eyJ…")  # optional: base_url="https://…"
```

`from_token()` accepts the token first, with `base_url` as an optional keyword argument.

## Discover parameters (PFM datasets)

For products such as **Volatile Load PFM-1**, **Volatile Spot PFM-1**, and **Volatile IDC DAS-1**, the SDK ships a structured catalog that mirrors the server rules (zone, issue time, and dataset-specific optional keys):

```python
from volatile_forecast_sdk import format_parameter_hints, list_catalog_dataset_names

print(list_catalog_dataset_names())
print(format_parameter_hints("Volatile Load PFM-1"))
```

Use **`ForecastQueryParams`** for a fluent builder with validation and tab-friendly enums:

```python
from volatile_forecast_sdk import (
    VolatileForecastClient,
    ForecastQueryParams,
    EuropeanBiddingZone,
    CommonTimeZone,
    ForecastPercentileField,
    TimeAggregation,
)

client = VolatileForecastClient()
client.login("user", "pass")

params = (
    ForecastQueryParams.for_dataset("Volatile Load PFM-1")
    .with_zone(EuropeanBiddingZone.NO_2)
    .with_time_zone(CommonTimeZone.EUROPE_BERLIN)
    .with_forecast_issue_latest()
    .with_fields(ForecastPercentileField.P50, ForecastPercentileField.P90)
    .with_aggregation(TimeAggregation.FIFTEEN_MINUTES)
    .with_forecast_horizon_hours(168)
)

result = client.execute_dataset_query(name="Volatile Load PFM-1", parameters=params)
rows = result.parsed_rows()
```

IDC DAS example (lean parameter set):

```python
from volatile_forecast_sdk import CommonTimeZone, EuropeanBiddingZone, ForecastQueryParams

params = (
    ForecastQueryParams.for_dataset("Volatile IDC DAS-1")
    .with_zone(EuropeanBiddingZone.DE_LU)
    .with_time_zone(CommonTimeZone.EUROPE_BERLIN)
    .with_forecast_issue_latest()
)
result = client.execute_dataset_query(name="Volatile IDC DAS-1", parameters=params)
```

**European bidding zones** are exposed as `EuropeanBiddingZone` and `EUROPEAN_BIDDING_ZONE_CODES` (ENTSO-E-style strings). **Time zones** use `CommonTimeZone` presets plus `validate_time_zone("Europe/Stockholm")` against the full IANA set (`zoneinfo.available_timezones()`).

## Refresh token policy

`VolatileForecastClient` applies a default `RefreshTokenPolicy`:

- **Proactive**: if the access token looks like a JWT, refresh when wall-clock time is within `leeway_seconds` of `exp` (requires a refresh token from `login`).
- **Reactive**: on HTTP **401** or GraphQL errors that look like auth failures, refresh once (configurable) and retry.

Disable automation (manual refresh only):

```python
from volatile_forecast_sdk import VolatileForecastClient, RefreshTokenPolicy

client = VolatileForecastClient(refresh_token_policy=RefreshTokenPolicy(enabled=False))
```

Tune behavior:

```python
RefreshTokenPolicy(
    enabled=True,
    leeway_seconds=300,
    max_reactive_refreshes=2,
    proactive_refresh=True,
    reactive_refresh_on_graphql_auth_error=True,
)
```

Helpers: `client.access_token_expires_at_unix()`, `decode_jwt_exp_unix(token)` (signature **not** verified—scheduling only).

## Classic dict parameters

You can still pass a plain mapping (no validation beyond the API):

```python
from volatile_forecast_sdk import VolatileForecastClient, normalize_bidding_zone, validate_time_zone

client = VolatileForecastClient()
client.login("user", "pass")

params = {
    "forecast_issue_time": "latest",
    "zone_code": normalize_bidding_zone("NO_2"),
    "time_zone": validate_time_zone("Europe/Berlin"),
}
result = client.execute_dataset_query(name="Volatile Spot PFM-1", parameters=params)
```

## API reference (high level)

| Symbol | Role |
|--------|------|
| `VolatileForecastClient` | GraphQL HTTP client, token policy, `execute_dataset_query` |
| `DEFAULT_BASE_URL` | Production API root (`https://api.volatile.de`); pass a different `base_url` to override |
| `ForecastQueryParams` | Validated fluent builder for query parameters |
| `EuropeanBiddingZone`, `EUROPEAN_BIDDING_ZONE_CODES`, `normalize_bidding_zone` | Curated bidding-zone codes |
| `CommonTimeZone`, `validate_time_zone`, `list_common_european_time_zones` | IANA time zones |
| `ForecastPercentileField`, `TimeAggregation` | Allowed `fields` / `aggregation` values |
| `format_parameter_hints`, `parameter_hints_for_dataset`, `STANDARD_FORECAST_DATASETS`, `IDC_DAS_DATASETS`, `STANDARD_PFM_DATASETS` | Documentation-oriented catalog |
| `RefreshTokenPolicy`, `decode_jwt_exp_unix` | Token lifecycle |
| `DatasetQueryResult.parsed_rows()` | Normalize row JSON strings vs objects |

The SDK mirrors the public GraphQL schema exposed by `https://api.volatile.de/graphql/`.
