Skip to content

Latest commit

 

History

History
48 lines (26 loc) · 11.5 KB

File metadata and controls

48 lines (26 loc) · 11.5 KB

Jeffy eval: ranaroussi/quantstats

Target: ranaroussi/quantstats (7,489 stars, verified via gh api repos/ranaroussi/quantstats --jq '.stargazers_count' on 2026-07-28) at upstream HEAD fbd10daed0227aa0d10da6513f1b15e7e98d7fae (tag v0.0.81), Python 3.13.8 / pandas 3.0.5 / numpy 2.5.1 / scipy 1.18.0, in a local clone. Nothing was pushed upstream. This is the library whose reports.html() output gets pasted into investor-facing tearsheets.

This is a full /jeffy loop run that reached machine-checked convergence. Four budgeted runs of 10, each in a fresh session, 40 iterations total: 29 task iterations, 7 audits, 4 wrapups. Converged at 4bb3ec3 on 2026-07-28 with every condition of the closing rule independently re-verified for this receipt: empty ledger, all 12 surface-inventory rows swept, only loop state changed past the Converged commit, the suite green at 393 passed from the hook's own re-run, and an evaluator PASS. An earlier version of this receipt described a single-pass manual audit of the same commit under Jeffy's method; the loop run replaces it, and the two are compared honestly at the bottom, in both directions.

The baseline was green and hiding everything. pytest tests -q at HEAD: 125 passed, 0 failures. Forty iterations later the suite is 393 passed and the diff against upstream is 7 source files, 570 insertions against 759 deletions - the library got smaller - plus 1,694 lines of tests. Every fix's tests were proven to fail against the unfixed code, and the closing evaluator re-proved it wholesale: reverting stats.py and reports.py to their pre-run state fails 65 of the run's tests in one command. Independent re-verification for this receipt went further: fixes.patch applied to a pristine clone at fbd10da turns the suite from 125 passed (with the new regression file alone failing 171 of its tests) to 393 passed, 0 failures.

What the loop found: 29 findings filed, 29 closed, none left behind

The run count by contract generation tells the story of the engine as much as the library. This conversion deliberately spanned three generations of Jeffy's sweep contract, each shipped after the previous run exposed a bias, and each generation immediately caught a class the previous one could not:

Run 1 - surface inventory (v1.3.0): the crash class. A process-global memoization cache in _prepare_returns keyed on a value hash that ignores container type, so any benchmark-free reports call poisoned the next benchmark call in the same process (QS-1, High; the cache was deleted outright, which also made reports.metrics 19.8% faster, since it existed to hide a 4ms inspect.stack() call). A one-column DataFrame crashed reports.metrics with UnboundLocalError (QS-2, High; closed as a class across nine entry points). Every benchmark-carrying report crashed on tz-aware input (QS-8, High). The package dropped timezones two contradictory ways, so monthly_returns and reports.metrics disagreed about which calendar year an Asia/Tokyo return belongs to (QS-9, High; unified at 9 sites, grep -rn "tz_convert" now empty). reports.metrics(match_dates=False) mutated the caller's own returns object (QS-4).

Run 2 - known-answer probes (v1.3.1): the wrong-number class. aggregate_returns silently ignored its documented 'M', 'Q' and 'Y' codes - compare(aggregate='M') returned daily rows where monthly belong, reachable from 9 public functions at 15 call sites (QS-13, High). ulcer_index computed sqrt(sum/(n-1)) while documenting the standard sqrt(sum/n), dragging UPI, serenity and three tearsheet rows with it (QS-14). The montecarlo module advertised outcome distributions that its permutation design makes degenerate - terminal-value std was 1e-15 float noise, and the sharpe spread was an artifact of pct_change dropping a different first row per path (QS-15; rewritten as a seeded bootstrap). probabilistic_ratio(annualize=True) returned 13.7 on a documented 0-to-1 probability scale (QS-16). The journal's own words: "every liveness probe passes, seeds reproduce, shapes are right, and the advertised distribution is still a point mass."

Runs 3 and 4 - documented-parameter contract: the inert-parameter class. The sweep drove all 78 public stats functions' parameters at values that must move the output, triaged 59 non-movers, discarded 55 as probe artifacts, and filed the four real ones. cagr(rf=0.05) returned its rf=0.0 value to eight decimals - a caller-name skip list in _prepare_returns silently voided the risk-free rate for cagr and gain_to_pain_ratio while reports.py passed rf into both, so one tearsheet showed excess-of-rf Sharpe beside a gross Gain/Pain (QS-18, High). make_index contained no resampling code at all - the word .resample appeared only in its docstring - so rebalance=None, "1ME" and "1QE" returned one identical series, wrong by 2.14 points over 5 years against a correct buy-and-hold (QS-19, High). rar and to_returns deducted a whole annual rate per daily observation (rar(rf=0.05) returned -1.0), and rolling_sharpe passed its rolling window into the rf slot, so the deduction scaled with window length (QS-22, High). The probabilistic-Sharpe family subtracted an annual rate from a per-observation ratio - shipped PSR at rf=0.05 was 0.061 where 0.196 is correct - and computed its standard error by subtracting the kurtosis excess twice, going nan for any strong strategy (QS-27, QS-28, both High). gain_to_pain_ratio's own documented resolution spellings raised on installed pandas, while the report layer had been quietly routed around the defect, keeping the suite green over it (QS-24, High).

Ten defect classes were settled with enumerating checks; two findings were Declined with recorded reasons; discipline held throughout - three separate audits caught their own probe errors before filing, and the iteration-8 closing audit triaged three sweep failures as probe-versus-convention disagreements rather than filing them.

The convergence, condition by condition

The closing audit rescored every dimension against a fully swept 12-row inventory: zero High, zero Medium, closeout began, the last Low was fixed, and the evaluator gate ran. The evaluator re-executed the verify command and all seven of the final run's acceptance checks, then ran a check no iteration had: revert the source, count the carnage - 65 failed. Its three residual observations were recorded in the journal rather than swallowed, including the honest note that negative risk-free rates remain inert by upstream design (if rf > 0), a genuine open question routed to the owner rather than silently decided. The Stop hook then verified the ledger, the Converged commit, the inventory and the green suite mechanically before the run was allowed to end.

Independently re-verified for this receipt from pristine upstream, fixed-versus-broken on the same seeded data: rf now moves cagr (-0.0138 vs -0.0608) where upstream is identical to ten decimals; aggregate_returns(r,'M') returns 116 rows from 2,520 where upstream returns 2,520, and unknown tokens now raise; montecarlo_cagr has std 0.0508 where upstream has 2.3e-16; ulcer_index matches sqrt(sum/n) exactly where upstream matches sqrt(sum/(n-1)).

Compared with the manual audit - in both directions, because neither wins

The earlier receipt was a single-pass manual audit of the same commit. The comparison is the most honest data this project has produced:

The loop filed 23 findings wholly absent from the audit, eight of them High: the cache container bug, both timezone classes, the caller-mutation class, make_index's missing resample, the annual-rate-per-daily-bar class, the PSR rf-units and double-subtracted-kurtosis defects, the unicode crash on cp1252 consoles, and the resolution-spellings defect the report layer had been routed around. It also wrote 268 net new tests and left the library smaller than it found it.

The audit found 6 defects the loop did not, and they are still present at the converged tree. kelly_criterion implements the textbook bet-sizing form whose output is scale-invariant - unchanged when every return is halved - which no capital-allocation recommendation can be; the loop's known-answer probe validated the same textbook formula the code implements, certifying the convention rather than testing it. information_ratio returns the per-period value, exactly sqrt(252) smaller than convention, printed directly under annualized Sharpe - structurally invisible to the loop's harnesses, because the function has no periods parameter to probe and the per-period value agrees with a per-period reference by construction. rolling_greeks' alpha uses whole-series means, so every rolling point contains lookahead. recovery_factor sums a numerator other functions compound - the loop looked straight at it twice and both times accepted it as the documented convention. The 3Y/5Y report windows use months=35/59 off-by-one offsets. risk_return_ratio's docstring claims it is the Sharpe ratio; it is Sharpe with rf forced to zero.

The pattern in those six is one pattern: they are convention defects - internally consistent numbers that are wrong in context, where the wrongness lives in what the docstring promises, what the adjacent tearsheet row implies, or what the quantity dimensionally means. A known-answer probe verifies a formula; it cannot adjudicate which formula the context demands. That is the current honest boundary between the loop and a domain-expert audit, it is stated here rather than discovered by a critic, and the six findings remain published, reproducible claims against upstream regardless of which instrument found them.

Convergence is therefore a claim about a contract, not about perfection: under Jeffy's severity rubric, envelope, and sweep contract as they stood, a full fresh-evidence audit found nothing High or Medium left on a fully swept surface, and an adversarial evaluator countersigned. Six known convention defects stand outside that contract's reach, named above.

Honest caveats

  • The four runs deliberately spanned three engine generations; runs 3 and 4, including every sweep the convergence rests on, ran under the final contract, and all 12 inventory rows were re-earned under it after being force-reopened between runs 2 and 3.
  • The evaluator's 65-failed revert check covered stats.py and reports.py; the full new-test-file count against pristine upstream is higher (171 of the regression file's tests fail there), re-measured independently for this receipt.
  • fixes.patch includes upstream's own .claude/ planning docs among its 12 files, because the loop updated them alongside the code they describe; the jeffy state files are excluded and verified absent.
  • Negative risk-free rates are inert across the package at HEAD and remain so after the run, recorded by the evaluator as an open owner decision rather than fixed unilaterally.

Convergence standard: evaluator countersigned. The adversarial evaluator's verdict for this run is in the narrative above; the standard each target met is recorded in evals/ATTEMPTS.md.

Status: the work lives in this eval's fixes.patch (applies cleanly to pristine fbd10da; 125 to 393 passing) and journal.md (all 42 entries across the four runs, as written). Findings were disclosed upstream with repros and a PR offer in ranaroussi/quantstats#537, which remains open with no maintainer reply as of 2026-07-28.