fix(tibber): deduct verkoopvergoeding (sell_fee) from feed-in sell price
Tibber's API `total` already includes the buy-side inkoopvergoeding
(verified from production data: total = spot×1.21 + energy_tax 0.11085 +
inkoopvergoeding 0.0248). Under net metering Tibber pays back
`total − verkoopvergoeding` per returned kWh (NL: EUR 0.28 -> 0.2552), so the
two EUR 0.0248 fees do NOT cancel — the feed-in price sits 0.0248 below buy.
Model the verkoopvergoeding as a first-class, always-subtracted contract
field `energy.sell_fee` (default 0.0248) instead of folding it into
`sell_adjust`. New sell formula:
sell = total − energy_tax − sell_fee − sell_adjust
`sell_adjust` now carries only the net-metering energy-tax refund
(= −energy_tax). Applied in both the billing strategy and the /prices
endpoint; recorded in the pricing snapshot. Frontend renders the field
automatically (dynamic profile form). Docs (references, m6) corrected to
drop the wrong "fees cancel" premise.
This commit is contained in:
@@ -209,12 +209,23 @@ def _tibber_strategy(
|
||||
query on ``starts_at``.
|
||||
|
||||
Formula (§3.4):
|
||||
- ``buy = total`` (Tibber's all-inclusive price, already includes tax)
|
||||
- ``sell = total − energy_tax − sell_adjust``
|
||||
- ``buy = total`` (Tibber's all-inclusive price; already includes energy
|
||||
tax, VAT and the buy-side ``inkoopvergoeding``)
|
||||
- ``sell = total − energy_tax − sell_fee − sell_adjust``
|
||||
- ``import_cost = (Δd1 + Δd2) × buy``
|
||||
- ``export_revenue = (Δr1 + Δr2) × sell``
|
||||
- ``net_cost = import_cost − export_revenue``
|
||||
|
||||
``sell_fee`` models Tibber's per-kWh **verkoopvergoeding** (feed-in fee,
|
||||
€0.0248/kWh incl. VAT since 2026-01-01). It is always deducted from the
|
||||
feed-in payout: even under the net-metering (saldering) scheme, Tibber pays
|
||||
``total − verkoopvergoeding`` per returned kWh (Tibber NL: "€0,28 − €0,0248
|
||||
= €0,2552"). ``total`` already contains the equal buy-side
|
||||
``inkoopvergoeding``, so the two fees do **not** cancel — the feed-in price
|
||||
sits ``sell_fee`` below the buy price. ``sell_adjust`` is a separate manual
|
||||
correction: under net metering it carries back the refunded energy tax
|
||||
(``sell_adjust = −energy_tax``), leaving ``sell = total − sell_fee``.
|
||||
|
||||
Tibber does not differentiate tariff slots (dal vs normal) — the 15-minute
|
||||
API price applies to the full delivered/returned volume.
|
||||
|
||||
@@ -248,11 +259,12 @@ def _tibber_strategy(
|
||||
|
||||
energy = values.get("energy", {})
|
||||
energy_tax = _to_decimal(energy.get("energy_tax", 0))
|
||||
sell_fee = _to_decimal(energy.get("sell_fee", 0))
|
||||
sell_adjust = _to_decimal(energy.get("sell_adjust", 0))
|
||||
|
||||
total = _to_decimal(price_row.total)
|
||||
buy = total
|
||||
sell = total - energy_tax - sell_adjust
|
||||
sell = total - energy_tax - sell_fee - sell_adjust
|
||||
|
||||
total_delivered = deltas.d1 + deltas.d2
|
||||
total_returned = deltas.r1 + deltas.r2
|
||||
@@ -269,6 +281,7 @@ def _tibber_strategy(
|
||||
"buy": str(buy),
|
||||
"sell": str(sell),
|
||||
"energy_tax": str(energy_tax),
|
||||
"sell_fee": str(sell_fee),
|
||||
"sell_adjust": str(sell_adjust),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user