second version
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
- Stahuje spotové ceny elektřiny z OTE CZ
|
||||
- Ukládá raw data bez vazby na lokalitu (sdílená tabulka)
|
||||
- Efektivní ceny (s marží) se dopočítávají per site přes view
|
||||
- Granularita: **15 minut** nativně (OTE CZ publikuje po hodinách → konvertujeme na 15min replikací)
|
||||
- Granularita: **15 minut** nativně (veřejný JSON `@@chart-data` s `time_resolution=PT15M`)
|
||||
|
||||
---
|
||||
|
||||
@@ -15,18 +15,20 @@
|
||||
|
||||
OTE CZ publikuje denní ceny zpravidla **den předem (D-1)** okolo 13:00–14:00 středoevropského času.
|
||||
|
||||
### Formát dat OTE CZ
|
||||
### Formát dat OTE CZ (implementace)
|
||||
|
||||
OTE publikuje hodinové ceny v EUR/MWh. Konverzní kroky:
|
||||
1. Stáhnout XML/JSON feed nebo scrape HTML tabulky
|
||||
2. Převést EUR/MWh → CZK/kWh (kurz ČNB nebo fixní koeficient dle konfigurace)
|
||||
3. Rozložit hodinový interval na 4× 15min sloty (stejná hodnota)
|
||||
4. Uložit do `market_interval_price`
|
||||
**Primární zdroj:** JSON grafu denního trhu (96 bodů na den):
|
||||
|
||||
### Alternativní API
|
||||
`https://www.ote-cr.cz/cs/kratkodobe-trhy/elektrina/denni-trh/@@chart-data?report_date=YYYY-MM-DD&time_resolution=PT15M`
|
||||
|
||||
- **OTE XML feed:** `https://www.ote-cr.cz/pubapi/v1/market-data/dam?date=YYYY-MM-DD&market=DAM&type=FIN`
|
||||
- Autentikace: nepotřebná pro veřejná data
|
||||
- Body: `data.dataLine[0].point[]` s `x` = 1…96 (15min slot), `y` = cena.
|
||||
- Jednotka ceny se bere z `axis.y.legend` (typicky **EUR/MWh**); kód podporuje i CZK/MWh a CZK/kWh.
|
||||
- Přepočet do `buy_raw_price_czk_kwh`: EUR/MWh → `× EUR_CZK / 1000`; CZK/MWh → `/ 1000`; CZK/kWh beze změny.
|
||||
- Časové razítko slotu: půlnoc v **timezone lokality** (`site.timezone`) + (x−1)×15 min → UTC.
|
||||
|
||||
### Legacy / alternativa
|
||||
|
||||
- **OTE pubapi:** `https://www.ote-cr.cz/pubapi/v1/market-data/dam?...` (hodinová data – v projektu už není primární)
|
||||
|
||||
---
|
||||
|
||||
@@ -36,14 +38,26 @@ OTE publikuje hodinové ceny v EUR/MWh. Konverzní kroky:
|
||||
|
||||
Samostatný modul (ne součást FastAPI, ale může být volán z ní jako task).
|
||||
|
||||
### Implementované provozní změny (2026-03)
|
||||
|
||||
- Robustní HTTP fetch přes `httpx`:
|
||||
- oddělené timeouty (`connect/read/write/pool`),
|
||||
- retry s exponential backoff pro transient chyby,
|
||||
- detailní chybové kódy (`http_status:*`, `timeout_or_connect:*`, `db_import:*`).
|
||||
- API endpoint `POST /api/v1/sites/{site_id}/prices/import` vrací při chybě konkrétní důvod.
|
||||
- Pokud je import volán bez `date`, importer nejdřív zkusí D+1 a při neúspěchu fallback na dnešní den.
|
||||
|
||||
### Kdy se spouští
|
||||
|
||||
| Trigger | Čas | Popis |
|
||||
|---|---|---|
|
||||
| Scheduled (cron) | každý den 14:00 CET | Stažení cen na zítřek (D+1) |
|
||||
| Scheduled (cron) | každý den 00:05 CET | Kontrola – ověření že dnešní data jsou v DB |
|
||||
| Manual trigger | na vyžádání | API endpoint `POST /admin/import-prices?date=YYYY-MM-DD` |
|
||||
| Retry | při chybě, 3× s backoffem | Automatický opakovaný pokus |
|
||||
| Scheduled (cron) | každý den 13:30 CET | Předběžný pokus importu (D+1 + doplnění dneška) |
|
||||
| Scheduled (cron) | každý den 14:00 CET | Hlavní import OTE (D+1 + doplnění dneška) |
|
||||
| Scheduled (cron) | každý den 00:05 CET | Backfill kontrola úplnosti 96 slotů pro dnešek/zítřek |
|
||||
| Manual trigger | na vyžádání | API endpoint `POST /api/v1/sites/{site_id}/prices/import?date=YYYY-MM-DD` |
|
||||
| Retry | při chybě | Automatický opakovaný pokus v importéru |
|
||||
|
||||
Cronové časy v tabulce jsou v **Europe/Prague** (CET/CEST): `AsyncIOScheduler` v `app/main.py` má `timezone=ZoneInfo("Europe/Prague")`; kontejner backendu má `TZ=Europe/Prague` v `docker-compose.yml`. Bez toho by se hodiny/minuty v cronu vyhodnocovaly v **UTC** a např. „13:30 CET“ by odpovídalo jinému okamžiku na hodinách.
|
||||
|
||||
### Logika importu
|
||||
|
||||
@@ -57,15 +71,11 @@ def import_prices_for_date(date: date, source: str = "OTE_CZ"):
|
||||
log.info("Data already exist, skipping")
|
||||
return
|
||||
|
||||
# 2. Stáhnout z OTE API
|
||||
raw_data = ote_client.fetch_dam_prices(date) # vrátí list hodinových cen v EUR/MWh
|
||||
# 2. Stáhnout chart-data (96× 15 min)
|
||||
raw_points = ote_client.fetch_chart_data_15m(date)
|
||||
|
||||
# 3. Konvertovat EUR/MWh → CZK/kWh
|
||||
eur_czk_rate = get_exchange_rate() # z konfigurace nebo ČNB API
|
||||
czk_per_kwh = [(price_eur_mwh * eur_czk_rate) / 1000 for price in raw_data]
|
||||
|
||||
# 4. Rozložit na 15min intervaly (1 hodina = 4 sloty se stejnou cenou)
|
||||
intervals = expand_hourly_to_15min(czk_per_kwh, date)
|
||||
# 3. Detekovat jednotku (EUR/MWh, CZK/MWh, …) a převést na CZK/kWh
|
||||
intervals = convert_points_to_czk_kwh(raw_points, date, site_tz)
|
||||
|
||||
# 5. Upsert do DB (idempotentní)
|
||||
db.upsert_many("market_interval_price", intervals, conflict_keys=["market_source", "interval_start"])
|
||||
@@ -98,12 +108,25 @@ Marže se konfigurují v `site_market_config`:
|
||||
| `sell_margin_fixed_czk` | Kč/kWh | -0.02 (srážka) |
|
||||
| `sell_margin_percent` | % | 0 |
|
||||
|
||||
**Zelený bonus** není součástí `fn_effective_sell_price` ani view efektivní prodejní ceny – jde o samostatný příjem z výroby, viz níže.
|
||||
|
||||
---
|
||||
|
||||
## Zelený bonus
|
||||
|
||||
- Konfigurace je na **`ems.asset_pv_array`** (konkrétní FVE pole), ne na `site` ani na střídači. Sloupce: `green_bonus_czk_kwh`, `green_bonus_valid_from`, `green_bonus_valid_to`, `green_bonus_meter_code`.
|
||||
- Sazba se typicky mění **ročně**; verzování je přes `valid_from` / `valid_to` (při změně uzavři staré období a zadej novou sazbu s novým `valid_from`).
|
||||
- **Výpočet příjmu** za interval: `ems.fn_green_bonus_revenue(pv_array_id, interval_start, production_wh)` kde `production_wh` je skutečná nebo odhadnutá výroba pole v daném 15min slotu (Wh). Bonus platí z **celkové výroby** pole (interní spotřeba, baterie, EV, TČ i export) – nejde o prodejní cenu.
|
||||
- **Nezahrnovat do** `fn_effective_sell_price` – spot + prodejní marže jsou odděleně od dotace; v auditu se bonus ukládá do `audit_interval.green_bonus_czk` (plní `fn_fill_audit_interval`).
|
||||
|
||||
Historicky mohou v DB zůstat sloupce `green_bonus_*` na `site_market_config`; efektivní prodejní cena je z nich se nepočítá.
|
||||
|
||||
---
|
||||
|
||||
## Konfigurace (env proměnné)
|
||||
|
||||
```env
|
||||
OTE_API_URL=https://www.ote-cr.cz/pubapi/v1/market-data/dam
|
||||
OTE_API_URL=https://www.ote-cr.cz/cs/kratkodobe-trhy/elektrina/denni-trh/@@chart-data
|
||||
OTE_IMPORT_HOUR=14 # hodina kdy se spouští denní import
|
||||
EUR_CZK_RATE=25.0 # fallback kurz pokud ČNB API nedostupné
|
||||
CNB_API_URL=https://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.xml
|
||||
|
||||
Reference in New Issue
Block a user