Skip to content

Average daily change and annualised rate ​

Sample the share rate once a day for three days, then fold the samples into an average daily change and an annualised rate.

The share rate is what one stETH share is worth in ETH, and it steps up once a day when Lido's oracle reports. Three days of daily samples show the steps; summarize turns them into one row you can read as a yield.

cql
let stETH = ethereum:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;

from stETH at latest-21600..latest every 7200 as s
| extend ethPerShare = format(s.getPooledEthByShares(1e18), 18)
| summarize first = first(ethPerShare), last = last(ethPerShare), since = first($timestamp), until = last($timestamp)
| extend days = todecimal(totalseconds(until - since)) / 86400
| project { since, until, first, last, dailyPct: (last / first - 1) / days * 100, aprPct: (last / first - 1) / days * 365 * 100 }

Open in workbench →

sinceuntilfirstlastdailyPctaprPct
2026-09-21T19:34:47Z2026-09-23T20:56:35Z1.244558067649087691.2447109157008669020.00597106097939007085151934534141509406432.1794372574773758608045610496165093334695

One row: when the first and last samples were taken, the share rate at each, the average change per day in percent, and that change times 365. About 0.006 % a day, about 2.2 % a year.

How it reads ​

at latest-21600..latest every 7200 is the last three days, sampled once a day, plus the head itself. extend puts the share rate on every row; summarize with no by folds all the rows into one, and first and last take their values in row order — for a range, block order, oldest first. since and until are the timestamps of those same two rows.

until - since is a duration; totalseconds makes it a number of seconds and todecimal a decimal, so days is the exact span between the two samples, a little over two days here rather than a round three. last / first - 1 is the growth over that span; divided by days it is the growth per day, times 100 a percentage, times 365 an annualised rate.

The rows move with the head. latest is pinned when the run starts, so the same query an hour later has a later until, and after the next rebase a new last; both percentages shift a little each day. That is the point: this is a live reading, not a record.

Why the answer is inexact ​

dailyPct has forty digits after the point, and they are not all meaningful. The two share rates are exact — format never rounds — but days is a division and last / first another, and a division involving a decimal is carried to 38 digits rather than rounded to something that looks tidy. Read the first four or five digits and treat the rest as the cost of asking for a ratio; Numbers says where exactness stops and why.

Variations ​

  • A longer window. at latest-50400..latest every 7200 is a week at the same step; the average smooths out a day the oracle reported early or late.
  • By calendar day. at time '2026-09-20'..'2026-09-23' every 1d, and take since and until from $sampleAt instead of $timestamp so days is a whole number.
  • The steps themselves. Leave the summarize and everything after it out and you have the daily samples — Sample a long range.

See also ​