vault_report.sections
Documentation for eth_defi.vault_report.sections Python module.
Vault rankings and tables for the monthly vault report.
Each report section is a ranked subset of the vault metrics loaded by
eth_defi.vault_report.data.fetch_vault_report_data(). The post
structure is described in eth_defi/vault_report/README-blog-post-outline.md.
Vaults are classified into groups; each group except Other is ranked in its own table:
Lending: strategy tagged as lending, or a known lending protocol, see
LENDING_PROTOCOL_SLUGSReal-world assets (RWA): strategy tagged as investing in, lending against or financing real-world assets, see
RWA_STRATEGY_TAGSPerpetual futures DEX: Hyperliquid, GRVT, Lighter and other native trading vaults
AMM pools: GMX GM and GLV pools and Curve-based YieldBasis pools
Tokenised funds: vaults flagged
tokenised_fund, e.g. money market fundsOther: everything else, e.g. yield aggregators and trading vaults. It has no section of its own; its vaults appear in the new vaults, per-chain, yield and risk and return sections
Tables rank by the annualised one-month return, like the website’s vault
pages, net of fees when fee data is known and gross otherwise, shown without a
net or gross marker. Charts use the steadier annualised three-month return,
CHART_RETURN.
How the universes are narrowed, see eth_defi.vault_report.report.generate_monthly_vault_report():
filter_eligible_vaults()drops blacklisted vaults, vaults without TVL or a one-month return, and stale vaults. The result feeds the report statistics and the inflows and outflows.select_comparable_vaults()drops vaults whose protocol is not identified, because their returns are often artefacts. Every table and chart that compares returns starts from here.The investability check (
eth_defi.vault_report.vault_checks) removes vaults that are not investable in practice.exclude_amm_pools()leaves AMM pools only in their own section.The
select_*andcalculate_*functions apply per-section TVL, activity, risk and outlier thresholds fromReportCriteria.
TVL history charts, calculate_protocol_tvl_history() and friends,
only drop blacklisted vaults, select_tvl_history_vaults(), so their
totals match the website’s historical TVL charts.
Units: returns, volatilities and drawdowns are fractions (0.05 = 5%), as in the top vaults export; TVL is in USD; timestamps are naive UTC.
Module Attributes
Protocol slug for vaults whose protocol we could not identify |
|
Technical risk label for vaults excluded from all listings |
|
Sharpe ratios above this are displayed as |
|
The top vaults export caps annualised returns at 10,000%, as the fraction 100.0 ( |
|
Label of a capped annualised return, like on the website |
|
Public vault sparkline images, rendered by |
|
Strategy tags that make a vault a lending vault |
|
Strategy tags that make a vault a real-world asset (RWA) vault, see |
|
Protocols whose vaults are lending vaults even without a strategy tag. |
|
Vault feature of AMM liquidity-provider shares, set by the scanner for GMX GM and GLV pools and Curve-based YieldBasis LTs, see |
|
Vault flag for tokenised funds, such as money market and treasury funds |
|
Protocol slugs that mean the vault's protocol has not been identified yet: generic ERC-4626 and ERC-7540 vaults and placeholders. |
|
Protocol names, lower case, that mean the same. |
|
Label of the single pile of vaults whose protocol is not identified. |
|
Vault group labels, stored in the |
|
Default columns for vault tables, same as in the previous blog posts. |
|
Columns for the per-chain table, chain first |
|
Right-aligned numeric columns |
|
Columns whose values must not wrap onto two lines, e.g. |
|
Column with the return used by all charts: the annualised three-month return, net of fees when known. |
|
Legacy vault page URLs and their current form. |
Functions
|
Calculate the TVL-weighted average three-month yield per group. |
|
Find the blockchains whose vault TVL changed the most over the last month, in dollars. |
|
Sum vault TVL history per blockchain. |
|
Average yield of the largest blockchains by TVL. |
|
Sum tokenised fund NAV history per fund. |
|
Average yield of the highest-yielding vault protocols. |
|
Sum vault TVL history per protocol. |
|
Average yield of the largest vault protocols by TVL. |
|
Find the vaults with the largest TVL changes over the last month, in dollars. |
|
Rewrite legacy vault page URLs to the website's current |
|
Classify a vault into one of the report's vault groups. |
|
Leave AMM pools out of the rankings and charts outside their own section. |
|
Leave out vaults whose risk rating is too high to feature in a chart. |
|
Remove vaults that must not appear in any listing. |
|
Find one period's results in a vault's |
|
Format an annualised return, preferring the net value. |
|
Format a Sharpe ratio. |
|
Format the current TVL for a table cell. |
|
Format one vault as HTML table cells. |
|
Minimum TVL of a vault group's table and chart: higher for AMM pools. |
|
Find blacklisted vaults. |
|
Check whether a vault's protocol has been identified. |
|
Sort vaults by a metric, best first. |
|
Render a report section as a HTML table for the blog. |
|
Select the vaults counted in the average yield charts. |
|
Best vaults on each chain for the per-chain chart. |
|
Select the vaults that may appear in performance comparisons. |
|
Select the best vaults of a vault group. |
|
Best stablecoin yield vaults launched recently. |
|
Vaults of the risk and return chart: the yield vaults and the perp DEX vaults. |
|
Select vaults whose TVL history is counted in the market TVL chart. |
|
Best vaults on each chain, including perpetual DEX chains. |
|
Stablecoin yield vaults: every group except perpetual futures DEX vaults. |
|
Escaped HTML link, or the escaped text alone when the URL is not a web link. |
Classes
Selection thresholds for the report sections. |
|
One ranked vault table in the report. |
- UNKNOWN_PROTOCOL_SLUG = 'protocol-not-yet-identified'
Protocol slug for vaults whose protocol we could not identify
- BLACKLISTED_RISK = 'Blacklisted'
Technical risk label for vaults excluded from all listings
- MAX_DISPLAYED_SHARPE = 100
Sharpe ratios above this are displayed as
>100.Lending vaults with near-zero volatility get Sharpe ratios in the millions, which carry no information beyond “very smooth”.
- CAPPED_ANNUALISED_RETURN = 100.0
The top vaults export caps annualised returns at 10,000%, as the fraction 100.0 (
max_cagrineth_defi.research.vault_metricsperiod metrics).Annualising a large one-month gain, common for perp DEX vaults and broken share tokens, overflows to absurd numbers, so the exporter clamps it. Values at the cap are displayed as
CAPPED_RETURN_LABEL, andrank_vaults()breaks the resulting ties.
- CAPPED_RETURN_LABEL = '>9,999%'
Label of a capped annualised return, like on the website
- SPARKLINE_URL = 'https://vault-sparklines.tradingstrategy.ai/sparkline-table-90d-{vault_id}.png'
Public vault sparkline images, rendered by
eth_defi.research.sparkline_exportPNG rather than SVG, because email clients such as Gmail strip SVG images from newsletters
- LENDING_STRATEGY_TAGS = frozenset({'lending', 'lending_looping', 'lending_optimisation'})
Strategy tags that make a vault a lending vault
- RWA_STRATEGY_TAGS = frozenset({'rwa', 'rwa_credit', 'rwa_lending', 'rwa_royalties'})
Strategy tags that make a vault a real-world asset (RWA) vault, see
eth_defi.vault.strategy_tag.StrategyTag.RWA lending vaults are RWA vaults, not lending vaults: their risk is the real-world borrowers and collateral. Tokenised funds and perp DEX vaults financing real-world credit keep their own groups.
- LENDING_PROTOCOL_SLUGS = frozenset({'3jane', '40acres', 'aave', 'arcadia-finance', 'curvance', 'dolomite', 'euler', 'fluid', 'frax', 'gearbox', 'llama-lend', 'morpho', 'sentiment', 'silo-finance', 'spark', 'term-finance'})
Protocols whose vaults are lending vaults even without a strategy tag.
Not every protocol adapter sets strategy tags, so the report also recognises the lending markets it knows, matched against the export’s
protocol_slug. Tagging these adapters would make this list unnecessary. Untagged RWA vaults of these protocols land in lending until tagged with thecategorise-vault-strategyskill.
- AMM_POOL_FEATURE = 'amm_pool_like'
Vault feature of AMM liquidity-provider shares, set by the scanner for GMX GM and GLV pools and Curve-based YieldBasis LTs, see
eth_defi.erc_4626.core.ERC4626Feature.amm_pool_like
- TOKENISED_FUND_FLAG = 'tokenised_fund'
Vault flag for tokenised funds, such as money market and treasury funds
- UNIDENTIFIED_PROTOCOL_SLUGS = frozenset({'erc-4626', 'protocol-not-yet-identified', 'unknown', 'unknown-erc-7450'})
Protocol slugs that mean the vault’s protocol has not been identified yet: generic ERC-4626 and ERC-7540 vaults and placeholders. The same set as the website’s
isUnknownVaultProtocol()insrc/lib/top-vaults/helpers.ts.
- UNIDENTIFIED_PROTOCOL_NAMES = frozenset({'', 'unknown', 'unknown vault protocol'})
Protocol names, lower case, that mean the same. Names starting with
<, such as<unknown ERC-4626>placeholders, are also unidentified, seeis_identified_protocol().
- OTHER_PROTOCOL = 'Other'
Label of the single pile of vaults whose protocol is not identified. Charts also sum their small protocols into it, so unidentified vaults never show as a protocol of their own.
- LENDING = 'lending'
Vault group labels, stored in the
groupcolumn byeth_defi.vault_report.data.prepare_vault_metrics(), seeclassify_vault(). Each group exceptOTHERhas its own best-performing section ineth_defi.vault_report.post.BEST_SECTIONS.
- class ReportCriteria
Bases:
objectSelection thresholds for the report sections.
One place for every editorial threshold, so a month’s post can be tuned with
dataclasses.replace()without touching the selectors. The investability check reuses the same selectors with deepertop_nandchain_top_nvalues to collect replacement candidates, seeeth_defi.vault_report.report.collect_top_lists().The defaults reproduce the rules in
README-blog-post-outline.md; the section notes ineth_defi.vault_report.report.make_criteria_notes()and the chart subtitles quote these values, so changing one changes the post text too.Returns, volatilities and drawdowns are fractions (0.25 = 25%), TVL is in USD.
- min_tvl: float
Minimum current TVL for the best-performing vault tables, the yield universe of the hero image and the risk and return chart.
Large vaults have no table of their own: this threshold and the TVL column cover them, see the outline’s change log.
- min_events: int
Minimum lifetime deposit and redemption events (export
event_count) for lending, RWA, other and new vaults.Filters out inactive vaults, whose returns no depositors stand behind. Perp DEX vaults and tokenised funds have no comparable onchain ERC-4626 events and are exempt, see
select_group().
- max_data_age: datetime.timedelta
Exclude vaults whose latest metrics (export
end_date) are older than this, relative to the report data date.A vault the scanner stopped reading keeps its last, possibly flattering, returns in the export, which would otherwise keep ranking.
- new_vault_min_tvl: float
Minimum TVL for the new vaults table, lower than
min_tvlbecause a vault launched weeks ago has not had time to grow
- include_amm_pools: bool
Include AMM pools in the rankings and charts outside their own section. AMM pools, such as GMX GM pools and YieldBasis, carry the market risk of the traded assets, so by default they are ranked only in the AMM pools section and counted only in the TVL summaries.
- new_vault_max_age: datetime.timedelta
Maximum age of a vault in the new vaults table
- performance_chart_vaults: int
Number of vaults in each performance chart; more equity curves in one shared-axis chart become unreadable
- crypto_benchmark_min_volatility: float
Vaults at or above this annualised three-month volatility are compared with BTC and ETH instead of the Treasury bill.
The report’s own extension of the website’s benchmark rules: a trading-like stablecoin vault is better judged against the crypto market it trades, see
eth_defi.vault_report.benchmarks.select_benchmarks().
- crypto_benchmark_max_drawdown: float
Vaults with a three-month drawdown, a negative fraction, at or below this are compared with BTC and ETH instead of the Treasury bill
- chart_max_return: float
Leave vaults above this annualised three-month return, 400%, out of the hero image, where one outlier number would dwarf the others
- chart_min_excluded_risk: int
Lowest technical risk level left out of the charts: the hero image, the performance charts and risk and return. Dangerous and above, see
eth_defi.vault.risk.VaultTechnicalRisk. Charts get shared without the tables, so tables keep these vaults. Unrated vaults are shown.
- hero_max_volatility: float
Leave vaults above this annualised three-month volatility out of the hero image
- scatter_max_return: float
Clip the risk and return scatter’s y axis at most at this annualised return, 100%; vaults above are left out
- yield_high_yield_min_protocol_tvl: float
Minimum TVL of a protocol in the high yield average yield chart, which ranks protocols by yield.
Lower than
yield_min_protocol_tvlso smaller, high-yield protocols can appear; the chart’s TVL column shows how much money earns the yield.
- yield_min_vault_tvl: float
Minimum TVL per vault to be counted in the average yield charts.
Lower than the table minimum because the averages are TVL-weighted: small vaults barely move them but still show as dots.
- yield_max_return: float
Exclude outlier vaults above this annualised three-month return, 400%, from the average yields
- yield_max_volatility: float
Exclude vaults above this annualised three-month volatility from the average yields.
Keeps volatile crypto index funds and AMM liquidity positions out of the stablecoin yield figures.
- yield_chart_max_return: float
Cap of the average yield charts’ x axis for the largest chains and protocols, 40%. The axis otherwise fits the data, see
eth_defi.vault_report.charts.create_average_yield_figure(); vault dots beyond it are not drawn but still count in the averages. The high yield chart is capped atyield_max_returninstead.
- check_buffer_ratio: float
Investability check: extra candidates collected for each top list, as a share of the vaults it shows, see
eth_defi.vault_report.vault_checks. Checking 50% deeper than shown lets excluded vaults be replaced without another agent round.
- check_max_rounds: int
Investability check: rounds of refilling the top lists after exclusions. Each round runs the agent, minutes of LLM time, so the number is capped; unchecked in-scope vaults after the last round stop the report.
- check_prescreen_min_tvl: float
Investability check: minimum TVL of an aggregate chart vault to prescreen
- check_max_escalations: int
Investability check: maximum number of prescreened vaults sent to the agent
- __init__(min_tvl=100000, min_events=10, top_n=20, max_data_age=datetime.timedelta(days=7), chain_min_tvl=100000, chain_top_n=3, new_vault_min_tvl=15000, include_amm_pools=False, amm_min_tvl=1000000, new_vault_max_age=datetime.timedelta(days=60), performance_chart_vaults=8, crypto_benchmark_min_volatility=0.25, crypto_benchmark_max_drawdown=- 0.1, chart_max_return=4.0, chart_min_excluded_risk=50, hero_max_volatility=0.5, scatter_max_return=1.0, yield_top_chains=10, yield_top_protocols=10, yield_min_protocol_tvl=1000000, yield_high_yield_min_protocol_tvl=150000, yield_min_vault_tvl=10000, yield_max_return=4.0, yield_max_volatility=0.5, yield_chart_max_return=0.4, tvl_change_top_n=10, check_buffer_ratio=0.5, check_max_rounds=3, check_prescreen_min_tvl=1000000, check_max_escalations=20)
- Parameters
min_tvl (float) –
min_events (int) –
top_n (int) –
max_data_age (datetime.timedelta) –
chain_min_tvl (float) –
chain_top_n (int) –
new_vault_min_tvl (float) –
include_amm_pools (bool) –
amm_min_tvl (float) –
new_vault_max_age (datetime.timedelta) –
performance_chart_vaults (int) –
crypto_benchmark_min_volatility (float) –
crypto_benchmark_max_drawdown (float) –
chart_max_return (float) –
chart_min_excluded_risk (int) –
hero_max_volatility (float) –
scatter_max_return (float) –
yield_top_chains (int) –
yield_top_protocols (int) –
yield_min_protocol_tvl (float) –
yield_high_yield_min_protocol_tvl (float) –
yield_min_vault_tvl (float) –
yield_max_return (float) –
yield_max_volatility (float) –
yield_chart_max_return (float) –
tvl_change_top_n (int) –
check_buffer_ratio (float) –
check_max_rounds (int) –
check_prescreen_min_tvl (float) –
check_max_escalations (int) –
- Return type
None
- VAULT_TABLE_COLUMNS = ['Vault', '3M history', '1M ann.', '3M ann.', 'Lifetime ann.', '3M Sharpe', 'TVL', 'Age (y)', 'Token', 'Chain', 'Protocol']
Default columns for vault tables, same as in the previous blog posts. Cell values come from
format_vault_cells(); there is deliberately no risk rating column, the rating only filters the charts.
- CHAIN_TABLE_COLUMNS = ['Chain', 'Protocol', 'Vault', '1M ann.', '3M ann.', 'Lifetime ann.', 'TVL', 'Age (y)', 'Token']
Columns for the per-chain table, chain first
- NUMERIC_COLUMNS = {'3M Sharpe', 'Age (y)'}
Right-aligned numeric columns
- NOWRAP_COLUMNS = {'1M ann.', '3M ann.', 'Lifetime ann.', 'TVL'}
Columns whose values must not wrap onto two lines, e.g.
>9,999%or$1.2Msplit at the comma or point
- class ReportSection
Bases:
objectOne ranked vault table in the report.
Built by
eth_defi.vault_report.report.build_report_sections(), rendered to HTML byrender_section_table()and written to the bundle’stables/*.csv.- vaults_df: pandas.DataFrame
Vaults in display order, rows from
eth_defi.vault_report.data.VaultReportData.vaults_df, already truncated to the table length
- columns: list[str]
Table columns, see
VAULT_TABLE_COLUMNS
- sparkline_ids: frozenset[str]
Vault ids with a published sparkline image, see
eth_defi.vault_report.data.fetch_available_sparklines(). Filled in after the tables are selected, so only their vaults are probed; other vaults get an empty “3M history” cell instead of a broken image.
- filter_eligible_vaults(vaults_df, data_end_at, criteria)
Remove vaults that must not appear in any listing.
The first filter of every ranked section and of the inflows and outflows; the report statistics count its result as the vaults with up-to-date data. Drops:
blacklisted vaults, see
is_blacklisted(): ratedBlacklistedin the export, or given a bad flag ineth_defi.vault.flagsince the export was madevaults without a known TVL (
current_nav, alsoNaNfor broken share tokens aboveMAX_VALID_NAV) or one-month return, which cannot be rankedstale vaults whose
end_dateis more thanReportCriteria.max_data_agebeforedata_end_at
Staleness is measured against the report data date, the newest
end_datein the export, not the wall clock, so regenerating an old report from a cached export gives the same result.- Parameters
vaults_df (pandas.DataFrame) – Vault metrics from
eth_defi.vault_report.data.prepare_vault_metrics(), withrisk,address,protocol,current_nav,one_month_cagr_bestandend_date(naive UTC) columns.data_end_at (datetime.datetime) – The report data date,
eth_defi.vault_report.data.VaultReportData.data_end_at, naive UTC.criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Filtered vault metrics, the same columns.
- Return type
- exclude_amm_pools(vaults_df, criteria)
Leave AMM pools out of the rankings and charts outside their own section.
AMM pool returns include the price moves of the pooled crypto assets, so ranking them next to stablecoin yield would mislead. The AMM pools section itself selects from the unfiltered universe, see
select_group(). SeeReportCriteria.include_amm_pools.- Parameters
vaults_df (pandas.DataFrame) – Vault metrics with the
groupcolumn.criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Vaults without AMM pools, or all vaults when AMM pools are included.
- Return type
- exclude_chart_risks(vaults_df, criteria)
Leave out vaults whose risk rating is too high to feature in a chart.
Charts get shared on social media without the tables around them, so a Dangerous vault in a chart looks like a recommendation. Tables keep these vaults. The comparison is written as
~(risk >= threshold)rather thanrisk < thresholdso unrated vaults,NaNrisk_numeric, are kept.- Parameters
vaults_df (pandas.DataFrame) – Vault metrics with
risk_numeric, the numericeth_defi.vault.risk.VaultTechnicalRiskvalue orNaN.criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds, see
ReportCriteria.chart_min_excluded_risk.
- Returns
Vaults rated below the threshold, and unrated vaults.
- Return type
- select_comparable_vaults(eligible_df)
Select the vaults that may appear in performance comparisons.
Vaults without an identified protocol, the
OTHER_PROTOCOLpile, are often broken or misread: generic ERC-4626 wrappers, lending pool receipts and tokens with unusual share accounting. They are left out of every table and chart that compares returns, and counted only in TVL summaries: the TVL by protocol chart, inflows and outflows and the report statistics.- Parameters
eligible_df (pandas.DataFrame) – Output of
filter_eligible_vaults().- Returns
Eligible vaults with an identified protocol.
- Return type
- is_identified_protocol(protocol, protocol_slug)
Check whether a vault’s protocol has been identified.
Generic ERC-4626 and ERC-7540 vaults, unknown and placeholder protocols are not identified. The report puts them all in one
OTHER_PROTOCOLpile until their protocols are mapped, following the website’sisUnknownVaultProtocol()(src/lib/top-vaults/helpers.tsin the frontend), so the report and the website agree on what counts as a protocol. Both the name and the slug are checked, because a placeholder can show up in either. Mapping a protocol in the vault metadata moves its vaults out of the pile with no report change.Called by
eth_defi.vault_report.data.prepare_vault_metrics()to fill theprotocol_identifiedcolumn.
- classify_vault(vault)
Classify a vault into one of the report’s vault groups.
Every vault belongs to exactly one group, which decides its best-performing section, its table thresholds in
select_group()and whether it counts as stablecoin yield. The rules are checked in priority order, the first match wins:AMM: the scanner’samm_pool_likevault feature. Theammstrategy tag is deliberately not used: vaults taggedamm, such as Gains Network gTrade and KiloEx, stay in their other groups.PERP_DEX: theperp_dex_trading_vaultflag, precomputed asis_perp_dex.TOKENISED_FUND: thetokenised_fundflag, so funds financing real-world credit stay with the other funds.RWA: an RWA strategy tag, checked before lending so RWA lending vaults count as RWA, seeRWA_STRATEGY_TAGS.LENDING: a lending strategy tag, or a known lending protocol for adapters that set no tags, seeLENDING_PROTOCOL_SLUGS.OTHER: everything else.
Applied row by row with
pandas.DataFrame.apply()ineth_defi.vault_report.data.prepare_vault_metrics(). List columns come from JSON and areNaNorNonewhen missing, hence theisinstancechecks.- Parameters
vault (pandas.Series) – Vault metrics row with
features,is_perp_dex,flags,strategy_tagsandprotocol_slug.features,flagsandstrategy_tagsare lists of enum values or missing.- Returns
AMM,PERP_DEX,TOKENISED_FUND,RWA,LENDINGorOTHER.- Return type
- CHART_RETURN = 'three_months_cagr_best'
Column with the return used by all charts: the annualised three-month return, net of fees when known. Tables rank by the one-month return like the website; charts use the steadier three-month return, because one month of an annualised return is dominated by a single good or bad week. Filled by
eth_defi.vault_report.data.prepare_vault_metrics().
- rank_vaults(df, column='one_month_cagr_best')
Sort vaults by a metric, best first.
The single ranking used by every table and chart selector, so a vault’s position is the same wherever it appears.
The export caps annualised returns at 10,000%,
CAPPED_ANNUALISED_RETURN, so several perp DEX or newly launched vaults can share the same top value. Ties are broken with the absolute, non-annualised return of the same period (exportthree_months_returnsorone_month_returns), which is not capped: three months for metrics whose name starts withthree_months, which includes the three-month Sharpe ranking, one month otherwise. Vaults without the metric sort last instead of being dropped, so callers decide whether to filter them.- Parameters
df (pandas.DataFrame) – Vault metrics with
columnand its tie-breaker column.column (str) – Ranking metric, by default the annualised one-month return.
- Returns
Sorted vault metrics, the same rows.
- Return type
- group_min_tvl(group, criteria)
Minimum TVL of a vault group’s table and chart: higher for AMM pools.
AMM pools need
ReportCriteria.amm_min_tvlinstead ofReportCriteria.min_tvl: they are exempt from the deposit event threshold, and the higher TVL stands in for it. Also quoted in the performance chart subtitles.- Parameters
group (str) – Vault group, see
classify_vault().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Minimum TVL in USD.
- Return type
- select_group(eligible_df, criteria, group, *, by='one_month_cagr_best')
Select the best vaults of a vault group.
The selector of every best-performing section in
eth_defi.vault_report.post.BEST_SECTIONS: the table withbythe section’s table metric, one-month return or three-month Sharpe, and the performance chart withbyCHART_RETURN. The result is not truncated, so the investability check can take deeper candidate lists from the same ranking.Applies the TVL threshold,
ReportCriteria.amm_min_tvlfor AMM pools, and the activity threshold for lending, RWA and other vaults. Perpetual futures DEX vaults and tokenised funds have no comparable onchain deposit events, and AMM pools have the higher TVL threshold instead. Vaults without the ranking metric are dropped, because a table or chart cannot place them.- Parameters
eligible_df (pandas.DataFrame) – Comparable vaults, see
select_comparable_vaults(), withgroup,current_nav,event_countand thebycolumn. AMM pools must still be included, as their own section uses this.criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
group (str) – Vault group, see
classify_vault().by (str) – Ranking metric column.
- Returns
All matching vaults, best first, not truncated.
- Return type
- select_yield_vaults(eligible_df, criteria)
Stablecoin yield vaults: every group except perpetual futures DEX vaults.
Perp DEX vaults are trading strategies, not yield, so they are left out of the hero image, whose headline is the best stablecoin yield. Used for the hero image and as the base of
select_risk_return_vaults(). Applies the table thresholds; tokenised funds are exempt from the deposit event threshold, like inselect_group().- Parameters
eligible_df (pandas.DataFrame) – Comparable vaults with AMM pools already left out, see
exclude_amm_pools().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Vaults above the TVL and activity thresholds, best first.
- Return type
- select_risk_return_vaults(yield_universe, ranked_df, criteria)
Vaults of the risk and return chart: the yield vaults and the perp DEX vaults.
Perp DEX vaults are selected like their tables, with the table TVL minimum and no deposit events, and need the three-month return the chart plots. The chart shows them as one Perpetual futures category, see
eth_defi.vault_report.charts.create_risk_return_figure(), so readers can see how much more volatility trading vaults take for their return. The caller then drops Dangerous vaults and vaults whose share price did not move.- Parameters
yield_universe (pandas.DataFrame) – Output of
select_yield_vaults().ranked_df (pandas.DataFrame) – Comparable vaults, AMM pools already left out unless included.
criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Vaults, best first.
- Return type
- select_new_vaults(eligible_df, criteria)
Best stablecoin yield vaults launched recently.
The New vaults table, and the base of its performance chart. Vault age comes from the export
yearsfield, lifetime in years, so the maximum ageReportCriteria.new_vault_max_ageis converted to years of 365 days. Perp DEX vaults are left out as trading strategies, and the lowerReportCriteria.new_vault_min_tvllets young vaults in; the deposit event threshold still applies to every group here, so a new vault needs real depositors.- Parameters
eligible_df (pandas.DataFrame) – Comparable vaults with AMM pools already left out, see
exclude_amm_pools().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Top
ReportCriteria.top_nnew vaults, best first by the one-month return.- Return type
- select_vaults_by_chain(eligible_df, criteria)
Best vaults on each chain, including perpetual DEX chains.
The Vaults on each chain table. Every group is ranked together, with no deposit event threshold, so chains like Hyperliquid whose vaults are perp DEX vaults are represented too.
- Parameters
eligible_df (pandas.DataFrame) – Comparable vaults with AMM pools already left out, see
exclude_amm_pools().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Up to
ReportCriteria.chain_top_nvaults per chain, sorted by chain name, then by one-month return.- Return type
- select_chain_chart_vaults(eligible_df, criteria)
Best vaults on each chain for the per-chain chart.
Like
select_vaults_by_chain(), but ranked by the charts’ annualised three-month return,CHART_RETURN, and leaving out vaults rated too risky for charts. The chart names the two best vaults on each chain and draws the rest as runners-up, seeeth_defi.vault_report.charts.create_chain_best_figure().- Parameters
eligible_df (pandas.DataFrame) – Eligible vaults, AMM pools and unidentified protocols already left out.
criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Up to
ReportCriteria.chain_top_nvaults per chain, best first within each chain.- Return type
- select_average_yield_vaults(eligible_df, criteria)
Select the vaults counted in the average yield charts.
The universe of the Yield by chain and protocol dot plots, and of the investability check’s prescreen of large chart vaults. The averages are meant to describe what stablecoin holders earn, so the filters remove what would skew a TVL-weighted mean:
small vaults below
ReportCriteria.yield_min_vault_tvloutliers above
ReportCriteria.yield_max_return, typically capped or broken returnsvolatile vaults above
ReportCriteria.yield_max_volatility, like tokenised crypto index funds, AMM liquidity positions and most perp DEX vaults, which are not stablecoin yield
There is no deposit event threshold here, unlike the tables; a small vault barely moves a TVL-weighted average. Vaults with a missing three-month return or volatility fail the comparisons and are dropped.
- Parameters
eligible_df (pandas.DataFrame) – Comparable vaults with AMM pools and, for the charts, Dangerous vaults already left out, with
current_nav,CHART_RETURNandthree_months_volatilitycolumns.criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
Selected vaults, unsorted.
- Return type
- calculate_average_yields(yield_vaults, group_column)
Calculate the TVL-weighted average three-month yield per group.
Weighting by current TVL answers “what does a dollar in this chain or protocol earn”, which is what the post compares with the T-bill; an unweighted mean would let many small vaults outvote the few large ones that hold most of the money. Computed as
sum(tvl * return) / sum(tvl)in one groupby, with the products precomputed in aweightedcolumn.- Parameters
yield_vaults (pandas.DataFrame) – Output of
select_average_yield_vaults().group_column (str) –
chainorprotocol.
- Returns
DataFrame indexed by group with columns
tvl(USD),avg_return(TVL-weighted annualised three-month return, 0.05 = 5%) andvault_count.- Return type
- calculate_chain_yields(yield_vaults, criteria)
Average yield of the largest blockchains by TVL.
Automates the “average vault yield per blockchain” figure, previously a screenshot of the chain overview. Unlike the protocol charts, unidentified protocols need no extra filter here: the input is already comparable vaults only.
- Parameters
yield_vaults (pandas.DataFrame) – Output of
select_average_yield_vaults().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
See
calculate_average_yields(), the topReportCriteria.yield_top_chainschains by TVL.- Return type
- calculate_protocol_yields(yield_vaults, criteria)
Average yield of the largest vault protocols by TVL.
The Yield by protocol, high TVL dot plot. Placeholder protocols, such as generic ERC-4626 vaults of unidentified protocols, are left out, so the
OTHER_PROTOCOLpile never shows up as a protocol. The TVL floor is applied before picking the largest, so a month with few large protocols shows fewer rows rather than small ones.- Parameters
yield_vaults (pandas.DataFrame) – Output of
select_average_yield_vaults().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
See
calculate_average_yields(), the topReportCriteria.yield_top_protocolsprotocols by TVL with at leastReportCriteria.yield_min_protocol_tvlTVL.- Return type
- calculate_high_yield_protocols(yield_vaults, criteria)
Average yield of the highest-yielding vault protocols.
The Yield by protocol, high yield dot plot. Complements
calculate_protocol_yields(), which shows the largest protocols: this ranks all identified protocols with at leastReportCriteria.yield_high_yield_min_protocol_tvlTVL by their TVL-weighted average yield. Small protocols with a few vaults can top this chart; the TVL floor keeps single test vaults out, and the chart’s TVL column shows how much money earns the yield.- Parameters
yield_vaults (pandas.DataFrame) – Output of
select_average_yield_vaults().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
See
calculate_average_yields(), the topReportCriteria.yield_top_protocolsprotocols by average yield.- Return type
- LEGACY_VAULT_URLS = ((re.compile('https://tradingstrategy\\.ai/trading-view/[a-z0-9-]+/vaults/'), 'https://tradingstrategy.ai/vaults/'), (re.compile('https://tradingstrategy\\.ai/trading-view/(?!vaults\\b)([a-z0-9-]+)/vaults\\b'), 'https://tradingstrategy.ai/vaults/chains/\\1'), (re.compile('https://tradingstrategy\\.ai/trading-view/vaults\\b'), 'https://tradingstrategy.ai/vaults'))
Legacy vault page URLs and their current form. The website moved vault pages from
/trading-view/and redirects the old paths; exports and posts made before the move still carry them.The post’s writing rules require the current paths, and rewriting saves readers a redirect per click. Applied in order: vault pages first, because the per-chain listing pattern also matches the start of a vault page path and would turn it into a
/vaults/chains/...link.
- canonical_vault_urls(text)
Rewrite legacy vault page URLs to the website’s current
/vaults/paths.Used on the export’s
trading_strategy_linkvalues and on the HTML copied from the previous post, seeLEGACY_VAULT_URLS.
- find_period(period_results, period)
Find one period’s results in a vault’s
period_results.The top vaults export stores per-period metrics as a list of dicts, one per period, see
eth_defi.research.vault_metrics.PeriodMetrics, rather than flat columns. The report reads the one-month TVL and the three-month maximum drawdown from here.
- calculate_tvl_changes(eligible_df, criteria)
Find the vaults with the largest TVL changes over the last month, in dollars.
The Inflows and outflows by vault chart. Uses the one-month period’s
tvl_startandtvl_endfrom the top vaults exportperiod_results. A TVL change includes both deposits and redemptions and the vault’s own returns. Net flows would be cleaner, but the export’s estimatedflow_valuecovers only about a fifth of the vaults, so dollar TVL changes are used for every vault alike.Some funds are listed under several share token addresses with the same TVL; duplicates with the same name, chain and change are counted once. Separate share classes of one fund have different TVLs and each appear.
Increases and decreases are ranked separately, so a month with one huge outflow still shows the largest inflows. A single change far larger than the rest is drawn off scale by
eth_defi.vault_report.charts.create_tvl_change_figure(), which labels the bars withname, falling back toaddress.- Parameters
eligible_df (pandas.DataFrame) – Output of
filter_eligible_vaults()with the investability check’s exclusions applied, withperiod_results,nameandchaincolumns. Unidentified protocols are kept: this is a TVL summary, not a performance comparison.criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
The
ReportCriteria.tvl_change_top_nlargest increases and decreases, withtvl_start,tvl_endandtvl_changecolumns in USD, sorted from the largest increase to the largest decrease.- Return type
- calculate_chain_tvl_changes(eligible_df, criteria)
Find the blockchains whose vault TVL changed the most over the last month, in dollars.
The Inflows and outflows by blockchain chart. Sums the one-month TVL changes of each chain’s vaults, after the same deduplication as
calculate_tvl_changes(), so a chain’s bar is its net change: inflows and outflows of its vaults cancel out.- Parameters
eligible_df (pandas.DataFrame) – Output of
filter_eligible_vaults().criteria (eth_defi.vault_report.sections.ReportCriteria) – Report thresholds.
- Returns
The
ReportCriteria.tvl_change_top_nlargest net increases and decreases, indexed by chain name, withname,address,tvl_start,tvl_endandtvl_changecolumns in USD, sorted from the largest increase to the largest decrease.- Return type
- format_return(net, gross)
Format an annualised return, preferring the net value.
The net return is shown when fee data exists and the gross return otherwise, without a net or gross marker, like the website. Values at the export’s 10,000% cap,
CAPPED_ANNUALISED_RETURN, are shown asCAPPED_RETURN_LABEL, because the capped number itself is not the vault’s return.
- format_sharpe(value)
Format a Sharpe ratio.
Near-zero volatility lending vaults can have Sharpe ratios in the millions, which are displayed as
>100.
- format_tvl(current)
Format the current TVL for a table cell.
Tables show the current TVL only, in dollars with
k,MandBsuffixes rather than full digits, see the writing rules inREADME-blog-post-outline.md. The bundle’s CSV files keep the exact values.
- web_link(text, url)
Escaped HTML link, or the escaped text alone when the URL is not a web link.
Links come from exports and other data: accept only
https://links, never e.g.javascript:URLs.
- format_vault_cells(row, sparkline_ids=frozenset({}))
Format one vault as HTML table cells.
Every value is HTML-escaped here, because names, symbols and links come from onchain data and the export, and the cells are pasted into the Ghost post as raw HTML. Vault names link to the vault page, chains and protocols to their website pages; unidentified protocols show as
OTHER_PROTOCOLwithout a link.- Parameters
row (pandas.Series) – Vault metrics row from
eth_defi.vault_report.data.prepare_vault_metrics().sparkline_ids (frozenset[str]) – Vault ids with a published sparkline; other vaults get an empty sparkline cell.
- Returns
Column label -> escaped cell HTML, for all columns in
VAULT_TABLE_COLUMNS.- Return type
- render_section_table(section)
Render a report section as a HTML table for the blog.
The table markup matches the tables in the previous blog posts, so the blog styles them the same way. The blog frontend (
BlogPostContent.svelte) wraps post tables in a horizontally scrollable container on narrow screens.- Parameters
section (eth_defi.vault_report.sections.ReportSection) – Report section.
- Returns
HTML
<table>string.- Return type
- is_blacklisted(vaults_df)
Find blacklisted vaults.
A vault is blacklisted when the export rates it
Blacklisted, which includes vaults with bad flags when the export was made (seeeth_defi.research.vault_metrics.apply_bad_flag_check()), or when this repository’seth_defi.vault.flaggives it a bad flag. The second rule applies blacklist entries added after the export, e.g. by the investability check, right away, without waiting for the next export cycle. Only flags ineth_defi.vault.flag.BAD_FLAGScount:review_neededvaults stay in the report.Used by
filter_eligible_vaults()andselect_tvl_history_vaults(). The flag lookup is a Python call per vault, keyed by address and protocol.- Parameters
vaults_df (pandas.DataFrame) – Vault metrics with
risk,addressandprotocolcolumns.- Returns
Boolean series,
Truefor blacklisted vaults.- Return type
- select_tvl_history_vaults(vaults_df)
Select vaults whose TVL history is counted in the market TVL chart.
Excludes blacklisted vaults, see
is_blacklisted(), like the website’s historical TVL charts, so the totals match the website. Unlike the rankings, stale vaults, unidentified protocols, AMM pools and vaults the investability check excluded are kept: the TVL charts report where money is, not where to invest, and a stale vault still has a history up to when it stopped.- Parameters
vaults_df (pandas.DataFrame) – All vault metrics,
eth_defi.vault_report.data.VaultReportData.vaults_df.- Returns
Vaults to include.
- Return type
- calculate_protocol_tvl_history(tvl_history, vaults_df, top_n=7)
Sum vault TVL history per protocol.
The Stablecoin TVL by DeFi vault protocol chart. The caller passes DeFi vaults only; tokenised funds have their own NAV chart, because their share classes would dominate this one. Grouping by
protocol_labelsends unidentified protocols to theOTHER_PROTOCOLband, so they count in the total without showing as a protocol.- Parameters
tvl_history (pandas.DataFrame) – Output of
eth_defi.vault_report.data.read_vault_tvl_history(), one column per vault id.vaults_df (pandas.DataFrame) – Vault metrics with a
protocol_labelcolumn, indexed by vault id.top_n (int) – Identified protocols shown separately, by their latest TVL. Smaller protocols and vaults without an identified protocol are summed as
OTHER_PROTOCOL.
- Returns
DataFrame with one column per protocol, largest first, then
Other.- Return type
- calculate_chain_tvl_history(tvl_history, vaults_df, top_n=7)
Sum vault TVL history per blockchain.
The Stablecoin TVL by blockchain chart: the same DeFi vaults as
calculate_protocol_tvl_history(), grouped by where they live.- Parameters
tvl_history (pandas.DataFrame) – Output of
eth_defi.vault_report.data.read_vault_tvl_history(), one column per vault id.vaults_df (pandas.DataFrame) – Vault metrics with a
chaincolumn, indexed by vault id.top_n (int) – Blockchains shown separately, by their latest TVL. The rest are summed as
Other.
- Returns
DataFrame with one column per blockchain, largest first, then
Otherif more blockchains remain.- Return type
Sum tokenised fund NAV history per fund.
The Stablecoin NAV by tokenised fund chart. A fund deployed on several chains under the same name is counted as one fund, because readers know the fund, not its deployments. A fund without a name falls back to its address.
- Parameters
tvl_history (pandas.DataFrame) – Output of
eth_defi.vault_report.data.read_vault_tvl_history(), one column per vault id.funds_df (pandas.DataFrame) – Tokenised fund metrics with
nameandaddresscolumns, indexed by vault id.top_n (int) – Funds shown separately, by their latest NAV. The rest are summed as
Other.
- Returns
DataFrame with one column per fund, largest first, then
Otherif more funds remain.- Return type