You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add ulcer index and Martin ratio to the returns accessor - #881
Adds two drawdown-aware risk metrics to ReturnsAccessor:
ulcer_index() / rolling_ulcer_index(): root mean square of the drawdown series. Unlike max drawdown it reflects both depth and duration of drawdowns.
martin_ratio() / rolling_martin_ratio(): annualized return divided by the ulcer index (the ulcer performance index).
Both are built on the existing drawdown_1d_nb and annualized_return_1d_nb, follow the same layout as calmar_ratio (1d/2d/rolling numba functions, accessor methods) and return NaN for an empty or drawdown-free series where the ratio would divide by zero. They run on the numba path only; they do not take an engine argument since vectorbt-rust has no counterpart yet.
I did not add them to stats() so existing stats output stays unchanged. Happy to add them if you want.
Tests: values are checked against an independent pandas reference, plus naming, rolling shape and the flat-series edge cases. tests/test_returns.py passes locally.
One definitional point on martin_ratio: the original Martin and McCann ulcer performance index is (annualized return minus the risk-free rate) divided by the ulcer index, the same way Sharpe uses excess return. The PR divides the raw annualized return, so the result only matches the textbook number when rf is 0, and it will read higher than other libraries' UPI for any non-zero rf. calmar_ratio here also has no rf, so staying consistent with it is defensible, but the docstring should say so ("annualized return, no risk-free adjustment") or the method could take a risk_free argument defaulting to 0 like sharpe_ratio/omega_ratio do. A small test with a non-zero rf against the pandas reference would pin whichever choice is made.
Thanks, good catch. martin_ratio and rolling_martin_ratio now take a risk_free argument (annualized, default 0) and the numerator is annualized return minus risk_free. The docstring says it defaults to the plain annualized return like calmar_ratio. I added a test with a non-zero rf against the pandas reference.
@polakowo this is ready for review. CI is green, and the earlier review point on risk_free is addressed. Small, self-contained addition next to calmar_ratio; happy to adjust naming or add the metrics to stats() if you prefer.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds two drawdown-aware risk metrics to
ReturnsAccessor:ulcer_index()/rolling_ulcer_index(): root mean square of the drawdown series. Unlike max drawdown it reflects both depth and duration of drawdowns.martin_ratio()/rolling_martin_ratio(): annualized return divided by the ulcer index (the ulcer performance index).Both are built on the existing
drawdown_1d_nbandannualized_return_1d_nb, follow the same layout ascalmar_ratio(1d/2d/rolling numba functions, accessor methods) and return NaN for an empty or drawdown-free series where the ratio would divide by zero. They run on the numba path only; they do not take anengineargument since vectorbt-rust has no counterpart yet.I did not add them to
stats()so existing stats output stays unchanged. Happy to add them if you want.Tests: values are checked against an independent pandas reference, plus naming, rolling shape and the flat-series edge cases.
tests/test_returns.pypasses locally.