Login

Formulas & Resolved Tariffs

Formulas define how tariffs combine into a final rate at a specific location. You configure a formula per location and direction, and the system evaluates it to produce a resolved tariff timeseries.

Copy linkConfiguring a Formula

A formula has three parts:

  1. Variables — a map from short names to tariff IDs
  2. Formula — an expression that combines the variables
  3. Direction — whether this formula is for import or export

Copy linkExample: Energy + Grid Fee + Margin

Given two existing tariffs — an energy tariff (energy-import) and a grid tariff (grid-import) — you can combine them with a fixed margin:

PUT /flex/locations/{locationId}/tariff-formula
Content-Type: application/json

{
  "direction": "import",
  "variables": {
    "energy": "energy-import",
    "grid": "grid-import"
  },
  "formula": "energy + grid + 0.02 * EUR/kWh"
}

This computes the final rate as: energy tariff rate + grid tariff rate + 2 cents/kWh margin. The EUR/kWh unit constant makes the literal 0.02 a rate value so it can be added to the other rates.

Copy linkExample: Spot Price with Floor and Markup

PUT /flex/locations/{locationId}/tariff-formula
Content-Type: application/json

{
  "direction": "import",
  "variables": {
    "spot": "spot-energy",
    "grid": "grid-import"
  },
  "formula": "max(spot, 0 * EUR/kWh) * 1.15 + grid + 0.03 * EUR/kWh"
}

This floors the spot price at zero (no negative prices), applies a 15% markup, adds the grid fee, and adds a 3 cent/kWh margin. The 0 * EUR/kWh creates a zero rate for the max comparison, and 0.03 * EUR/kWh creates the margin rate.

Copy linkFormula Syntax

Copy linkOperators

OperatorDescriptionExample
+Additionenergy + grid
-Subtractiongross - discount
-Negation-spot
*Multiplication1.15 * spot
/Divisionspot / 1000
( )Grouping(spot + grid) * markup

Copy linkCommon Functions

FunctionDescriptionExample
min(a, b)Minimum of two valuesmin(spot, 0.50 * EUR/kWh)
max(a, b)Maximum of two valuesmax(spot, 0 * EUR/kWh)
clamp(x, lo, hi)Constrain between boundsclamp(spot, 0 * EUR/kWh, 0.50 * EUR/kWh)
abs(x)Absolute valueabs(spot)
round(x, n)Round to n decimal placesround(spot + grid, 4)

These are all the supported functions. clamp(x, lo, hi) is equivalent to min(max(x, lo), hi). The value arguments of min, max, clamp, and abs must have matching dimensions — for example, min(spot, 0.50 * EUR/kWh) works because both arguments are rates. The second argument of round(x, n) is always a scalar precision (number of decimal places) and is exempt from this rule.

Copy linkDecimal Literals

Decimal numbers like 0.03, 1.15, 0 are always scalar (dimensionless). They work directly as multipliers or divisors (1.15 * spot, spot / 1000) but cannot be added to or subtracted from rate values.

To use a constant as a rate, multiply it by a unit constant: 0.03 * EUR/kWh.

Copy linkUnit Constants

Unit constants like EUR/kWh, USD/kWh, or NOK/kWh convert a scalar literal into a rate. A unit constant evaluates to 1 with rate dimension, so 0.03 * EUR/kWh produces a rate of 0.03 in that currency.

The currency code in the unit constant must match the currency of the rate tariffs in the formula. Variable names that are exactly three uppercase letters (e.g., EUR, USD) are reserved for this syntax and cannot be used as variable names.

Copy linkDimensional Rules

The formula system enforces dimensional correctness. There are two types of values:

  • Rate — a price in currency per kWh (e.g., 0.28 EUR/kWh). Tariffs with per: "kWh" produce rate values.
  • Scalar — a dimensionless multiplier (e.g., 1.15). Tariffs with per: "scalar" produce scalar values.
LHS, RHS+-*/
rate, raterateratescalar
rate, scalarraterate
scalar, raterate
scalar, scalarscalarscalarscalarscalar

Number literals are always scalar. To create a rate constant, use a unit constant: 0.03 * EUR/kWh.

The formula must evaluate to a rate. The API validates dimensional correctness when you create a formula and returns an error if the formula is invalid.

Copy linkCurrency and Direction Rules

All rate-valued tariffs in a formula must share the same currency. The formula's direction must match the direction of all referenced tariffs (including scalar tariffs). The API validates these constraints when you create a formula.

Copy linkScalar Tariffs

If a coefficient in your formula changes over time, use a scalar tariff instead of a literal.

For example, if the spot markup factor varies, create a scalar tariff to hold it:

POST /flex/tariffs/spot-markup
Content-Type: application/json

{
  "direction": "import",
  "per": "scalar"
}

Scalar tariffs have no currency since they're dimensionless. They still require a direction, which is validated against the formula's direction.

Push coefficient values:

PUT /flex/tariffs/spot-markup/timeseries
Content-Type: application/json
Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890

{
  "to": "2024-06-16T00:00:00+02:00",
  "values": [
    { "at": "2024-06-15T00:00:00+02:00", "rate": 1.15 },
    { "at": "2024-06-15T12:00:00+02:00", "rate": 1.20 }
  ]
}

Then use it in a formula:

PUT /flex/locations/{locationId}/tariff-formula
Content-Type: application/json

{
  "direction": "import",
  "variables": {
    "spot": "spot-energy",
    "markup": "spot-markup",
    "grid": "grid-import"
  },
  "formula": "max(spot, 0 * EUR/kWh) * markup + grid + 0.02 * EUR/kWh"
}

Now the markup changes from 1.15 to 1.20 at midday without updating the formula itself.

Copy linkRetrieving a Formula

GET /flex/locations/{locationId}/tariff-formula?direction=import

The direction query parameter is required. Returns a single formula for the specified direction.

Copy linkDeleting a Formula

DELETE /flex/locations/{locationId}/tariff-formula?direction=import

Copy linkResolved Tariffs

The resolved tariffs endpoint evaluates the location's formula over a time range and returns the computed rate at each interval. The query range must not exceed 90 days.

GET /flex/locations/{locationId}/tariffs/resolved?from=2024-06-15&to=2024-06-16&direction=import&pageSize=50

{
  "locationId": "4eaeb363-296d-4ccc-a973-7805e6f400bd",
  "direction": "import",
  "currency": "EUR",
  "per": "kWh",
  "from": "2024-06-15",
  "to": "2024-06-16",
  "timezoneName": "Europe/Berlin",
  "intervals": [
    {
      "type": "resolved",
      "startAt": "2024-06-15T00:00:00+02:00",
      "endAt": "2024-06-15T01:00:00+02:00",
      "formula": "max(spot, 0 * EUR/kWh) * 1.15 + grid + 0.03 * EUR/kWh",
      "rate": 0.248
    },
    {
      "type": "resolved",
      "startAt": "2024-06-15T01:00:00+02:00",
      "endAt": "2024-06-15T02:00:00+02:00",
      "formula": "max(spot, 0 * EUR/kWh) * 1.15 + grid + 0.03 * EUR/kWh",
      "rate": 0.225
    }
  ],
  "pagination": {
    "after": "MjAyNC0wNi0xNVQwMTowMDowMC4wMDBaJjE=",
    "before": null
  }
}

Use pageSize, after, and before query parameters to paginate through intervals.

Copy linkHow Intervals Work

Each interval has a type — either resolved (with a computed rate) or unresolved (indicating missing data). A new resolved interval starts whenever any input tariff's rate changes (e.g., the spot price updates hourly).

Copy linkUnavailable Data

If a tariff in the formula lacks data for part of the requested range, those intervals return as unresolved. The system never carries rates forward past the last pushed value, preventing stale prices.

For example, if grid has data pushed until 12:00, querying 00:00–24:00 returns:

{
  "intervals": [
    {
      "type": "resolved",
      "startAt": "2024-06-15T00:00:00+02:00",
      "endAt": "2024-06-15T12:00:00+02:00",
      "formula": "max(spot, 0 * EUR/kWh) * 1.15 + grid + 0.03 * EUR/kWh",
      "rate": 0.248
    },
    {
      "type": "unresolved",
      "startAt": "2024-06-15T12:00:00+02:00",
      "endAt": "2024-06-16T00:00:00+02:00"
    }
  ]
}

Only intervals where all input tariffs have pushed data produce resolved results. If any tariff lacks data for a portion of the requested range, that portion is unresolved.

Copy linkEndpoint Reference

TaskMethodEndpoint
Get formulaGET/flex/locations/{locationId}/tariff-formula?direction={direction}
Set formulaPUT/flex/locations/{locationId}/tariff-formula
Delete formulaDELETE/flex/locations/{locationId}/tariff-formula?direction={direction}
Get resolved tariffsGET/flex/locations/{locationId}/tariffs/resolved?from={from}&to={to}&direction={direction}
Previous articleTariffs
Was this article helpful?