Skip to content

Rank a contribution table over a window, not always all history - #32

Merged
mspinola merged 1 commit into
mainfrom
claude/exposure-table-window
Aug 24, 2026
Merged

Rank a contribution table over a window, not always all history#32
mspinola merged 1 commit into
mainfrom
claude/exposure-table-window

Conversation

@mspinola

Copy link
Copy Markdown
Owner

0.9.0. exposure.contribution_table called expanding_pct_rank unconditionally, so a caller could not hold it to the same stretch of weeks as the aggregate percentile it sits under.

cot-analyzer's /exposure is that caller. Its Lookback control moved the headline, the band and the rank scale while the contributions table beneath them stayed on all history, so on "52 weeks" the page said "higher than 97% of the last 52 weeks" three lines above a column headed "%ile" measured against twenty years, both on screen at once with nothing saying they were different questions. Live, Gold alone on 52 weeks: headline 62nd percentile, table row 99.

The change

-def contribution_table(members, *, when=None, min_rank_periods=104):
+def contribution_table(members, *, when=None, window=None, min_rank_periods=104):
-    ranks = expanding_pct_rank(series, min_rank_periods)
+    ranks = windowed_pct_rank(series, window, min_rank_periods)

window=None is the expanding form, so every existing caller is unchanged, pinned by a test asserting frame equality between the bare call and window=None. windowed_pct_rank already clamps min_periods to the window, so a 26-week window against a 104-week floor needs nothing from the caller.

Why here rather than in the page

cot-analyzer computes no metrics of its own. Per-member ranking done there would have been the first exception, and the alternative considered (leave the table expanding and explain it in the copy) is not a fix: copy can describe two bases, it cannot make them comparable, and the control exists because the window is the reader's choice.

One trap in the tests

test_a_window_renormalises_where_the_expanding_form_does_not reads inverted on purpose. Commercial net is negative, so a step back toward zero ranks HIGH in notional: expanding 66.7 against windowed(3) 100.0. The comment says so, because a future reader will otherwise see it as a typo and "fix" it.

Verification

389 tests pass with both stores set, ruff check src tests clean, check_dep_floors.py clean.

Consumer

cot-analyzer's branch claude/zealous-sutherland-b45608 pins cotmetrics[options]>=0.9.0 and cannot merge until this lands. That PR is itself stacked on cot-analyzer #64.

🤖 Generated with Claude Code

0.9.0. `contribution_table` called `expanding_pct_rank` unconditionally, so a
caller could not hold it to the same stretch of weeks as the aggregate
percentile it sits under. cot-analyzer's /exposure is that caller: its Lookback
control moved the headline, the band and the rank scale while the contributions
table beneath them stayed on all history, so the page showed "higher than 97% of
the last 52 weeks" three lines above a column headed "%ile" measured against
twenty years, with nothing on either saying they were different questions.

The parameter belongs here rather than in the page. cot-analyzer computes no
metrics of its own, and per-member ranking done there would have been the first
exception to that.

`window=None` is the expanding form, so every existing caller is unchanged;
`windowed_pct_rank` already clamps min_periods to the window, so a 26-week
window against a 104-week floor needs nothing from the caller.

Two tests. The renormalisation one walks into a sign trap worth naming:
commercial net is negative, so a step back toward zero ranks HIGH in notional,
and the numbers read inverted for that reason rather than by mistake.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mspinola
mspinola merged commit b93286a into main Aug 24, 2026
4 checks passed
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.

1 participant