feat(eod): allocate Pricing & Timing / Rotation basis-gap slices to specific stocks (schema 2.2)#347
Merged
Merged
Conversation
…pecific stocks (schema 2.1→2.2) Decomposes the aggregate pricing_timing_usd sleeve per-ticker using the schema-2.1 ib_market_value/market_value fields (PR343), which telescopes exactly: each held/entered name's basis gap folds into its own position row, each fully-exited name's folds into Rotation. The generic "Pricing & Timing" line now carries only the genuinely-unattributable leftover (~$0 whenever both days' snapshots have complete schema-2.1 data). Unattributed is deliberately left as a portfolio-level residual — no per-ticker basis exists for it in current data, and fabricating one would violate the fail-loud/no-silent-allocation doctrine. New position fields: position_alpha_usd (pure settled-close economic alpha) + pricing_timing_contrib_usd (this name's basis-gap slice), alongside the existing combined alpha_contrib_usd. Exact tie-out preserved (dollar_alpha == sum(components.contrib_usd)), verified by new TestPricingTimingPerTicker cases covering retained/new-entry/exited/ missing-data scenarios. Closes config-I2046.
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.
Summary
Closes
alpha-engine-config-I2046. crucible-executor PR343 (schema 2.1) added per-tickerib_market_value/market_valuefields specifically as a "breadcrumb for future per-name attribution" but never used them for attribution. This PR is that follow-through: it decomposes the aggregatepricing_timing_usdsleeve (the IB-mark-vs-settled-close basis gap) per ticker instead of leaving it as a portfolio-wide generic bucket.Why this decomposes exactly (no residual/fudge needed)
mark_basis(t) = Σ_{held today} (ib_market_value_i − market_value_i)andmark_basis(t−1) = Σ_{held yesterday} (ib_market_value_i − market_value_i), sopricing_timing_usd = mark_basis(t) − mark_basis(t−1)telescopes into an EXACT per-name sum: each ticker held on either day contributesbasis_today_i − basis_prior_i(zero baseline on the day it wasn't held). This is not an approximation — the per-ticker sum equals the aggregate to the last cent, verified by new tests.What changed
positioncomponent'scontrib_usd(exposed separately too, asposition_alpha_usd[pure settled-close economic alpha] +pricing_timing_usd[this name's slice], so the breakdown isn't lost).rotationcomponent the same way.ib_market_value) — will read ~$0 on any day both snapshots have complete schema-2.1 data.positions[], 2 new fields onalpha_attribution/nav_reconciliation).Tests
New
TestPricingTimingPerTickerclass (4 cases: retained position, same-day new entry, fully-exited name, missing-data fallback) plus updated existing tests for the schema bump. Full suite: 1465 passed, 4 skipped (no regressions).Test plan
positions[].pricing_timing_contrib_usdpopulated for retained names and the "Pricing & timing" component shrinks toward $0 on a no-legacy-data day🤖 Generated with Claude Code