Hi,
The Sharpe ratio dashboard widget currently combines two different time conventions in the same fraction. I think this is worth discussing separately from the existing threads on annualized volatility (issue #5842, and the older Rendite / Volatilität Grafik), because it is not a matter of display preference.
The mismatch
Risk.Volatility computes sqrt(sum/(count-1) * count) — the daily standard deviation multiplied by the square root of the number of trading days retained in the reporting period. The volatility is therefore scaled to the length of the reporting period, and coincides with the conventional annualized figure only when that period is about one year. This is by design; AndreasB explained exactly this in the 2017 thread linked above.
The Sharpe ratio widget then computes (IRR - risk-free rate) / volatility, where:
getPerformanceIRR()is a per-annum rate —NPVFunctiondiscounts withdays / 365.0;- the configured risk-free rate is a per-annum rate;
- the volatility is not annualized.
The numerator is annualized, the denominator is not. They only cancel out when the reporting period happens to be one year.
Two examples from the same portfolio, on the same day
Same file, same data, same configured risk-free rate of 4.00%. Only the reporting period differs.
| Year to date (31 Dec 2025 → 24 Aug 2026, 236 days) |
Since inception (10 Oct 2020 → 24 Aug 2026, 2144 days) |
|
|---|---|---|
| IRR | 91.11% p.a. | 17.76% p.a. |
| TTWROR | 52.01% cumulative / 91.11% p.a. | 146.70% cumulative / 16.62% p.a. |
| Volatility widget | 13.76% | 66.20% |
| Volatility, annualized | 17.11% | 27.31% |
| Sharpe ratio widget | 6.33 | 0.21 |
| Sharpe, annualized/annualized | 5.09 | 0.50 |
| Sharpe, period/period | 3.59 | 1.82 |
The tooltips confirm the inputs: (91.11237% - 4.00%) / 0.138 = 6.329 and (17.75551% - 4.00%) / 0.662 = 0.208.
Year to date:
Since inception:
Three observations:
The distortion changes direction with the window. Over eight months the ratio is inflated; over 5.9 years it is understated by more than half. Only the denominator grows with the length of the period, so a short window flatters and a long window penalises.
Most of the apparent gap between the two windows is an artifact. A user reading this dashboard sees 6.33 versus 0.21 — a factor of 30. Under a consistent convention it would be 5.09 versus 0.50, a factor of 10. Two thirds of the difference comes from the length of the reporting period, not from the portfolio. This is precisely what makes it impossible to track a portfolio’s risk-adjusted performance over time, or to place a short-window and a long-window widget side by side on the same dashboard.
The volatility figure itself is hard to interpret. 66.20% for a diversified multi-asset portfolio looks like a leveraged fund; the annualized figure is 27.31%, which is unremarkable for an equity-heavy portfolio. The widget is simply labelled “Volatility”, and nothing in the label or the tooltip signals that this number is not on the same scale as the figures published in fund fact sheets.
There are only two internally consistent ways to build the Sharpe ratio — annualize both terms, or annualize neither. Both are defensible: the first is what fact sheets publish, the second makes no extrapolation and simply describes what happened during the window. The value currently displayed is neither.
Proposal
Add an EnumBasedConfig with Policy.EXACTLY_ONE to the widget context menu — Period / Annualized — following the existing CostMethodConfig pattern, on the Volatility, Semideviation and Sharpe ratio widgets.
For the Sharpe ratio the setting would apply to the whole fraction, not to a single term: Annualized annualizes the volatility, Period de-annualizes the return and the risk-free rate. The ratio is then consistent by construction under either choice, and the user picks the question they want answered.
Two remarks on scope:
- For Volatility and Semideviation this is purely additive: current behaviour remains the default and existing dashboards are unaffected.
- For the Sharpe ratio it is unavoidably a behaviour change, because today’s value is in neither convention. Whichever default is chosen, existing dashboards will show a different number. That is your call to make, and the main reason I am asking before writing any code.
Issue #5842 proposes a separate ANNUALIZED_VOLATILITY widget instead, mirroring the TTWROR / TTWROR_ANNUALIZED precedent. I am happy with either approach for the volatility itself — but a separate widget does not address the Sharpe ratio, which needs its two terms on a common scale regardless.
A secondary question
When annualizing, the reference duration can be the reporting period or the time the portfolio actually held assets. For a whole-portfolio series held continuously the two coincide — in both examples above the portfolio is invested throughout — so this is an edge case. It matters when the data series is a single security or a benchmark: a three-year window on a position bought six months ago would spread six months of fluctuation over three years and understate the risk.
There is precedent on both sides in the code base: getFinalAccumulatedAnnualizedPercentage() annualizes over getActualInterval(), while PerformanceHeatmapWidget deliberately uses getFirstHoldingInterval() to avoid exactly this distortion. I would default to whatever the IRR uses, so that numerator and denominator stay consistent, unless you prefer otherwise.
I am happy to implement this, tests and label translations included, once the direction is clear.
Thanks for the great work on this application.

