Skip to content

Data Layer

APY Methodology (Single Source of Truth)

All trailing APY figures in the application -- asset coverage table, carry strategy columns, money market rates, managed strategy chart -- derive from one function: annualizeRatio in src/lib/data/apy.ts.

APY = (index_end / index_start) ^ (SECONDS_PER_YEAR / elapsed_seconds) - 1

where elapsed_seconds is the actual number of seconds between the two index readings (not a nominal 30 days or 7 days), and SECONDS_PER_YEAR = 31,536,000.

This is the realised compound return over the window, annualised once. It is the correct, unbiased representation of a yield measured on a compounding index. Critically, it avoids the over-statement that results from averaging sub-window annualised rates: if a 30-day window is broken into 120 six-hour sub-windows and each sub-window's annualised rate is averaged, the result is a biased upward estimate of the true 30-day return.

The function returns null when no snapshot old enough to span the requested window exists. A flat or declining index legitimately produces 0% or negative APY and is reported as such -- no floor is applied.

Trailing windows used:

MetricWindow
APY 30d30 calendar days
APY 7d7 calendar days
APY 24h1 calendar day
APY 90d90 calendar days
1M returnAPY 30d / 12 (linear approximation)
YTD returnCumulative from Jan 1 of the current year
1Y returnTrailing 365-day compound

Yield Source by Token Type

Yield-bearing wrappers (ERC-4626 / share-rate tokens): sUSDe, sUSDS, syrupUSDC, syrupUSDT, reUSD, wstETH, weETH, rETH, tETH, osETH, ezETH all accrue yield by appreciation of the token's exchange rate against the underlying. The index used is token_yield_apy.share_rate. The rate is read on the token itself, except where it lives on an external contract (osETH via the StakeWise vault controller's convertToAssets, ezETH via Renzo's getRate() rate provider), configured per token by a rateSource.

Lending protocol reserves: USDC, USDT, GHO, ETH, WBTC etc. as lent on Fluid LL, Aave v3, SparkLend. Supply index: fluid_ll_apy.supply_exchange_price, aave_v3_reserve_apy.supply_index, sparklend_reserve_apy.supply_index. Borrow index: the corresponding borrow_exchange_price / borrow_index columns.

Fluid DEX pool fees: Fee yield is a flow, not an accumulated index, so it cannot be measured by a ratio. The trailing fee APY for a pool is:

fee_apy = (sum of fee_window_usd over the window) * FEE_WINDOWS_PER_YEAR
          / (sum of (smart_col_tvl + smart_debt_tvl) over the window)

where FEE_WINDOWS_PER_YEAR = 1460 (four 6-hour windows per day, 365 days). The summed-over-summed form is a TVL-weighted average of each window's fee yield. Returns null when fewer than two snapshots are available in the window.

Carry APY Composition

For each strategy type, the collateral (target) and debt (funding) leg APYs are composed from multiple index components, then summed.

T1 Single strategy:

target_apy = token_yield_apy.supply_apy (wrapper appreciation)
funding_apy = fluid_ll_apy.borrow_apy + token_yield_apy.supply_apy (of the debt token)

The second term on the funding side is non-zero only when the borrowed token is itself a yield-bearing wrapper (e.g. borrowing wstETH means the USD-denominated debt grows at both the venue borrow rate and wstETH's own appreciation, additive).

T4 Smart-Col / Smart-Debt strategy:

target_apy = fee_apy + sum_k( weight_col_k * (token_yield_apy_k + fluid_ll_supply_apy_k) )
funding_apy = sum_k( weight_debt_k * (fluid_ll_borrow_apy_k + token_yield_apy_k) ) - fee_apy

where weight_col_k and weight_debt_k are the USD-proportion of each token in the smart-col and smart-debt sides respectively, drawn from the latest pool snapshot. The fee APY appears with a positive sign on the target leg (LPs earn fees) and a negative sign on the funding leg (borrowers effectively receive fees for providing the pool's debt liquidity).

Aave v3 / SparkLend E-mode:

target_apy = token_yield_apy.supply_apy + reserve_table.supply_apy (wrapper appreciation + venue supply interest)
funding_apy = reserve_table.borrow_apy + token_yield_apy.supply_apy (of debt token)

The token_yield_apy term is COALESCE(..., 0) -- it contributes zero when the asset is not a yield-bearing wrapper, leaving just the venue rate.

Carry Distributional Statistics

For the Sharpe ratio and carry volatility columns in the CarriesTable, the application computes distributional statistics over the sample of 6-hour carry observations within each trailing window.

Sample construction: All (smartColApy_t - smartDebtApy_t) observations with snapshot_ts > latest_snapshot - windowDays * 86400s. The window boundary is exclusive: the snapshot exactly N days old falls in the prior window.

Minimum sample: If fewer than 3 observations exist in the window, all distributional stats are returned as null.

Carry mean:

carryMean = sum(carry_t) / n  (arithmetic mean)

Carry vol (sample standard deviation):

carryVol = sqrt( sum((carry_t - carryMean)^2) / (n - 1) )

Uses the unbiased n-1 estimator. Values below 1e-12 (a numerical floor guarding against floating-point cancellation on perfectly flat rate windows) are returned as null to avoid spuriously infinite Sharpe ratios.

Sharpe:

carrySharpe = carryMean / carryVol

No risk-free subtraction is applied. The Sharpe here is reward-to-risk of the gross carry, not of the excess return over the risk-free rate. The carry itself is already the excess: it is the net spread above funding cost, so the relevant question is how variable that spread is.

Interpretation note for wrapper-heavy strategies: The token_yield_apy.supply_apy column stores a 24-hour trailing rate rather than a 6-hour spot rate. In strategies where the target leg is a yield-bearing wrapper (sUSDe, wstETH, etc.), consecutive 6-hour carry observations share approximately 75% of their underlying window, introducing positive autocorrelation. The sample standard deviation therefore underestimates true carry volatility by roughly a factor of two relative to an independent-observation equivalent (daily striding). Cross-strategy Sharpe comparisons are most reliable within the same strategy class (wrapper-heavy vs. pure-lending). A Newey-West or daily-strided estimator is a known improvement not yet implemented.

Leveraged Position Simulation

The grownLeveragedPosition function in src/lib/sim/leveraged-position.ts models a buy-and-hold levered carry trade.

collateral_t = L * equity * (1 + targetApy)^years
debt_t       = (L - 1) * equity * (1 + fundingApy)^years
equity_t     = max(0, collateral_t - debt_t)

where L is leverage, years = days / 365. Both legs compound independently at their respective APYs. There is no rebalancing -- this is the exact equity path of a position opened once and held, not a continuously levered or Kelly-optimised model. Equity is floored at zero (cannot go negative).

Max-leverage carry (table column): The table's rightmost column shows the carry at maximum permissible leverage given the vault's LLTV. Maximum leverage is derived from LLTV as:

max_leverage = 1 / (1 - LLTV)

For example, a vault with LLTV of 0.85 supports a maximum leverage of 1 / (1 - 0.85) = 6.67x. The displayed max-lev carry is the gross carry multiplied by that leverage factor.

SOFR Series

SOFR rates are read from the onchain_credit.sofr_rates table, which stores daily ACT/360 rates and pre-computed rolling averages (30d, 90d, 180d). The 30-day rolling average is the primary benchmark shown on yield charts. All values are in percent (3.56 = 3.56%).

USD Prices

Live USD prices for tokens involved in capacity calculations are fetched from the DefiLlama price API (llama-prices.ts). This is used to express strategy borrowable headroom in USD terms when the debt asset is not a stablecoin.

Private documentation. creddit.xyz