Skip to main content

Python client for Volatile forecast data via the public GraphQL API.

Project description

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 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):

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:

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:

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):

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:

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):

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):

from volatile_forecast_sdk import VolatileForecastClient, RefreshTokenPolicy

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

Tune behavior:

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).

Rate-limit retries (HTTP 429)

The public API enforces per-user, per-operation budgets. When you exceed a budget it replies with HTTP 429 plus a Retry-After header. The SDK detects this automatically, sleeps the indicated number of seconds, and retries — up to max_rate_limit_retries times. Each sleep is capped at rate_limit_retry_cap_seconds so a misbehaving server can't pin your process indefinitely.

client = VolatileForecastClient(
    access_token="...",
    max_rate_limit_retries=3,           # default
    rate_limit_retry_cap_seconds=60.0,  # default
)

If the budget is exhausted, the SDK raises RateLimitError — a subclass of HTTPError — carrying the parsed metadata so you can decide what to do next:

from volatile_forecast_sdk import RateLimitError

try:
    client.execute_dataset_query(name="Volatile IDC DAS-1", parameters=params)
except RateLimitError as e:
    # e.retry_after, e.tier, e.window, e.limit, e.operation
    log.warning("Throttled on %s (%s): wait %ss", e.operation, e.window, e.retry_after)
    raise

Set max_rate_limit_retries=0 to disable the built-in loop entirely (useful if you want to handle 429s with your own scheduler).

Classic dict parameters

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

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, IDCField, TimeAggregation Allowed fields / aggregation values
ForecastStatus Per-slot forecast-quality enum (0..7) returned by IDC datasets — see Forecast quality below
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/.

Forecast quality (IDC datasets)

IDC DAS datasets attach a per-slot quality label so consumers can filter or weight the forecast. Two columns are returned alongside the quantile / MC fields:

  • forecast_status (int 0..7) — see table below.
  • in_scope (bool) — true for ID1 slots in the 1–6 h horizon (the MC layer's active scope). false for ID3 slots or ID1 slots outside that window; MC columns (signal, expected_edge, p_buy, p_sell, …) will be null by design — not a degradation. The LightGBM quantile columns (id1_p10/50/90, id3_p10/50/90) are populated regardless.
forecast_status Name Meaning
0 OK All inputs fresh; VWAP-S0 (≤6 h) or DA-S0 (>6 h by design)
1 OK_DA_FALLBACK ≤6 h slot, no VWAP, crawler is fresh → liquidity gap, DA-S0 used
2 OK_IDA_FALLBACK Same as 1 but intraday auction is newer than DA → IDA-S0 used
3 WATCH Non-critical input source stale OR borderline VWAP age (15–30 min)
4 VWAP_STALE VWAP older than 30 min — likely crawler lag
5 IMPUTED LightGBM substituted defaults for one or more missing features; data feed is fresh, model output is mildly biased
6 STALE_CRITICAL Data feed itself is stale: EPEX continuous-trades crawler down within MC scope, or day-ahead price missing for the delivery date
7 FAILED LightGBM produced NaN at this slot (model load / feature build failure)

Statuses are monotonically worse — higher is more degraded. Suggested thresholds:

  • LightGBM-only traders: skip slots with forecast_status >= 5.
  • MC-signal traders: skip slots with forecast_status >= 4.
  • Conservative consumers: only trade forecast_status <= 1.

Difference between status 5 and status 6

These are the two most commonly confused values, and they imply different operational responses:

  • 5 IMPUTED — the model improvised. Input features had one or more NaNs at inference time and the wrapper substituted a default (categorical missing-path, Ridge median, or analytical_cf=0). The data feed is fresh; only some engineered features were unavailable. Forecast is in the right ballpark; reduce position sizing.
  • 6 STALE_CRITICAL — the data feed stopped. The EPEX continuous- trades crawler is stale within the MC horizon, or the day-ahead price is missing entirely. The model may have produced a number, but it's reflecting old or absent market reality. Skip the slot.
from volatile_forecast_sdk import (
    ForecastQueryParams, ForecastStatus, IDCField, VolatileForecastClient,
)

params = (
    ForecastQueryParams.for_dataset("Volatile IDC DAS-1")
    .with_zone("DE_LU")
    .with_forecast_issue_latest()
    .with_fields(
        IDCField.ID1_P50, IDCField.ID3_P50,
        IDCField.SIGNAL, IDCField.EXPECTED_EDGE,
        IDCField.FORECAST_STATUS, IDCField.IN_SCOPE,
    )
)
result = client.execute_dataset_query(
    name="Volatile IDC DAS-1", parameters=params,
)
rows = [
    r for r in result.parsed_rows()
    if r.get("in_scope") and int(r.get("forecast_status", 7)) < ForecastStatus.VWAP_STALE
]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

volatile_forecast_sdk-0.4.1.tar.gz (31.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

volatile_forecast_sdk-0.4.1-py3-none-any.whl (29.1 kB view details)

Uploaded Python 3

File details

Details for the file volatile_forecast_sdk-0.4.1.tar.gz.

File metadata

  • Download URL: volatile_forecast_sdk-0.4.1.tar.gz
  • Upload date:
  • Size: 31.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for volatile_forecast_sdk-0.4.1.tar.gz
Algorithm Hash digest
SHA256 e405e9b994bfc5a61b030331ba4326f0c4fab5cc93dcd4d51637f277b0b7fa31
MD5 e7642e3efe2aed30060ff465e43226a5
BLAKE2b-256 1170f3a3cf05fc40fd930a8b48d1ef9b79a8860df286b28cfe2ba80aab9044b7

See more details on using hashes here.

File details

Details for the file volatile_forecast_sdk-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for volatile_forecast_sdk-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5efab77c60c66cc3965c1476910834bbef2c6c91620dae718a6785ce55a84a61
MD5 8fe3dc74e9e3a04905b559fdae19d920
BLAKE2b-256 6a39362caa69873d33b11c94fc699ec17a70d11fa7ae3e23250301d8047a9175

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page