Electricity buy/sell logic

Hi,

I'm new node-red, but had read this thread but I couldn't quite do exactly what I'm trying to do, which is see if I could use my battery to trade electricity (and also power my own loads).

I can pull from the API these fields:

PointOfConnectionCode
TradingDate
TradingPeriodNumber
RunDateTime
DollarsPerMegawattHour

And at midnight I get forecast for the day coming plus one day, and they're 30min trading periods. So essentially 96 periods, though the prices can fluctuate slightly from these forecasts throughout the day, but the API will allow me to ping it once a minute.

What I'd like to do is figure out some dynamic logic to figure out when is the cheapest power over the forecast period, use that to charge the battery, and when its the most expensive and send it to the grid.

Does anyone have any suggestions? Thanks!

Provide some sample data and I am sure someone will chime in.

To provide sample data...

  • Add a debug node to the HTTP Request
  • Locate the msg in debug panel
  • Hover mouse over payload and click the [Copy Path] button
  • Paste code in a code fence (click the code [</>] button, paste data)

So, without any sample data, here is something to get you started...

flow (use CTRL+I to import)

[{"id":"c36db0db5407437e","type":"function","z":"49f61d916c8f6022","name":"dummy data (replace with http request)","func":"const todayDate = new Date(new Date().setHours(0, 0, 0, 0))\nconst tomorrowDate = new Date(new Date(Date.now() + 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0));\nconst today = todayDate.toLocaleDateString('fr-CA')\nconst tomorrow = tomorrowDate.toLocaleDateString('fr-CA')\nconst data = []\nfor (let i = 0; i < 48; i++) {\n    const h = i % 24\n    const time = (h + '').padStart(2,'0') + ':' + ((h % 2) ? '30' : '00')\n    data.push({\n        PointOfConnectionCode: \"test_data\",\n        TradingDate: i > 23 ? tomorrow : today,\n        TradingPeriodNumber: i + 1,\n        RunDateTime: time,\n        DollarsPerMegawattHour: +((Math.random() * 10) + 36).toFixed(2)\n    })\n    \n}\nmsg.payload = data\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1270,"y":480,"wires":[["4f8cdbb0e76d6de5","cce565d66a05bf1d"]]},{"id":"fb8adcc002d8d25e","type":"inject","z":"49f61d916c8f6022","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1020,"y":480,"wires":[["c36db0db5407437e"]]},{"id":"4f8cdbb0e76d6de5","type":"debug","z":"49f61d916c8f6022","name":"debug 10","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1520,"y":480,"wires":[]},{"id":"cce565d66a05bf1d","type":"function","z":"49f61d916c8f6022","name":"get best times","func":"const data = msg.payload\nconst min = data.reduce(function (prev, curr) {\n    return prev.DollarsPerMegawattHour < curr.DollarsPerMegawattHour ? prev : curr\n})\nconst max = data.reduce(function (prev, curr) {\n    return prev.DollarsPerMegawattHour > curr.DollarsPerMegawattHour ? prev : curr\n})\n\nmsg.payload = {min, max}\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1340,"y":540,"wires":[["322cff4cb23f2849"]]},{"id":"322cff4cb23f2849","type":"debug","z":"49f61d916c8f6022","name":"debug 11","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1500,"y":540,"wires":[]}]

Which given data like this...

[
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 1,
        "RunDateTime": "00:00",
        "DollarsPerMegawattHour": 37.88
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 2,
        "RunDateTime": "01:30",
        "DollarsPerMegawattHour": 36.76
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 3,
        "RunDateTime": "02:00",
        "DollarsPerMegawattHour": 39.5
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 4,
        "RunDateTime": "03:30",
        "DollarsPerMegawattHour": 37.97
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 5,
        "RunDateTime": "04:00",
        "DollarsPerMegawattHour": 43.25
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 6,
        "RunDateTime": "05:30",
        "DollarsPerMegawattHour": 36.92
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 7,
        "RunDateTime": "06:00",
        "DollarsPerMegawattHour": 42.61
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 8,
        "RunDateTime": "07:30",
        "DollarsPerMegawattHour": 37.4
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 9,
        "RunDateTime": "08:00",
        "DollarsPerMegawattHour": 37.07
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 10,
        "RunDateTime": "09:30",
        "DollarsPerMegawattHour": 43.31
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 11,
        "RunDateTime": "10:00",
        "DollarsPerMegawattHour": 43.02
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 12,
        "RunDateTime": "11:30",
        "DollarsPerMegawattHour": 38.59
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 13,
        "RunDateTime": "12:00",
        "DollarsPerMegawattHour": 38.49
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 14,
        "RunDateTime": "13:30",
        "DollarsPerMegawattHour": 36.64
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 15,
        "RunDateTime": "14:00",
        "DollarsPerMegawattHour": 36.51
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 16,
        "RunDateTime": "15:30",
        "DollarsPerMegawattHour": 36.81
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 17,
        "RunDateTime": "16:00",
        "DollarsPerMegawattHour": 40.22
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 18,
        "RunDateTime": "17:30",
        "DollarsPerMegawattHour": 40.96
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 19,
        "RunDateTime": "18:00",
        "DollarsPerMegawattHour": 44.69
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 20,
        "RunDateTime": "19:30",
        "DollarsPerMegawattHour": 41.5
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 21,
        "RunDateTime": "20:00",
        "DollarsPerMegawattHour": 44.37
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 22,
        "RunDateTime": "21:30",
        "DollarsPerMegawattHour": 38.79
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 23,
        "RunDateTime": "22:00",
        "DollarsPerMegawattHour": 36.49
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-07-31",
        "TradingPeriodNumber": 24,
        "RunDateTime": "23:30",
        "DollarsPerMegawattHour": 42.54
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 25,
        "RunDateTime": "00:00",
        "DollarsPerMegawattHour": 43.48
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 26,
        "RunDateTime": "01:30",
        "DollarsPerMegawattHour": 37.42
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 27,
        "RunDateTime": "02:00",
        "DollarsPerMegawattHour": 40.74
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 28,
        "RunDateTime": "03:30",
        "DollarsPerMegawattHour": 42.15
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 29,
        "RunDateTime": "04:00",
        "DollarsPerMegawattHour": 44.88
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 30,
        "RunDateTime": "05:30",
        "DollarsPerMegawattHour": 43.72
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 31,
        "RunDateTime": "06:00",
        "DollarsPerMegawattHour": 38.61
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 32,
        "RunDateTime": "07:30",
        "DollarsPerMegawattHour": 44.18
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 33,
        "RunDateTime": "08:00",
        "DollarsPerMegawattHour": 40.82
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 34,
        "RunDateTime": "09:30",
        "DollarsPerMegawattHour": 45.23
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 35,
        "RunDateTime": "10:00",
        "DollarsPerMegawattHour": 43.82
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 36,
        "RunDateTime": "11:30",
        "DollarsPerMegawattHour": 39.03
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 37,
        "RunDateTime": "12:00",
        "DollarsPerMegawattHour": 43.94
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 38,
        "RunDateTime": "13:30",
        "DollarsPerMegawattHour": 37.11
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 39,
        "RunDateTime": "14:00",
        "DollarsPerMegawattHour": 38.82
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 40,
        "RunDateTime": "15:30",
        "DollarsPerMegawattHour": 44.18
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 41,
        "RunDateTime": "16:00",
        "DollarsPerMegawattHour": 39.98
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 42,
        "RunDateTime": "17:30",
        "DollarsPerMegawattHour": 36.33
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 43,
        "RunDateTime": "18:00",
        "DollarsPerMegawattHour": 39.97
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 44,
        "RunDateTime": "19:30",
        "DollarsPerMegawattHour": 43.75
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 45,
        "RunDateTime": "20:00",
        "DollarsPerMegawattHour": 44.54
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 46,
        "RunDateTime": "21:30",
        "DollarsPerMegawattHour": 42.12
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 47,
        "RunDateTime": "22:00",
        "DollarsPerMegawattHour": 45.27
    },
    {
        "PointOfConnectionCode": "test_data",
        "TradingDate": "2022-08-01",
        "TradingPeriodNumber": 48,
        "RunDateTime": "23:30",
        "DollarsPerMegawattHour": 44.08
    }
]

will return this...

I do something very similar here in Australia with a company called Amber electric

They expose us to the Wholesale spot price market on a 5 minute basis with 30 minute settlement periods.

We have a large Solar and battery system and arbitrage the grid with the batteries.

During winter (now in Australia) we actually change to a fixed price provider and then in Spring Change back to Amber again

Basically their API provides pricing data similar to what you have outlined.

What i did was work out what a roundtrip on a battery charge and discharge cycle costs me (in terms of maintenance and wear and tear) on the battery pack and then aim to exceed that.

Where it become tricky is that you have to look ahead also to make sure you do not drain your batteries too far so you are then exposed to the spot market price also.

Let me know what specific info you want and i will see what i can do

Craig

1 Like

Thanks Craig!

I'm in NZ, and with the advent of APIs being released by the grid operator I think we'll probably start seeing more wholesale price offerings, but right now there's only one retailer, and they don't have an API, which means I need to do it myself.

I'm currently using a Fronius Gen24 and BYD HVS, in my paper testing the HV batteries did better than the LV ones. Did you just divide your estimated cycle count by the cost of the battery to work out how much each cycle would cost?

This is the part that I'm trying to create some dynamic logic for, NZ and Oz have some pretty large fluctuations in price, so theoretically I can create some significant arbitrage. How did you go about trying to figure that out?

Honestly our winter is silly, you can get stuff like this, where I could have charged for 8c then sold for 32c:

So are you exposed to the wholesale rate (and it fluctuates) or is it set once a day for the next 24 hours ?

What we went for was 3 single phase AC coupled inverters with 13.3Kwh battery packs attached to each so we can pump out and absorb a max of 15KW at any time (5KW per inverter) - these were the biggest single phase inverters that were not a crazy price (about $1500 each) and worked with LV batteries (we use the Alpha ESS secondary battery modules)

I have a long running thread on it all on whirlpool - here if you want to read some of it and what we went through etc

What i was doing (because our rates are estimated 24 hours in advance - but vary every 5 minutes - which are then aggregated into 30 minute prices) was to take the forecast for the next 24 hours - put it in an array and then sort that from lowest to highest - i then did a search in that array for any values that were 20% higher than the average and made a 2nd array with those.

Then i looked at the weather report - if it was going to be an above average heat day (summer) i would then assume that there would be spikes in the evening from about 4PM onwards and would prioritise that over any other selling earlier in the day i.e. fully charge the batteries from Solar and top up from the grid prior to peak hour pricing starting.

I then put in a simple switch node - if 5min price was greater than 50c push power to the grid at 20% of capacity, if price was 75c push power to grid at 40% etc

Where ours gets tricky and i never really got it mastered is that the prices are calculated for all the wholesalers every 5 minutes - so a Big Battery can see a price spike and sell into it for 5 minutes and get paid on that - and then the price drops back to normal - whereas we get paid on the average of the 6 x 5 minute windows that make up a 30 minute block - so i could start selling at $1 to the grid and pumping out 9KW for say the first 15 minutes of a trading block and then the price would drop right back down as Gas generators jumped back in and the average price for the 30 minutes would only end up at 50c

As we are big electricity users in Winter (all heating etc) and we only get pitiful sun on our solar (low side of the street and some big trees) we moved to a normal TOU provider and will move back to Amber once Winter is over.

Craig

Thanks for starting this interesting topic.
I worked on some ideas too, but never finished.

I'm living in the North of Switzerland.
My energy provider offers two price levels, the higher one applies during the work day.
I've got 7kW-peak solar array, 45kWh on a perfect day. This fluctuates enormously.
The household uses about 10kWh/day (heating is done with a wood stove).
For mobility we have Tesla 3 car which uses 8kWh/day on average (range 0 .. 40kWh/day)
I use energy meters at strategic points, collect the data with node-red and store it in influxdb, use grafana for visualisation (as many others do too).
There is a solarcast service with quite accurate prognosis for the hourly solar power generation.

With this data at hand I analysed energy cost saving potential and if it is worthwhile to install a battery.

I found that the a battery is not paying back. ROI > 10 years.
And for cost optimisation it is best to store the excess energy in the Tesla battery, and this can be done manually without much effort.
I use the grid as battery which is more cost efficient.

I had high hopes of expanding my node-red bases imperium at home. But I failed.

It fluctuates, the API send the purple line (which is the forecast) and then the yellow line is the actual.

I'd written a "dumb" formula to say if this price is one of the largest 10 of the day and battery capacity wont drop below 5% then sell, and conversely if smallest 10 and won't push above 100%, then buy.

I'd also written an if statement to say if battery is full and at least one of the smallest is ahead then sell.

But the problem is that whilst this is fine, its nowhere near good enough for what it could be. And I can use excel reasonably well, but apparently not good enough to figure out a way to dynamically do this.

Hey Urs, I actually got inspired to do this by a paper I read about someone who'd done it for NordPool, and it was exceptionally profitable. I'll see if I can find it again, but I think the payback was like 4.5 years on the battery.

The caveat being, it depends on your inverter and your battery (as a sidebar, never get a Tesla Powerwall).

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.