AI Solar Panel
016 Battery and Tariff Optimisation 1,375 words · 6 min

Agile, Go, Flux or Cosy: Matching a Tariff to Your Load Shape

Ask which is the best octopus tariff for solar battery owners on any UK energy forum and you will get four confident answers in nine minutes. Flux, because it was built for batteries. Intelligent Go, because 7p. Agile, because you can beat the average. Cosy, because two windows beat one. Every one of those answers is correct for somebody’s house. None of them is an answer about yours.

The thing that decides it is not the tariff’s reputation. It’s the shape of your half-hourly import after solar and battery have already done their work, crossed with the shape of the tariff’s price curve. Both of those are numbers you can download. So download them.

Get your own half-hourly data first

Everything below runs on 17,520 rows a year of real consumption, not a modelled profile. If you’re already with Octopus, the REST API gives you the lot:

curl -u "$OCTOPUS_KEY:" \
  "https://api.octopus.energy/v1/electricity-meter-points/$MPAN/meters/$SERIAL/consumption/?period_from=2025-09-01T00:00Z&period_to=2026-09-01T00:00Z&page_size=25000&order=period"

Your API key is in the developer section of your account dashboard; the MPAN and meter serial are on the same page. Export is a second meter point with its own MPAN, and you want that too. If you’re not with Octopus, n3rgy will serve you thirteen months of half-hourly data straight from the DCC for free, and a Hildebrand Glow CAD paired with the Bright app gives you live readings plus history with no supplier involvement at all.

Tariff prices come from the same API without authentication:

GET /v1/products/AGILE-24-10-01/electricity-tariffs/E-1R-AGILE-24-10-01-M/standard-unit-rates/?period_from=2025-09-01T00:00Z

That’s the Yorkshire (M) region. Swap the letter for yours, and hit /v1/products/ first because product codes rotate. Two years of Agile prices is about 35,000 rows, which pandas or DuckDB will join to your consumption on the timestamp in one line. I keep mine in a single DuckDB file, roughly 14 MB for three years of import, export and prices.

The house I’m going to use

A 1930s semi in Leeds. Gas boiler, no EV, 5.2 kWp split across south-west and north-east roofs, 9.5 kWh of usable battery behind a 3.6 kW hybrid inverter. Over the twelve months to September 2026 it generated 4,750 kWh, used 5,200 kWh, and after the battery had shifted everything it could, still needed 2,630 kWh from the grid and exported 2,180 kWh.

That residual 2,630 kWh is the whole ballgame. It is 78% November to February. It arrives in two lumps a day, early morning and 17:00 to 21:00. And the battery can absorb 9.5 kWh per fill, no more, which turns out to matter more than any headline unit rate.

Encoding the four price structures

Each tariff is a function from a timestamp to a price. Write them as windows and the comparison becomes trivial:

TARIFFS = {
    "cosy":  {"windows": [((4,0),(7,0), 12.9), ((13,0),(16,0), 12.9),
                          ((16,0),(19,0), 41.9)], "default": 29.6, "sc": 51.9},
    "go":    {"windows": [((0,30),(5,30), 8.7)], "default": 28.4, "sc": 49.8},
    "flux":  {"windows": [((2,0),(5,0), 17.6), ((16,0),(19,0), 41.0)],
              "default": 29.3, "sc": 53.4},
    # agile is a lookup, not a rule: 17,520 prices joined on timestamp
}

Those p/kWh figures are the Yorkshire rates my script pulled in late September 2026. Pull your own; regional spread on the day rate alone is over 4p, which is worth about £25 a year on this load.

The battery dispatch model is the part people get wrong. Don’t assume perfect foresight. I run a deliberately dumb rule that any real inverter can execute: fill to 100% in the cheapest window, discharge on demand, never discharge below 10%, 88% round-trip efficiency, and cap charge power at 3.6 kW. If a tariff only wins under a clairvoyant optimiser, it won’t win in your house. Choosing the dispatch rules is a separate problem from choosing the tariff, and if you want to go deeper on that half of it, the battery and tariff optimisation pillar covers the scheduling side properly.

The result

Same house, same year, same 17,520 rows, four price structures:

tariff pairing                    import    export     net
------------------------------------------------------------
Agile + Outgoing Agile           £596.24   £337.08   £259.16
Cosy + Outgoing Fixed 15p        £640.18   £318.00   £322.18
Go + Outgoing Fixed 15p          £674.82   £327.00   £347.82
Flux (import + export)           £868.55   £433.46   £435.09

£176 a year separates the best and worst option, for identical hardware in an identical year. And the tariff explicitly marketed at battery owners comes last.

Flux loses for a reason that shows up clearly once you break it out. It earns this house £115 more on export than the flat 15p deal does, mostly from dumping 380 kWh into the 16:00 to 19:00 window at 30.7p. It also costs £228 more on import, because its cheap window is three hours at 17.6p rather than six hours at 12.9p. Flux is priced for a house with a big summer surplus and modest winter import. This house is the other way round, so Flux quietly bills it £113 a year for the privilege of a good export rate on electricity it doesn’t have.

Go loses on geometry. Five cheap hours at 8.7p is the lowest rate in the table, and it is completely wasted, because a 9.5 kWh battery fills once and then has nothing left by 18:00 on a January evening. Roughly 1,200 kWh a year lands on the 28.4p day rate with nowhere to hide.

Now change one number. Rerun the same script with a 13.5 kWh battery:

go     9.5 kWh: £347.82      go    13.5 kWh: £196.13
cosy   9.5 kWh: £322.18      cosy  13.5 kWh: £293.76

Go goes from third to first and beats Cosy by £98, because one overnight fill finally covers a whole winter day and the 8.7p rate gets to do its job. The tariff ranking flipped on battery capacity. Nobody’s forum post about which tariff is better can tell you that, because the answer isn’t a property of the tariff.

Agile wins the median and owns the tail

Agile topping the table needs a caveat that only data can supply. My charging logic targets the four cheapest half-hours between 23:00 and 06:00, which achieved a blended 11.9p across the year. Lovely. Then I pointed the same script at the winter of 2022-23, which the API will still serve you, and Agile came out £340 behind Cosy over that period.

So the honest reading isn’t “Agile is best”. It’s that Agile was £63 better last year and has a fat, quantifiable downside tail, while Cosy’s worst case and best case are 4p apart. Run the last three winters separately and you get a distribution, not a number. Whether £63 of expected value is worth that variance is a judgement, but at least it’s a judgement about a spread you’ve actually measured rather than a vibe from someone in a different region with a heat pump.

A cross-check worth doing: the Octopus Compare app will replay your consumption against current tariffs in about a minute. It does not model a battery, so its verdict will differ from yours. When it disagrees by more than about 15%, that’s usually your dispatch assumptions being too generous, not the app being wrong.

Keep the model running after you switch

Rates move, your load shape moves, and the winner moves with them. Install BottlecapDave’s HomeAssistant-OctopusEnergy integration and your prices, consumption and export all land in Home Assistant as long-term statistics, which you can dump back out to your DuckDB file on a cron. Add Solcast for a real generation forecast rather than a seasonal average. If you want the dispatch itself optimised against tomorrow’s prices, Predbat already does this and will happily control a GivEnergy, Fox or Solis inverter from the same data.

I re-run the comparison on the first of every month against a trailing twelve months. It takes eleven seconds. Twice in two years it has told me to switch, once for a reason I would never have guessed: adding an immersion diverter moved 340 kWh of summer demand into the middle of the day, which handed Cosy’s 13:00 window a job it hadn’t had before.

The half-hourly data is free, the prices are free, and the arithmetic is a join and a groupby. Point them at each other before the next person tells you what the best tariff is.