Skip to main content
In short: a pool’s advertised APR is not what your liquidity position earns. Your position only earns fees while it is in range, and earns nothing on the days the price is out of range. This tool works out what a range would really have earned, day by day, on every network and fee tier at once, so you can tell whether a big number is real and which fee tier to pick. Tool: get_lp_yield It tells you what a price range would have earned in fees, from each pool’s own daily history. Name two tokens and the ranges you are weighing. It walks every pool that trades them, on every network and at every fee tier, and reports what each range would have collected. That answers the three questions an advertised rate cannot: which network, which fee tier, and which range.

Why an advertised rate cannot answer this

A pool’s quoted APY is the last 24 hours, across the pool’s whole book, for liquidity spread everywhere. Your position is not spread everywhere. It sits in a band, and on any day the price is outside that band it earns nothing, while the quoted rate keeps being quoted. So this walks history a day at a time: the pool’s fees that day, times your share of the liquidity that was in range, and zero on the days the price had left.
Pass every range you are weighing in one call. ranges is a list and each costs nothing extra, against a round trip per range otherwise. Omit it and you get the widest band that held the price for the whole window: the ceiling any narrower band is measured against.

A range is quote per one base

With one stablecoin in the pair that is dollars per coin and needs no thought. On WBTC/WETH or USDC/USDT it does: 25–40 and 0.025–0.04 are the same band from opposite ends. Set priced_in to say which, and the answer always reports the direction it used.

It answers with two halves, never one

The table is fees only. Beneath it comes CASH IN HAND: what the deposit is worth at the end, the impermanent loss, and the total against simply holding the same two tokens. People read the fee figure as the outcome, but it is only half of the result. A position can collect 31% in fees and still finish behind the coins it was made of.

Standing findings it reports

Measured across 2024–2026 on all three networks, and repeated with every answer because they are the conclusions people most often reach the other way:
  • One wide band beats several narrow ones at the same total capital. Splitting lost money at every division tested, at 2, 3, 5, 10 and 20 bands, on every network and both assets. A narrow band sits near its own edge more often, and a position at its edge holds one asset and earns least.
  • Fee tier depends on the asset and the band. On single wide bands 0.05% beat 0.30% on BTC and lost to it on ETH, by about 13% each way. Narrow the band and it flips. Compare tiers within one band; across bands the comparison means nothing.
  • If a band is too large for the pool, spread across networks, not across ranges. Splitting the range does not reduce your share of any pool. It only idles capital.

Where the history comes from

It comes from the pool history stream: 41 pools across Ethereum, Base and Arbitrum, reaching back to 2021-05-05.
There is one copy of that history and this reads it over the network.There used to be two: this system kept its own synced copy beside the backtester’s. They drifted to 39 pools against 32, 30 in common, because the two syncs ran on different schedules against an indexer that backfills. With two copies, the same question had two answers, and which one you saw depended on which service you happened to ask.If a survey and a backtest of the same pool disagree about what it earned, nobody is likely to notice. They would believe whichever answer they saw.

When it cannot answer

A pool with no stored history is reported as unread, never as a pool that earned nothing. The two must never read the same way, and for a while they did: the survey once answered “no WETH/USDC pool with liquidity” for a pair with three live pools and 1,956 days of history, because it was asking a database that held no pools at all. That answer was false and confident, and it went to somebody deciding where to put money. An empty answer is now an error that names the reason.