Skip to content

feat(monitor): measure the baseline firing rate over a recent window - #118

Merged
mspinola merged 1 commit into
mainfrom
claude/rate-window
Aug 3, 2026
Merged

feat(monitor): measure the baseline firing rate over a recent window#118
mspinola merged 1 commit into
mainfrom
claude/rate-window

Conversation

@mspinola

@mspinola mspinola commented Aug 3, 2026

Copy link
Copy Markdown
Owner

The opportunity-set channel compares the live firing rate against the baseline's. The baseline's was measured over the validated log's whole span, which is right for a stationary book and quietly wrong for one whose rate has trended: the full-span mean sits below what the book currently does, so the ratio reads healthy while the rate falls.

Not hypothetical

Measured on the real 47-market trend book:

Window Rate
full span (40yr) 23.4/yr
last 10 years 29.2/yr
last 7 years 33.9/yr
last 5 years 35.3/yr

The trend is +0.49 trades/yr per year (r=+0.63), so the rate has roughly doubled over the log. Anchored to the full span, a collapse to 30% below current behaviour still scores 1.02 against the baseline and can never cross the 0.6 line. The one channel covering a signal that stops firing while its per-trade edge holds was structurally desensitised, not merely uncalibrated.

What changed

from_log gains rate_window_years, and EdgeBaseline records it. The default is unchanged (whole span) so nothing moves silently for existing callers, and the window rides in the verdict's reason line, because two baselines with the same rate and different windows are not making the same claim.

It is a windowed measurement taken once, at promotion, not a rolling one, and that distinction is the whole architecture: recomputing it later would re-fit the reference onto the drifted reality. edge_monitor still has no parameter from which a baseline or its rate could be rebuilt, pinned by test_the_window_never_re_measures_after_promotion.

Two edges chosen deliberately

  • A window holding fewer than two trades falls back to the whole span rather than returning None, because switching the channel off entirely is worse than a stale rate that the baseline's own field reveals as un-windowed.
  • An explicitly supplied trades_per_year clears the window, since recording one this code did not apply would be a lie about provenance.

386 tests pass, ruff clean.

🤖 Generated with Claude Code

The opportunity-set channel compares the live firing rate against the baseline's.
The baseline's was measured over the validated log's WHOLE span, which is right for
a stationary book and quietly wrong for one whose rate has trended: the full-span
mean sits below what the book currently does, so the ratio reads healthy while the
rate falls.

Not hypothetical. On the real 40-market trend book this is measured, not argued:

  full span (40yr)   23.4 trades/yr
  last 10 years      29.2
  last 7 years       33.9
  last 5 years       35.3

The trend is +0.49 trades/yr per year (r=+0.63), so the rate has roughly doubled
over the log. Anchored to the full span, a collapse to 30% BELOW current behaviour
still scores 1.02 against the baseline and can never cross the 0.6 line. The one
channel covering a signal that stops firing while its per-trade edge holds was
structurally desensitised, not merely uncalibrated.

from_log gains rate_window_years, and EdgeBaseline records it. The default is
unchanged (whole span) so nothing moves silently for existing callers, and the
window rides in the verdict's reason line because two baselines with the same rate
and different windows are not making the same claim.

It is a windowed measurement taken ONCE, at promotion, not a rolling one, and the
distinction is the whole architecture: recomputing it later would re-fit the
reference onto the drifted reality. edge_monitor still has no parameter from which
a baseline or its rate could be rebuilt, pinned by a test.

Two edges chosen deliberately: a window holding fewer than two trades falls back to
the whole span rather than returning None, because switching the channel off
entirely is worse than a stale rate the baseline's own field reveals; and an
explicitly supplied trades_per_year clears the window, since recording one this
code did not apply would be a lie about provenance.

386 tests pass, ruff clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mspinola
mspinola merged commit 278b83a into main Aug 3, 2026
3 checks passed
@mspinola
mspinola deleted the claude/rate-window branch August 3, 2026 03:41
mspinola added a commit that referenced this pull request Aug 3, 2026
#118 has been stranded on main since 0.6.0, which matters more than a normal
stranded feature: npf already depends on rate_window_years while declaring
crucible>=0.6.0, and that API does not exist in 0.6.0. A PyPI install of npf could
resolve a crucible that raises TypeError inside EdgeBaseline.from_log.

That is the same floor bug fixed this morning, recurring within hours, and the guard
written to prevent it did not catch it: it compares the pin against a hand-maintained
constant, so it fires only when someone has already remembered. npf's follow-up
replaces it with a check against the floor RELEASE rather than a restatement of the
pin.

Contents, both concerning the firing-rate channel:

  * rate_window_years, measuring the baseline firing rate over the last N years of
    the validated log rather than its whole span. On a book whose rate has trended
    the whole-span mean sits below current behaviour, so the channel compares live
    trades against a rate the book left behind. Measured on a real 40-year book:
    23.4/yr full span against 33.9/yr over 7 years, which moved the trip point from
    41% to 60% of current behaviour.
  * The record of why that channel still cannot escalate to DEGRADED. Three
    arrival-process detectors were built and all deliver a false-alarm rate 3x to 5x
    worse than stated; calibrating empirically leaves the delivered budget spanning
    5.3 to 77.9 years for a stated 10. The claim that arrivals are approximately
    Poisson is deleted rather than softened.

390 tests pass, ruff clean, mkdocs --strict clean. python -m build then twine check
both artifacts PASSED.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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