diff --git a/dao/DOCS.md b/dao/DOCS.md
index cb1be64..b3c3fd6 100644
--- a/dao/DOCS.md
+++ b/dao/DOCS.md
@@ -234,7 +234,9 @@ Ook het ophalen van dynamische uurprijzen (day ahead prices) stel je in via
Daar is alles al ingevuld, maar je kunt het aanpassen aan jouw situatie:
De belangrijkste onderdelen zijn:
* source day ahead: waar haal je de data vandaan: nordpool is een goede
- eerste keuze
+ eerste keuze. Je kunt ook `energypriceforecast` gebruiken als forecast-fallback voor markten waar je voor publicatie van de officiele day-ahead prijs al een prijsreeks wilt gebruiken.
+ * energypriceforecast-api-url: optionele override voor de DAO-price feed van Energy Price Forecast EU
+ * energypriceforecast-country: optionele marktcode zoals `nl`, `de`, `dk1` of `no3`
* energy taxes consumption: energiebelasting (euro/kWh, ex BTW) bij afname
* energy taxes production: energiebelasting bij teruglevering (euro/kWh, ex BTW)
* cost supplier consumption: kosten leverancier voor levering (euro/kWh, ex BTW)
@@ -652,8 +654,10 @@ Het is allemaal optioneel.
| **meteoserver-key** | | string | | |
| **meteoserver-model** | | string | harmonie | keuze uit harmonie of gfs |
| **meteoserver-attempts** | | getal | 2 | aantal ophaal pogingen |
-| **prices** | source day ahead | string | nordpool | keuze uit: nordpool / entsoe / easyenergy / tibber |
+| **prices** | source day ahead | string | nordpool | keuze uit: nordpool / entsoe / easyenergy / tibber / energypriceforecast |
| | entsoe-api-key | string | | alleen bij entsoe als source |
+| | energypriceforecast-api-url | string, url | https://api.energypriceforecast.eu/api/v1/dao/prices | optioneel, alleen bij energypriceforecast |
+| | energypriceforecast-country | string | | optioneel, alleen bij energypriceforecast |
| | regular high | getal | | |
| | regular low | getal | | |
| | switch to low | integer | 23 | |
@@ -923,11 +927,19 @@ De meteodata worden opgehaald bij meteoserver. Ook hiervoor heb je een key nodig
### **prices**
* source day ahead, default "nordpool"
- Hier bepaal je waar je je day ahead prijzen vandaan wilt halen. Je hebt de keuze uit drie bronnen:
+ Hier bepaal je waar je je day ahead prijzen vandaan wilt halen. Je hebt de keuze uit vijf bronnen:
* nordpool
* entsoe
* easyenergy
- * tibber
+ * tibber
+ * energypriceforecast
+
+ Kies je voor **energypriceforecast**, dan gebruikt DAO de aparte DAO-feed van Energy Price Forecast EU. Die feed combineert officiele day-ahead prijzen met forecast-slots voor uren die nog niet officieel gepubliceerd zijn.
+ * energypriceforecast-api-url:
+ Optionele override voor de DAO-price feed. Standaard:
+ `https://api.energypriceforecast.eu/api/v1/dao/prices`
+ * energypriceforecast-country:
+ Optionele expliciete marktcode voor de feed, bijvoorbeeld `nl`, `de`, `dk1` of `no3`. Laat je dit leeg, dan probeert DAO te mappen vanuit de ingestelde landcode.
Als je kiest voor **entsoe** dan moet je hieronder een api key invullen.
* entsoe-api-key:
diff --git a/dao/data/options_example.json b/dao/data/options_example.json
index 8043d5f..120e073 100644
--- a/dao/data/options_example.json
+++ b/dao/data/options_example.json
@@ -13,6 +13,8 @@
"prices": {
"source day ahead": "nordpool",
"entsoe-api-key": "!secret entsoe-api-key",
+ "energypriceforecast-api-url": "https://api.energypriceforecast.eu/api/v1/dao/prices",
+ "energypriceforecast-country": "nl",
"regular high": 0.5,
"regular low": 0.4,
"switch to low": 23,
@@ -590,4 +592,4 @@
}
]
}
-}
\ No newline at end of file
+}
diff --git a/dao/data/options_start.json b/dao/data/options_start.json
index 359b22b..e683dc2 100644
--- a/dao/data/options_start.json
+++ b/dao/data/options_start.json
@@ -12,6 +12,8 @@
"meteoserver-key": "!secret meteoserver-key",
"prices": {
"source day ahead": "nordpool",
+ "energypriceforecast-api-url": "https://api.energypriceforecast.eu/api/v1/dao/prices",
+ "energypriceforecast-country": "nl",
"regular high": 0.50,
"regular low": 0.40,
"switch to low": 23,
diff --git a/dao/lib/da_prices.py b/dao/lib/da_prices.py
index 55cedce..76b9e5b 100644
--- a/dao/lib/da_prices.py
+++ b/dao/lib/da_prices.py
@@ -20,6 +20,27 @@ class DaPrices:
self.interval = str(config.interval or "1hour").lower()
self.country = country if country is not None else "NL"
+ def _resolve_energypriceforecast_country(self):
+ configured = getattr(self.config.prices, "energypriceforecast_country", None)
+ if configured:
+ return str(configured).strip().lower()
+ mapping = {
+ "NL": "nl",
+ "BE": "be",
+ "DE": "de",
+ "FR": "fr",
+ "AT": "at",
+ "CZ": "cz",
+ "DK1": "dk1",
+ "DK2": "dk2",
+ "NO1": "no1",
+ "NO2": "no2",
+ "NO3": "no3",
+ "NO4": "no4",
+ "NO5": "no5",
+ }
+ return mapping.get(str(self.country or "").upper(), "nl")
+
def get_prices(
self, source, _start: datetime.datetime = None, _end: datetime.datetime = None
):
@@ -192,6 +213,44 @@ class DaPrices:
)
self.db_da.savedata(df_db)
+ if source.lower() == "energypriceforecast":
+ now_ts = datetime.datetime.now(datetime.timezone.utc).timestamp()
+ end_ts = end.timestamp() if hasattr(end, "timestamp") else now_ts + 48 * 3600
+ hours = max(1, min(168, math.ceil((end_ts - now_ts) / 3600)))
+ api_url = (
+ getattr(self.config.prices, "energypriceforecast_api_url", None)
+ or "https://api.energypriceforecast.eu/api/v1/dao/prices"
+ )
+ country = self._resolve_energypriceforecast_country()
+ url = f"{api_url}?country={country}&hours={hours}"
+ resp = get(url, timeout=15)
+ resp.raise_for_status()
+ payload = json.loads(resp.text)
+ entries = payload.get("entries") or []
+ logging.info(
+ f"Day ahead prijzen van Energy Price Forecast EU ({country}): \n"
+ f"{pp.pformat(entries[:24], indent=2)}"
+ )
+ df_db = pd.DataFrame(columns=["time", "code", "value"])
+ for entry in entries:
+ start_raw = str(entry.get("start") or "")
+ value = entry.get("value")
+ if not start_raw:
+ continue
+ try:
+ dt = datetime.datetime.fromisoformat(start_raw.replace("Z", "+00:00"))
+ time_stamp = int(dt.timestamp())
+ value = float(value)
+ except (TypeError, ValueError):
+ continue
+ df_db.loc[df_db.shape[0]] = [str(time_stamp), "da", value]
+ logging.debug(
+ f"Day ahead prijzen (source: energypriceforecast, db-records): \n "
+ f"{df_db.to_string(index=False)}"
+ )
+ self.db_da.savedata(df_db)
+ return
+
if source.lower() == "tibber":
now_ts = datetime.datetime.now().timestamp()
get_ts = start.timestamp()
diff --git a/dao/prog/config/models/pricing.py b/dao/prog/config/models/pricing.py
index e63d9ba..06c3060 100644
--- a/dao/prog/config/models/pricing.py
+++ b/dao/prog/config/models/pricing.py
@@ -11,12 +11,12 @@ from datetime import date
class PricingConfig(BaseModel):
"""Day-ahead pricing and tariff configuration."""
- source_day_ahead: Literal['nordpool', 'entsoe', 'tibber'] = Field(
+ source_day_ahead: Literal['nordpool', 'entsoe', 'tibber', 'energypriceforecast'] = Field(
default='nordpool',
alias="source day ahead",
description="Source for day-ahead prices",
json_schema_extra={
- "x-help": "Data source for day-ahead electricity market prices. 'nordpool' for Nordic/Baltic, 'entsoe' for European markets, 'tibber' if using Tibber integration.",
+ "x-help": "Data source for day-ahead electricity market prices. 'nordpool' for Nordic/Baltic, 'entsoe' for European markets, 'tibber' if using Tibber integration, 'energypriceforecast' for forecast fallback from Energy Price Forecast EU.",
"x-ui-section": "Prices"
}
)
@@ -40,6 +40,42 @@ class PricingConfig(BaseModel):
}
}
)
+ energypriceforecast_api_url: Optional[str] = Field(
+ default="https://api.energypriceforecast.eu/api/v1/dao/prices",
+ alias="energypriceforecast-api-url",
+ description="Energy Price Forecast EU DAO API URL",
+ json_schema_extra={
+ "x-help": "Custom API endpoint for Energy Price Forecast EU DAO prices. Expected response: format=dao-prices with entries[].",
+ "x-ui-section": "Prices",
+ "x-ui-rules": {
+ "effect": "SHOW",
+ "condition": {
+ "scope": "#/properties/source_day_ahead",
+ "schema": {
+ "const": "energypriceforecast"
+ }
+ }
+ }
+ }
+ )
+ energypriceforecast_country: Optional[str] = Field(
+ default=None,
+ alias="energypriceforecast-country",
+ description="Override country code for Energy Price Forecast EU",
+ json_schema_extra={
+ "x-help": "Optional explicit country/market code for Energy Price Forecast EU, for example 'nl', 'de', 'dk1' or 'no3'. Leave empty to map from DAO country automatically.",
+ "x-ui-section": "Prices",
+ "x-ui-rules": {
+ "effect": "SHOW",
+ "condition": {
+ "scope": "#/properties/source_day_ahead",
+ "schema": {
+ "const": "energypriceforecast"
+ }
+ }
+ }
+ }
+ )
# Date-based tariff configurations (date string -> value)
energy_taxes_consumption: dict[str, float] = Field(
@@ -167,7 +203,7 @@ Configure electricity market prices and tariff components for accurate cost opti
## Price Components
Total electricity cost consists of:
-1. **Market price**: Day-ahead spot price (nordpool/entsoe/tibber)
+1. **Market price**: Day-ahead spot price (nordpool/entsoe/tibber/energypriceforecast)
2. **Energy taxes**: Government energy taxes
3. **Supplier costs**: Your supplier's markup/fees
4. **VAT**: Value-added tax on sum of above
@@ -192,6 +228,7 @@ System uses tariff active on optimization date.
- **nordpool**: Nord Pool (Nordic/Baltic markets)
- **entsoe**: ENTSO-E Transparency Platform (all European markets)
- **tibber**: Tibber API (if using Tibber as supplier)
+- **energypriceforecast**: Energy Price Forecast EU DAO endpoint (forecast fallback before official publication)
## Tips