AI Solar Panel
027 Battery and Tariff Optimisation 1,491 words · 7 min

Arbitrage Maths: When Charging From the Grid Beats Storing Your Own Solar

Most battery advice rests on an assumption nobody says out loud: that a kilowatt-hour of your own solar is free. It isn’t. The moment you have an export tariff, every kWh you divert into the battery is a kWh you chose not to sell, and that forgone revenue is the actual price of stored sunshine. Put a number on it and solar battery grid charging arbitrage stops looking like a trick for people without panels. On a specific and fairly common class of UK tariff pairing, it is the correct default, and storing your own generation is the expensive option.

The derivation where the losses cancel

Four prices do all the work here. Call I the off-peak import rate, E the export rate available at the moment the sun is shining, P the import rate at the time you actually want the energy, and η the measured round-trip efficiency of your battery and inverter together.

Now price one kWh delivered out of the battery into your house, by each route.

Route A — store your own solar
  To deliver 1 kWh you must put 1/η kWh of solar into the battery.
  Each of those kWh could have been sold at E.
  Cost = E / η

Route B — export the solar, charge from the grid
  To deliver 1 kWh you must import 1/η kWh at the off-peak rate.
  Cost = I / η

Route B is cheaper when   I / η  <  E / η
                 i.e.     I  <  E

The η divides out. Both routes push energy through the same cells, take the same conversion hit, and wear the pack by the same amount, so efficiency is not a tiebreaker between them. It is a common factor.

This is the bit that trips people up. The instinctive objection to grid charging is “but you lose 13% of everything you put in.” True, and you lose exactly the same 13% of the solar you put in. Round-trip losses are a reason to use the battery less, never a reason to prefer one source of charge over another.

Where efficiency does still decide things

Two other decisions genuinely hinge on η, and conflating them with the one above is where most spreadsheets go wrong.

Whether to grid-charge at all, rather than simply importing at the time of use, needs I / η < P, or equivalently I < η·P. At 87% round trip against a 26.8p day rate, your off-peak rate has to beat 23.3p. Intelligent Octopus Go at 7p clears that by a mile, which is why overnight charging is uncontroversial in winter.

Cycle wear is the other one, and it behaves the same way as η: it applies to both routes identically, so it cancels in the A-versus-B comparison. A £3,400 10 kWh LFP pack rated for 6,000 cycles costs roughly 5.7p per kWh cycled. That number decides whether to cycle. It does not decide what to cycle.

Worked example: Intelligent Octopus Go plus Outgoing Fixed

Here is a real pairing a lot of UK households are sitting on: import on Intelligent Octopus Go (7.0p between 23:30 and 05:30, 26.8p the rest of the day, South East region, autumn 2026) and export on Octopus Outgoing Fixed at 15p flat.

System: 5.2 kWp array, 9.5 kWh usable LFP, hybrid inverter, measured round trip 87%, G99 export limit 5 kW.

A good June day: 31 kWh generated, 11 kWh consumed, of which 4.5 kWh falls inside daylight and 6.5 kWh falls outside it. The 4.5 kWh of direct self-consumption is identical under both strategies, so ignore it and look at the 6.5 kWh that has to come out of the battery. Delivering that needs 7.5 kWh of charge.

Store the solarExport it, charge at night
Solar into battery7.5 kWh0
Exported19.0 kWh @ 15p = £2.8526.5 kWh @ 15p = £3.98
Off-peak import07.5 kWh @ 7p = £0.53
Net for the day+£2.85+£3.45

Sixty pence, which is just 7.5 × (15p − 7p). Across roughly 150 days when you have real surplus, that is £90 a year for changing a schedule, with no hardware involved. Scale it to a 20 kWh pack cycling 15 kWh and the same arithmetic gives £1.20 a day, about £180 a season. And in summer the battery stops being a solar store and becomes two other things: a grid-arbitrage asset overnight, and a buffer for the generation your export limit won’t let out.

The same maths, four tariff pairs, three different answers

Import tariffOff-peak IExport tariffEVerdict
Intelligent Octopus Go7.0pOutgoing Fixed15pGrid-charge. 8p margin per kWh.
Octopus Go8.5pSEG (legacy 4.1p)4.1pStore the solar. Exporting loses 4.4p/kWh.
Octopus Flux~16p (02:00–05:00)5p night / 18p day / 31p peakvariesGrid-charge against the midday 18p, but self-consume through the 16:00–19:00 peak: avoiding a 40p import beats a 31p export.
Agile + Outgoing Agile2p–9p overnightoften 2p–5p midday, occasionally negativevariesUsually store. Summer midday wholesale is frequently below the overnight import price.

Flux rewards a three-way split within a single day, and Agile inverts the headline result entirely because its export price collapses at exactly the hours you have surplus. The rule I < E is a rule, not a slogan, and it has to be evaluated per half-hour slot against your own region’s unit rates. Which tariff pair you are on matters far more than which battery you bought, and the trade-offs between them are worth working through properly in battery and tariff optimisation before you touch any scheduling.

The edge case that flips it back: clipped kWh

Export limits break the clean algebra, in your favour. A 6.5 kWp array behind a 3.68 kW G98 connection on a clear June noon might be producing 5.1 kW while the house draws 0.4 kW. The meter will pass 3.68 kW. The remaining kilowatt gets curtailed by the inverter, and its export value is not 15p, it is zero.

For those kWh, E = 0, and 0 < 7p, so storing them wins outright. Any day with midday clipping, you want the battery empty at 11:00 and filling from the array, which is the exact opposite of what you want on a hazy day where nothing clips. This is why static schedules underperform: the right answer depends on the shape of the generation curve, not just the tariff.

Tooling that will actually do this

Predbat, running as a Home Assistant add-on against GivTCP, Solis, Solax or Fox, is the serious option. It solves exactly this problem every ten minutes using Solcast forecasts plus live Octopus rates, and it will choose to export solar and grid-charge if that is what the numbers say. The relevant knobs in apps.yaml:

inverter_loss: 0.04
battery_loss: 0.05
battery_loss_discharge: 0.05
metric_battery_cycle: 1.0        # p/kWh of wear charged against each cycle
rate_low_threshold: 0.0          # 0 = let the optimiser decide, don't hard-code a price

Check those key names against the current Predbat docs, because the schema moves between releases. EMHASS is the alternative if you prefer formulating it yourself as a linear program.

For the spreadsheet route, pull your own half-hourly data rather than trusting an app’s monthly summary:

curl -s -u "$OCTOPUS_KEY:" \
  "https://api.octopus.energy/v1/electricity-meter-points/$MPAN/meters/$SERIAL/consumption/?period_from=2026-06-01T00:00Z&group_by=" \
  | jq -r '.results[] | [.interval_start, .consumption] | @tsv'

Pair that with the standard-unit-rates endpoint for your tariff code and you have everything needed to compute Σ Q·(E − I) over a real month. Hildebrand’s Bright app and the Glowmarkt API do the same job if you are not an Octopus customer.

Measure η yourself before you trust any of this. Datasheet figures quote cell efficiency and ignore the inverter, standby draw and BMS overhead. Charge from the grid overnight with no solar and no load on the battery, read the import delta, then discharge into a known load the next evening and read the output. A 9.5 kWh pack that takes 11.2 kWh to fill and returns 9.1 kWh is running at 81%, not the 95% on the brochure, and that gap is worth about £25 a year in wrongly-priced decisions.

The 15p Outgoing Fixed rate is a commercial decision by one supplier, not a law of physics. The day it becomes 8p, I < E fails at a 7p import rate, your optimiser should start hoarding solar again, and the only thing that needs to change is one cell in the model. Set a reminder to re-run it whenever either side of that inequality moves.