python-entsoe 0.4.2__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,105 +1,61 @@
1
1
  ---
2
2
  name: entsoe
3
3
  description: Query European electricity market data (ENTSO-E Transparency Platform). Use when the user asks about electricity prices, load, generation, transmission, or balancing data for European countries.
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  ---
6
6
 
7
7
  # ENTSO-E Data Assistant
8
8
 
9
- You have access to the `python-entsoe` CLI and library for querying the ENTSO-E Transparency Platform (European electricity market data).
9
+ You have access to the `python-entsoe` library and CLI for querying the ENTSO-E Transparency Platform (European electricity market data).
10
10
 
11
- ## CLI Reference
11
+ ## When to use what
12
12
 
13
- ### Prices
13
+ - **Python scripts** (default): reproducible, composable, saveable. Use for any data work the user will want to keep or iterate on.
14
+ - **CLI**: quick one-shot lookups, exploration, sanity checks. Use when the user wants a fast answer they won't need again.
15
+ - **If unsure**: ask the user whether they want a script or a quick CLI check.
14
16
 
15
- ```bash
16
- # Day-ahead prices for one country
17
- entsoe prices day-ahead -s 2024-06-01 -e 2024-06-08 --country FR
18
-
19
- # Multi-country
20
- entsoe prices day-ahead -s 2024-06-01 -e 2024-06-08 --country FR --country ES
21
- ```
22
-
23
- ### Load
24
-
25
- ```bash
26
- # Actual total system load
27
- entsoe load actual -s 2024-06-01 -e 2024-06-08 --country FR
28
-
29
- # Day-ahead load forecast
30
- entsoe load forecast -s 2024-06-01 -e 2024-06-08 --country FR
31
- ```
32
-
33
- ### Generation
34
-
35
- ```bash
36
- # Actual generation per type (all types)
37
- entsoe generation actual -s 2024-06-01 -e 2024-06-08 --country FR
38
-
39
- # Filter by PSR type
40
- entsoe generation actual -s 2024-06-01 -e 2024-06-08 --country FR --psr solar --psr wind_onshore
41
-
42
- # Generation forecast (wind/solar)
43
- entsoe generation forecast -s 2024-06-01 -e 2024-06-08 --country FR
44
-
45
- # Installed capacity
46
- entsoe generation capacity -s 2024-06-01 -e 2024-06-08 --country FR
47
-
48
- # Per production unit
49
- entsoe generation per-plant -s 2024-06-01 -e 2024-06-08 --country FR
50
- ```
51
-
52
- ### Transmission
53
-
54
- ```bash
55
- # Physical cross-border flows
56
- entsoe transmission flows -s 2024-06-01 -e 2024-06-08 --from FR --to ES
57
-
58
- # Scheduled commercial exchanges
59
- entsoe transmission exchanges -s 2024-06-01 -e 2024-06-08 --from FR --to ES
60
-
61
- # Net transfer capacity
62
- entsoe transmission capacity -s 2024-06-01 -e 2024-06-08 --from FR --to ES
63
- ```
64
-
65
- ### Balancing
17
+ ## Python Library (default)
66
18
 
67
- ```bash
68
- # Imbalance prices
69
- entsoe balancing prices -s 2024-06-01 -e 2024-06-08 --country FR
70
-
71
- # Imbalance volumes
72
- entsoe balancing volumes -s 2024-06-01 -e 2024-06-08 --country FR
73
- ```
19
+ ```python
20
+ from entsoe import Client
74
21
 
75
- ### Exec (ad-hoc pandas expressions)
22
+ client = Client() # reads config file, then ENTSOE_API_KEY env var
76
23
 
77
- Run any pandas expression against fetched data:
24
+ # --- Prices ---
25
+ df = client.prices.day_ahead("2024-06-01", "2024-06-08", country="FR")
26
+ df = client.prices.day_ahead("2024-06-01", "2024-06-08", country=["FR", "ES"])
78
27
 
79
- ```bash
80
- # Descriptive statistics on prices
81
- entsoe exec prices day-ahead -s 2024-06-01 -e 2024-06-08 -c FR -x "df.describe()"
28
+ # --- Load ---
29
+ df = client.load.actual("2024-06-01", "2024-06-08", country="FR")
30
+ df = client.load.forecast("2024-06-01", "2024-06-08", country="FR")
82
31
 
83
- # Daily mean load
84
- entsoe exec load actual -s 2024-06-01 -e 2024-06-08 -c FR -x "df.resample('D').mean()"
32
+ # --- Generation ---
33
+ df = client.generation.actual("2024-06-01", "2024-06-08", country="FR")
34
+ df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type="solar")
35
+ df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type=["solar", "wind_onshore"])
36
+ df = client.generation.forecast("2024-06-01", "2024-06-08", country="FR")
37
+ df = client.generation.installed_capacity("2024-06-01", "2024-06-08", country="FR")
38
+ df = client.generation.per_plant("2024-06-01", "2024-06-08", country="FR")
85
39
 
86
- # Generation with PSR filter
87
- entsoe exec generation actual -s 2024-06-01 -e 2024-06-08 -c FR --psr solar -x "df.head(20)"
40
+ # --- Transmission ---
41
+ df = client.transmission.crossborder_flows("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
42
+ df = client.transmission.scheduled_exchanges("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
43
+ df = client.transmission.net_transfer_capacity("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
88
44
 
89
- # Transmission analysis
90
- entsoe exec transmission flows -s 2024-06-01 -e 2024-06-08 --from FR --to ES -x "df.describe()"
45
+ # --- Balancing ---
46
+ df = client.balancing.imbalance_prices("2024-06-01", "2024-06-08", country="FR")
47
+ df = client.balancing.imbalance_volumes("2024-06-01", "2024-06-08", country="FR")
91
48
  ```
92
49
 
93
- ### Output formats
50
+ ### Key conventions
94
51
 
95
- All commands support:
96
-
97
- ```bash
98
- --format table # Default: Rich table in terminal
99
- --format csv # CSV output
100
- --format json # JSON output
101
- --output file.csv # Write to file instead of stdout
102
- ```
52
+ - **Timezone**: All string dates are interpreted in Europe/Brussels (CET) by default
53
+ - **Auto year-splitting**: Date ranges > 1 year are automatically split into yearly API requests
54
+ - **Rate limiting**: ENTSO-E has a 400-request/minute limit; the library handles retries
55
+ - **Multi-value support**: Pass lists for `country` or `psr_type` to get combined results with label columns
56
+ - **Transmission**: Uses `country_from`/`country_to`; multi-value adds a `border` column
57
+ - **No caching**: Data is fetched fresh on each request
58
+ - **Custom exceptions**: `ENTSOEError`, `AuthenticationError`, `APIResponseError`, `InvalidParameterError`, `NetworkError`
103
59
 
104
60
  ## Country Codes
105
61
 
@@ -143,60 +99,78 @@ Bidding zones: `DK_1`, `DK_2`, `NO_1`–`NO_5`, `SE_1`–`SE_4`, `IT_NORTH`, `IT
143
99
  | waste | Waste | B17 |
144
100
  | other | Other | B20 |
145
101
 
146
- Use shorthands with `--psr` (e.g. `--psr solar --psr wind_onshore`), or ENTSO-E codes (e.g. `--psr B16`).
102
+ Use shorthands with `psr_type=` (Python) or `--psr` (CLI), e.g. `psr_type="solar"` or `--psr solar`.
147
103
 
148
- ## Python Library
104
+ ## CLI Reference (quick lookups)
149
105
 
150
- ```python
151
- from entsoe import ENTSOEClient
106
+ ### Catalog (offline)
152
107
 
153
- client = ENTSOEClient() # reads config file, then ENTSOE_API_KEY env var
108
+ ```bash
109
+ entsoe catalog list # All data categories and endpoints
110
+ entsoe catalog show prices # Detailed info for a category
111
+ entsoe catalog countries # Country/bidding zone codes
112
+ entsoe catalog psr # PSR (generation) types
113
+ entsoe catalog borders # Border groups and interconnections
114
+ ```
115
+
116
+ ### Fetch data
154
117
 
118
+ ```bash
155
119
  # Prices
156
- df = client.prices.day_ahead("2024-06-01", "2024-06-08", country="FR")
157
- df = client.prices.day_ahead("2024-06-01", "2024-06-08", country=["FR", "ES"])
120
+ entsoe prices day-ahead -s 2024-06-01 -e 2024-06-08 -c FR
121
+ entsoe prices day-ahead -P week -c FR,ES # Period shorthand
158
122
 
159
123
  # Load
160
- df = client.load.actual("2024-06-01", "2024-06-08", country="FR")
161
- df = client.load.forecast("2024-06-01", "2024-06-08", country="FR")
124
+ entsoe load actual -P month -c FR
125
+ entsoe load forecast -s 2024-06-01 -e 2024-06-08 -c FR
162
126
 
163
127
  # Generation
164
- df = client.generation.actual("2024-06-01", "2024-06-08", country="FR")
165
- df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type="solar")
166
- df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type=["solar", "wind_onshore"])
167
- df = client.generation.forecast("2024-06-01", "2024-06-08", country="FR")
168
- df = client.generation.installed_capacity("2024-06-01", "2024-06-08", country="FR")
169
- df = client.generation.per_plant("2024-06-01", "2024-06-08", country="FR")
128
+ entsoe generation actual -P week -c FR --psr solar --psr wind_onshore
129
+ entsoe generation forecast -P week -c FR
130
+ entsoe generation capacity -s 2024-01-01 -e 2024-12-31 -c FR
131
+ entsoe generation per-plant -P week -c FR
170
132
 
171
133
  # Transmission
172
- df = client.transmission.crossborder_flows("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
173
- df = client.transmission.scheduled_exchanges("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
174
- df = client.transmission.net_transfer_capacity("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
134
+ entsoe transmission flows -P week --from FR --to ES
135
+ entsoe transmission exchanges -P week --border ES-*,FR-*
175
136
 
176
137
  # Balancing
177
- df = client.balancing.imbalance_prices("2024-06-01", "2024-06-08", country="FR")
178
- df = client.balancing.imbalance_volumes("2024-06-01", "2024-06-08", country="FR")
138
+ entsoe balancing prices -P week -c FR
139
+ entsoe balancing volumes -P week -c FR
179
140
  ```
180
141
 
181
- ## Configuration
142
+ ### Exec (ad-hoc pandas)
143
+
144
+ `exec` is a subcommand **within each namespace**:
182
145
 
183
146
  ```bash
184
- # Store your API key persistently (recommended)
185
- entsoe config set api-key YOUR_KEY
147
+ entsoe prices exec day-ahead -P week -c FR -x "df.describe()"
148
+ entsoe load exec actual -P 7d -c FR -x "df.resample('D').mean()"
149
+ entsoe generation exec actual -P month -c FR --psr solar -x "df.head(20)"
150
+ entsoe transmission exec flows -P week --from FR --to ES -x "df.describe()"
151
+ entsoe balancing exec prices -P week -c NL -x "df.describe()"
152
+ ```
186
153
 
187
- # Verify it's stored
188
- entsoe config get api-key
154
+ ### Period shorthand (`-P`)
155
+
156
+ All data commands support `-P` as an alternative to `--start`/`--end`:
157
+
158
+ ```
159
+ -P today, -P yesterday, -P week, -P month, -P ytd, -P 14d
160
+ ```
161
+
162
+ ### Output options
163
+
164
+ ```
165
+ --format table|csv|json (default: table)
166
+ --output file.csv (write to file instead of stdout)
189
167
  ```
190
168
 
191
- The config file is stored at `~/.config/entsoe/config.toml`.
169
+ ## Configuration
192
170
 
193
- ## Key Conventions
171
+ ```bash
172
+ entsoe config set api-key YOUR_KEY
173
+ entsoe config get api-key
174
+ ```
194
175
 
195
- - **Timezone**: All string dates are interpreted in Europe/Brussels (CET) by default
196
- - **Auto year-splitting**: Date ranges exceeding 1 year are automatically split into yearly API requests
197
- - **Rate limiting**: ENTSO-E has a 400-request/minute limit; the library handles retries
198
- - **Multi-value support**: Pass lists for country or psr_type to get combined results with label columns
199
- - **Transmission**: Uses `--from`/`--to` instead of `--country`; multi-value adds a `border` column
200
- - **API key resolution**: config file (`~/.config/entsoe/config.toml`) > `ENTSOE_API_KEY` env var
201
- - **No caching**: Data is fetched fresh on each request (unlike python-esios)
202
- - **Custom exceptions**: `ENTSOEError`, `AuthenticationError`, `APIResponseError`, `InvalidParameterError`, `NetworkError`
176
+ Config file: `~/.config/entsoe/config.toml`. API key resolution: config file > `ENTSOE_API_KEY` env var.
entsoe/__init__.py CHANGED
@@ -3,6 +3,7 @@
3
3
  from .client import Client, ENTSOEClient
4
4
  from .exceptions import ENTSOEError, InvalidParameterError, NoDataError, RateLimitError
5
5
  from ._mappings import COUNTRY_NAMES, PSR_CODES, country_name, psr_name
6
+ from . import catalog
6
7
 
7
8
  __version__ = "0.4.2"
8
9
  __all__ = [
@@ -14,6 +15,7 @@ __all__ = [
14
15
  "NoDataError",
15
16
  "PSR_CODES",
16
17
  "RateLimitError",
18
+ "catalog",
17
19
  "country_name",
18
20
  "psr_name",
19
21
  ]
entsoe/_mappings.py CHANGED
@@ -354,3 +354,150 @@ def psr_name(identifier: str) -> str:
354
354
  """
355
355
  code = lookup_psr(identifier)
356
356
  return PSR_TYPES[code]
357
+
358
+
359
+ # ── Border / neighbour data (for CLI --border flag) ───────────────────────
360
+
361
+ # Direct interconnections between bidding zones used by ENTSO-E.
362
+ # Each key maps to the set of zones it has a physical border with.
363
+ NEIGHBOURS: dict[str, set[str]] = {
364
+ "ES": {"FR", "PT"},
365
+ "FR": {"ES", "BE", "CH", "DE_LU", "GB", "IT_NORTH"},
366
+ "PT": {"ES"},
367
+ "BE": {"FR", "NL", "DE_LU", "GB"},
368
+ "NL": {"BE", "DE_LU", "GB", "NO_2", "DK_1"},
369
+ "DE_LU": {"FR", "BE", "NL", "AT", "CH", "CZ", "DK_1", "DK_2", "PL", "SE_4", "NO_2"},
370
+ "AT": {"DE_LU", "CH", "CZ", "HU", "SI", "IT_NORTH"},
371
+ "CH": {"FR", "DE_LU", "AT", "IT_NORTH"},
372
+ "IT_NORTH": {"FR", "CH", "AT", "SI"},
373
+ "GB": {"FR", "BE", "NL", "IE_SEM", "NO_2"},
374
+ "IE_SEM": {"GB"},
375
+ "DK_1": {"DE_LU", "NL", "NO_2", "SE_3", "DK_2"},
376
+ "DK_2": {"DE_LU", "DK_1", "SE_4"},
377
+ "NO_1": {"NO_2", "NO_3", "NO_5", "SE_3"},
378
+ "NO_2": {"NO_1", "NO_5", "NL", "DK_1", "DE_LU", "GB"},
379
+ "NO_3": {"NO_1", "NO_4", "NO_5", "SE_2"},
380
+ "NO_4": {"NO_3", "SE_1", "SE_2", "FI"},
381
+ "NO_5": {"NO_1", "NO_2", "NO_3"},
382
+ "SE_1": {"NO_4", "SE_2", "FI"},
383
+ "SE_2": {"NO_3", "NO_4", "SE_1", "SE_3"},
384
+ "SE_3": {"NO_1", "DK_1", "SE_2", "SE_4", "FI"},
385
+ "SE_4": {"DE_LU", "DK_2", "SE_3", "PL", "LT"},
386
+ "FI": {"NO_4", "SE_1", "SE_3", "EE"},
387
+ "EE": {"FI", "LV"},
388
+ "LV": {"EE", "LT"},
389
+ "LT": {"LV", "SE_4", "PL"},
390
+ "PL": {"DE_LU", "CZ", "SK", "LT", "SE_4"},
391
+ "CZ": {"DE_LU", "AT", "PL", "SK"},
392
+ "SK": {"CZ", "PL", "HU"},
393
+ "HU": {"AT", "SK", "RO", "HR", "SI", "RS"},
394
+ "SI": {"AT", "IT_NORTH", "HU", "HR"},
395
+ "HR": {"HU", "SI", "RS", "BA"},
396
+ "RS": {"HU", "HR", "BA", "RO", "BG", "MK", "ME", "AL"},
397
+ "BA": {"HR", "RS", "ME"},
398
+ "RO": {"HU", "RS", "BG"},
399
+ "BG": {"RO", "RS", "MK", "GR", "TR"},
400
+ "MK": {"RS", "BG", "GR", "AL"},
401
+ "GR": {"BG", "MK", "AL", "IT_SUD", "TR"},
402
+ "IT_SUD": {"GR"},
403
+ "AL": {"RS", "MK", "GR", "ME"},
404
+ "ME": {"RS", "BA", "AL"},
405
+ "TR": {"BG", "GR"},
406
+ }
407
+
408
+ # Named border groups for convenient CLI usage.
409
+ # ── Border parsing (library-level) ────────────────────────────────────────
410
+
411
+
412
+ def parse_borders(specs: str | list[str]) -> list[tuple[str, str]]:
413
+ """Parse border specifications into deduplicated (from, to) pairs.
414
+
415
+ Supported formats:
416
+
417
+ - ``"ES-FR"`` — single border
418
+ - ``"ES-FR,ES-PT"`` — comma-separated within a string
419
+ - ``["ES-*", "FR-*"]`` — list with wildcards (all neighbours)
420
+ - ``"iberian"`` — named group
421
+
422
+ Deduplicates pairs while preserving insertion order, so
423
+ ``"ES-*,FR-*"`` won't produce ``(ES, FR)`` and ``(FR, ES)`` twice
424
+ if both wildcards expand to include each other.
425
+
426
+ Args:
427
+ specs: A single spec string or list of spec strings.
428
+
429
+ Returns:
430
+ Deduplicated list of ``(country_from, country_to)`` tuples.
431
+
432
+ Raises:
433
+ InvalidParameterError: On unknown country or invalid spec format.
434
+ """
435
+ from .exceptions import InvalidParameterError
436
+
437
+ if isinstance(specs, str):
438
+ specs = [specs]
439
+
440
+ flat = [v.strip() for raw in specs for v in raw.split(",") if v.strip()]
441
+ seen: set[tuple[str, str]] = set()
442
+ pairs: list[tuple[str, str]] = []
443
+
444
+ def _add(pair: tuple[str, str]) -> None:
445
+ if pair not in seen:
446
+ seen.add(pair)
447
+ pairs.append(pair)
448
+
449
+ for spec in flat:
450
+ low = spec.lower()
451
+
452
+ # Named group
453
+ if low in BORDER_GROUPS:
454
+ for p in BORDER_GROUPS[low]:
455
+ _add(p)
456
+ continue
457
+
458
+ # Pair: A-B or A-*
459
+ if "-" in spec:
460
+ parts = spec.split("-", 1)
461
+ a, b = parts[0].strip().upper(), parts[1].strip().upper()
462
+ if b == "*":
463
+ if a not in NEIGHBOURS:
464
+ raise InvalidParameterError(
465
+ f"No known neighbours for '{a}'. "
466
+ f"Available: {', '.join(sorted(NEIGHBOURS.keys()))}"
467
+ )
468
+ for n in sorted(NEIGHBOURS[a]):
469
+ _add((a, n))
470
+ else:
471
+ _add((a, b))
472
+ continue
473
+
474
+ raise InvalidParameterError(
475
+ f"Invalid border spec '{spec}'. Use A-B, A-*, or a group name "
476
+ f"({', '.join(sorted(BORDER_GROUPS.keys()))})."
477
+ )
478
+
479
+ return pairs
480
+
481
+
482
+ BORDER_GROUPS: dict[str, list[tuple[str, str]]] = {
483
+ "iberian": [("ES", "FR"), ("ES", "PT"), ("PT", "ES"), ("FR", "ES")],
484
+ "nordic": [
485
+ ("NO_1", "NO_2"), ("NO_1", "NO_3"), ("NO_1", "NO_5"), ("NO_1", "SE_3"),
486
+ ("NO_2", "NO_1"), ("NO_2", "NO_5"), ("NO_2", "NL"), ("NO_2", "DK_1"), ("NO_2", "DE_LU"), ("NO_2", "GB"),
487
+ ("NO_3", "NO_1"), ("NO_3", "NO_4"), ("NO_3", "NO_5"), ("NO_3", "SE_2"),
488
+ ("NO_4", "NO_3"), ("NO_4", "SE_1"), ("NO_4", "SE_2"), ("NO_4", "FI"),
489
+ ("NO_5", "NO_1"), ("NO_5", "NO_2"), ("NO_5", "NO_3"),
490
+ ("SE_1", "NO_4"), ("SE_1", "SE_2"), ("SE_1", "FI"),
491
+ ("SE_2", "NO_3"), ("SE_2", "NO_4"), ("SE_2", "SE_1"), ("SE_2", "SE_3"),
492
+ ("SE_3", "NO_1"), ("SE_3", "DK_1"), ("SE_3", "SE_2"), ("SE_3", "SE_4"), ("SE_3", "FI"),
493
+ ("SE_4", "DE_LU"), ("SE_4", "DK_2"), ("SE_4", "SE_3"), ("SE_4", "PL"), ("SE_4", "LT"),
494
+ ("DK_1", "DK_2"), ("DK_1", "DE_LU"), ("DK_1", "NL"), ("DK_1", "NO_2"), ("DK_1", "SE_3"),
495
+ ("DK_2", "DK_1"), ("DK_2", "DE_LU"), ("DK_2", "SE_4"),
496
+ ("FI", "NO_4"), ("FI", "SE_1"), ("FI", "SE_3"), ("FI", "EE"),
497
+ ],
498
+ "baltic": [
499
+ ("EE", "FI"), ("EE", "LV"),
500
+ ("LV", "EE"), ("LV", "LT"),
501
+ ("LT", "LV"), ("LT", "SE_4"), ("LT", "PL"),
502
+ ],
503
+ }