APR computations

This page describe the various methods used to compute Lagoon Vaults APR.

In order to compute those metrics, we will rely on the Period Summaries given by the Lagoon subgraphs.

Period summaries are piece of data that summaries key vault metrics evolution. A period being the time between two updates of TotalAssets.

As a reminder, here is what we can find inside a Period Summary:

type PeriodSummary @entity(immutable: false) {
  id: Bytes!
  vault: Bytes! # address
  totalAssetsAtStart: BigInt!
  totalSupplyAtStart: BigInt!
  totalAssetsAtEnd: BigInt!
  totalSupplyAtEnd: BigInt! # Before fee taking
  netTotalSupplyAtEnd: BigInt! # After fee taking
  blockNumber: BigInt! 
  blockTimestamp: BigInt! # Timestamp of the start of the period
  duration: BigInt! # Duration of the period
}

Single Period APR

How to compute the linear Net APR of a single period ?

Using totalAssetsAtStart and totalSupplyAtStart we can get the pricePerShareAtStart:

import { VaultUtils } from '@lagoon-protocol/v0-core';

const ppsAtStart = VaultUtils.convertToAssets(10n ** BigInt(vaultDecimals), {
    decimalsOffset: vaultDecimals - assetDecimals,
    totalAssets: summary.totalAssetsAtStart,
    totalSupply: summary.totalSupplyAtStart,
  });

Then using totalAssetsAtEnd and netTotalSupplyAtEnd we can get the netPricePerShareAtEnd:

const netPPSAtEnd = VaultUtils.convertToAssets(10n ** BigInt(vaultDecimals), {
    decimalsOffset: vaultDecimals - assetDecimals,
    totalAssets: summary.totalAssetsAtEnd,
    totalSupply: summary.netTotalSupplyAtEnd,
  });

Using those 2 values we can compute the evolution and annualize it using the period duration.

const gain = netPPSAtEnd - ppsAtStart;
const periodSeconds = newestTimestampB - oldestTimestampB;
const decimals = 18;
const SECONDS_PER_YEAR = 365n * 24n * 60n * 60n;

# used to increase the precision of the result
const INCREASE_PRECISION = 10n ** BigInt(decimals + 2);


const periodYield = gain * SECONDS_PER_YEAR * INCREASE_PRECISION;
return Number(
    formatUnits(periodYield / (periodSeconds * oldestPrice), decimals)
);

How to compute the linear Gross APR of a single period ?

The method is exactly the same as the one used previously. The difference will reside in the totalSupply used at the end of the period. This time we use summary.totalSupplyAtEnd.

const netPPSAtEnd = VaultUtils.convertToAssets(10n ** BigInt(vaultDecimals), {
    decimalsOffset: vaultDecimals - assetDecimals,
    totalAssets: summary.totalAssetsAtEnd,
    totalSupply: summary.totalSupplyAtEnd,
 });

Multiple periods APR

How to select a subset of periods summaries for a given duration, eg. 30 days?

The 30day Net APR computed right now in Lagoon tries to get the APR of the longest period inferior or equal to 30day from the most recent ended period. Fist, we take the most recent ended PeriodSummary timestamp, and subtract 30 days from it. This gives us the minimum timestamp of our starting Period Summary. We search through the summaries until we find the one with the smallest timestamp superior to the minimum timestamp. Then we compute the APR using the startPricePerShare of the oldest period and the endPricePerShare of most recent ended period.

export function getPeriodSummariesSubset(
  periodSummaries: GetPeriodSummariesQuery['periodSummaries'],
  duration: number
): GetPeriodSummariesQuery['periodSummaries'] {
  const filtered: GetPeriodSummariesQuery['periodSummaries'] = [];
  const mostRecentSummary = periodSummaries[periodSummaries.length - 1];
  const until = Number(mostRecentSummary.blockTimestamp) - duration;

  for (let i = periodSummaries.length - 1; i >= 0; i--) {
    const summary = periodSummaries[i];
    if (Number(summary.blockTimestamp) < until) break;

    filtered.push(summary);
  }
  return filtered;
}

How to compute a linear 30day Gross APR ?

It is not possible to do this, as it would only remove the fees for the last period used, which is not an accurate indicator of what the yield would have been without fees over multiple periods.

How to compute a Time-Weighted Rate Return (TWRR) 30day ?

The summaries subset selection will be the same as previously described.

In this computation we will do the average of the linear APR of the various periods, weighted by their duration.

Here is an example:

Period
Duration
APR

1

400

10%

2

600

20%

TotalDuration = 400 + 600 = 1000.

Period 1 Time Weighted APR (P1TW) : 400 / 1000 * 10% = 40% * 10 = 4.

Period 2 Time Weighted APR (P2TW) : 600 / 1000 * 20% = 60% * 20 = 12.

TWRR : P1TW + P2TW = 16%.

Here is a typescript example to compute the gross TWRR over a subset of Period Summaries.

We compute the Gross TWRR because we use summary.totalSupplyAtEnd. If we use summary.netTotalSupplyAtEnd we will get the Net TWRR.

function computeTWRR(
    summaries: PeriodSummaryEvent[],
    decimals: number,
  ): number
     {
    let grossTWR = 0;
    let totalDuration = 0;

    for (let i = 0; i < summaries.length; i++) {
      const summary = summaries[i];

      const ppsAtStart = VaultUtils.convertToAssets(10n ** BigInt(vaultDecimals), {
        decimalsOffset: vaultDecimals - assetDecimals,
        totalAssets: summary.totalAssetsAtStart,
        totalSupply: summary.totalSupplyAtStart,
      });
      
      const grossPPSAtEnd = VaultUtils.convertToAssets(10n ** BigInt(vaultDecimals), {
        decimalsOffset: vaultDecimals - assetDecimals,
        totalAssets: summary.totalAssetsAtEnd,
        totalSupply: summary.totalSupplyAtEnd,
      });
      

      const duration = Number(currentPeriod.duration);

      const grossApr = this.computeApr({
        startPricePerShare: pricePerShareAtStart,
        endPricePerShare: grossPricePerShareAtEnd,
        duration,
      });

      grossTWR += grossApr * duration;
      totalDuration += duration;
    }
    return grossTWR: grossTWR / totalDuration;
  }

Last updated