Sharpe ProAPI · AUM flows · weekly intel · research
Access
Sharpe Finance
Connect your wallet
Transparency

Methodology

How Sharpe Finance derives risk, complexity, and exposure metrics from on-chain Morpho data. Every formula is open and auditable — if you disagree with a weight, you can replicate the calculation.

Data source

All metrics are computed from the public Morpho Blue GraphQL API at blue-api.morpho.org/graphql. We index every whitelisted MetaMorpho (V1) vault and every listed Morpho V2 vault across the eleven chains Morpho is deployed on — Ethereum, Base, Arbitrum, Optimism, Polygon, Unichain, Monad, HyperEVM, Katana, Stable, Tempo — ordered by total assets in USD. The dataset refreshes every 5 minutes (Next.js ISR), and the getDashboardData orchestrator falls back to bundled fixtures if the API is unreachable so the surface stays usable offline.

Vault history (APY, TVL) is taken directly from the API; we median-filter spike points above 50% APY because the underlying time-series occasionally carries IRM-snapshot artifacts. Risk and complexity scores are derived locally — composition logic is open in lib/risk-model.ts, exposure derivation in lib/morpho.ts. The depeg system reads CoinGecko spot, on-chain Chainlink USD aggregators, and issuer-side stress (Aave V3 GHO facilitator buckets, FRAX collateral ratio, USDe funding rate) directly via viem.

Morpho V2 vaults wrap V1 MetaMorpho positions through adapter contracts. We resolve those adapters through the V2 GraphQL fragment MetaMorphoAdapter.metaMorpho.address, look up the underlying V1 vault in the same fetch, and inherit its allocations weighted by adapter share. A V2 vault whose adapters don't resolve (e.g. direct MorphoMarketV1 adapters whose per-position detail exceeds the API's complexity cap in bulk) is scored with a maturity-factor opacity surcharge — never as a flat placeholder.

How we measure yield

Morpho vaults are managed mandates, not fixed instruments. Curators rebalance allocations continuously, so a trailing multi-month average of a vault's APY is the realized yield of a portfolio composition that no longer exists. We deliberately do not use trailing windows as decision inputs.

Each vault has two present-tense yield numbers:

  • Spot APY state.netApy at query time, net of vault fees, including active reward token emissions. The number on the headline. Volatile, but accurate for "what's earning right now."
  • Base APY (ex-rewards) — state.netApyWithoutRewards at query time, same instant and same allocations as the spot, just with the emission slice stripped. The rate that survives an active reward program ending. Stored on every vault as sustainableApy for historical-naming reasons.

Scoring, weighting, and recommendations use Base APY, not Spot. A short-term incentive that doubles a vault's headline doesn't double the value of holding the vault, and we don't want the universe-weighted APY in the topbar to flatter the surface every time a curator launches an emission campaign. Spot is still shown wherever we display yield, and the gap (the rewards slice) is surfaced explicitly.

A vault is flagged boosted when spot is more than 1.5× base — i.e. rewards account for more than ~33% of total yield. The boosted chip appears alongside the headline; the tooltip explains the split.

Trailing 30d and 90d averages remain on the vault detail page as historical context, labelled as backward-looking. They do not feed any scoring, ranking, recommendation, or aggregation.

Source-of-truth fields: lib/morpho.ts → computeSustainableApy, lib/morpho.ts → isApyBoosted.

Risk score (0–100)

Risk is the expected magnitude of principal loss under stress. It is computed as a composite of seven weighted factors plus two non-negotiable floors. Every vault renders the full decomposition on its detail page — the composite number is just max(weightedSum, warningFloor, depegFloor).

Three properties of this model worth flagging. (a) The protocol's own safety warnings — bad_debt_unrealized, incompatible_oracle_feeds, unsafe_vault_as_market_collateral — enter as composite floors rather than as weighted inputs. They cannot be averaged-down by clean structural factors. We deliberately exclude deposit_disabled, invalid_name, and invalid_symbol from the floor — the first is a vault lifecycle state (supply queue cleared, withdrawals still work), the latter two are upstream label-quality flags. Surfacing them as RED risk alerts inflates noise. The "Deposits closed" badge carries that signal instead. (b) Sustainable APY enters as a named factor (yield anomaly) with both a peer-cohort z-score and an absolute band, so cohorts that are uniformly elevated do not hide their risk premium behind low intra-cohort dispersion. (c) Morpho V2 vaults wrap V1 MetaMorpho positions through adapter contracts; we resolve those adapters via GraphQL and score V2 against the same factor model as V1 rather than assigning a constant.
weightedSum =
0.22 · collateralQuality + 0.20 · liquidation + 0.18 · yieldAnomaly
+ 0.12 · concentration + 0.10 · structural + 0.10 · maturity + 0.08 · liquidity

composite = max(weightedSum, warningFloor, depegFloor)

The model is built on a single principle: in an efficient lending market, excess yield is risk someone is bearing. A risk model that cannot explain the yield surface is wrong. We treat the protocol's own warnings as hard floors (Morpho's risk team has more context than us), we treat persistent yield anomalies relative to peer cohorts as risk we may not yet model, and we treat Morpho V2 vaults whose adapters can't be resolved at bulk as opaque — not zero-risk.

floor
Warning floorHard minimum imposed by Morpho's own protocol-level safety flags on the vault or its active markets. bad_debt_realized→90, bad_debt_unrealized→80, incompatible_oracle_feeds→65, unsafe_vault_as_market_collateral→60, hardcoded_oracle / not_whitelisted_oracle→55, not_whitelisted→55, unrecognized_collateral_asset→40. Unknown YELLOW→30, unknown RED→50. Lifecycle/metadata warnings (deposit_disabled, invalid_name, invalid_symbol) are excluded — they're surfaced as state badges instead. Market-scoped warnings only count when the vault has ≥10% of its TVL in that market, so dust positions on freshly-labeled assets don't trip an alert. The composite cannot fall below this number.
22%
Collateral qualityPer-collateral score, share-weighted across the vault's allocations. Each asset's score = 0.50 · volScore(σ) + 0.25 · mechanismScore + 0.20 · liquidityScore + 0.05 · tierResidual (sum = 1.00). volScore is derived from a Sharpe-style realized σ: 30 daily log returns from CoinGecko, sample stddev (Bessel-corrected, n − 1 denominator), annualized via √365 (crypto is 24/7, no trading-day adjustment). Standard error on σ from 30 obs is ±13%; n < 14 falls back to the tier anchor with no weight loss. The σ → 0..100 mapping is piecewise linear, calibrated so vanilla stables and T-bill NAV tokens sit near 0 (σ ≈ 0.5%), wstETH ≈ 50 (σ ≈ 30%), BTC ≈ 65 (σ ≈ 50%), broken pegs saturate at 100. mechanismScore = mean of three axes — oracle (chainlink_reference = 0, proxy = 10, internal_accountant = 30, hardcoded = 60), redemption path (instant_onchain = 0, queued = 20, offchain_T+n = 40, permissioned = 60, none = 80), issuer class (qualified_custodian = 0, regulated_mmf_admin = 10, dao_decentralized = 20, audited_defi_team = 30, multisig = 50, anon = 80) — hand-curated for ~45 collateral assets. liquidityScore = 0.65 · turnoverPenalty + 0.35 · sizePenalty derived from 30d avg CoinGecko volume / market cap (1inch on-chain depth probes land in Phase 2b). tierResidual is the asset-taxonomy penalty (rwa-tbill = 2, stable-fiat = 10, crypto-major = 18, stable-yield = 22, crypto-staked = 32, stable-synth = 38, crypto-restaked = 52, pendle-pt = 58, wrapper = 68, exotic = 78); it acts as the fallback bucket — when a sub-axis is unavailable for an asset, its weight collapses to tier so the composite stays well-defined. Holder concentration is the open Phase 2c sub-axis; its eventual ~0.10 weight will come from the vol slot, not tier. Also used to dampen the concentration factor's class-HHI term — concentrated exposure to safe collateral is not punished the way concentrated exposure to exotic collateral is.
10%
StructuralShare-weighted LLTV buffer adequacy only — the question 'are LLTVs aggressive vs. how much this collateral can actually move'. Asset-quality used to live here but moved to collateralQuality so the two dimensions don't double-count. Buffer is dampened for correlated pairs (wstETH/WETH, LBTC/WBTC, USDC/USDT, T-bill/USD-stable) — depeg risk between them is captured by collateral quality, not double-counted as buffer slack. Unknown allocations (V2 opaque) default to 25.
20%
LiquidationP(bad debt over 30d) proxy. For each market, headroom (1 − LLTV) is measured in σ-units of the collateral's 30-day realized vol. < 1.5σ headroom is the danger zone; < 0.8σ ramps the component above 50. Utilization above 70% amplifies it — high-util markets have fewer suppliers willing to provide liquidity during a cascade. RWA T-bill collateral has σ ≈ 0.002 — even a 96% LLTV market has 25σ headroom, so this factor sleeps for RWA-backed vaults.
18%
Yield anomalyTwo components, combined via max. (a) Peer-cohort z-score on sustainable APY: vaults paying >1σ above their loan-asset peers contribute proportionally. (b) Absolute-yield band: any sustainable APY ≥ 6% contributes at minimum 8 points; ≥ 10% contributes 32; ≥ 15% contributes 60. The absolute band catches entire cohorts whose yield is elevated regardless of within-cohort dispersion (frxUSD, eUSD, PT-heavy stables). Excess yield is risk we may not yet model.
12%
ConcentrationThree terms: market-HHI within the vault, asset-class-HHI within the vault (DAMPENED by collateral quality — a 100% T-bill basket pays no class-HHI penalty), and curator's universe TVL share. A curator running >10% of total Morpho whitelisted TVL starts to matter — their failure isn't idiosyncratic, it's systemic across their vaults. The dampener is what fixes the prior model's blind spot: a fully-concentrated RWA vault should not score higher than a diversified crypto basket purely on diversification.
8%
LiquidityCap-weighted utilization position + share of TVL sitting in markets above 95% utilization (functionally locked) − idle buffer share. A 95%+ vault scoring 80 reflects that a same-day redemption is more likely to queue than to settle.
10%
MaturityVintage penalty: newly-deployed vaults score worse. Age saturates at 18 months. TVL-time integral on a log scale — a $200M / 18mo vault scores stronger than a $1M / 3mo one even when the structural factors are identical. V2 vaults pay a 10-point base opacity surcharge; a further 25 when adapters can't be resolved to underlyings.
floor
Depeg floorWhen the vault's loan asset is showing an active depeg signal, the composite is floored at: 25 for watch (score ≥ 40), 50 for warning (score ≥ 60), 80 for critical (score ≥ 80). The depeg system reads CoinGecko spot, Chainlink oracle, and issuer-side stress (Aave facilitator state for GHO, FRAX collateral ratio, USDe funding rate). See the loan-asset peg health section below.

Bands. < 20 = blue-chip; 20–35 = mainstream; 35–55 = elevated; 55–75 = high; 75+ = critical.

Cross-protocol: same model, different inputs. The factor decomposition is protocol-agnostic. For Morpho the collateral-quality input is the per-market collateral asset (vaults route across many markets, each with its own collateral). For Aave V3 the supplier doesn't see per-borrow collateral — v1 approximates the basket as the reserve's own asset tier and the framework will swap in true basket-weighted scoring (per-collateral debt outstanding from UiPoolDataProvider) when the cross-protocol view ships. Same taxonomy, same 0..100 scale; the universe cohort math (USDC on Aave vs USDC on Morpho) stays apples-to-apples.

Market signal (0–100)

A second risk signal derived purely from what borrowers actually pay. In an efficient market, supply APY reflects the demand for leverage on that collateral — higher APY means borrowers are willing to pay more, which is a market-implied risk premium.

marketSignal = clamp(APY × 8, 0, 100)

Fixed scale (12.5% APY caps at 100) so the number is comparable across time, not just relative to the current universe.

Divergence is the diagnostic. When our structural risk score is higher than the market signal, our model thinks the vault is riskier than borrowers do — either we're over-counting risk on under-utilized collateral, or the market is mispricing. When the market signal is higher than the structural score, the vault is yielding more than its collateral composition would suggest — usually a sign of strong borrower demand for that loan asset (not exotic strategy risk).

Loan-asset peg health (0–100)

Every Morpho vault is denominated in a single loan asset (USDC, USDT, DAI, …). If that asset drifts off its peg, every depositor eats the move — and worse, Morpho's lending markets price collateral against a Chainlink oracle, not the open market. So a depeg can move LTVs and trigger liquidations before the spot price even reflects the stress.

For each USD-pegged loan asset we read two sources every cache cycle and surface the dominant deviation:

  • CoinGecko spot — centralized aggregate of CEX and DEX prices. The "market" price.
  • Chainlink USD aggregator — the oracle price. This is what Morpho actually liquidates against; the value used to compute every market's LTV.
spotDev = |spotPrice − 1|
oracleDev = |oraclePrice − 1|
divergence = |spotPrice − oraclePrice|

deviation = max(spotDev, oracleDev, divergence)
pegScore = clamp(deviation / 0.02 × 100, 0, 100)

Score saturates at 200 bps (2%) deviation. Bands: healthy below 30, watch 30–60, warning 60–80, critical 80+. When spot and oracle diverge by more than 30 bps the dashboard surfaces the gap explicitly — that's the dangerous regime where the market knows something the oracle hasn't priced in (or vice versa).

Why this is separate from the risk score. Asset-quality penalty (in the risk score above) is a static assumption — vanilla stables get 0.03 because they're usually on peg. The peg health signal is the live override: it tells you whether the assumption holds right now.

Vault-health sub-score. On top of the spot/oracle price comparison, we read issuer-side contracts directly so the score reflects backing stress, not just market price:

  • GHO — Aave V3 facilitator bucket utilization. Below 85% = 0; ramps linearly to max when the bucket is fully drawn.
  • FRAX global_collateral_ratio(). 20% under-collateralization saturates risk (deficit × 5).
  • USDC / USDT paused() state on the issuer contract. Paused = instant max (this is the kill-switch Circle and Tether can hit unilaterally).

The composite peg score is max(priceScore, vaultHealthScore) — either signal flashing pulls overall severity up.

Bad-debt VaR (0–100% over 30d)

Per-market VaR-style estimate of P(vault eats bad debt) over a 30-day horizon. Two thresholds matter: dropToLiquidation = 1 − ltv/lltv (when liquidators take over) and dropToBadDebt = 1 − ltv (when collateral value falls below debt). The 14%-to-26% band between them on a typical wstETH market is the liquidator's window — whether bad debt occurs there depends on liquidator efficacy, not σ alone.

Probability under log-normal returnsσ_30d = σ_annual × √(30/365). For a target drop d, z = ln(1 − d) / σ_30d (negative for any drop), and P_normal(T=30) = Φ(z). σ-headroom = |z| — the TradFi-interpretable distance to bad debt. 3σ headroom ≈ 0.13% probability, 1.5σ ≈ 6.7%, < 1σ ≈ 16%+. The combined output, labeled E[loss] (30d), is a SEVERITY-WEIGHTED EXPECTED LOSS, not a probability: E[loss] = P(drop ∈ [liq, bd]) · (1 − L) · LGD_window + P(drop > bd) · 1.0. The first term captures liquidator failure inside the window with mid-window LGD (≈ ½ · (drop_bd − drop_liq)); the second is the insolvency tail with assumed 100% loss. Result is a fraction of position value, not a probability — see the caveats below.

σ_30d = σ_annual × √(30/365)
z(drop) = ln(1 − drop) / σ_30d
P_normal(30d) = Φ(z(drop_bd))

P_bd = P(drop ∈ [drop_liq, drop_bd]) · (1 − L) · LGD_window
+ P(drop > drop_bd) · 1.0

Liquidator efficacy decompositionL = oracleFactor · profitMarginFactor · liquidityFactor · keeperFactor · chainFactor. Multiplicative because being weak on any single axis crushes liquidator success (reliability-chain logic). oracleFactor: Chainlink reference = 0.95, proxy = 0.88, internal accountant = 0.70, hardcoded = 0.10. profitMarginFactor: liquidation bonus minus expected slippage at a $5M trade — slippage is derived from marketCap directly (USDC at $50B+ → ~0.05% slip; mF-ONE at ~$30M → ~15%) so the liquidity axis doesn't double-count into the margin. liquidityFactor: maps the same liquidityScore the collateralQuality factor uses (turnover + size proxy) into a multiplicative factor — inverse-related to depth. keeperFactor: chain-baseline today (Ethereum 0.95, L2s 0.85, newer chains 0.55) — refined by per-market `Liquidate` event counts in Phase 2. chainFactor: per-chain rail reliability from historical bad-debt incidents (Ethereum 0.95, Base / Arbitrum / Optimism 0.92, Polygon 0.88, Unichain 0.78, newer chains 0.70). The bottleneck axis is reported only when the weakest factor is < 0.85 AND ≥ 0.10 below the second-weakest — otherwise the system is labeled balanced. The headline E[loss] uses L's conservative lower bound (L − 0.15 · (1 − L)).

L = oracleFactor · profitMarginFactor · liquidityFactor
· keeperFactor · chainFactor

Fat-tail adjustmentNormal log-returns under-state crypto tails by a factor of 2–5× depending on regime — empirically log returns on ETH/BTC/LSTs are closer to Student-t with df≈3-5. We label P_normal and P_stressed = P_normal × 3 separately rather than folding the multiplier silently. Both are shown; the reader picks the framing. KNOWN LIMITATION: the 3× multiplier is applied uniformly across asset classes — RWA T-bill NAV tokens have genuinely normal-shaped tails, so the ×3 over-states their tail probability; LRTs and exotic strategies may need ×4-5. Per-tier multipliers are on the Phase 2 roadmap once more realized-drawdown data is collected.

Honest caveats(1) Ltv per market is the aggregate, not per-borrower — the worst individual borrower is typically pinned just below lltv. (2) Bad debt during exchange / oracle outages is excluded — the L factor captures normal-operations failure, not infrastructure unavailability. (3) Correlated failure (same collateral class dropping across multiple markets simultaneously) is captured by the worst-market headline, not the share-weighted aggregate. (4) The 30d window is fixed at the CoinGecko sample size; longer horizons would need exponentially-tilted σ estimates we don't yet pull.

Complexity score (0–100)

Complexity measures strategy intricacy independent of risk. A vault can be simple and risky (one collateral at 95% LLTV), or complex and safe (well-managed split across many low-risk markets). The score reflects how many independent moving parts the strategy depends on.

score = 0.50 · weightedNovelty
+ 0.20 · maxNovelty
+ 0.15 · parameterSurface
+ 0.15 · noveltyDiversity
50%
Share-weighted noveltyEach collateral has a novelty value (vanilla = 0, LST = 0.15, LRT = 0.50, Pendle PT = 0.85, …). Weighted by allocation share. A vault with 5% Pendle and 95% USDC contributes less than 50/50.
20%
Novelty floorMax single-asset novelty across the vault's collaterals. A 5% Pendle slice still means the curator must monitor PT mechanics, so the vault is not 'simple' even if Pendle is a minority allocation.
15%
Parameter surfaceUnique market count, scaled so 1 market = 0 and 10+ markets = 1. More markets means more LLTVs, oracles, and IRMs for the curator to maintain.
15%
Novelty diversityCount of distinct novelty buckets present (LST / LRT / Pendle / yield-wrapper / exotic), normalized to 5. A vault that touches all 5 buckets is structurally more intricate than one that only uses LSTs.
Asset taxonomy
ClassExamplesQuality penaltySafe bufferNovelty
RWA · T-bill / MMFBUIDL, USYC, OUSG, USDM, USDY, USTB, FOBXX, JTRSY, EUTBL, BR DI fund tokens0.022%0.00
Vanilla stableUSDC, USDT, PYUSD, RLUSD, GUSD0.103%0.00
Vanilla BTC / ETHWETH, WBTC, cbBTC, tBTC0.1810%0.00
Newer BTC bridgesLBTC, kBTC, FBTC, uniBTC0.3213%0.00
Sky / crypto-coll stablessUSDS, sDAI, USDS, DAI, FRAX, crvUSD0.223%0.20
LSTwstETH, stETH, cbETH, rETH0.3212%0.15
LRTweETH, ezETH, rsETH, pufETH0.5218%0.50
Synthetic / credit stablesUSDe, USDe, syrupUSDC, syrupUSDT, USR0.386%0.45
Pendle PT/YTPT-sUSDE, PT-cUSD, …0.585%0.85
Junior tranche / exoticAA_FalconXUSDC, USCC, mF-ONE, RLP, XAUt0.7818%0.65
Selection surfaces — how recommendations are picked

Two surfaces in the app proactively recommend vaults: the auto-rendered "Best vault per loan asset" row on the dashboard and portfolio pages, and the wallet-gated "Upgrade your positions" cards on the portfolio page. Both share a common selection pipeline — a deterministic investability gate, the same scoring formula, then surface-specific thresholds.

1. Investability gate

A vault must clear every check below before it's eligible for any recommendation surface. The gate is the same code path everywhere (lib/investable.ts).

  • Deposits open — Morpho's deposit_disabled flag clears the supply queue; we hard-fail those.
  • No RED warning — riskFactors.warning < 50 (excludes bad_debt_realized/unrealized, incompatible_oracle_feeds, unsafe_vault_as_market_collateral, and every other RED-tier flag from lib/risk-model.ts).
  • No critical depeg — loan-asset depeg.score < 60 (excludes watch-band and above; details on the depeg card).
  • No issuer pause — if the issuer-side stress sub-signal reports paused (Aave facilitator off, Circle minting paused, etc.) the vault is ineligible regardless of yield.
  • Minimum TVL — $10M for major assets (USDC, USDT, DAI, WETH, wstETH, cbBTC, WBTC), $2M for niche assets.
  • Utilization < 95% — above that, deposits route into a market structurally locked for withdrawals.
  • Liquidity headroom — liquidityUsd / tvl ≥ 3% instant-redeem floor. When a wallet position size is in scope, the destination must also hold liquidityUsd ≥ 2× positionUsd so the move is executable AND leaves the next exiting depositor room.
  • Minimum age — 1 month on-chain operational history.
  • V2 opacity — Morpho V2 vaults whose adapters didn't resolve through GraphQL are excluded from recommendations. We don't surface what we can't audit.

2. Risk-adjusted score

Every candidate that clears the gate is scored with the same formula:

score = baseApy × (1 − risk/100) × (1 − complexity/200)
  • baseApy — the vault's present-tense yield with active reward emissions stripped (Morpho's netApyWithoutRewards). See the "How we measure yield" section above. We never use trailing-window averages for scoring — a managed vault's past composition is not a forward signal.
  • risk discount — full weight. A vault at risk=50 gets half credit on yield.
  • complexity discount — half weight (×200, not ×100). Complexity is a soft penalty: "you have to read more docs", not "you might lose money".

We deliberately do not add a liquidity multiplier here — liquidity is already a sub-factor inside riskScore and the gate has already filtered thin-float vaults, so a boost would triple-count.

3. Ranking, tiebreaks, and stability

  • Primary sort — score descending.
  • Tiebreak 1 — TVL descending. Larger vaults generally offer more headroom and curator skin.
  • Tiebreak 2 — vault id ascending. Stable identity prevents API-side reorderings from swapping near-ties between ISR cycles.
  • Boost demotion — if the top pick is boosted (spot > 1.5× base, i.e. rewards account for >33% of yield) AND its base APY is itself > 1.25× the cohort base-APY median, we swap it with the first non-boosted alternate. Two filters: rewards inflate spot, and the vault's base rate is already abnormally high.
  • Curator diversification — no curator appears more than once in the top-N for an asset. Alternates are picked from distinct curators so an allocator comparing "winner + alternates" sees alternative desks, not three vaults from the same shop.
  • Winner stability gap — we compute (score₁ − score₂) / score₁ and surface "near tie" framing in the UI when it's small.

4. Upgrade finder (wallet-aware)

When a wallet is connected, every held position is scored with the same formula and matched against candidates of the same loan asset that pass the gate at the user's actual position size. A candidate is surfaced only if it clears one of two lenses:

  • Yield upgrade — same-chain: ≥20% relative score lift AND ≥0.3 absolute score points (guards against noise on a low-score source). Cross-chain: ≥35% relative score AND ≥75 bps on sustainable APY — bridges have real costs that flat percentage improvements ignore.
  • Safety upgrade — same-chain only: ≥8 absolute AND ≥20% relative risk-points reduction, while preserving ≥95% of the source's base APY. Both conditions apply because 8 absolute points off a 70 is only 11% relative — not "much safer".
  • Source-stuck flag — if the user's CURRENT vault fails the position-sized gate (thin float, closed deposits), the card surfaces a warning so the user knows their exit may queue before they initiate the move.

5. Additions — fill structural gaps in your portfolio

When a wallet is connected, we deterministically scan the connected positions for three kinds of gaps and surface one vault per gap that closes it. No AI involvement in selection — the gap signal is read directly from the user's actual breakdown.

  • Asset gap — major loan assets (USDC, USDT, DAI, WETH, wstETH, cbBTC, WBTC) where the holder's allocation is < 5% of NAV. First such asset in display order produces one suggestion: the highest risk-adjusted investable vault for that asset.
  • Chain gap — fires when the holder sits on fewer than 2 distinct chains. Recommends the top vault on the first uncovered chain in [Ethereum, Base, Arbitrum].
  • Curator diversification — fires when one curator represents > 50% of NAV. Recommends a top vault from a DIFFERENT curator in the same asset class as the concentrated holding.

6. Goal-driven AI portfolio builder

You type a 1-line goal + capital + risk ceiling; the AI translates that into a STRICT JSON filter spec (asset universe, risk/complexity ceilings, exposure exclusions, diversification caps); deterministic code applies the spec against the investability-gated universe and returns an equal-weighted portfolio.

The AI never picks vault names or curator names — only constraint values. Every vault that lands in the output passes the same investability gate documented above. The same goal + constraints always produces the same spec (1-hour cache); the spec is re-resolved against the live universe on every request so the picks track current allocations.

Model: Claude Sonnet 4.6, max 400 output tokens, schema-validated. Rate-limited per IP at 5 req/min, capped at 300 requests per UTC day across all users. Source: lib/ai/portfolio-builder.ts + app/api/ai/portfolio-build/route.ts.

What's not in the score
  • Curator track record. We display vault counts and aggregate TVL per curator, but the risk score doesn't penalize new / unknown curators yet.
  • Timelocks and guardian quality. These are governance signals the API exposes (we surface guardian on the vault page) but don't factor into the score.
  • Vault cap utilization. V1 vaults don't expose per-market caps on the GraphQL API, so the capacity column is displayed as 1.5× current TVL — a defensible placeholder, not a measurement. V2 exposes hard caps; we're not yet pulling them.
  • Per-position LTV distribution. We use LLTV (protocol parameter) as a proxy for liquidation proximity. The actual current LTV per borrower position is determined by borrower behavior, which the API doesn't aggregate cheaply. The liquidation factor uses LLTV-headroom in σ-units, which is the right structural signal — but it can't tell you whether borrowers are *currently* sitting close to the liquidation line.
  • V2 adapter resolution at full depth. We resolve V2 → V1 MetaMorpho adapters through GraphQL. We do not yet walk into MorphoMarketV1Adapter positions at bulk — the GraphQL complexity cap blocks it (a single page of position-resolved V2 vaults exceeds 1.5M of the 1M ceiling). Those V2 vaults are scored with the opacity surcharge until per-vault adapter walks land.
Aave V3 — cross-protocol scoring

Aave V3 reserves are scored on the same 0–100 composite scale as Morpho vaults, with per-factor inputs adapted to Aave's structurally different shape (per-asset reserves vs curator-allocated markets, no allocation surface, governance-set parameters instead of curator discretion).

Cross-protocol cohort math

For each asset, the cohort spans both Aave V3 reserves and Morpho vaults on that asset. APYs are normalized to percent and compared like-for-like. This is the read U1/B2 personas can't get anywhere else — USDC on Aave (the conservative floor) sitting next to USDC on Steakhouse / Re7 / Felix, with z-scores telling you exactly how much spread the curated vaults are paying for their concentration risk.

Factor-by-factor adaptations

Floor
WarningAave doesn't publish a 'bad_debt_unrealized' enum; status flags fill the same role. paused → 70, frozen → 50, deprecated → 80. Acts as a hard floor on the composite.
0.22
Collateral qualitySame taxonomy as Morpho. v1 approximation: the reserve's own asset tier is used as a proxy for the protocol-wide collateral basket backing that reserve's borrows. A USDC reserve scores 10 (stable-fiat); a wstETH reserve scores 32 (crypto-staked). Phase 2 replaces this with the true basket-weighted score computed from per-collateral debt outstanding via UiPoolDataProvider — drop-in, same scale.
0.14
StructuralLiquidation bonus (wide bonus = governance pricing in volatility), reserve factor (higher = governance taking bigger cut on risky asset), supply cap pressure, isolation mode flag, siloed borrowing flag.
0.16
LiquidationPer-reserve liquidationThreshold (LLTV). Aggressive LLTVs (85%+) score higher. Aave LLTVs are uniform per reserve, not per-market like Morpho.
0.18
LiquidityUtilization curve position + supply-cap redemption cliff. Same shape as Morpho; the cap acts as a structural ceiling on new supply when binding.
0.14
Yield anomalyCross-protocol z-score on the asset cohort. Aave above cohort median is the flagged direction (Aave is usually the conservative floor); below median is expected and unpenalized.
0.06
ConcentrationThis reserve's USD supply as a share of total tracked supply for that asset (Aave + Morpho combined). High share → systemic dependency on a single venue.
0.04
MaturityAave V3 is mature on every chain we cover (Ethereum since Mar 2022, Base + Arbitrum since 2023). Held at 0 in v1; recently-listed reserves would elevate this once we track inception.
0.06 + floor
DepegComposite signal on the underlying asset, weighted at 0.06 inside the blend AND acting as a hard floor when the asset is in watch / warning / critical band. Liquidations on Aave V3 act on the Chainlink oracle regardless of spot.

Composite

score = max( warningFloor, depegFloor, 0.22·collateralQuality + 0.14·structural + 0.16·liquidation + 0.18·liquidity + 0.14·yieldAnomaly + 0.06·concentration + 0.04·maturity + 0.06·depeg )

Same hard-floor logic as Morpho: when a status flag or depeg signal binds, it overrides the weighted blend. This makes the score conservative under structural stress, regardless of factor-level readings.

Found something we got wrong? Back to dashboard — every formula lives in lib/morpho.ts, lib/aave/risk.ts and is fully auditable.