Value each week at the multiplier in force that week - #24
Merged
Conversation
exposure applied ONE point value, today's, to a market's whole COT history. That is wrong wherever an exchange re-denominated a contract. ICE halved the Russell multiplier from $100 to $50 on 2016-12-05 and converted each open lot into two, with no CFTC rename to mark it, so 740 of RTY's 1,247 priced weeks were valued at half their true notional and dollar risk. Audited in #22, data added in marketdata#23. Splits the question in two rather than changing point_values in place: point_values() can this market be priced at all? Current spec, unchanged, still the membership gate, so a market with no multiplier still drops out naming itself. point_value_series() what was a given WEEK worth? Defers to marketdata's regime table for declared symbols and returns the flat current value otherwise. market_exposure multiplies elementwise and reports the week's multiplier in the point_value column, so a reader checking why an old week is large can see what produced it. Measured against the real store: RTY 740 weeks at 100.0 and 507 at 50.0, median pre-cut |notional| $1.33bn -> $2.66bn, post-cut unchanged, Russell share of Equities 11.9% -> 21.2%. All 46 other markets bit-identical. The percentile is the reason this mattered more than the level. expanding_pct_rank ranks each week against its own past, so an under-scaled first half made every post-2016 reading come out more extreme rather than merely smaller. Against a marketdata without the regime API this RAISES and names the version. Falling back to the flat series would return a plausible number that is wrong by 2x on RTY, and nothing downstream could tell. Same reasoning as returning NaN where a multiplier was never established (LBR before 1995) rather than a guess: a gap is visible on a chart and a guess is not. Floor: crucible-marketdata>=0.2.0, a correctness floor rather than a convenience one. crucible-marketdata was also missing from check_dep_floors.py's INTERNAL set, so that floor would not have been checked at all; npf's and cot-analyzer's copies already listed it. Verified the check now fails against 0.1.0. 352 passed (8 new), ruff clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The consumer half of #22. This is the change that actually fixes the 740 understated Russell weeks.
Important
Merge marketdata#23 first. CI checks out marketdata's default branch, and this declares
crucible-marketdata>=0.2.0. Until that merges, cotmetrics CI fails at the dependency-floor step withcrucible-marketdata: installed 0.1.0 does NOT satisfy declared '>=0.2.0'. That is the intended behaviour, not a flake.The problem
exposureapplied one point value, today's, to a market's entire COT history. ICE halved the Russell multiplier from $100 to $50 effective 2016-12-05 and converted each open lot into two, with no CFTC rename to mark it, so 740 of RTY's 1,247 priced weeks were valued at half their true notional and dollar risk.The shape of the fix
The question is split in two rather than changing
point_valuesin place:point_values()contract_specs, unchangedpoint_value_series()Keeping the membership gate on the current table matters: a regime table cannot say a market is unpriceable, and MFS/MME must keep dropping out and naming themselves.
market_exposurenow multiplies elementwise and reports the week's multiplier in thepoint_valuecolumn, so a reader checking why an old week is large can see what produced it.Measured against the real store
Every figure matches what #22 predicted, including the 740 count and the 21.2% share.
The percentile is why this mattered more than the level.
expanding_pct_rankranks each week against its own past, so an under-scaled first half made every post-2016 reading come out more extreme rather than merely smaller.Two deliberate refusals
Against a marketdata without the regime API this raises and names the version. It does not fall back to the flat series. That fallback would return a plausible number that is wrong by 2x on RTY, and nothing downstream could tell — which is the exact failure class this whole thread of work exists to remove.
Where a multiplier was never established, the dollars are NaN, not a guess (LBR before 1995). A gap is visible on a chart; a guess is not. Tested that such weeks drop out of
aggregate_exposurerather than silently entering a sum as a zero.A gap found on the way
crucible-marketdatawas missing fromscripts/check_dep_floors.py'sINTERNALset, so the floor declared inpyproject.tomlwas never actually checked. It did not matter while the floor was a nominal>=0.1.0; it matters now that it is a correctness floor. npf's and cot-analyzer's copies of the same script already listed it. Verified the check now fails against 0.1.0 and passes against 0.2.0.Checks
pytest tests/ -q: 352 passed (8 new), up from 344ruff check src tests: cleancheck_dep_floors.py: passes on 0.2.0, proven to fail on 0.1.0aggregate_exposure: Equities 1,109 weeks / 6 markets, whole book 45 markets, MFS and MME still dropped with the same messageThe audit doc in #22 is point-in-time and deliberately not amended, per the
analysis/lifecycle rule. This PR is the record of what was done about it.Still open
npf
validation/costs.pycarries the same defect at roughly a tenth the severity: it applies today'sPoint ValueandTick Valueto historical trades, and the slippage term cancels because both halve, leaving only the fixed commission term wrong. Not in this PR.Recap in plain language
cotmetrics now values each week using the contract multiplier that was actually in force that week, instead of today's. Only one market in 47 is affected, but it is affected badly: the Russell's dollar figures before December 2016 were exactly half what they should have been, which also made every reading since then look more extreme than it was. Everything else in the book is unchanged to the last digit.
This is a confirmed correction, not a judgment call: the exchange notice gives the date and the ratio, and every number here matches what the audit predicted before the code was written. The dollar-risk percentile's prerequisite is now met.
🤖 Generated with Claude Code