For the complete documentation index, see llms.txt. This page is also available as Markdown.

Cost optimization

Voltmasters' price-based control algorithm: plan the battery around dynamic energy prices to minimize cost.

Cost optimization is Voltmasters' price-based control algorithm. It minimizes the energy cost of the installation by deciding, every control cycle, how to charge and discharge the battery, based on forecasted energy prices and forecasts of PV production and consumption.

How it works

Every control cycle the algorithm:

  • Looks ahead over the forecast horizon at the dynamic (day-ahead) prices, the expected PV production and the expected consumption.

  • Plans the battery so energy is stored when it is cheap (or from PV surplus) and used, or injected, when it is expensive. This forward plan is the battery schedule.

  • Only acts when it pays off: charging and discharging are planned only when the price difference is large enough to outweigh the battery's round-trip losses (a configurable minimum price difference).

  • Respects the limits: the battery's minimum/maximum state of charge and the grid connection limits (maximum injection and consumption).

The detailed plan, the event types and a worked example are described on the Battery schedule page. The rest of this page explains how the algorithm reaches those decisions.

Two loops: planning and acting

Cost optimization runs as two cooperating loops that share one artefact: the battery schedule.

  • The planning loop looks at the whole forecast horizon and works out the cheapest charge/discharge plan. It is the "brain". The plan is recomputed regularly, at every new 15-minute slot and whenever a fresh forecast arrives, so it always reflects the latest prices, production and the real battery state.

  • The control loop runs every control cycle (about once per second). It reads the single schedule entry that covers now, turns it into concrete battery, PV and EV setpoints, and adapts those to live measurements and limits. It is the "hands".

Splitting the work this way means the expensive look-ahead optimization does not have to run every second, while the fast control loop can still react to reality between plans.

What the algorithm needs

The plan is built from a mix of forecasts, live measurements and project configuration:

Input
Where it comes from
Role in the algorithm

Day-ahead consumption & injection prices (per 15-min slot)

Forecast provider (dynamic/day-ahead market)

Tells the plan when energy is cheap to buy and when it is lucrative to sell

PV production forecast

Forecast provider (weather-based)

Tells the plan when free/cheap solar energy is available to store

Consumption forecast

Forecast provider

Tells the plan when the site will need energy: the deficits worth covering

Battery state of charge

Live measurement per battery

The starting point of the projected SoC trajectory

Battery limits & efficiency

Project configuration

Min/max SoC, charge/discharge rate, rated power, round-trip efficiency

Grid connection limits

Project configuration

Maximum import (charging headroom) and maximum export (injection cap)

Minimum price difference

Project configuration

The spread a charge/discharge pair must beat to be worth the loss and wear

Battery injection sales

Project configuration

Whether selling stored energy back to the grid is allowed at all

The core idea: price arbitrage within the limits

At heart the algorithm does energy arbitrage: store energy while it is cheap (from PV surplus or low-priced grid hours) and release it while it is expensive, either by covering the site's own consumption or by injecting it back to the grid for revenue.

Two things keep this honest:

  • Round-trip losses. Energy lost on the way into and out of the battery means a stored kilowatt-hour delivers less than a full kilowatt-hour. The plan sizes every charge and discharge using the battery's round-trip efficiency.

  • The minimum price difference. A charge/discharge pair is only planned when the price gap between the cheap slot and the expensive slot clears a configurable threshold. This covers the round-trip loss and battery wear, and stops the battery from cycling for a marginal gain.

Everything the optimizer plans must also fit inside the state-of-charge bounds and the grid connection limits; those constraints are threaded through every step below.

Building the schedule, step by step

The planning loop turns the forecast horizon into a battery schedule through a fixed pipeline. Each stage adds or refines plans on top of the previous one:

  1. Energy sources per slot. For every slot the algorithm works out how much energy could be stored there and at what price: the PV overshoot (solar above the local consumption, energy that would otherwise be exported, priced at the injection price) and the grid headroom (how much can still be imported under the grid limit, priced at the consumption price). Both are capped so the combined charging power never exceeds what the batteries can absorb.

  2. Identify deficit slots. Slots where forecast consumption is higher than forecast PV are deficits: the site will need to draw energy. These are the slots worth covering from the battery, and the expensive ones are the prime targets.

  3. Plan charging for deficits. This is the heart of the optimizer. Walking the deficits, it searches earlier slots for the cheapest charging sources whose price is at least the minimum price difference below the deficit's price, so only pairs that clearly pay off are planned. PV is preferred over grid (it is cheaper); grid sources are taken cheapest-first. Every allocation is checked against the projected SoC so the battery is never planned beyond its capacity, and the matching discharge is booked into the deficit slot.

  4. Negative-price charging. When the consumption price drops below zero, you are paid to consume, so the algorithm plans extra charging during those slots, while reserving room for PV that cannot be curtailed, to avoid having to pay to inject it later.

  5. Injection discharge. When selling stored energy is enabled, the algorithm finds slots with an attractive injection price (positive and above the minimum price difference) and plans discharges there, highest price first. It never discharges below the reserve, and never within the last four hours of the horizon (too uncertain to commit storage to). An injection can also be added as a bonus on top of an existing deficit discharge.

  6. Non-controllable PV storage. PV that physically cannot be curtailed, and would otherwise be exported cheaply, is captured and re-allocated to the most valuable injection slots instead of being spilled.

  7. Build entries and project SoC. The plans become timed schedule entries (charge, discharge or idle), each with an action and a goal (see Battery schedule). Remaining slots become idle, or compensate PV surplus where free solar still flows into the battery. The state of charge is projected forward across the whole horizon so the complete plan stays inside the min/max bounds, and the entries are sorted by time.

When the plan is stored, entries for slots that have already started are preserved and merged with the fresh plan, and entries older than 14 days are pruned. This keeps a record of what was actually planned and executed for each slot.

What keeps the plan feasible

Several mechanisms run through the pipeline to keep the schedule physically sound and stable:

  • Projected SoC trajectory. The algorithm simulates the battery's energy level slot by slot. Charging is never planned above the maximum SoC, and discharging never brings the projected SoC below the effective minimum (the configured minimum SoC plus any reserve).

  • Recharge segments. The horizon is split into independent budgets at major PV-charging periods. A deficit after a sunny afternoon cannot spend energy that belongs to deficits before it. This is what keeps multi-day plans stable instead of shifting around on every regeneration.

  • Grid limits everywhere. Charging power per slot is capped by the import headroom that is still free under the grid limit; injection and discharge are capped by the export limit. Both PV export and battery injection share the same export budget.

  • Reserves. Energy set aside (for example for EV power-boost) is treated as off-limits for ordinary discharge, so the plan never spends it.

From plan to setpoint: the control cycle

The schedule expresses intent; the control loop turns it into action. Every cycle the cost-optimization algorithm looks up the entry for now, reads its action type and planned power, and converts that into a setpoint, always clamped to the live state and the grid limits.

How each action behaves at runtime:

Action type
What the control loop does

charge_at_max_power

Charge at the maximum power the grid import headroom and charge rate allow

follow_scheduled_power

Follow the exact planned power (grid charging, or discharging to inject), capped by the limits

compensate_pv_surplus

Charge with the live PV surplus only, no grid charging

compensate_production_deficit

Discharge just enough to cover the live production deficit

idle

Leave the battery alone

In the same cycle the algorithm also sets the controllable loads, the EV-charger allocation, and the PV-inverter setpoints (curtailing only when needed to respect the export limit).

Because the cycle works from live measurements, it absorbs forecast error automatically: the compensate_* actions size themselves from what is actually happening right now, while follow_scheduled_power and charge_at_max_power follow the plan within the limits.

Closing the loop

After each 15-minute slot completes, the controller records how much was actually charged (from PV and from grid) and discharged, and compares it against the plan. If less energy was stored than planned, a shortfall adjustment removes the shortfall from the most expensive target slots first. The next regeneration then re-plans from the true battery state, so the strategy continuously self-corrects as reality diverges from the forecast.

Worked example: evening high prices

The chart below shows the evening high prices scenario. Electricity is cheap and sunny during the day and becomes much more expensive in the evening peak.

Cost optimization, evening high prices scenario
The optimizer charges during the cheap, sunny daytime and discharges into the expensive evening peak, buying low and using high.

Reading it against the pipeline: step 2 marks the evening as a string of expensive deficit slots; step 3 plans the daytime charging (mostly stored PV surplus) to cover them, because the day-to-evening price spread clears the minimum price difference; and the control loop then discharges through the peak, with the projected SoC climbing to its maximum during the day and falling back overnight. The full event-by-event breakdown of this scenario is on the Battery schedule page.

Relationship to an external signal

Cost optimization is a local strategy. When an external signal partner is actively steering the installation, that partner takes precedence and cost optimization does not drive the battery. When no external signal is active, or the partner is on standby, the battery follows the cost-optimization schedule.

Last updated