epftoolbox2
A modern Python library for electricity price forecasting with modular data pipelines and model evaluation. Built as a complete rewrite of the original epftoolbox, this library provides a flexible, extensible framework for downloading energy market data, building forecasting models, and evaluating their performance.
Installation
pip install epftoolbox2
or with uv
uv add epftoolbox2
Verify Installation
After installation, you can verify your setup and check system information:
import epftoolbox2
epftoolbox2.verify()
Key Features
- Data Sources: ENTSOE (load, generation, day-ahead prices on an hourly or 15-minute grid, cross-border transfer capacity, installed capacity, with
prefixto combine zones), Open-Meteo (OpenMeteoSource, archived weather forecasts as known at a UTC cutoff, withapi="previous_runs"for a long lead-time-bucket archive orapi="single_runs"for one exact model run per day, and alocationsdict to combine several locations into per-location columns, one or more aggregated series per variable viaagg(mean, optionally population- or capacity-weighted, plus median, min, max and sum), or both), Calendar (national and regional holidays, bridge days, weekday, hour, month, daylight, a 31-entry market-regime catalog with ex-ante dates and sources, opt-in national or regional-share school holidays via the OpenHolidays API), CSV files (CsvSource), EpftoolboxBenchmark (the Lago et al. 2021 datasets NP, PJM, FR, BE, DE, downloaded on demand) - Transformers: Resample, Timezone conversion, Lag features (local calendar days), DST normalisation to 24 slots a day (DSTNormalize), daily profile and daily min/max/mean/last stats (DailyProfile, DailyStats), custom column arithmetic (Lambda), Impute (leak-safe gap filling)
- Presets:
epftoolbox2.presets, ready-made predictor lists for the LEAR and expert-model benchmarks from the literature - Validators: DSTStructure (rows per local day), Coverage (duplicate and unsorted timestamps, per-column outages, missing timestamps and nulls), Plausibility (SDAC price limits, frozen values), EDA statistics
- Models: OLS, LassoCV
- Scalers: standardisation (default) or variance-stabilising transformations for spiky prices (asinh, N-PIT), fitted per forecast on training data only
- Evaluators: MAE, RMSE, rMAE (relative to a base model)
- Exporters: Excel with conditional formatting, Terminal output, CSV
- Caching: Built-in per-source Parquet cache that fetches only missing ranges, refetches not-yet-published data and is safe for parallel jobs
- Pipelines: Data and model pipelines that can be saved and loaded with .yaml files. An optional market timezone makes dates mean local calendar days, duplicate column names across sources raise instead of colliding silently, and
api_keyis written to YAML as an${ENV_VAR}reference instead of plain text - Multiprocessing: Process-based parallelism with inner thread pools. SLURM array mode (one model per task) with
epf reportto merge the results - Safeguards: The model's input (resolution, time stamps, column names, predictors) is checked before any fit, and forecasts that could not be made are counted per model (
report.forecast_counts()) - Information Set: Optional
issue_timecheck that every predictor is known at the moment the forecast is issued, with built-in availability rules for the shipped sources and an empirical leakage probe (epftoolbox2.information_set) - Result Manifests: Resumable results (
save_dir) are fingerprinted with the configuration that produced them (the predictors' ordered layout, data up totest_endand its time zone, model params, ...). Resuming into results made with a different configuration raises instead of silently mixing them (epftoolbox2.results.manifest) - Extensibility: Extensible base classes for data sources, transformers, validators, models, evaluators, and exporters
Quick Start
Standalone source use
import pandas as pd
from epftoolbox2.data.sources import EntsoeSource
source = EntsoeSource("PL", api_key="YOUR_KEY", type=["load", "price"])
df = source.fetch(pd.Timestamp("2024-01-01", tz="UTC"), pd.Timestamp("2024-06-01", tz="UTC")) # [start, end), UTC
Standalone transformer use
from epftoolbox2.data.transformers import TimezoneTransformer, ResampleTransformer
df = TimezoneTransformer(target_tz="Europe/Warsaw").transform(df) # sources return UTC
df = ResampleTransformer(freq="1h").transform(df) # aggregates or step-fills; never fills real gaps
Standalone validator use
from epftoolbox2.data.validators import CoverageValidator
validator = CoverageValidator(columns=["load_actual", "price"], min_coverage=1.0)
result = validator.validate(df)
print(result) # VALID/INVALID with errors, warnings and details; result.stats has one row per column
Data Pipeline
Download and process electricity market data from ENTSOE, weather forecasts from Open-Meteo, and calendar features.
from epftoolbox2.pipelines import DataPipeline
from epftoolbox2.data.sources import EntsoeSource, OpenMeteoSource, CalendarSource
from epftoolbox2.data.transformers import TimezoneTransformer, ResampleTransformer, LagTransformer
from epftoolbox2.data.validators import CoverageValidator
pipeline = (
# timezone is the single place the market zone needs to be written: sources/transformers below that
# compute their own local time inherit it and leave their own timezone/target_tz unset.
DataPipeline(timezone="Europe/Warsaw") # optional: dates below then mean local calendar days
.add_source(EntsoeSource("PL", api_key="YOUR_KEY", type=["load", "price"]))
.add_source(OpenMeteoSource(latitude=52.23, longitude=21.01))
.add_source(CalendarSource("PL", holidays="binary", weekday="onehot", daylight=True))
.add_transformer(TimezoneTransformer())
.add_transformer(ResampleTransformer(freq="1h"))
# load_actual is only known for D once D is over; never offer its same-day (unlagged) value.
.add_transformer(LagTransformer(columns=["load_actual"], lags=[1], freq="day"))
# calendar features are known arbitrarily far in advance, so d+1..d+7 is safe.
.add_transformer(
LagTransformer(
lags=range(-7, 0),
freq="day",
columns=[
"is_monday",
"is_tuesday",
"is_wednesday",
"is_thursday",
"is_friday",
"is_saturday",
"is_sunday",
"is_holiday",
"daylight_hours",
],
)
)
.add_validator(CoverageValidator(columns=["load_actual", "price"], min_coverage=1.0))
)
df = pipeline.run("2024-01-01", "2024-06-08", cache=True)
Standalone model use
from epftoolbox2.models import OLSModel
seasonal_indicators = [
"is_monday_d+{horizon}",
"is_tuesday_d+{horizon}",
"is_wednesday_d+{horizon}",
"is_thursday_d+{horizon}",
"is_friday_d+{horizon}",
"is_saturday_d+{horizon}",
"is_sunday_d+{horizon}",
"is_holiday_d+{horizon}",
"daylight_hours_d+{horizon}",
]
model = OLSModel(predictors=["load_actual_d-1", *seasonal_indicators], name="OLS")
ref = model.run(df, test_start="2024-04-01", test_end="2024-06-01", target="price") # where the forecasts are
Standalone evaluator use
import pandas as pd
from epftoolbox2.evaluators import MAEEvaluator
predictions = pd.DataFrame({"prediction": [48.0, 52.5], "actual": [50.0, 51.0]})
mae = MAEEvaluator().compute(predictions) # 1.75
Standalone exporter use
from epftoolbox2.evaluators import MAEEvaluator
from epftoolbox2.exporters import ExcelExporter
from epftoolbox2.results import EvaluationReport
report = EvaluationReport({"OLS": ref}, [MAEEvaluator()]) # the object ModelPipeline.run() returns
ExcelExporter("results.xlsx").export(report)
Model Pipeline
Train and evaluate forecasting models with built-in metrics and export capabilities.
from epftoolbox2.pipelines import ModelPipeline
from epftoolbox2.models import OLSModel, LassoCVModel
from epftoolbox2.evaluators import MAEEvaluator
from epftoolbox2.exporters import ExcelExporter
pipeline = (
ModelPipeline(issue_time="11:45", issue_timezone="Europe/Warsaw") # issued before the 12:00 CET gate closure
.add_model(OLSModel(predictors=["load_actual_d-1", *seasonal_indicators], name="OLS"))
.add_model(LassoCVModel(predictors=["load_actual_d-1", *seasonal_indicators], name="Lasso"))
.add_evaluator(MAEEvaluator())
.add_exporter(ExcelExporter("results.xlsx"))
)
report = pipeline.run(df, test_start="2024-04-01", test_end="2024-06-01", target="price")
issue_time (and optionally issue_timezone) checks every predictor against the time the forecast is actually
issued, before any model is fitted. It raises InformationSetError if a predictor uses a value not known yet (e.g.
same-day load_actual). Built-in availability rules cover the shipped sources. See epftoolbox2.information_set for
custom rules, accepted assumptions, and leakage_probe, an empirical check that perturbs unknown values and asserts
forecasts don't move.
Examples
See the examples/ folder for complete working examples. Each script defines a main() that takes the date ranges it uses (and a directory for its output files when it writes any), so it can also be imported and called on a short window, for example main(data_start="2024-01-01", data_end="2024-03-01").
Metadata
Release files for epftoolbox2 2.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| epftoolbox2-2.3.0.tar.gz | 688.2 kB | Without provenance | Details for epftoolbox2-2.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| epftoolbox2-2.3.0-py3-none-any.whl | Python 3 | none | any | Details for epftoolbox2-2.3.0-py3-none-any.whl |
Total release size: 912.6 kB
Release files / epftoolbox2-2.3.0.tar.gz
| Download URL | epftoolbox2-2.3.0.tar.gz |
|---|---|
| Size | 688.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
27439a51a0e5fcca15098450d2d28e2ffe1544404b910183a06685e8db49a996
|
|
BLAKE2b-256 checksum How to use checksums |
bbbdb4fdb108dc7bb2d981065afbe743f5f543224d9a8b1c5a4cd99ab6d59b6b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / epftoolbox2-2.3.0-py3-none-any.whl
| Download URL | epftoolbox2-2.3.0-py3-none-any.whl |
|---|---|
| Size | 224.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
73154034311ec5cf483bb9a27a3506442e6097cbdd537c3934561efe38486597
|
|
BLAKE2b-256 checksum How to use checksums |
d7556b8f2d8525eae7bec7bf58c5da07b116e4ed5433c34909679b6343f44fc0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for epftoolbox2 2.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.