Heatmap: an Offside column, so the grid says who is losing - #59
Merged
Conversation
Every positioning column on this page is about size: how unusual it is, how extreme, how many dollars of risk. None of them says whether the people holding the position are up or down. This adds that, reading cotmetrics.offside: how far Large Specs sit from their own average cost, in the market's weekly sigma. Its own group rather than a third Exposure column, because it reads a different cohort (LEG_LARGE, not the LEG_SPEC dollar-risk column) and answers the opposite question. A basis on the summed large+small net describes a trader who is both cohorts at once, and the two have different costs and behave differently when under water. Large Specs is also the cohort every published figure for this measure is quoted on. No percentile, unlike Risk %ile, and the asymmetry is deliberate: dollar risk is incomparable across markets so it must be ranked against a market's own history first, while offside is already comparable by construction (dividing by the market's own sigma is what the measure does) and 0 means "at cost" everywhere. Ranking it would replace a readable quantity with a percentile of one. Only the losing tail lights, at <= -2 sigma, in the bear colour. That is a P&L statement, not a direction verdict: the number is the sign of the cohort's own mark, so red means "these holders are down", not "this market goes lower". The tooltip says so explicitly, and says the deep readings do NOT forecast a position being cut, because that exact inference was pre-registered, tested against 44 markets and did not hold (npf #214, verdict "adverse-move proxy"). A tooltip that let a reader draw it would be asserting the thing that failed. Verified against the live store: all 18 rows in Energies/Metals/Currencies populate with consistent basis/mark pairs, and the page registry still loads all 16 routes, which is the check that matters when use_pages imports every page at startup. Floor raised to cotmetrics>=0.7.0 for the same reason: against 0.6.0 the import takes down the whole registry, not one column. Co-Authored-By: Claude Fable 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.
What this adds
An Offside column on the heatmap, in its own "Cost Basis · Large Specs" group: how far Large Specs sit from their own average cost, in the market's weekly standard deviations. Negative is under water. Reads
cotmetrics.offside(mspinola/cotmetrics#28); this repo computes nothing.Every positioning column on this page is about size — how unusual, how extreme, how many dollars of risk. None said whether the holders are up or down.
Four decisions worth reviewing
Its own group, not a third Exposure column. It reads a different cohort (
LEG_LARGE, where the dollar-risk column usesLEG_SPEC) and answers the opposite question. A basis computed on the summed large+small net describes a trader who is both cohorts at once, and the two have different average costs and behave differently when under water. Large Specs is also the cohort every published figure for this measure is quoted on.No percentile, unlike Risk %ile. Dollar risk is incomparable across markets, so it must be ranked against a market's own history before it means anything. Offside is already comparable by construction: dividing by the market's own sigma is what the measure does, and 0 means "at the cohort's average cost" in every market. Ranking it would replace a readable quantity with a percentile of one.
Only the losing tail lights, and the colour is a P&L statement. Lit at
<= -2sigma in the bear colour. That is the sign of the cohort's own mark, not a direction verdict: red means "these holders are down", not "this market goes lower". Being deep in profit is not distress, so the column is deliberately asymmetric where a z-score column would not be.-2is a display threshold, deliberately rounder than any figure in the study (pooled p10 is about -1.7).The tooltip refuses to promise capitulation. The obvious inference from a lit cell — they are trapped, so they must fold — was pre-registered, tested against 44 markets and did not hold (npf#214, verdict "adverse-move proxy"; within equally severe adverse moves, deep-offside cohorts capitulated no more often and Small Traders significantly less). The copy says the number is a reading of who is losing and not a forecast, and two tests pin that wording so it cannot quietly drift into a claim.
Verification
render_heatmap_layoutat target date 2026-06-30 returns Euro -2.09 and Corn -5.91, both matching the emitted conditionparams.value != null && params.value <= -2.0and rendering in#FF4D4D.+/-formatting and dim styling all render as intended; screenshot behaviour matches Risk %ile.use_pagesimports every page at startup.check_dep_floors.pyOK.Floor
cotmetrics[options]>=0.7.0. This is an import-time floor rather than a nominal one: against 0.6.0 the module does not exist and, becauseuse_pagesimports every page at startup, the ImportError takes down the whole route registry rather than one column.Merge mspinola/cotmetrics#28 first, or CI here cannot resolve the floor.
🤖 Generated with Claude Code