Summary
Since v0.11.0 the adjusted SOPR series (sth_asopr_24h, sth_asopr_1w, and the all-cohort asopr) report ~2.0-2.3 across the entire history. Adjusted SOPR should hover around 1, like raw SOPR does. The cause is a one-line vec-selection mismatch: the under-1h per-block transfer-volume vec is subtracted from the cohort's cumulative value-created vec, so the "adjusted" numerator is effectively unadjusted.
Reproduced on a local v0.11.0 instance and live on bitview.space (2026-08-11); the relevant files are unchanged through v0.11.2 and main (26f07f0).
Observed
GET /api/series/sth_sopr_24h/day1?start=-5 -> 0.9999, 1.0029, 0.9999, 0.9982, 0.9972 (fine)
GET /api/series/sth_asopr_24h/day1?start=-5 -> 2.1691, 1.9377, 2.1882, 2.0091, 2.2855 (should be ~1)
Component evidence
Pulling the component series for the last days (values in cents):
| date |
sth_value_destroyed_sum_24h |
sth_adj_value_created_sum_24h |
sth_adj_value_destroyed_sum_24h |
| 2026-08-09 |
3,218,907,835,807 |
3,227,511,565,312 |
1,474,972,327,074 |
| 2026-08-10 |
4,712,233,636,460 |
4,668,370,715,877 |
2,323,586,346,378 |
| 2026-08-11 |
4,603,299,156,779 |
4,590,457,923,918 |
2,008,515,682,879 |
Two things follow:
- The destroyed side is adjusted correctly.
sth_value_destroyed - under_1h_value_destroyed reproduces sth_adj_value_destroyed to the cent.
- The created side is not adjusted at all.
sth_adj_value_created / sth_value_destroyed equals raw sth_sopr to 5+ decimals (e.g. 4,590,457,923,918 / 4,603,299,156,779 = 0.99722 = raw SOPR on 2026-08-11), i.e. adj_value_created carries the unadjusted value created.
So the served ratio is raw_created / adj_destroyed ~= 1 / adjusted_share ~= 2.2.
Recomputing adjusted SOPR from raw series via the per-cohort identity created = destroyed * sopr:
(sth_destroyed * sth_sopr - u1h_destroyed * u1h_sopr) / (sth_destroyed - u1h_destroyed)
gives 0.9998 / 0.9967 / 0.9940 for the same days - the expected ~1 band.
Root cause
crates/brk_computer/src/distribution/cohorts/utxo/groups.rs (~line 596): the two under-1h sources for the adjusted computation are taken from different vec shapes:
let under_1h_value_created = self
.age_range
.under_1h
.metrics
.activity
.transfer_volume
.block // <-- per-block vec
.cents
.clone();
let under_1h_value_destroyed = self
.age_range
.under_1h
.metrics
.realized
.sopr
.value_destroyed
.cumulative // <-- cumulative vec
.height
.read_only_clone();
AdjustedSopr::compute_rest_part2 (crates/brk_computer/src/distribution/metrics/realized/adjusted.rs) then subtracts both from the cohort's cumulative base vecs (transfer_volume.inner.cumulative.cents.height and sopr.value_destroyed.cumulative.height, passed in metrics/cohort/extended_adjusted.rs). Subtracting a per-block value from a cumulative series is a near-no-op, which matches the evidence above exactly. Both call sites (sth and all in groups.rs) pass the same vec, so both cohorts' asopr are affected.
Suggested fix
Select the under-1h cumulative height transfer-volume vec, mirroring the value_destroyed selection, so both sides of the ratio subtract cumulative from cumulative.
Happy to test a fix against our recomputed reference series.
Summary
Since v0.11.0 the adjusted SOPR series (
sth_asopr_24h,sth_asopr_1w, and theall-cohortasopr) report ~2.0-2.3 across the entire history. Adjusted SOPR should hover around 1, like raw SOPR does. The cause is a one-line vec-selection mismatch: the under-1h per-block transfer-volume vec is subtracted from the cohort's cumulative value-created vec, so the "adjusted" numerator is effectively unadjusted.Reproduced on a local v0.11.0 instance and live on bitview.space (2026-08-11); the relevant files are unchanged through v0.11.2 and
main(26f07f0).Observed
Component evidence
Pulling the component series for the last days (values in cents):
sth_value_destroyed_sum_24hsth_adj_value_created_sum_24hsth_adj_value_destroyed_sum_24hTwo things follow:
sth_value_destroyed - under_1h_value_destroyedreproducessth_adj_value_destroyedto the cent.sth_adj_value_created / sth_value_destroyedequals rawsth_soprto 5+ decimals (e.g. 4,590,457,923,918 / 4,603,299,156,779 = 0.99722 = raw SOPR on 2026-08-11), i.e.adj_value_createdcarries the unadjusted value created.So the served ratio is
raw_created / adj_destroyed ~= 1 / adjusted_share ~= 2.2.Recomputing adjusted SOPR from raw series via the per-cohort identity
created = destroyed * sopr:gives 0.9998 / 0.9967 / 0.9940 for the same days - the expected ~1 band.
Root cause
crates/brk_computer/src/distribution/cohorts/utxo/groups.rs(~line 596): the two under-1h sources for the adjusted computation are taken from different vec shapes:AdjustedSopr::compute_rest_part2(crates/brk_computer/src/distribution/metrics/realized/adjusted.rs) then subtracts both from the cohort's cumulative base vecs (transfer_volume.inner.cumulative.cents.heightandsopr.value_destroyed.cumulative.height, passed inmetrics/cohort/extended_adjusted.rs). Subtracting a per-block value from a cumulative series is a near-no-op, which matches the evidence above exactly. Both call sites (sthandallingroups.rs) pass the same vec, so both cohorts' asopr are affected.Suggested fix
Select the under-1h cumulative height transfer-volume vec, mirroring the
value_destroyedselection, so both sides of the ratio subtract cumulative from cumulative.Happy to test a fix against our recomputed reference series.