Skip to content

feat(factors): bind valley_price_quantile + fold binding.py into code_hash (D5 C4b) - #107

Merged
StackOverFlow11 merged 5 commits into
mainfrom
feat/factor-refactor-d5-c4b-vpq-binding
Jul 28, 2026
Merged

feat(factors): bind valley_price_quantile + fold binding.py into code_hash (D5 C4b)#107
StackOverFlow11 merged 5 commits into
mainfrom
feat/factor-refactor-d5-c4b-vpq-binding

Conversation

@StackOverFlow11

Copy link
Copy Markdown
Owner

D5 C4 (part b) — bind valley_price_quantile + fold binding.py into code_hash

Second half of D5 C4: binds the last unbound minute factor into the service/materializer machinery, closes the "binding.py is load-bearing but outside code_hash's reach" follow-up (lead-adjudicated), and enables the factor in the unified runner with a full three-mode reconciliation.

What changed (5 commits)

  1. vpq binding (declarative, no isinstance dispatch — red line feat(neutralize): P2-3 point-in-time SW industry covariate (replace current-tag) #5). valley_price_quantile_20 residualizes via per-day cross-sectional OLS ⇒ its value depends on the universe, so per D4c the store holds the per-symbol intermediate (raw_qbar) and the cross-sectional combine runs at read-assembly. Its combine needs daily close — the one engine extension: a declarative DailyCombineInput/combine_daily slot on MinuteStreamBinding, fed by materialize.combine_daily_panel (decision-lagged close, 22 lagged days of left-end warmup so the shifted panel's first row is not a spurious NaN; under-warm clamping now logs one disclosure line). Existing 11 binding entries: zero diff (pinned).
  2. The shifted-panel third path, equivalence pinned on both sides. The factor's internal T-1 (reversal_20's shift(1)) would be double-lagged by the decision view to close_{d−2}; the landed path passes a shifted panel and disables the internal T-1 — measured bitwise identical (Series.equals) to the legacy path, with break controls (~8.5e-02) proving the tests can fail, re-verified by the reviewer with different fixtures and novel perturbations (negative/zero closes, NaN injection).
  3. binding.py folded into code_hash (lead ruling Q2): the file carrying load-bearing combine semantics now moves every registered factor's store key (mutation-verified: touch binding.py → 18/18 hashes change; touch an unrelated module → 0/18). One-time full invalidation of the dev-only artifacts/factor_store/ disclosed; over-invalidation direction is safe and documented.
  4. Runner enablement: vpq served by run-factor-eval; NeutralizationCoverage wired via mechanism B (raw intermediate re-read through the new service.stored_payload — same key/fingerprint/coverage engine, not a second data path); anchors-mode fixed to serve cross-sectional factors over the full universe (5 anchor symbols < min_cross_section=10 structurally yields all-NaN — would have bitten intraday_amp_cut in C5 too).
  5. Panels-leg class boundaries for cross-sectional factors (lead-adjudicated after 100% decomposition of the first FAIL): ① pooled monthly-monotonicity exemption anchored to the structural partial month (month of the frozen panel's first finite value) — the initial positional implementation ("first month with diffs") was probed by the reviewer, shown bypassable, and fixed with reverted-mutation evidence; ② new named class threshold_flip_contamination (window [2023-06-01, 2023-07-14]; direct symbol 600623.SH ≤ 2e-04; others ≤ 1e-06; ≤ 20,000 cells) for the PRV critical-bar flip × per-day OLS contamination family — input-side change excluded (ledgers zero writes since before the freeze; qbar bitwise across geometries, max 1.1e-16, independently recomputed by the reviewer); ③ float tail gains an abs floor (≤ 1e-12 is dust regardless of rel, fixing near-zero-residual rel misfire) and a per-class cap (bars-only 101, cross-sectional 1,000).

Real-run reconciliation (cache-only, stk_mins_live_calls=0)

vpq three modes, independently rerun by the reviewer with matching counts: panels rc=0 — warmup=24,568 (monthly 902 → 19,980 → 3,667 → 19, exempt 2021-07 structurally, then monotone), float_tail=883 (≤1,000), flip_contamination=19,177 (≤20,000), unclassified=0; reports 4/4 OK (registered classes only); anchors 5/5 (ok=4 incl. two rel=0.0 and two ~1e-15; warmup=1). Fresh artifacts factor_eval_valley_price_quantile_20_exec_*: verdicts Watch/Watch (exec no-book predictive=PASS, ic=0.0270/icir=0.426; with-book incremental=PASS) — same verdicts as the frozen baseline, numeric shifts all inside the registered warmup-aggregate effect.

Review

Two rounds, separate executor: APPROVE-WITH-NITS (0 CRIT/HIGH/MEDIUM; LOW-1 positional-exemption bypass + NIT-1 silent clamp) → fixes → delta re-review: both CLOSED, PR approved, including the reviewer re-running their own bypass probe against the new code (FAILs), reverting the fix themselves to prove the tests bite, and independently recomputing the qbar double-geometry evidence (1,241/1,241 cells, max|diff|=1.11e-16).

Gates (independently rerun by the lead)

pytest 2533 passed (node-ID diff: 0 pre-existing displaced), ruff clean, run-phase0 anchor ic 0.9600 / annual 0.8408, validate-config 32/32, secret-value scan 0, exec_baseline_freeze --verify 77/77 @ 45c14aa.

NOT in this PR

Remaining 7 factors' real-run reconciliation + full four-leg audit (C5), old-runner deletion (C6), mmp_ew (D6c), run-registry (D7). Known non-blocking registrations: LOW-1's residual semantic edge (a factor whose first finite value genuinely starts mid-window exempts that month — self-consistent with the ruling), the C4a LOW-1/LOW-2 items (C5 hardening list).

…+ fold binding.py into code_hash (D5 C4)

vpq was the only unbound minute factor: its residualize is a per-date
cross-sectional OLS on a T-1 20-day reversal, so its value depends on the
loaded universe AND on the daily close panel.

* binding: new vpq stream entry (per_symbol = compute_valley_price_quantile_stats
  -> raw_qbar intermediate, stored per D4c) with a DECLARED combine_daily input
  (DailyCombineInput on MinuteStreamBinding — a declaration the engine consults,
  never isinstance dispatch, red line #5); vpq joins
  CROSS_SECTIONAL_MINUTE_FACTORS; the _DEFERRED machinery is retired. Existing
  table entries are byte-untouched.
* engine: materialize.combine_daily_panel loads the declared close column, applies
  the decision lag (row d carries close_{d-1}) and trims to the declared warmup
  (reversal_days + 2 trailing trading days — rev20's 21 closes + the lag's leading
  NaN row, section-6.18 nested-lookback trap); service._assemble routes it to the
  combine at read-assembly over the requested universe. The pooled saturation loop
  reads the value's output dates off the intermediate for daily-bound factors
  (their combine contract preserves the intermediate's rows exactly).
* third path: reversal_20_shifted (internal T-1 OFF on the shifted panel) is
  algebraically identical to the legacy reversal_20 on the un-lagged panel —
  pinned bit-for-bit on BOTH sides (binding unit test with a sabotage control at
  max|diff| 4.6e-2 proving non-vacuity; service read-through vs the legacy
  per-symbol-stats + one residualization reference at max|diff| 0.0, plus a hard
  zero-NaN-divergence assertion over the left-edge 22-trading-day window).
* code_hash: factors/compute/minute/binding.py joins the enumerated shared set —
  it carries load-bearing value semantics while the D4c shape validation covers
  only column names. ONE-TIME EFFECT, disclosed: every factor's store key changes
  (measured 18/18 registered classes), so a factor store filled before this commit
  is wholesale invalid and must be recomputed once (dev-only artifact).
* decision-cutoff visibility: vpq is now measured through its per-symbol qbar
  stage (routed by the binding tables); the measured-clean set is 11 again.
vpq is the eleventh and last subject the unified exec-only runner could
not serve; the C4b binding (declared daily combine input) made the engine
path ready, so the readable deferral is retired and vpq rides the SAME
service.panel read-through as every other factor.

* mechanism B disclosure (catalogue §三): NeutralizationCoverage has no
  diagnostics sink — the runner reduces it AFTER the panel read from the
  stored raw_qbar intermediate + reversal_20 on the bundle's close panel
  (pinned bit-for-bit equal to the combine's shifted-panel path) + the
  served residual, and ships it as the add-Section
  "neutralization_coverage". The legacy runner only LOGGED it, so the
  frozen artifact carries no such section.
* factors.service.stored_payload: the disclosure's read-back of the
  STORED payload (value for most factors, per-symbol intermediate for a
  cross-sectional one) through the same key/fingerprint/coverage engine
  as panel(), sliced to exactly the request (never a superset left by a
  wider earlier fill); registered as a probed symbol entry point.
* reconcile registration (catalogue §七之四, pre-registered): the new
  section's JSON subtree is matched by section NAME (never bare index)
  and its MD lines by dataclass-derived prefixes; any other added
  section stays unregistered and fails. Blank separator lines register
  only alongside a registered section.
* the runner log/report state vpq's real cold-fill cost in the same
  terms as the other factors: pooled + cross-sectional + daily-bound
  means the saturation load (declared 2015-01-05 floor) before the store
  is warm.

Gates: pytest 2518 passed; ruff clean; node-ID diff vs afb697c is
+12/-1 (the one removal is the replaced readable-deferral test; zero
existing tests squeezed out).
…chors mode (D5 C4)

Measured on valley_price_quantile's first real anchors reconcile: the
anchors harness requested only the anchor rows' symbols, but a
cross-sectional factor's served value is a function of the REQUESTED
universe (D4c) — 5 names < the combine's min_cross_section floor served
all-NaN and 4/5 rows false-FAILED. stores_intermediate factors are now
served over the full config universe and the anchor cells are looked up
inside it (the hand side was computed over that universe); per-symbol
factors keep the cheap anchor-only request. intraday_amp_cut shares the
property, so C5 would have hit the same wall.

After the fix, vpq anchors: 5/5 — 4 rows reconcile (2 at rel=0.0, 2 at
~1e-15) + the 2021-07-30 row lands in the registered
warmup_left_extension class.

Also registers the vpq real-reconcile decomposition in the catalogue
(§七之五): reports 4/4 OK; the panels-leg differences fully attributed
to three blocks — the registered warmup_left_extension (24,516 cells,
with a structural partial-first-month that defeats the pooled monthly
monotonicity rule), the 600623.SH classification-flip block (19,143
cells, same symbol/window as threshold_flip_tail, one directly affected
name + OLS-coefficient contamination), and float dust on near-zero
residuals that the rel-based float tail misjudges. Class-boundary
revisions are left to the lead; the facts are registered, nothing
rounded away.

Gates: pytest 2518 passed; ruff clean.
@StackOverFlow11
StackOverFlow11 merged commit f6d5a60 into main Jul 28, 2026
StackOverFlow11 added a commit that referenced this pull request Jul 28, 2026
…5-c4b

docs: progress for refactor D5 C4b (PR #107)
@StackOverFlow11
StackOverFlow11 deleted the feat/factor-refactor-d5-c4b-vpq-binding branch July 28, 2026 08:34
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