4.3 KiB
4.3 KiB
Modul: Market Prices (Spotové ceny OTE CZ)
Co modul dělá
- 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í)
Zdroj dat: OTE CZ
URL: https://www.ote-cr.cz/cs/kratkodobe-trhy/elektrina/denni-trh
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
OTE publikuje hodinové ceny v EUR/MWh. Konverzní kroky:
- Stáhnout XML/JSON feed nebo scrape HTML tabulky
- Převést EUR/MWh → CZK/kWh (kurz ČNB nebo fixní koeficient dle konfigurace)
- Rozložit hodinový interval na 4× 15min sloty (stejná hodnota)
- Uložit do
market_interval_price
Alternativní API
- 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
Kdo stahuje data
Python service: price_importer
Samostatný modul (ne součást FastAPI, ale může být volán z ní jako task).
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 |
Logika importu
# Pseudologika importu (implementace v price_importer.py)
def import_prices_for_date(date: date, source: str = "OTE_CZ"):
# 1. Zkontrolovat jestli data pro daný den už existují
existing = db.query("SELECT COUNT(*) FROM market_interval_price WHERE interval_start::date = %s AND market_source = %s", date, source)
if existing > 0 and not force_reimport:
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
# 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)
# 5. Upsert do DB (idempotentní)
db.upsert_many("market_interval_price", intervals, conflict_keys=["market_source", "interval_start"])
log.info(f"Imported {len(intervals)} intervals for {date}")
Struktura DB záznamu
Viz 03-data-model.md → tabulka market_interval_price.
Klíčové body:
buy_raw_price_czk_kwhasell_raw_price_czk_kwhjsou oddělené- Pro OTE CZ je v první verzi
sell_raw_price = buy_raw_price(reference cena) imported_atslouží pro audit importů
Efektivní ceny per site
Viz view market_vw_site_effective_price v 03-data-model.md.
Marže se konfigurují v site_market_config:
| Parametr | Typ | Příklad |
|---|---|---|
buy_margin_fixed_czk |
Kč/kWh | 0.05 (5 haléřů/kWh) |
buy_margin_percent |
% | 2.5 |
sell_margin_fixed_czk |
Kč/kWh | -0.02 (srážka) |
sell_margin_percent |
% | 0 |
Konfigurace (env proměnné)
OTE_API_URL=https://www.ote-cr.cz/pubapi/v1/market-data/dam
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
PRICE_IMPORT_RETRY_COUNT=3
PRICE_IMPORT_RETRY_BACKOFF_SEC=300
Monitoring a alerting
- Alert pokud do 16:00 nejsou v DB ceny na zítřek
- Log každého importu (datum, počet intervalů, zdroj, trvání)
- Endpoint
GET /health/prices?date=YYYY-MM-DD→ vrátí počet importovaných intervalů
Otevřené body
- Kurz EUR/CZK: fixní hodnota vs denní stahování z ČNB – rozhodnout před implementací
- OTE nabízí i intraday ceny – zatím neimplementujeme
- Sell price: OTE nemá oddělenou nákupní a prodejní raw cenu, obě = DAM cena; může se lišit u jiných zdrojů