Skip to content

Sharpe ratio variance and probabilistic sharpe ratio#232

Merged
tschm merged 6 commits intoJebel-Quant:mainfrom
mjvakili:prob_sharp_ratio
Nov 11, 2025
Merged

Sharpe ratio variance and probabilistic sharpe ratio#232
tschm merged 6 commits intoJebel-Quant:mainfrom
mjvakili:prob_sharp_ratio

Conversation

@mjvakili
Copy link
Copy Markdown
Contributor

@mjvakili mjvakili commented Nov 10, 2025

Adds the following functions:

  • sharpe_variance
  • prob_sharpe_ratio

Reference:

  • Baily & Lopez de Prado 2012

Summary by CodeRabbit

  • New Features

    • Added Sharpe variance calculation to quantify variance of the Sharpe Ratio.
    • Added probabilistic Sharpe ratio to assess significance of observed Sharpe versus a benchmark.
  • Tests

    • Added tests covering the new Sharpe variance and probabilistic Sharpe computations, including edge cases and parameterized benchmark scenarios.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 10, 2025

Walkthrough

Two new per-column statistical methods were added to Stats: sharpe_variance (asymptotic Sharpe variance with optional annualization) and prob_sharpe_ratio (CDF of observed Sharpe vs. a benchmark). Corresponding tests were added verifying numerical results for multiple benchmark values.

Changes

Cohort / File(s) Summary
Stats: new methods
src/jquantstats/_stats.py
Added `sharpe_variance(self, series: pl.Series, periods: int
Tests: Sharpe-related coverage
tests/test_stats.py
Added test_sharpe_var(stats) and parametrized test_prob_sharpe_ratio(stats, benchmark_sr) (benchmarks: 0.0, 0.5, 1.0). New import: scipy.stats.norm. Tests compute expected values using skew, kurtosis, unannualized Sharpe, sample size, and compare against new methods.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Tests
    participant Stats as Stats Class
    participant Series as pl.Series
    participant Dist as Normal CDF

    rect rgb(230, 247, 255)
    Test->>Stats: call sharpe_variance(series, periods?)
    Note right of Stats: compute mean, sd, skew, kurtosis\nderive asymptotic variance, annualize if periods
    Stats-->>Test: variance (float or NaN)
    end

    rect rgb(245, 255, 230)
    Test->>Stats: call prob_sharpe_ratio(series, benchmark_sr)
    Note right of Stats: compute observed SR\nuse sharpe_variance to get variance\nif variance>0 -> z=(obs-bench)/sqrt(var)
    Stats->>Dist: norm.cdf(z)
    Dist-->>Stats: probability
    Stats-->>Test: probability (float or NaN)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to inspect closely:
    • Statistical formulas and edge-case handling in sharpe_variance (annualization, sample-size use).
    • Variance sign checks and NaN returns in prob_sharpe_ratio (avoid dividing by non-positive variance).
    • Test correctness: that expected values mirror the implemented formula and cover invalid inputs.

Poem

🐇 I hopped through data, tail a-twitch,
Calculated variance, found the stitch.
A benchmark met, the CDF shone bright,
Numbers nodded, tests took flight.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the two main additions to the codebase: sharpe_variance and prob_sharpe_ratio methods.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed07496 and 50f9fc7.

📒 Files selected for processing (1)
  • src/jquantstats/_stats.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/jquantstats/_stats.py (1)
src/jquantstats/_data.py (1)
  • _periods_per_year (196-216)
🪛 Ruff (0.14.3)
src/jquantstats/_stats.py

11-11: Unused noqa directive (non-enabled: PLR0904)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (2)
src/jquantstats/_stats.py (2)

433-474: LGTM! Well-implemented Sharpe variance calculation.

The implementation correctly follows the Bailey & López de Prado (2012) formula, using the unannualized Sharpe ratio in the variance calculation and then scaling by periods for annualization. Error handling for edge cases (None values, zero standard deviation) is comprehensive.


475-513: LGTM! Probabilistic Sharpe Ratio correctly implemented.

The implementation correctly uses the benchmark_sr in the variance formula (not the observed SR), which is the proper approach per Bailey & López de Prado (2012) for testing the null hypothesis. The docstring appropriately clarifies that benchmark_sr should be unannualized, matching how the observed SR is calculated.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/jquantstats/_stats.py (2)

433-472: Consider clarifying the annualization behavior in the docstring.

The docstring states "annualized if requested" (line 451), but the method doesn't have a boolean flag to control annualization. It always multiplies the base variance by the periods parameter (defaulting to _periods_per_year). Consider updating the docstring to be more precise about this behavior.

Suggested clarification:

         Returns:
-            float: The asymptotic variance of the Sharpe ratio (annualized if requested).
+            float: The asymptotic variance of the Sharpe ratio, scaled by the periods parameter.

474-510: Clarify whether benchmark_sr should be annualized or unannualized.

The docstring doesn't specify whether the benchmark_sr parameter should be annualized or unannualized. Based on the implementation (line 506 uses it directly in the variance formula with unannualized observed_sr), it appears benchmark_sr should be unannualized. Making this explicit would prevent user confusion.

Suggested enhancement:

         Args:
             series (pl.Series): The series to calculate probabilistic Sharpe ratio for.
-            benchmark_sr (float): The target Sharpe ratio to compare against.
+            benchmark_sr (float): The target Sharpe ratio to compare against (unannualized).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17ea2ee and ed07496.

📒 Files selected for processing (2)
  • src/jquantstats/_stats.py (2 hunks)
  • tests/test_stats.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/test_stats.py (3)
tests/test_quantstats.py (1)
  • stats (9-19)
src/jquantstats/_stats.py (5)
  • sharpe_variance (434-472)
  • skew (88-98)
  • kurtosis (101-113)
  • sharpe (414-431)
  • prob_sharpe_ratio (475-510)
src/jquantstats/_data.py (1)
  • _periods_per_year (196-216)
src/jquantstats/_stats.py (1)
src/jquantstats/_data.py (1)
  • _periods_per_year (196-216)
🪛 Ruff (0.14.3)
src/jquantstats/_stats.py

11-11: Unused noqa directive (non-enabled: PLR0904)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (3)
tests/test_stats.py (3)

6-6: LGTM!

The import is correctly added to support the new test_prob_sharpe_ratio test.


318-339: LGTM!

The test correctly validates the sharpe_variance implementation by manually computing the expected variance using the same formula components (skew, kurtosis, unannualized Sharpe ratio, sample size, and periods per year).


342-353: LGTM!

The parametrized test validates the probabilistic Sharpe ratio implementation across multiple benchmark values (0.0, 0.5, 1.0), correctly replicating the variance calculation and CDF computation.

Comment thread src/jquantstats/_stats.py Outdated
@tschm
Copy link
Copy Markdown
Member

tschm commented Nov 10, 2025

@mjvakili please adjust the docstrings as suggested by coderabbitai

@mjvakili
Copy link
Copy Markdown
Contributor Author

I modified the docstring of sharpe_variance to clarify the annualization of Sharpe Ratio Variance if the number of periods per year is provided by user or if it is inferred from the input data.

I also made it clear in the docstring of prob_sharpe_ratio that the parameter benchmark_sr should be unannualized.

@tschm tschm merged commit fe3b106 into Jebel-Quant:main Nov 11, 2025
8 checks passed
@tschm
Copy link
Copy Markdown
Member

tschm commented Nov 11, 2025

Many thanks. Great job @mjvakili

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants