fintech-algorithms
Using a coding agent? Give it the skill: npx skills add IslamBaraka90/Fintech-Algorithms-Library What it does →

Liquidity-Provider Fee APR

Install and import#

bash
npm install fintech-algorithms
ts
import { feeApr } from "fintech-algorithms/digital-assets-and-on-chain-finance/liquidity-and-liquidation/liquidity-provider-fee-apr";

Signature#

feeApr(data, config)

Sums attributable fee income over a daily ledger — volume × fee rate × LP share × (1 − protocol share) — divides it by time-weighted average LP capital, then scales the observed period linearly by annualization_days / elapsed_days. The annualisation is simple, not compounded. Impermanent loss, token-price P&L, incentive emissions, range uptime, gas and taxes are all outside this metric.

Parameters#

NameTypeNotes
data{ rows: { timestamp?, duration_days?, volume, fee_rate, lp_share, lp_capital }[] }A non-empty ledger of intervals. volume is nonnegative, fee_rate and lp_share are fractions in [0, 1], lp_capital is positive, and duration_days defaults to 1. timestamp is carried through to the trace and is not used in the arithmetic.
config{ annualization_days?, protocol_fee_share?, capital_basis? }annualization_days defaults to 365 and must be positive; protocol_fee_share defaults to 0 and lies in [0, 1]; capital_basis is time_weighted (the default) or ending.

Returns#

{ elapsed_days, gross_volume, lp_fee_income, time_weighted_average_capital, capital_denominator, capital_basis, protocol_fee_share, simple_fee_apr, period_fee_return, annualization_days, trace }

The LP fee income and the capital denominator that produced simple_fee_apr, the unannualised period_fee_return, and a per-row trace carrying daily_fee_income, cumulative_fee_income and the running_apr recomputed over the prefix. All numbers are rounded to ten decimal places.

Errors#

  • When data or config is not an object, or rows is missing or empty — throws
  • When a row value is missing, non-finite, or outside its declared range — throws
  • When annualization_days is not positive, or protocol_fee_share is outside [0, 1] — throws
  • When capital_basis is neither time_weighted nor ending — throws

Complexity: time O(rows), space O(rows).

Worked example#

executed Captured by running this function on the input its own test provides. Real output of real code — but not asserted against a published figure.

Input#

data
{
  "rows": [
    {
      "timestamp": "2026-01-01T00:00:00Z",
      "duration_days": 1,
      "volume": 2500000,
      "fee_rate": 0.003,
      "lp_share": 0.02,
      "lp_capital": 1000000
    },
    {
      "timestamp": "2026-01-02T00:00:00Z",
      "duration_days": 1,
      "volume": 2738636.056514657,
      "fee_rate": 0.003,
      "lp_share": 0.02,
      "lp_capital": 1000000
    },
    {
      "timestamp": "2026-01-03T00:00:00Z",
      "duration_days": 1,
      "volume": 2930007.3153574164,
      "fee_rate": 0.003,
      "lp_share": 0.02,
      "lp_capital": 1000000
    }
  ]
}
config
{
  "annualization_days": 365,
  "protocol_fee_share": 0,
  "capital_basis": "time_weighted"
}

Call#

feeApr(data, config)

Returns#

object with 11 fields: elapsed_days, gross_volume, lp_fee_income, time_weighted_average_capital, capital_denominator, capital_basis, protocol_fee_share, simple_fee_apr, …

{
  "elapsed_days": 365,
  "gross_volume": 912500000,
  "lp_fee_income": 54750,
  "time_weighted_average_capital": 1000000,
  "capital_denominator": 1000000,
  "capital_basis": "time_weighted",
  "protocol_fee_share": 0,
  "simple_fee_apr": 0.05475,
  "period_fee_return": 0.05475,
  "annualization_days": 365,
  "trace": [
    {
      "index": 0,
      "timestamp": "2026-01-01T00:00:00Z",
      "volume": 2500000,
      "lp_capital": 1000000,
      "daily_fee_income": 150,
      "cumulative_fee_income": 150,
      "running_apr": 0.05475
    },
    {
      "index": 1,
      "timestamp": "2026-01-02T00:00:00Z",
      "volume": 2738636.056514657,
      "lp_capital": 1000000,
      "daily_fee_income": 164.3181633909,
      "cumulative_fee_income": 314.3181633909,
      "running_apr": 0.0573630648
    },
    {
      "index": 2,
      "timestamp": "2026-01-03T00:00:00Z",
      "volume": 2930007.3153574164,
      "lp_capital": 1000000,
      "daily_fee_income": 175.8004389214,
      "cumulative_fee_income": 490.1186023123,
      "running_apr": 0.0596310966
    }
  ]
}

Other exports#

This module also exports impermanentLoss, healthFactor, liquidationPrice, liquidationWaterfall, runTopic. Every module additionally exports run as an alias of its primary function, and a meta object carrying its catalog id, domain, family, shape and article URL.

Diagrams#

Liquidity-Provider Fee APR — boundary audit
Liquidity-Provider Fee APR — calculation map
Liquidity-Provider Fee APR — diagnostic deep dive
Liquidity-Provider Fee APR — family handoff
Liquidity-Provider Fee APR — parameter sensitivity
Liquidity-Provider Fee APR — scenario suite

Calculation flow#

Liquidity-Provider Fee APR — Calculation and Decision Flow
flowchart LR
    A["Identify protocol, version, chain, and valuation time"] --> B["Validate finite inputs and common units"]
    B --> C["Attribute observed fees and annualize against capital-days"]
    C --> D["Return value plus state/reason"]
    D --> E["Check equality, zero, and unsupported states"]
    E --> F["Compare sensitivity profiles"]
    F --> G{"Operational evidence complete?"}
    G -->|No| H["Keep as synthetic or descriptive calculation"]
    G -->|Yes| I["Add live source, oracle, execution, and finality controls"]

How it works#

This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.

Read the article →

References#

  • Uniswap v2 Core — Uniswap; Hayden Adams, Noah Zinsmeister, and Dan Robinson
  • Uniswap v3 Core — Uniswap; Hayden Adams, Noah Zinsmeister, Moody Salem, River Keefer, and Dan Robinson
  • UniswapV3Pool.sol — Uniswap; Uniswap Labs and protocol contributors
  • Evidence boundary

The rest of the Liquidity and Liquidation family#