research.vault_metrics
Documentation for eth_defi.research.vault_metrics Python module.
Vault metrics calculations.
Calculate various performance reports and charts for vaults.
Module Attributes
Period -> Perioud duration, max sparse sample mismatch |
Functions
|
Create charts and tables to analyse a vault performance. |
|
Takes a returns series and calculates cumulative returns. |
Calculate daily returns for each vault in isolation |
|
Calculate hourly returns for each vault in isolation |
|
|
Calculate lifetime metrics for each vault in the provided DataFrame. |
|
Calculate profit after external fees have been reduced from the share price change. |
|
Convert a cumulative gross return series to a cumulative net return series after fees. |
|
Convert a share price series to net return series after fees. |
Calculate performance metrics for each vault. |
|
|
Calculate metrics for one period. |
|
Calculate returns from resampled share price series. |
|
Calculate annualized Sharpe ratio from hourly returns. |
|
Calculate rankings for all periods inside PeriodMetrics objects. |
|
Process a single vault metadata + prices to calculate its full data. |
|
Clean lifetime data so we have only valid vaults. |
|
Create combined net / (gross) returns column for display. |
|
Create 2% / 20% style labels to display variosu kinds of vault fees. |
|
Check that VaultDatabase has metadata for all price_df vaults and vice versa. |
|
Render a chart and tearsheet for a single vault. |
|
Export lifetime metrics row to a fully JSON-serializable dict. |
Display fees to .1 accuracy if there are .1 fractions, otherwise as int. |
|
|
Format FFN report for human readable output. |
|
Format table for human readable output. |
|
Format vault database for human readable output. |
|
Format vault header for human readable output. |
|
Get PeriodMetrics for a specific period from the results list. |
|
Calculate returns from resampled returns series. |
|
Create a slug from protocol name for URLs. |
|
Create a slug from vault metadata for URLs. |
|
Create slugs for a set of vaults. |
|
Replace values with abs(x) < eps by 0. |
Classes
Tearsheet metrics for one period. |
|
One vault data analysed |
- Percent
Percent as the floating point.
0.01 = 1%
- class PeriodMetrics
Bases:
objectTearsheet metrics for one period.
- period_start_at: pandas._libs.tslibs.timestamps.Timestamp | None
When was start share price sampled
Share price at beginning
Share price at end
- __init__(period, error_reason=None, period_start_at=None, period_end_at=None, share_price_start=None, share_price_end=None, raw_samples=0, samples_start_at=None, samples_end_at=None, daily_samples=0, returns_gross=None, returns_net=None, cagr_gross=None, cagr_net=None, volatility=None, sharpe=None, max_drawdown=None, tvl_start=None, tvl_end=None, tvl_low=None, tvl_high=None, ranking_overall=None, ranking_chain=None, ranking_protocol=None)
- Parameters
period (Literal['1W', '1M', '3M', '6M', '1Y', 'lifetime']) –
error_reason (str | None) –
period_start_at (pandas._libs.tslibs.timestamps.Timestamp | None) –
period_end_at (pandas._libs.tslibs.timestamps.Timestamp | None) –
share_price_start (float | None) –
share_price_end (float | None) –
raw_samples (int) –
samples_start_at (pandas._libs.tslibs.timestamps.Timestamp | None) –
samples_end_at (pandas._libs.tslibs.timestamps.Timestamp | None) –
daily_samples (int) –
returns_gross (float | None) –
returns_net (float | None) –
cagr_gross (float | None) –
cagr_net (float | None) –
volatility (float | None) –
sharpe (float | None) –
max_drawdown (float | None) –
tvl_start (float | None) –
tvl_end (float | None) –
tvl_low (float | None) –
tvl_high (float | None) –
ranking_overall (int | None) –
ranking_chain (int | None) –
ranking_protocol (int | None) –
- Return type
None
- LOOKBACK_AND_TOLERANCES: dict[Literal['1W', '1M', '3M', '6M', '1Y', 'lifetime'], tuple[pandas._libs.tslibs.offsets.DateOffset, pandas._libs.tslibs.timedeltas.Timedelta]] = {'1M': (<DateOffset: days=30>, Timedelta('60 days 00:00:00')), '1W': (<DateOffset: days=7>, Timedelta('12 days 00:00:00')), '1Y': (<DateOffset: days=360>, Timedelta('410 days 00:00:00')), '3M': (<DateOffset: days=90>, Timedelta('135 days 00:00:00')), '6M': (<DateOffset: days=180>, Timedelta('225 days 00:00:00')), 'lifetime': (<DateOffset: years=100>, Timedelta('36500 days 00:00:00'))}
Period -> Perioud duration, max sparse sample mismatch
- fmt_one_decimal_or_int(x)
Display fees to .1 accuracy if there are .1 fractions, otherwise as int.
- slugify_protocol(protocol)
Create a slug from protocol name for URLs.
- slugify_vault(name, symbol, address, existing_slugs)
Create a slug from vault metadata for URLs.
- create_fee_label(fee_data)
Create 2% / 20% style labels to display variosu kinds of vault fees.
Order is: management / performance / deposit / withdrawal fees.
- Parameters
fee_data (eth_defi.vault.fee.FeeData) –
- resample_returns(returns_1h, freq='D')
Calculate returns from resampled returns series.
- Parameters
returns_1h (pandas.core.series.Series) – The original returns series.
- Return type
pandas.core.series.Series
- calculate_returns(share_price, freq='D')
Calculate returns from resampled share price series.
- Parameters
share_price (pandas.core.series.Series) –
- Return type
pandas.core.series.Series
- calculate_cumulative_returns(cleaned_returns, freq='D')
Takes a returns series and calculates cumulative returns.
The cleaned returns series is created by
eth_defi.research.wrangle_vault_prices.
- Parameters
cleaned_returns (pandas.core.series.Series) –
- zero_out_near_zero_prices(s, eps=1e-09, clip_negatives=True)
Replace values with abs(x) < eps by 0. Optionally clip negatives to 0.
Keeps NaN as-is, turns +/- inf into NaN.
- calculate_net_profit(start, end, share_price_start, share_price_end, management_fee_annual, performance_fee, deposit_fee, withdrawal_fee, seconds_in_year=31557600.0, sample_count=None)
Calculate profit after external fees have been reduced from the share price change.
- Parameters
start (datetime.datetime) – Start datetime of the investment period.
end (datetime.datetime) – End datetime of the investment period.
share_price_start (float) – Share price at the start of the investment period.
share_price_end (float) – Share price at the end of the investment period.
management_fee_annual (float) – Annual management fee as a percent (0.02 = 2% per year).
performance_fee (float) – Performance fee as a percent (0.20 = 20% of profits).
deposit_fee (float | None) – Deposit fee as a percent (0.01 = 1% fee), or None if no fee.
withdrawal_fee (float | None) – Withdrawal fee as a percent (0.01 = 1% fee), or None if no fee.
sample_count (int | None) – If we have not enough returns data, do not try to calculate profit.
- Returns
Net profit as a floating point (0.10 = 10% profit).
- Return type
- calculate_net_returns_from_price(name, share_price, management_fee_annual, performance_fee, deposit_fee, withdrawal_fee, seconds_in_year=31557600.0, zero_epsilon=0.001, freq='h')
Convert a share price series to net return series after fees.
- Parameters
name (str) – For debugging
share_price (pandas.core.series.Series) – Share price series with datetime index.
management_fee_annual (float | None) – Annual management fee as a percent (0.02 = 2% per year).
performance_fee (float | None) – Performance fee as a percent (0.20 = 20% of profits).
deposit_fee (float | None) – Deposit fee as a percent (0.01 = 1% fee), or None if no fee.
withdrawal_fee (float | None) – Withdrawal fee as a percent (0.01 = 1% fee), or None if no fee.
freq – The time series frequency (hourly, daily, etc) for management fee calculation.
- Returns
Cumulative net profit as a floating point (0.10 = 10% profit).
- Return type
pandas.core.series.Series
- calculate_net_returns_from_gross(name, cumulative_returns, management_fee_annual, performance_fee, deposit_fee, withdrawal_fee, seconds_in_year=31557600.0)
Convert a cumulative gross return series to a cumulative net return series after fees.
This function correctly models a High-Water Mark (HWM) for performance fees, which requires an iterative calculation (a loop). This loop operates on Numpy arrays for maximum speed.
Management fees are accrued based on the time delta of each period.
Performance fees are charged only on profits above the highest net value.
Deposit fees are applied once at the start (t=0).
Withdrawal fees are applied once at the end (t=T).
- Parameters
name (str) – Name for the returned pandas Series.
cumulative_returns (pandas.core.series.Series) – A pandas Series with a DatetimeIndex representing the cumulative gross return index (e.g., 1.0, 1.02, 1.05) OR cumulative gross profit (e.g., 0.0, 0.02, 0.05).
management_fee_annual (Optional[float]) – Annual management fee as a decimal (e.g., 0.02 for 2%).
performance_fee (Optional[float]) – Performance fee as a decimal (e.g., 0.20 for 20% of profits above the High-Water Mark).
deposit_fee (Optional[float]) – Fee applied to the initial deposit as a decimal (e.g., 0.01 for 1%).
withdrawal_fee (Optional[float]) – Fee applied to the final withdrawal as a decimal (e.g., 0.01 for 1%).
seconds_in_year – The number of seconds in a year for precise management fee accrual.
- Returns
A pandas Series of the cumulative net profit (e.g., 0.10 for 10%).
- Return type
pandas.core.series.Series
- calculate_sharpe_ratio_from_returns(hourly_returns, risk_free_rate=0.0, year_multiplier=365)
Calculate annualized Sharpe ratio from hourly returns.
- slugify_vaults(vaults)
Create slugs for a set of vaults.
Always give the primary slug to the vault that was created first.
Mutates VaultRow data in-place
- Parameters
vaults (dict[eth_defi.vault.base.VaultSpec, eth_defi.vault.vaultdb.VaultRow]) – The vault metadata entries.
- Return type
list[eth_defi.vault.vaultdb.VaultRow] | None
- calculate_period_metrics(period, gross_fee_data, net_fee_data, share_price_hourly, share_price_daily, tvl, now_)
Calculate metrics for one period.
- Parameters
period (Literal['1W', '1M', '3M', '6M', '1Y', 'lifetime']) – Period identifier (1W, 1M, 3M, 6M, 1Y, lifetime)
gross_fee_data (eth_defi.vault.fee.FeeData) – Fee data before fee mode adjustments
net_fee_data (eth_defi.vault.fee.FeeData) – Fee data after fee mode adjustments (for net return calculations)
share_price_hourly (pandas.core.series.Series) – Hourly share price series with DatetimeIndex
share_price_daily (pandas.core.series.Series) – Daily share price series with DatetimeIndex
tvl (pandas.core.series.Series) – Total value locked series with DatetimeIndex
now – The reference timestamp (usually the last timestamp in the data)
now_ (pandas._libs.tslibs.timestamps.Timestamp) –
- Returns
PeriodMetrics dataclass with calculated metrics
- Return type
- calculate_vault_record(prices_df, vault_metadata_rows, month_ago, three_months_ago, vault_id=None)
Process a single vault metadata + prices to calculate its full data.
Exported to frontend, everything
- Parameters
prices_df (pandas.core.frame.DataFrame) – Price DataFrame for a single vault
vault_metadata_rows (dict[eth_defi.vault.base.VaultSpec, eth_defi.vault.vaultdb.VaultRow]) – Dictionary of vault metadata keyed by VaultSpec
month_ago (pandas._libs.tslibs.timestamps.Timestamp) – Timestamp for 1-month lookback
three_months_ago (pandas._libs.tslibs.timestamps.Timestamp) – Timestamp for 3-month lookback
vault_id (str | None) – Vault ID string. If not provided, extracted from prices_df[“id”].
- Returns
Series with calculated metrics
- Return type
pandas.core.series.Series
- calculate_lifetime_metrics(df, vault_db, returns_column='returns_1h')
Calculate lifetime metrics for each vault in the provided DataFrame.
All-time returns
3M returns, latest
1M returns, latest
Volatility (3M)
Lookback based on the last entry.
- Parameters
vault_db (eth_defi.vault.vaultdb.VaultDatabase | dict[eth_defi.vault.base.VaultSpec, eth_defi.vault.vaultdb.VaultRow]) – Pass all vaults or subset of vaults as VaultRows, or full VaultDatabase
df (pandas.core.frame.DataFrame) –
returns_column (str) –
- Returns
DataFrame, one row per vault.
- Return type
pandas.core.frame.DataFrame
- get_period_metrics(period_results, period)
Get PeriodMetrics for a specific period from the results list.
- Parameters
period_results (list[eth_defi.research.vault_metrics.PeriodMetrics]) – List of PeriodMetrics objects from a vault record
period (Literal['1W', '1M', '3M', '6M', '1Y', 'lifetime']) – The period to find (e.g., “1W”, “1M”, “3M”, “6M”, “1Y”, “lifetime”)
- Returns
The matching PeriodMetrics or None if not found
- Return type
- calculate_vault_rankings(results_df, min_tvl_chain_protocol=10000, min_tvl_overall=50000)
Calculate rankings for all periods inside PeriodMetrics objects.
Updates PeriodMetrics objects in-place within the period_results lists. Rankings are calculated for all 6 periods (1W, 1M, 3M, 6M, 1Y, lifetime).
Vaults are excluded from rankings if: - They have no CAGR data (zero or NaN) - They have an error_reason set - They are blacklisted (risk == VaultTechnicalRisk.blacklisted) - Their period TVL is below the threshold
- Parameters
- Returns
DataFrame with rankings updated in PeriodMetrics objects
- Return type
pandas.core.frame.DataFrame
- clean_lifetime_metrics(lifetime_data_df, broken_max_nav_value=99000000000, lifetime_min_nav_threshold=100.0, max_annualised_return=3.0, min_events=25, logger=<built-in function print>)
Clean lifetime data so we have only valid vaults.
Filter out vaults that have broken records or never saw daylight
- Returns
Cleaned lifetime dataframe
- Parameters
lifetime_data_df (pandas.core.frame.DataFrame) –
- Return type
pandas.core.frame.DataFrame
- combine_return_columns(gross, net, new_line=' ', mode='percent', profit_presentation='split')
Create combined net / (gross) returns column for display.
E.g. 8.3% (10.5%)
- format_lifetime_table(df, add_index=False, add_address=False, add_share_token=False, drop_blacklisted=True, profit_presentation='split')
Format table for human readable output.
See
calculate_lifetime_metrics()- Parameters
add_index – Add 1, 2, 3… index column
add_address –
Add address as a separate column.
For vault address list copy-pasted.
drop_blacklisted – Remove vaults we have manually flagged as troublesome.
df (pandas.core.frame.DataFrame) –
profit_presentation (Literal['split', 'net_only']) –
- Returns
Human readable data frame
- Return type
pandas.core.frame.DataFrame
- class VaultReport
Bases:
objectOne vault data analysed
- rolling_returns_chart: plotly.graph_objs._figure.Figure
Rolling returns chart
- hourly_df: pandas.core.frame.DataFrame
All hourly columns
- __init__(vault_metadata, rolling_returns_chart, performance_stats, daily_returns, hourly_returns, hourly_df)
- Parameters
vault_metadata (dict) –
rolling_returns_chart (plotly.graph_objs._figure.Figure) –
performance_stats (ffn.core.PerformanceStats) –
daily_returns (pandas.core.series.Series) –
hourly_returns (pandas.core.series.Series) –
hourly_df (pandas.core.frame.DataFrame) –
- Return type
None
- analyse_vault(vault_db, prices_df, spec, returns_col='returns_1h', logger=<built-in function print>, chart_frequency='daily')
Create charts and tables to analyse a vault performance.
We plot our annualised 1 month rolling returns on the chart, to see how vaults move in the direction of the markets, or what kind of outliers there are
- Parameters
vault_db (eth_defi.vault.vaultdb.VaultDatabase) – Database of all vault metadata
price_df –
Cleaned price and returns data for all vaults.
Can be be in any time frame.
id – Vault chain + address to analyse, e.g. “1-0x1234567890abcdef1234567890abcdef12345678”
chart_frequency (Literal['hourly', 'daily']) –
Do we plot based on daily or hourly datapoints.
Hourly data has too many points, chocking Plotly.
prices_df (pandas.core.frame.DataFrame) –
spec (eth_defi.vault.base.VaultSpec) –
returns_col (str) –
- Returns
Analysis report to display.
None if the vault does not have price data.
- Return type
- calculate_performance_metrics_for_all_vaults(vault_db, prices_df, logger=<built-in function print>, lifetime_min_nav_threshold=100.0, broken_max_nav_value=99000000000, cagr_too_high=10000, min_events=25)
Calculate performance metrics for each vault.
Only applicable to stablecoin vaults as cleaning units are in USD
Clean up idle vaults that have never seen enough events to be considered active
Calculate lifetime returns, CAGR, NAV, etc.
Filter out results with abnormal values
- Returns
DataFrame with lifetime metrics for each vault, indexed by vault name.
- Parameters
vault_db (eth_defi.vault.vaultdb.VaultDatabase) –
prices_df (pandas.core.frame.DataFrame) –
- Return type
pandas.core.frame.DataFrame
- format_vault_database(vault_db, index=True)
Format vault database for human readable output.
- Parameters
vault_db (eth_defi.vault.vaultdb.VaultDatabase) – Vault database to format
- Returns
DataFrame with vault metadata, with human readable columns
- Return type
pandas.core.frame.DataFrame
- format_vault_header(vault_row)
Format vault header for human readable output.
- Returns
DataFrame with formatted performance metrics
- Parameters
vault_row (pandas.core.series.Series) –
- Return type
pandas.core.series.Series
- format_ffn_performance_stats(report, prefix_series=None)
Format FFN report for human readable output.
Return a Series with formatted performance metrics
Multiple series can be combined to a comparison table
- Parameters
prefix_data – Extra header data to insert.
report (ffn.core.PerformanceStats) – FFN performance report to format
prefix_series (pandas.core.series.Series | None) –
- Returns
DataFrame with formatted performance metrics
- Return type
pandas.core.series.Series
- cross_check_data(vault_db, prices_df, printer=<built-in function print>)
Check that VaultDatabase has metadata for all price_df vaults and vice versa.
- Returns
Number of problem entries.
Should be zero.
- Parameters
vault_db (eth_defi.vault.vaultdb.VaultDatabase) –
prices_df (pandas.core.frame.DataFrame) –
- Return type
- calculate_daily_returns_for_all_vaults(df_work)
Calculate daily returns for each vault in isolation
- Parameters
df_work (pandas.core.frame.DataFrame) –
- Return type
pandas.core.frame.DataFrame
- calculate_hourly_returns_for_all_vaults(df_work)
Calculate hourly returns for each vault in isolation
- Parameters
df_work (pandas.core.frame.DataFrame) –
- Return type
pandas.core.frame.DataFrame
- display_vault_chart_and_tearsheet(vault_spec, vault_db, prices_df, render=True)
Render a chart and tearsheet for a single vault.
Use in notebooks
- :param render;
Disable rendering in tests
- Parameters
vault_spec (eth_defi.vault.base.VaultSpec) –
vault_db (eth_defi.vault.vaultdb.VaultDatabase) –
prices_df (pandas.core.frame.DataFrame) –
- export_lifetime_row(row)
Export lifetime metrics row to a fully JSON-serializable dict.
Recursively handles nested dicts, lists, tuples, sets, and dataclasses.
Normalizes pandas, numpy, datetime, and custom types.
Preserves legacy fee field names.
- Parameters
row (pandas.core.series.Series) –
- Return type