Impermanent Loss Calculation System Development
LP position in USDC/ETH pool on Uniswap V2 opened at ETH price $2000. Three months later ETH costs $3500. User sees "+$847 profit" in the interface, closes the position — and gets less than if they'd simply held ETH. This is impermanent loss. The system's task is show this difference clearly, before and after, with forecast at different price scenarios.
IL math — where implementations fail
Formula for Uniswap V2 (constant product)
Impermanent loss as function of price ratio k = P_current / P_initial:
IL = 2 * sqrt(k) / (1 + k) - 1
At k=1 (price unchanged) — IL=0. At k=4 (price 4x) — IL≈-5.72%. At k=0.25 (price 1/4) — same -5.72% (symmetric).
JavaScript/TypeScript implementation via BigNumber or decimal.js is mandatory for precision. Using native Math.sqrt(k) on regular floats gives error at very large or small k values. At k=0.000001 (99.9999% drop) native float loses significant digits.
Concentrated liquidity (Uniswap V3) — different math
For V3 position with range [Pa, Pb] and current price P the IL formula is far more complex. It depends whether P is inside the range or outside:
P within [Pa, Pb]:
value_LP = liquidity * (sqrt(P) - sqrt(Pa)) + liquidity * (1/sqrt(P) - 1/sqrt(Pb))
value_hodl = amount0_initial * P + amount1_initial
IL = value_LP / value_hodl - 1
P < Pa (exited below range): entire position converted to token1 (USDC), IL calculated as if LP sold all token0 at Pa price when exiting range and held token1 to now.
P > Pb: entire position in token0 (ETH), likewise.
This non-trivial logic many IL calculators ignore, computing via simplified V2 formula — giving 40-70% wrong results for V3 positions.
Accounting for accumulated fees
IL is difference between hodl and LP strategy. But LP also earns trading fees. Correct metric: net P&L = fees collected - impermanent loss.
For historical calculation: request Collect(tokenId, recipient, amount0, amount1) events from The Graph or Uniswap V3 subgraph, sum per position. Compare with IL calculated by historical prices at position open moment.
Calculation error: many take current position balance and compare to initial deposit at current prices — doesn't account for already withdrawn fees and price path taken. Need historical reconstruction: initial deposit → each collect → current state.
System architecture
Data sources
On-chain via The Graph — Uniswap V3 subgraph on mainnet (and L2: Arbitrum, Optimism, Polygon) has all position events: positions, positionSnapshots, collects, transactions. GraphQL query by tokenId returns full history.
Chainlink Historical Prices — for historical prices at open/close use Chainlink getRoundData(roundId). Find roundId matching needed timestamp via binary search on latestRoundData and getRoundData.
Alternative: CoinGecko API /coins/{id}/market_chart for historical OHLCV data — simpler but adds external dependency and rate limit constraints.
Uniswap V3 SDK — Position.fromAmounts(), Position.token0PriceLower, Position.token1PriceUpper for current position state calculation from tick and liquidity data from contract.
Forecast calculation
User wants to see: "if ETH rises to $5000, my IL will be X, fees Y, net P&L Z".
Algorithm:
- From current position: liquidity, tickLower, tickUpper, accumulated fees
- Set target price as parameter
- Calculate new token0/token1 distribution at target price via Uniswap V3 SDK
- Calculate IL = (value_at_target - hodl_value_at_target) / hodl_value_at_target
- For fees: extrapolate via historical trading volume data (The Graph) multiplied by fee rate
Honest fee forecast: fees depend on trading volume and whether position stays in-range. If at target price position exits range — fees stop accruing. Often overlooked.
Visualization
Key charts:
- IL vs Price chart: IL curve as price function for current position + comparison with hodl. For V3 — with range boundary markers.
- Break-even price: at what price accumulated fees cover IL. Horizontal line at net P&L = 0.
- Historical P&L timeline: by days, split fees vs IL.
Stack: React + recharts or Victory. Data via own API (Node.js + PostgreSQL for caching historical data) + direct The Graph calls.
| Component | Data source | Update |
|---|---|---|
| Current position | Uniswap V3 NonfungiblePositionManager | On each request |
| Historical prices | Chainlink / CoinGecko | Cache 1 hour |
| Accumulated fees | The Graph subgraph | Cache 5 min |
| Historical snapshots | The Graph positionSnapshots | Cache 1 hour |
Working process
Analytics (1 day). Determine: only Uniswap V3 or need V2/Curve/Balancer (each has its own IL math). Which networks: mainnet + L2.
Development (3-5 days). Math functions → data fetching layer → API → frontend charts. TypeScript + Zod for validating data from The Graph (subgraph can return null for young positions).
Timeline estimates
Calculator for V2 positions with historical calculation — 3 days. Full system with V3 concentrated liquidity, forecast calculator and visualization — 5-7 days.
Cost calculated individually.







