vault_report.post
Documentation for eth_defi.vault_report.post Python module.
Assemble the monthly vault report blog post HTML.
The post body is Ghost-compatible HTML:
Tables, the podcast list and the table of contents placeholder are wrapped in
<!--kg-card-begin: html-->comments, which Ghost imports as raw HTML cards, keeping their markup intactCharts are Ghost image cards
The post has no editor callouts. The parts the editor writes (the month’s highlight, the report content updates, community news and commentary, the feature image) are listed in the editor workflow of
README-vault-report.md.
Evergreen sections (About the report, Next steps) are copied from the
previous report post, so edits made in Ghost carry over month to month. The
Partners section is rendered from PARTNERS, so partners are named
by organisation, not by X handle.
The section order, headings, introductions and notes follow the Writing
rules in README-blog-post-outline.md: keep them when changing the
templates below, and update the rules when a decision changes.
The templates are data, not code: BEST_SECTIONS defines each ranked
vault group once, and SECTION_TEMPLATES the order and text of every
data section. build_post_html() walks the templates and includes a
section only when its table or one of its charts was generated, so a missing
input (no prices, a failed benchmark download, an empty group) silently
removes a section instead of leaving an empty heading in the post.
build_post_html() is called twice per run by
eth_defi.vault_report.report: once with bundle-relative image paths
for post.html and the local preview, and once with the uploaded Ghost
image URLs for the draft. It is a pure function of PostContext,
so both renderings have the same text.
Module Attributes
Slug prefix of the monthly report posts. |
|
Vault pages on the website, linked from the section introductions |
|
Glossary on the website, linked from the section introductions |
|
Ghost theme placeholder that the theme fills with a table of contents of the post headings, as in the earlier reports |
|
Report partners as (organisation name, link), thanked in the Partners section. |
|
Evergreen sections used when there is no previous post to copy them from, by heading id. |
|
Chart key of the new vaults performance chart in |
|
Best-performing vault groups, each with a table and a performance chart; |
|
Post sections of the best-performing vault groups, by key |
|
Data sections in display order. |
Functions
|
Render the report post body. |
|
Wrap the post body into a standalone HTML page for local preview. |
|
Extract a |
|
Create a human-readable report month. |
|
Create the post URL slug. |
|
Create the post title. |
|
Read new integration entries from the changelog after a date. |
|
Render the Partners section, thanking each partner by name with a link. |
Classes
One ranked vault group of the post: its table, performance chart and section. |
|
Everything needed to render the post body. |
|
Static content of one data section of the post. |
- REPORT_SLUG_PREFIX = 'the-best-performing-stablecoin-vaults'
Slug prefix of the monthly report posts. The Content API finds the previous report by this prefix, so changing it breaks the month-to-month continuity of the evergreen sections and the link to the previous report.
- VAULTS_URL = 'https://tradingstrategy.ai/vaults'
Vault pages on the website, linked from the section introductions
- GLOSSARY_URL = 'https://tradingstrategy.ai/glossary'
Glossary on the website, linked from the section introductions
- TABLE_OF_CONTENTS = '<!--kg-card-begin: html-->\n<div id="table-of-contents"></div>\n<!--kg-card-end: html-->'
Ghost theme placeholder that the theme fills with a table of contents of the post headings, as in the earlier reports
- PARTNERS = (('Arbitrum DAO', 'https://x.com/@arbitrumdao_gov'), ('Envio', 'https://x.com/@envio_indexer'), ('dRPC', 'https://x.com/@dRPCorg'), ('IPOR', 'https://x.com/@ipor_io'), ('Goldsky', 'https://x.com/@goldskyio'), ('IceCreamSwap', 'https://x.com/@icecream_swap'), ('GRVT', 'https://x.com/@grvt_io'), ('Orderly Network', 'https://x.com/@OrderlyNetwork'), ('Lagoon Finance', 'https://x.com/@lagoon_finance'), ('Webacy', 'https://x.com/mywebacy'), ('Quants Spaces', 'https://quants.space/'))
Report partners as (organisation name, link), thanked in the Partners section. Maintained here rather than copied from the previous post, so the names stay organisation names, not X handles; edit this list when the partners change.
- render_partners_section(partners=(('Arbitrum DAO', 'https://x.com/@arbitrumdao_gov'), ('Envio', 'https://x.com/@envio_indexer'), ('dRPC', 'https://x.com/@dRPCorg'), ('IPOR', 'https://x.com/@ipor_io'), ('Goldsky', 'https://x.com/@goldskyio'), ('IceCreamSwap', 'https://x.com/@icecream_swap'), ('GRVT', 'https://x.com/@grvt_io'), ('Orderly Network', 'https://x.com/@OrderlyNetwork'), ('Lagoon Finance', 'https://x.com/@lagoon_finance'), ('Webacy', 'https://x.com/mywebacy'), ('Quants Spaces', 'https://quants.space/')))
Render the Partners section, thanking each partner by name with a link.
Rendered from
PARTNERSevery month rather than copied from the previous post like the other evergreen sections, because earlier posts named partners by X handle; the writing rules name them by organisation.
- DEFAULT_EVERGREEN_SECTIONS = {'about-the-report': '<h2 id="about-the-report">About the report</h2><p>In this post, we examine the performance of <a href="https://tradingstrategy.ai/glossary/decentralised-finance" rel="noreferrer">DeFi</a> vaults. Vaults can be considered "self-custodial investment strategies" in traditional finance: smart contracts that let users deposit funds from their cryptocurrency wallets and execute a predefined strategy with investors\' money.</p>', 'next-steps': '<h2 id="next-steps">Next steps</h2><p>Visit our <a href="https://tradingstrategy.ai/vaults">vaults page</a> for real-time dashboards. If you have any questions, <a href="https://tradingstrategy.ai/community">contact us on Discord, email or Twitter</a>.</p>'}
Evergreen sections used when there is no previous post to copy them from, by heading id.
Normally
build_post_html()copies these sections from the previous report, so the editor’s wording in Ghost carries over. The defaults cover a run without the Ghost Content API (local previews, tests) or a previous post that lacks the heading; the editor can then fix the text in Ghost once and later reports inherit it.
- class SectionTemplate
Bases:
objectStatic content of one data section of the post.
build_post_html()renders a section as its heading, introduction, criteria notes, charts and table, in that order. A section is included when its table or any of its charts exists. A grouping heading, seegroup, is included when any of its subsections is, so the post never shows a heading with nothing under it.The writing rules give every section an introduction paragraph that links to the matching tradingstrategy.ai page or glossary entry, except The best-performing vaults, which goes straight to its subsections;
test_every_section_introduces_itself_with_website_linksenforces it.- key: str
Section key: the table key in
PostContext.tablesand the section notes key
- class BestSection
Bases:
objectOne ranked vault group of the post: its table, performance chart and section.
The single definition of a best-performing section. The tables, the chart selection, the chart panel, the criteria notes and the post section are all derived from it, see
BEST_SECTIONS. Keeping them in one place means a new vault group, or a changed ranking metric, cannot leave the table, the chart and the investability check candidates (eth_defi.vault_report.report.collect_top_lists()) out of step.Tables rank by the one-month return like the website’s live ranking, while the charts rank by the steadier three-month return, see
chart_metric, so the two lists of a section may differ.- group: str
Vault group, see
eth_defi.vault_report.sections.classify_vault()
- subject: str
The ranked vaults in chart titles and alt texts, e.g.
the best-performing lending vaults
- measure: Literal['equity', 'sharpe']
Chart lines:
equitycurves, or the rollingsharperatio ranked bymetric
- property chart_key: str
Key of the performance chart in
PostContext.charts.
- property chart_metric: str
Column that picks and ranks the chart vaults: the steadier three-month return, or the Sharpe ratio.
- property template: eth_defi.vault_report.post.SectionTemplate
The post section of this group.
- __init__(key, group, heading, heading_id, subject, benchmark='their benchmarks', metric='one_month_cagr_best', measure='equity', ranked_by='by 3M return', link='tradingstrategy.ai/vaults', intro='')
- NEW_VAULTS_CHART = 'new_performance'
Chart key of the new vaults performance chart in
PostContext.charts
- BEST_SECTIONS = (BestSection(key='lending', group='lending', heading='Lending vaults', heading_id='best-performing-lending-vaults', subject='the best-performing lending vaults', benchmark='their benchmarks', metric='one_month_cagr_best', measure='equity', ranked_by='by 3M return', link='tradingstrategy.ai/vaults', intro='<p>Vaults that supply stablecoins to <a href="https://tradingstrategy.ai/glossary/lending-protocol">lending protocols</a> such as Morpho and Euler, and earn the interest borrowers pay. See all <a href="https://tradingstrategy.ai/vaults/strategies/lending">lending vaults</a>.</p>'), BestSection(key='rwa', group='rwa', heading='Real-world asset (RWA) vaults', heading_id='best-performing-rwa-vaults', subject='the best-performing RWA vaults', benchmark='their benchmarks', metric='one_month_cagr_best', measure='equity', ranked_by='by 3M return', link='tradingstrategy.ai/vaults', intro='<p>Vaults that invest in, lend against or finance <a href="https://tradingstrategy.ai/glossary/rwa">real-world assets</a>, such as private credit, trade finance and royalties. See all <a href="https://tradingstrategy.ai/vaults/strategies/rwa">RWA vaults</a>.</p><p>Real-world asset vaults are a new category and may still contain misclassifications, as we are refining the different RWA categories.</p>'), BestSection(key='perp_dex', group='perp_dex', heading='Perpetual futures DEX vaults by return', heading_id='best-performing-perp-dex-vaults', subject='the best-performing perp DEX vaults', benchmark='BTC and ETH', metric='one_month_cagr_best', measure='equity', ranked_by='by 3M return', link='tradingstrategy.ai/vaults', intro='<p>Vaults on <a href="https://tradingstrategy.ai/glossary/perpetual-future">perpetual futures</a> DEXes such as Hyperliquid, GRVT and Lighter, which are discretionary, directional algorithmic or market making vaults. See all <a href="https://tradingstrategy.ai/vaults/strategies/perpetual-futures">perpetual futures vaults</a>.</p>'), BestSection(key='perp_dex_sharpe', group='perp_dex', heading='Perpetual futures DEX vaults by Sharpe ratio', heading_id='best-performing-perp-dex-vaults-by-sharpe', subject='perp DEX vaults with the best Sharpe ratio', benchmark='BTC and ETH', metric='three_months_sharpe_best', measure='sharpe', ranked_by='by 3M Sharpe ratio', link='tradingstrategy.ai/vaults', intro='<p>The same vaults ranked by their three-month <a href="https://tradingstrategy.ai/glossary/sharpe">Sharpe ratio</a>, the return per unit of volatility. The ranking rewards steady returns over high but volatile ones; see <a href="https://tradingstrategy.ai/glossary/risk-adjusted-return">risk-adjusted return</a>.</p>'), BestSection(key='amm', group='amm', heading='AMM pools', heading_id='best-performing-amm-pools', subject='the best-performing AMM pools', benchmark='their benchmarks', metric='one_month_cagr_best', measure='equity', ranked_by='by 3M return', link='tradingstrategy.ai/vaults', intro='<p><a href="https://tradingstrategy.ai/glossary/amm">AMM</a> liquidity pools, such as GMX GM and GLV pools and YieldBasis pools, earn trading fees, but their returns also move with the prices of the pooled crypto assets. See all <a href="https://tradingstrategy.ai/vaults/strategies/amm">AMM vaults</a>.</p>'), BestSection(key='tokenised_funds', group='tokenised_fund', heading='Tokenised funds', heading_id='best-performing-tokenised-funds', subject='the best-performing tokenised funds', benchmark='their benchmarks', metric='one_month_cagr_best', measure='equity', ranked_by='by 3M return', link='tradingstrategy.ai/vaults/funds', intro='<p>The <a href="https://tradingstrategy.ai/glossary/tokenised-fund">tokenised funds</a> with the best returns. Their yields follow money market, treasury and credit rates, and many are open only to qualified or institutional investors. See all <a href="https://tradingstrategy.ai/vaults/funds">tokenised funds</a>.</p><p>Often there is no clear distinction between a vault and a tokenised fund. From the technical point of view, both use exactly the same smart contract technology with whitelisting. For the report, we define a project as a tokenised fund if we have found evidence that they have self-declared themselves as a tokenised fund.</p>'))
Best-performing vault groups, each with a table and a performance chart;
SECTION_TEMPLATESsets their display order. Perpetual futures DEX vaults appear twice, ranked by return and by Sharpe ratio, because a raw return ranking of trading vaults favours the most volatile ones.
- BEST_TEMPLATES = {'amm': SectionTemplate(key='amm', heading_id='best-performing-amm-pools', heading='AMM pools', intro='<p><a href="https://tradingstrategy.ai/glossary/amm">AMM</a> liquidity pools, such as GMX GM and GLV pools and YieldBasis pools, earn trading fees, but their returns also move with the prices of the pooled crypto assets. See all <a href="https://tradingstrategy.ai/vaults/strategies/amm">AMM vaults</a>.</p>', charts=(('amm_performance', '90-day performance of the best-performing AMM pools against their benchmarks'),), level=3, group=False), 'lending': SectionTemplate(key='lending', heading_id='best-performing-lending-vaults', heading='Lending vaults', intro='<p>Vaults that supply stablecoins to <a href="https://tradingstrategy.ai/glossary/lending-protocol">lending protocols</a> such as Morpho and Euler, and earn the interest borrowers pay. See all <a href="https://tradingstrategy.ai/vaults/strategies/lending">lending vaults</a>.</p>', charts=(('lending_performance', '90-day performance of the best-performing lending vaults against their benchmarks'),), level=3, group=False), 'perp_dex': SectionTemplate(key='perp_dex', heading_id='best-performing-perp-dex-vaults', heading='Perpetual futures DEX vaults by return', intro='<p>Vaults on <a href="https://tradingstrategy.ai/glossary/perpetual-future">perpetual futures</a> DEXes such as Hyperliquid, GRVT and Lighter, which are discretionary, directional algorithmic or market making vaults. See all <a href="https://tradingstrategy.ai/vaults/strategies/perpetual-futures">perpetual futures vaults</a>.</p>', charts=(('perp_dex_performance', '90-day performance of the best-performing perp DEX vaults against BTC and ETH'),), level=3, group=False), 'perp_dex_sharpe': SectionTemplate(key='perp_dex_sharpe', heading_id='best-performing-perp-dex-vaults-by-sharpe', heading='Perpetual futures DEX vaults by Sharpe ratio', intro='<p>The same vaults ranked by their three-month <a href="https://tradingstrategy.ai/glossary/sharpe">Sharpe ratio</a>, the return per unit of volatility. The ranking rewards steady returns over high but volatile ones; see <a href="https://tradingstrategy.ai/glossary/risk-adjusted-return">risk-adjusted return</a>.</p>', charts=(('perp_dex_sharpe_performance', '90-day performance of perp DEX vaults with the best Sharpe ratio against BTC and ETH'),), level=3, group=False), 'rwa': SectionTemplate(key='rwa', heading_id='best-performing-rwa-vaults', heading='Real-world asset (RWA) vaults', intro='<p>Vaults that invest in, lend against or finance <a href="https://tradingstrategy.ai/glossary/rwa">real-world assets</a>, such as private credit, trade finance and royalties. See all <a href="https://tradingstrategy.ai/vaults/strategies/rwa">RWA vaults</a>.</p><p>Real-world asset vaults are a new category and may still contain misclassifications, as we are refining the different RWA categories.</p>', charts=(('rwa_performance', '90-day performance of the best-performing RWA vaults against their benchmarks'),), level=3, group=False), 'tokenised_funds': SectionTemplate(key='tokenised_funds', heading_id='best-performing-tokenised-funds', heading='Tokenised funds', intro='<p>The <a href="https://tradingstrategy.ai/glossary/tokenised-fund">tokenised funds</a> with the best returns. Their yields follow money market, treasury and credit rates, and many are open only to qualified or institutional investors. See all <a href="https://tradingstrategy.ai/vaults/funds">tokenised funds</a>.</p><p>Often there is no clear distinction between a vault and a tokenised fund. From the technical point of view, both use exactly the same smart contract technology with whitelisting. For the report, we define a project as a tokenised fund if we have found evidence that they have self-declared themselves as a tokenised fund.</p>', charts=(('tokenised_funds_performance', '90-day performance of the best-performing tokenised funds against their benchmarks'),), level=3, group=False)}
Post sections of the best-performing vault groups, by key
- SECTION_TEMPLATES = (SectionTemplate(key='best', heading_id='the-best-performing-vaults', heading='The best-performing vaults', intro='', charts=(), level=2, group=True), SectionTemplate(key='new', heading_id='best-performing-new-vaults', heading='New vaults', intro='<p>The best-performing vaults launched recently. Their short history makes their returns less reliable, so treat them as vaults to watch. See all <a href="https://tradingstrategy.ai/vaults/new-vaults">new vaults</a>.</p>', charts=(('new_performance', 'Performance of the best-performing new vaults since their launch against their benchmarks'),), level=3, group=False), SectionTemplate(key='lending', heading_id='best-performing-lending-vaults', heading='Lending vaults', intro='<p>Vaults that supply stablecoins to <a href="https://tradingstrategy.ai/glossary/lending-protocol">lending protocols</a> such as Morpho and Euler, and earn the interest borrowers pay. See all <a href="https://tradingstrategy.ai/vaults/strategies/lending">lending vaults</a>.</p>', charts=(('lending_performance', '90-day performance of the best-performing lending vaults against their benchmarks'),), level=3, group=False), SectionTemplate(key='rwa', heading_id='best-performing-rwa-vaults', heading='Real-world asset (RWA) vaults', intro='<p>Vaults that invest in, lend against or finance <a href="https://tradingstrategy.ai/glossary/rwa">real-world assets</a>, such as private credit, trade finance and royalties. See all <a href="https://tradingstrategy.ai/vaults/strategies/rwa">RWA vaults</a>.</p><p>Real-world asset vaults are a new category and may still contain misclassifications, as we are refining the different RWA categories.</p>', charts=(('rwa_performance', '90-day performance of the best-performing RWA vaults against their benchmarks'),), level=3, group=False), SectionTemplate(key='perp_dex', heading_id='best-performing-perp-dex-vaults', heading='Perpetual futures DEX vaults by return', intro='<p>Vaults on <a href="https://tradingstrategy.ai/glossary/perpetual-future">perpetual futures</a> DEXes such as Hyperliquid, GRVT and Lighter, which are discretionary, directional algorithmic or market making vaults. See all <a href="https://tradingstrategy.ai/vaults/strategies/perpetual-futures">perpetual futures vaults</a>.</p>', charts=(('perp_dex_performance', '90-day performance of the best-performing perp DEX vaults against BTC and ETH'),), level=3, group=False), SectionTemplate(key='perp_dex_sharpe', heading_id='best-performing-perp-dex-vaults-by-sharpe', heading='Perpetual futures DEX vaults by Sharpe ratio', intro='<p>The same vaults ranked by their three-month <a href="https://tradingstrategy.ai/glossary/sharpe">Sharpe ratio</a>, the return per unit of volatility. The ranking rewards steady returns over high but volatile ones; see <a href="https://tradingstrategy.ai/glossary/risk-adjusted-return">risk-adjusted return</a>.</p>', charts=(('perp_dex_sharpe_performance', '90-day performance of perp DEX vaults with the best Sharpe ratio against BTC and ETH'),), level=3, group=False), SectionTemplate(key='amm', heading_id='best-performing-amm-pools', heading='AMM pools', intro='<p><a href="https://tradingstrategy.ai/glossary/amm">AMM</a> liquidity pools, such as GMX GM and GLV pools and YieldBasis pools, earn trading fees, but their returns also move with the prices of the pooled crypto assets. See all <a href="https://tradingstrategy.ai/vaults/strategies/amm">AMM vaults</a>.</p>', charts=(('amm_performance', '90-day performance of the best-performing AMM pools against their benchmarks'),), level=3, group=False), SectionTemplate(key='by_chain', heading_id='best-performing-vaults-on-each-chain', heading='Vaults on each chain', intro='<p>The best-performing vaults on each blockchain, for investors who stay on one chain. The <a href="https://tradingstrategy.ai/vaults/chains">blockchains page</a> lists the vaults of every chain.</p>', charts=(('by_chain_best', 'The two best-performing vaults on each chain'),), level=3, group=False), SectionTemplate(key='tokenised_funds', heading_id='best-performing-tokenised-funds', heading='Tokenised funds', intro='<p>The <a href="https://tradingstrategy.ai/glossary/tokenised-fund">tokenised funds</a> with the best returns. Their yields follow money market, treasury and credit rates, and many are open only to qualified or institutional investors. See all <a href="https://tradingstrategy.ai/vaults/funds">tokenised funds</a>.</p><p>Often there is no clear distinction between a vault and a tokenised fund. From the technical point of view, both use exactly the same smart contract technology with whitelisting. For the report, we define a project as a tokenised fund if we have found evidence that they have self-declared themselves as a tokenised fund.</p>', charts=(('tokenised_funds_performance', '90-day performance of the best-performing tokenised funds against their benchmarks'),), level=3, group=False), SectionTemplate(key='average_yields', heading_id='yield-by-chain-and-protocol', heading='Yield by chain and protocol', intro='<p>To better understand market competition between blockchains and protocols, we also compare projects by best returns, not just highest TVL.</p><p>In each chart, a small dot is a vault, and the large dot is the <a href="https://tradingstrategy.ai/glossary/total-value-locked-tvl">TVL</a>-weighted average annualised three-month return, compared with the 3-month US Treasury bill as the <a href="https://tradingstrategy.ai/glossary/risk-free-rate">risk-free rate</a>.</p>', charts=(), level=2, group=True), SectionTemplate(key='protocol_yields', heading_id='average-yield-by-protocol-high-tvl', heading='Yield by protocol, high TVL', intro='<p>The largest vault protocols by TVL, which hold most of the stablecoin deposits. See all <a href="https://tradingstrategy.ai/vaults/protocols">vault protocols</a>.</p>', charts=(('protocol_yields', 'Stablecoin vault yield of the largest protocols against the US Treasury bill'),), level=3, group=False), SectionTemplate(key='protocol_high_yields', heading_id='average-yield-by-protocol-high-yield', heading='Yield by protocol, high yield', intro='<p>The protocols paying the highest average yield. High yields usually come with higher risk, so check where the yield comes from before investing. The <a href="https://tradingstrategy.ai/vaults/yield-protocol">vault yield by protocol</a> chart compares the yields of every protocol.</p>', charts=(('protocol_high_yields', 'Stablecoin vault yield of the highest-yielding protocols against the US Treasury bill'),), level=3, group=False), SectionTemplate(key='chain_yields', heading_id='average-yield-by-blockchain', heading='Yield by blockchain', intro='<p>Which blockchains pay the most on stablecoins. Browse the vaults of each chain on the <a href="https://tradingstrategy.ai/vaults/chains">blockchains page</a>.</p>', charts=(('chain_yields', 'Stablecoin vault yield on the largest blockchains against the US Treasury bill'),), level=3, group=False), SectionTemplate(key='risk_return', heading_id='risk-and-return', heading='Risk and return', intro='<p>Higher returns usually come with higher <a href="https://tradingstrategy.ai/glossary/volatility">volatility</a>. Vaults above and to the left of the crowd offer better returns for their risk. The <a href="https://tradingstrategy.ai/vaults/yield-risk">vault yield and risk</a> chart compares the returns of all vaults with their risk ratings.</p>', charts=(('risk_return', 'Volatility risk and return of stablecoin vaults by strategy'),), level=2, group=False), SectionTemplate(key='tvl', heading_id='vaults-and-tokenised-funds-tvl', heading='Vaults and tokenised funds TVL', intro='<p>How much money stablecoin vaults and tokenised funds hold, by protocol, blockchain and fund, over the last 12 months. The <a href="https://tradingstrategy.ai/vaults/historical-tvl-stablecoin">stablecoin TVL</a> chart has the full history.</p>', charts=(), level=2, group=True), SectionTemplate(key='protocol_tvl', heading_id='stablecoin-tvl-by-defi-vault-protocol', heading='Stablecoin TVL by DeFi vault protocol', intro='<p>Where the stablecoins deposited in DeFi vaults are, by protocol, over the last 12 months. Tokenised funds are shown separately below. The <a href="https://tradingstrategy.ai/vaults/historical-tvl-protocol">live chart</a> has the full history.</p>', charts=(('protocol_tvl', 'Stablecoin TVL by DeFi vault protocol'),), level=3, group=False), SectionTemplate(key='chain_tvl', heading_id='stablecoin-tvl-by-blockchain', heading='Stablecoin TVL by blockchain', intro='<p>The same deposits by the blockchain they are on. The <a href="https://tradingstrategy.ai/vaults/historical-tvl-chain">live chart</a> has the full history.</p>', charts=(('chain_tvl', 'Stablecoin TVL by blockchain'),), level=3, group=False), SectionTemplate(key='fund_nav', heading_id='stablecoin-nav-by-tokenised-fund', heading='Stablecoin NAV by tokenised fund', intro='<p>Tokenised funds bring money market, treasury and credit funds onchain. The chart shows their net asset value (NAV) by fund over the last 12 months. See the <a href="https://tradingstrategy.ai/vaults/funds">tokenised funds page</a> for the live data.</p>', charts=(('fund_nav', 'Stablecoin NAV by tokenised fund'),), level=3, group=False), SectionTemplate(key='flows', heading_id='inflows-and-outflows', heading='Inflows and outflows', intro='<p>Where the money moved: the vaults and blockchains whose total value locked grew or shrank the most over the last 30 days. A TVL change includes deposits, redemptions and the returns the vaults earned. Browse all vaults and their current TVL on the <a href="https://tradingstrategy.ai/vaults">vaults page</a>.</p>', charts=(), level=2, group=True), SectionTemplate(key='tvl_changes', heading_id='inflows-and-outflows-by-vault', heading='Inflows and outflows by vault', intro='<p>The vaults with the largest TVL increases and decreases, in dollars. The <a href="https://tradingstrategy.ai/vaults/high-tvl">high TVL vaults</a> page ranks the vaults with at least $2M TVL.</p>', charts=(('tvl_changes', 'The largest vault TVL increases and decreases over the last 30 days'),), level=3, group=False), SectionTemplate(key='chain_tvl_changes', heading_id='inflows-and-outflows-by-blockchain', heading='Inflows and outflows by blockchain', intro='<p>The net TVL change of all vaults on each blockchain, for the blockchains with the largest increases and decreases. The <a href="https://tradingstrategy.ai/vaults/chains">blockchains page</a> shows the TVL of every chain.</p>', charts=(('chain_tvl_changes', 'The largest net TVL increases and decreases by blockchain over the last 30 days'),), level=3, group=False))
Data sections in display order. Follows the Writing rules in
README-blog-post-outline.md: the best-performing vaults first, with no introduction or caption, then yield by chain and protocol, risk and return, vaults and tokenised funds TVL, and inflows and outflows last, with its by-vault and by-blockchain subsections. Every other section opens with an introduction linking to tradingstrategy.ai.
- class PostContext
Bases:
objectEverything needed to render the post body.
Built by
eth_defi.vault_report.report.generate_monthly_vault_report()with bundle-relative image paths, and copied byeth_defi.vault_report.report.publish_report_draft()with the uploaded Ghost image URLs. Onlychartsandpodcast_imagesdiffer between the two.- stats: list[str]
Summary statistics bullet points for the report content updates section, plain text
- tables: dict[str, str]
Section key -> rendered HTML table, see
eth_defi.vault_report.sections.render_section_table(), which escapes the vault data. Sections without vaults are absent.
- charts: dict[str, str]
Chart key -> image URL or relative path. Charts that were not rendered are absent.
- criteria_notes: dict[str, list[str]]
Section or chart key -> bullet points describing the selection criteria; only what the chart and table do not show
- previous: Optional[eth_defi.vault_report.ghost.GhostPost]
Previous report post with its HTML body, from the Ghost Content API, if found. Source of the evergreen sections and of the “previous report” link.
- podcasts: list[eth_defi.vault_report.podcasts.PodcastEpisode]
Latest podcast episodes, newest first; the section is left out when empty
- podcast_images: dict[str, str]
Podcast guest logos and service icons -> image URL or relative path, see
eth_defi.vault_report.podcasts.render_podcast_episodes()
- __init__(month_label, stats, tables, charts, criteria_notes, previous=None, podcasts=<factory>, podcast_images=<factory>)
- Parameters
- Return type
None
- make_month_label(data_end_at)
Create a human-readable report month.
- Parameters
data_end_at (datetime.datetime) – Report data date.
- Returns
E.g.
September 2026.- Return type
- make_report_title(month_label)
Create the post title.
- make_report_slug(data_end_at)
Create the post URL slug.
- Parameters
data_end_at (datetime.datetime) – Report data date.
- Returns
E.g.
the-best-performing-stablecoin-vaults-september-2026.- Return type
- extract_section_html(post_html, heading_id)
Extract a
<h2>section, up to the next<h2>, from post HTML.Used to carry the evergreen sections (About the report, Next steps) over from the previous report, so wording the editor improved in Ghost is not lost the next month. The copied HTML is cleaned of what Ghost and older posts leave behind:
?ref=...ghost.iotracking parameters, vault links at their legacy/trading-view/paths and empty paragraphs.A regular expression is enough here, rather than an HTML parser, because Ghost renders headings with the
idas their first attribute and a section ends at the next<h2or at the end of the post (Next steps is the last section).
- read_changelog_entries(changelog_path, since, keywords=('vault', 'protocol'))
Read new integration entries from the changelog after a date.
Offers the editor a starting point for the report content updates section. Only
feat: Add ...entries are considered, as refactors and performance work are not interesting to report readers. The command line script reads entries since the previous report’s publication date and passes them toreport.json; the post itself never lists them, because the editor picks and rewrites the ones worth telling readers about.- Parameters
changelog_path (pathlib.Path) – Path to
CHANGELOG.md, entries formatted as- feat: Add something (YYYY-MM-DD).since (datetime.date) – Include entries dated after this day.
keywords (tuple[str, ...]) – Include only entries mentioning one of these words, case-insensitive.
- Returns
Entry texts without the
feat:prefix, in changelog order (newest first).- Return type
- build_post_html(context)
Render the report post body.
The post follows the structure in the Writing rules of
README-blog-post-outline.md:Opening paragraph and the Ghost table of contents placeholder
About the report, copied from the previous post
Report content updates: a link to the previous report and the generated statistics; the editor writes the month’s integrations under the opening sentence
DeFi vault community news: an introduction only, the editor writes the news
Latest podcasts, when episodes were read
The data sections of
SECTION_TEMPLATES, each only when it has contentPartners, rendered from
PARTNERS, and Next steps, copied from the previous post
The post has no editor callouts or placeholders: the sections the editor completes are listed in the editor workflow of
README-vault-report.mdinstead.Tables and the podcast list are wrapped in
<!--kg-card-begin: html-->comments so Ghost imports each as one raw HTML card and keeps its markup, seeeth_defi.vault_report.ghost.GhostAdminClient.create_or_update_draft(). Charts become Ghost image cards.Vault data reaches the HTML already escaped by the table renderer; the criteria notes and the templates are trusted HTML from this repository.
- Parameters
context (eth_defi.vault_report.post.PostContext) – Report content. Image references may be bundle-relative paths or uploaded URLs; the text is the same either way.
- Returns
Ghost-compatible post HTML.
- Return type
- build_preview_html(title, post_html, feature_image=None)
Wrap the post body into a standalone HTML page for local preview.
Lets the operator and reviewers read the generated post from the bundle with a browser, e.g. with
GHOST_DRAFT=falseor when the Ghost site is private. The minimal light stylesheet only makes tables and images readable; it does not reproduce the blog’s dark theme, so check the final look in the Ghost editor.