Beta — Data under validation. Values may contain errors.

ED.RANGE

Generic time-series access for any of 120+ metrics — average value or total energy.

Generic + metrics

Signature

ED.RANGE(metric, [start], [end], [agg], [zone], [headers], [noDate], [method], [tz], [unit])

Description

Generic time-series accessor for any metric in the catalog. Output is a spill array with auto-formatted period and value columns. Use ED.METRICS() to discover available metrics.

Aggregation is controlled by agg: 0=native, 5min/10min/15min, 1=hourly, 2=daily (default), 3=monthly, 4=quarterly, 5=semiannual, 6=annual, 7=total.

**Granularity floor by metric family:** energy_prices (dayahead, pvpc, balancing) and energy_volumes (gen_*, demand_*, etc.) support all agg values down to native (0/RAW, "5min", "15min"). Calendar-based metrics — commodities (brent, eua_co2, gold, …), futures (futures_yr, …) and capture prices (capture_*) — are stored at daily granularity in calendar tables; aggregations finer than daily (agg=0/RAW, "5min", "15min", "30min", or agg=1/H) are silently promoted to daily rather than rejected. **C2 liquidation (c2_*) is bucketed by calendar date here too, but its rows are NOT daily**: the settlement files are hourly (c2_rrtt_price, c2_rrtt_cost) or quarter-hourly (the other eight), and each day is reduced with the metric's natural aggregate — AVG for prices, SUM for energies and costs. Sub-daily c2_* reads are NOT offered on any surface: the liquidation hour index is 0-based over most of the history and 1-based recently, so an hour-addressed lookup could not be trusted. Daily and coarser buckets are unaffected.

**Unit (unit parameter):** by default each bucket holds the AVERAGE of the stored values. For metrics whose stored unit is MW (generation, demand, balancing band capacity, cross-border flows) you can pass unit="MWh" to convert to total energy delivered in each bucket — multiplied by the metric's native sample interval (5 min for ESIOS Spain gen/demand, 15 min for balancing, 60 min for ENTSO-E). Calling it on a price (EUR/MWh) or already-MWh metric returns a 400 error.

Pass tz to switch the timezone interpretation: 0/madrid (default, DST-aware), 1/cet (UTC+1 fixed — 8760 hourly buckets/year), or 2/utc. See /docs/concepts for why this matters around the spring/autumn DST transitions. The method argument is a no-op for everything except capture_* metrics, where 0=realized and 1=PBF.

Parameters

NameTypeDefaultDescription
metric*stringMetric name (see ED.METRICS).
startdateStart. Defaults: omit end for today (Madrid, with a small overnight cutoff), omit start for 30 days before end.
enddateEnd. Omit for today.
agg0-7 | string2 (daily)Aggregation is controlled by agg: 0=native, 5min/10min/15min, 1=hourly, 2=daily (default), 3=monthly, 4=quarterly, 5=semiannual, 6=annual, 7=total.
zonestring"ES"Zone (electricity metrics only). Iberian "ES"/"PT", EU/external day-ahead zones, and the SENP islands "ES_CAN"/"ES_BAL" (demand + generation) / "ES_CEU"/"ES_MEL" (PVPC + thermal generation gen_diesel/gen_gas_turbine). The dispatch_price metric uses the per-subsystem SENP zones ("ES_CAN_GC"/"ES_CAN_TF"/"ES_CAN_GO"/"ES_CAN_HI"/"ES_CAN_PA"/"ES_CAN_LZFV" + "ES_BAL"/"ES_CEU"/"ES_MEL"). OMIP futures/forwards/swaps accept "ES" only.
headers0 | 10Set 1 for header row.
noDate0 | 10Set 1 to omit the period column.
method0 | 10capture_* metrics: 0=realized, 1=PBF. Ignored for other metrics.
tz0 | 1 | 2"madrid"Timezone interpretation.
unit"MW" | "MWh""MW"For MW-stored metrics: "MW" returns AVG (default). "MWh" converts to total energy via SUM × native_sample_minutes/60.

* Required.

Returns

Spill array — [period, value] rows with format inferred from the metric's unit (or MWh if unit="MWh").

Examples

=ED.RANGE("eua_co2", "2020-01-01",, 3)CO2 monthly since 2020
=ED.RANGE("dayahead", "2025-01-01", "2025-12-31", 1, "PT", 1)PT hourly day-ahead with headers
=ED.RANGE("demand_real", "2025-07-01", "2025-07-01", 0)Real demand 5-min for one day (MW)
=ED.RANGE("demand_real", "2025-01-01",, 6, , , , , , "MWh")Real demand annual energy (MWh) — total electricity consumed
=ED.RANGE("gen_total", "2025-01-01", "2025-12-31", 6, , , , , , "MWh")Total domestic generation 2025 (MWh) — virtual aggregate, T.Real real-time
=ED.RANGE("gen_total_measured", "2025-01-01", "2025-12-31", 6)Same total but from REE\'s official measured series (settled, MWh)
=ED.RANGE("gen_renewable", "2025-01-01", "2025-12-31", 3, , , , , , "MWh")Renewable generation monthly (MWh) — solar+wind+hydro+biomass+…
=ED.RANGE("gen_fossil", "2025-01-01", "2025-12-31", 6, , , , , , "MWh")Fossil-fuel generation 2025 — for emissions analysis
=ED.RANGE("afrr_energy_up", "2025-01-01",, 3)aFRR upward energy activated, monthly. Already MWh-stored — SUM aggregation auto-applied.
=ED.RANGE("gen_diesel", "2025-01-01",, 3, "ES_CAN")Canarias diesel-engine generation, monthly (SENP island-only tech)
=ED.RANGE("dispatch_price", "2025-01-01",, 3, "ES_CAN_TF")Tenerife SENP dispatch marginal cost, monthly (Sphgenf, EUR/MWh)
=ED.RANGE("dispatch_demand_price", "2025-01-01",, 3, "ES_CAN")Canarias SENP demand acquisition price, monthly (Sphdem, per-archipelago, EUR/MWh)
=ED.RANGE("brent", "2010-01-01",, 6)Brent annual since 2010
=ED.RANGE("futures_yr", "2015-01-01",, 2)OMIP Cal+1 front-contract daily history since 2015 (same contract ED.GET returns)
=ED.RANGE("forwards_m", "2015-01-01",, 3)OMIP Base forward front-month, monthly average — one of the 14 metrics that used to come back empty

Notes

  • Virtual gen aggregates (gen_total, gen_renewable, gen_thermal, gen_fossil) are computed on-the-fly: per civil hour the API averages the constituent gen_* metrics, then sums those over your bucket. They behave like any other MW metric — both unit=MW (default) and unit=MWh work.
  • gen_total constituents (ES peninsular): solar_pv, solar_thermal, wind, nuclear, hydro, combined_cycle, coal, cogeneration, biomass, fuel_gas, pumped_storage, plus ENTSO-E catch-alls (oil, geothermal, other_renewable, waste, other) for non-Iberian zones. With this set the totals match REE published peninsular generation within ~1-2%.
  • gen_total excludes gen_exchanges (interconnections, not domestic production) and gen_pumped_consumption (charging load, not generated energy). To include imports add gen_exchanges manually: =ED.RANGE("gen_total", …) + ED.RANGE("gen_exchanges", …).
  • gen_thermal includes nuclear + cogeneration; gen_fossil excludes nuclear. Use gen_fossil for emissions analysis.
  • **Authoritative alternative — gen_total_measured**: REE's official post-settlement total (ESIOS id 10043, sum of per-province measured generation). Hourly, ~2-3 day lag, already in MWh. Use this when your output needs to match REE's published reports to the decimal. The virtual gen_total is the same concept built from real-time T.Real indicators — slightly different numbers (1-2%) but available with no settlement lag.
  • **Auto-SUM for MWh-stored metrics**: when a metric is already stored as energy (MWh) — e.g. gen_total_measured, intraday_s*_energy, afrr_energy_up/down, mfrr_*_assign_* — bucket aggregation uses SUM instead of AVG. Asking for unit="MWh" on these is a no-op (already energy). Asking for unit="MW" is silently ignored (energy can't be converted back to power without knowing the time span).
  • Granularity floor: time-series metrics (energy_prices / energy_volumes / virtual gen_*) accept all agg values; calendar metrics silently promote sub-daily aggregations to daily. For commodities, futures and capture_* that is because they really are one row per day. **c2_* is different**: the C2 liquidation files are hourly (c2_rrtt_price, c2_rrtt_cost) or quarter-hourly (the other eight) — ED.RANGE still buckets them by calendar date, reducing each day with AVG for prices and SUM for energies/costs, so a daily c2_rrtt_cost figure is the day's TOTAL over its 24 hourly rows. There is deliberately no sub-daily c2_* read (and ED.GET returns empty for these metrics): the liquidation hour index is 0-based for most historical dates and 1-based for recent ones, so a per-hour value could not be trusted. Date bucketing is immune to that.
  • **SENP islands (ADR-026)**: ES_CAN / ES_BAL carry real demand + generation by tech (archipelago aggregate, incl. island-only gen_diesel / gen_gas_turbine / gen_steam, plus for Baleares gen_other_renewables / gen_auxiliary / gen_exchanges (Balearic link) / gen_waste); ES_CEU / ES_MEL carry PVPC plus thermal generation (gen_diesel / gen_gas_turbine). No island day-ahead. Requesting an unpopulated (metric, island) pair returns a 400 listing what IS available for that zone rather than an empty result.
  • **SENP dispatch cost (dispatch_price)**: hourly marginal cost of the island merit-order dispatch (Sphgenf, art. 67.f RD 738/2015) — a settlement cost, not a market price. Per-subsystem: ES_CAN_GC / ES_CAN_TF / ES_CAN_GO / ES_CAN_HI / ES_CAN_PA / ES_CAN_LZFV (Canarias) + ES_BAL / ES_CEU / ES_MEL. History from 2017-04 (ES_BAL from 2018-12); ES_CAN_TF / ES_CAN_GO consolidate at the monthly settlement (~M+1), the rest refresh intra-month (~D+2). No ES_CAN aggregate and no peninsular ES series.
  • **SENP demand price (dispatch_demand_price)**: the demand-side companion (Sphdem settlement, RD 738/2015) — a settlement cost, not a market price. PER-ARCHIPELAGO, so the aggregate ES_CAN IS valid: ES_CAN / ES_BAL / ES_CEU / ES_MEL. Modern era only, from 2018-12 (monthly C2). Not on peninsular ES nor on the per-subsystem ES_CAN_* zones.
  • **⚠ OMIP futures/forwards/swaps — the numbers changed, and 14 metrics started working.** Applies to the 18 OMIP metrics (futures_yr/q/m/ppa, futures_solar_*, futures_peak_*, forwards_*, swaps_*). **What it returns now:** one point per trading day, holding the settlement of the FRONT contract — the listed contract with the nearest delivery — for that instrument and tenor. That is the same contract ED.GET has always returned, so the two functions finally agree. **What it returned before:** the average of the ENTIRE forward curve, blended across all five OMIP instruments (Base + Solar + Peak + Forwards + Swaps), because the query filtered by contract type but not by instrument. On 2026-07-24 futures_yr returned **43.62** that way; the correct front-contract figure is **70.90**. If you have those older numbers in a workbook, expect a visible step up when the cells refresh — the old series was wrong, not merely different. **Also fixed:** the 14 non-Base metrics (forwards_*, swaps_*, futures_peak_*, futures_solar_*) used to return an empty series — and 0 when asked for a total; they now return their full history: from 2015-01-01 for forwards/swaps/peak, 2018-09-04 for solar, 2022-07-12 for the PPA curves. **Expect roll-over steps:** a front-contract series jumps when the front contract changes (each month/quarter/year), which is inherent to the convention, not a data error. **Zone "ES" only** — any other zone is now an explicit error rather than a silent Spanish number. **swaps_ppa stays empty on purpose**: OMIP has never listed an SWB PPA contract.
  • **FTB, FWB and SWB carry the same price for the same CONTRACT — by clearing-house rule.** Seeing futures_yr = forwards_yr = swaps_yr (and the same for _q / _m) is expected, not a bug: OMIClear assigns the same settlement reference price to every power derivative sharing an underlying asset, and the Base futures / forward / swap contracts all share SPEL BASE. They differ in contract type, cash-vs-physical delivery, open interest and traded volume — not in price. Checked contract by contract on every shared contract-day since 2015: no discrepancies. **But the SERIES are not guaranteed to match.** Each metric follows the front contract of its own instrument, and the instruments do not delist on the same calendar: OMIP drops an expiring month from the forwards board before it drops it from the futures board, and on those days the two metrics are quoting different deliveries. In practice forwards_m differs from futures_m on about 17 trading days a year (173 of 2,694 since 2015) — most recently 2026-06-30, 83.30 vs 90.00 EUR/MWh, because the forwards front had already rolled to Aug-26 while the futures front was still Jul-26. The yearly and quarterly tenors, and all the swaps_*, have never diverged. If you need a like-for-like comparison, compare a specific delivery rather than the two front series. futures_peak_* (SPEL PEAK) and futures_solar_* (SPEL SOLAR) have a different underlying asset, so the rule does not apply to them and their prices genuinely differ.
  • **A total (agg=7) over a window with no data returns #N/A, not 0.** Previously an empty window produced 0 — a perfectly plausible EUR/MWh figure and impossible to tell apart from a real one. This affects every metric family, not just OMIP. If you were relying on the old 0 (e.g. in a SUM), wrap the call: =IFNA(ED.RANGE(...), 0).

Related functions