Skip to content

Market & Sessions

The Market, also called the Internal Market or Local Energy Market (LEM), is arguably RECreation's most important tool. It generates transactions between members, using the information currently uploaded, for every available session (status OPEN).

Because it is computationally expensive, the Market is an independent module. It is also the first operation to run, which makes it the front line for error checking: most problems with a community's configuration surface here, and only rarely in the Settlement module that runs afterwards.

Start here when something fails

If a community does not behave as expected, run the Market first. Missing meter data, incomplete contracts or opportunity costs, and bilateral prices that do not cover every session are all reported at this stage, before settlement is ever attempted.

flowchart LR
    A[Community configuration<br/>and uploaded data] --> B[Market<br/>clearing]
    B -->|Transactions| C[Settlement]

What is a Session?

A Session corresponds to one Interval Settlement Period (ISP) — a 15-minute interval aligned with smart meter reading resolution and OMIE's market time granularity. Sessions are the atomic unit for market clearing and settlement.

Session Lifecycle

%%{init: {"themeVariables": {"fontSize": "16px"}, "flowchart": {"nodeSpacing": 60, "rankSpacing": 120, "curve": "basis"}}}%%
flowchart LR
    S(( )) -->|Session created| OPEN
    OPEN -->|Market clearing runs| CLEARED
    CLEARED -->|Settlement computed| SETTLED
    SETTLED --> E(( ))
    CLEARED -.->|Reset| OPEN
    SETTLED -.->|Reset| OPEN

Dashed arrows are resets: see When Sessions Reset.

Status Description
OPEN Session is active; meter data can be uploaded
CLEARED Market has cleared; transactions recorded
SETTLED Financial settlement computed for all members

When Sessions Reset

A CLEARED or SETTLED session goes back to OPEN whenever something it was computed from changes after the fact. RECreation does this automatically so that results never disagree with the data behind them. Only the sessions in the affected period are reset.

When a session resets:

  • Its transactions, offers, allocations and settlement outputs are deleted.
  • Calculated opportunity costs for those sessions are deleted too, and are calculated again at the next market run.
  • Uploaded meter measurements and uploaded opportunity costs are kept.
  • You must run market clearing, and then settlement, again for those dates.

Typical examples:

What you change Sessions that reset
Upgrade a battery's max_power from 5 kW to 10 kW with start_date: 2026-01-14, after the market already ran for January Sessions from January 14 onward. January 1 to 13 keep the 5 kW version and are not affected
Correct a contract's tariff cycle or contracted power for a past month Every session in the period the correction covers
End a meter or resource with DELETE ...?effective_at=2026-06-01 when June sessions were already cleared Sessions from June 1 onward
Change a member's ownership share of a resource, or when a member joins or leaves, for a past period Sessions in that period
Mark a CPE pair as is_active: false in RESP for a past period Sessions in that period, since the self-consumption grid-access tariff on their transactions changes
Change a fixed bilateral price for a past period Sessions in that period
Upload opportunity costs for past intervals The sessions those intervals belong to
Upload or correct meter loads, DSO data, allocation coefficients or per-interval bilateral prices The sessions for the timestamps contained in the upload
Settlement runs after the structure changed since market clearing The affected sessions are reopened for market clearing instead of being settled

Future changes

A change that only applies from a future date does not touch sessions that already exist for earlier dates. Only retroactive changes reset sessions.

See Business Validity for how dated changes work and how a market run splits into windows when the structure changes part-way through.

Local Energy Market (LEM)

In the LEM, members with surplus energy (producers/prosumers) trade with members needing energy (consumers). The market clearing algorithm matches offers and determines:

  • Who buys from whom
  • The clearing price (€/kWh)
  • The traded quantity (kWh)

Pricing

The internal trading price is derived from the community's market_rate:

Rate Price Derivation
VANILLA_INTERMEDIATE_MARKET_RATE price = OMIE day-ahead spot price × market_divisor
VANILLA_SDR price derived from supply-demand ratio within the community
VANILLA_CV price derived from crossing value of buy/sell curves
FIXED_PRICE fixed value set in community configuration
BILATERAL_PRICE agreed bilaterally between members before market clearing, with a price for each 15-minute interval — see Per-Interval Bilateral Prices
FIXED_BILATERAL_PRICE agreed bilaterally between members, with one dated price per provider → receiver pair — see Fixed Bilateral Prices
TREE_DNAC dynamic allocation coefficients computed via decision-tree algorithm; price determined by the bilateral agreement between Promoter and each receiver, set per 15-minute interval
FIXED_TREE_DNAC same as TREE_DNAC, but the Promoter–receiver price is one dated fixed price per pair

Contract dependency

Mechanisms that reference OMIE prices or retailer rates (e.g. VANILLA_INTERMEDIATE_MARKET_RATE, INDEXED) require valid contract information for each CPE.

Opportunity Costs

An opportunity cost is what a CPE would pay or earn if it dealt with its retailer instead of the community, for one 15-minute interval. Together the two values bound the internal price: a member would never buy locally above what the retailer charges, nor sell locally below what the retailer pays for surplus.

Field Required Description
datetime_timestamp ✅ Start of the 15-minute interval (Portuguese timezone)
meter_id ✅ UUID of the CPE the opportunity cost refers to
l_buy ✅ Price at which the CPE would buy energy from its supplier (€/kWh). Upper reference bound for the internal price
l_sell ✅ Price at which the CPE would sell its surplus to its supplier (€/kWh). Lower reference bound for the internal price

Market clearing needs an opportunity cost for every CPE and every interval, and it gets them in one of two ways:

  • Calculated from contracts. When the CPE's contract has tariff values (retailer prices, grid-access tariffs and OMIE prices where indexed), the platform computes l_buy and l_sell itself. Nothing needs to be uploaded.
  • Uploaded by the integrator. When retailer pricing cannot be shared (contract buy_type and sell_type set to NO_VALUES), upload the values with POST /opportunity_costs. The upload must cover every session and every CPE in the period being cleared; otherwise market clearing is refused with an error saying the uploaded opportunity costs do not cover all sessions and meters.

Uploaded values take precedence: if any are present for the period, the platform uses them instead of calculating its own. Calculated values are stored for reference and are overwritten the next time the market runs, whereas uploaded ones are kept.

Uploads reopen sessions

Uploading opportunity costs for past intervals invalidates the market sessions they touch: the session goes back to OPEN and its transactions and settlement outputs are deleted. Run market clearing and settlement again for those dates.

Opportunity costs and KPIs

Opportunity costs are enough to clear the market, but without contract pricing the cost-related KPIs, such as savings against individual self-consumption, are not available.

Bilateral Prices

With a bilateral market rate, the price is not derived from the market: each provider → receiver member pair has a price agreed beforehand and supplied by the integrator. There are two ways to supply it. Which one is used depends on the community's market_rate:

Fixed Per-interval
Market rates FIXED_BILATERAL_PRICE, FIXED_TREE_DNAC BILATERAL_PRICE, TREE_DNAC
Price per pair One price, applied to every interval of the period it covers A separate price for each 15-minute interval
Time dimension Dated: start_date / end_date, like other versioned entities Time series: each row has its own datetime_timestamp
Changing the price Send a new price for [start_date, end_date]; earlier history is kept Upload rows for the intervals that change
Endpoints /bilateral-prices/fixed /bilateral-prices
Coverage required At least one provider/receiver price pair At least one pair for every session being cleared
Best for Stable agreements, such as a Promoter selling at 80 €/MWh for the whole contract Prices that vary through the day or over time, such as peak and off-peak rates

Only the prices of the community's current market rate are used. Prices uploaded for the other mode are ignored.

Fixed Bilateral Prices

Fixed bilateral prices set one price (€/kWh) for a provider → receiver member pair, applied to every interval of the period it covers. They are dated:

Field Required Description
provider_user_id ✅ UUID of the selling member
receiver_user_id ✅ UUID of the buying member
price ✅ Price in €/kWh
start_date ✅ First day (inclusive, Europe/Lisbon) on which this price applies
end_date — Last day (inclusive) on which this price applies. null = open-ended
  • POST /bilateral-prices/fixed creates prices; GET /bilateral-prices/fixed lists them with their periods.
  • PUT /bilateral-prices/fixed/{receiver_user_id}/{provider_user_id} sets a new price for [start_date, end_date], keeping the price outside that period.
  • DELETE /bilateral-prices/fixed/{receiver_user_id}/{provider_user_id}?effective_at=YYYY-MM-DD removes the price from that day. Add &end_date=YYYY-MM-DD to remove only that period.

See Business Validity.

Per-Interval Bilateral Prices

Per-interval bilateral prices let the price of a provider → receiver pair change from one 15-minute interval to the next. They are a time series.

Field Required Description
datetime_timestamp ✅ Start of the 15-minute interval the price applies to
provider_user_id ✅ UUID of the selling member
receiver_user_id ✅ UUID of the buying member
price ✅ Price in €/kWh
  • POST /bilateral-prices uploads a list of rows. Processing runs in the background and the response returns a task_id to poll.
  • GET /bilateral-prices/by_provider returns a provider's prices over a date range, and GET /bilateral-prices/pagination lists the rows with filters.
  • Market clearing is refused unless every session being cleared has at least one provider → receiver price. If members may act as either producer or consumer in an interval, provide prices for all possible pairs.

Choosing between them

If the price for a pair never changes within a period, use the fixed form: it is one record instead of one per interval, and changing it later is a single dated update. Use per-interval prices only when the price actually varies by interval.

Transactions

Each cleared trade generates a Transaction record with:

Field Description
datetime_timestamp Start of the 15-minute ISP interval (Portuguese timezone)
provider_resource_id Resource selling energy
receiver_resource_id Resource buying energy
provider_user_id Member who owns the selling resource
receiver_user_id Member who owns the buying resource
provider_meter_id DSO meter behind the selling resource
receiver_meter_id DSO meter behind the buying resource
transaction_type INTRA_CPE or PUBLIC_CPE
price Price before grid-access tariffs (€/kWh)
energy Energy traded (kWh)

Transaction Types

Type Description
INTRA_CPE Both resources are behind the same DSO meter. No self-consumption grid-access tariff applies
PUBLIC_CPE Resources are behind different DSO meters connected through the public grid (RESP). The buyer is charged the self-consumption grid-access tariff

Ownership and compensation

If a transaction occurs between two resources with fractional ownership, the resulting compensation — whether paid or received — is allocated to each resource's owners in proportion to their ownership shares.