feat(resistance): resistance-v2 continuous supply score module (PR-C)#1979
Conversation
Resistance_supply: pure O(1) overhead-supply score from the PR-B sketch columns — proximity-weighted histogram mass saturated at the v1 heavy-zone bar count, horizon floors for supply the histogram cannot see (recent-but-far / 130-260w / 260-520w), virgin = 0. Letter grade derivable for display back-compat; virgin test breakout >= max_high_520w is bit-equal to v1 (parity-tested incl. the tie). Insufficient_history scores a configurable non-zero default (0.5) so unknown history is never rewarded as virgin. No consumers yet (PR-D wires the screener weight; default-off there). Plan: dev/plans/resistance-v2-supply-sketches-2026-07-15.md (D5).
|
Reviewed SHA: 44d3fd4 Structural QC — Resistance Supply Score Module (PR-C)
VerdictAPPROVED No structural findings. Pure function, fully configurable, comprehensive tests, no cross-cutting dependencies. |
dayfine
left a comment
There was a problem hiding this comment.
Reviewed SHA: 44d3fd4
Behavioral QC — resistance-v2 supply score (PR-C)
Verdict below is posted with --comment (GitHub blocks --approve on self-authored PRs). The behavioral verdict is APPROVED.
Ran in isolated jj workspace off main@origin, PR head checked out inside it; dune build @fmt + dune build analysis/weinstein/resistance/ + dune runtest analysis/weinstein/resistance/test/ via docker exec trading-1-dev (TRADING_DATA_DIR set) → 9/9 tests OK, exit 0. The @fmt warnings are pre-existing ocamldoc-comment warnings in unrelated files (resistance_sketch.mli, snapshot_schema.mli, laggard_cf) — none in this PR's files.
Contract Pinning Checklist
| # | Check | Status | Notes |
|---|---|---|---|
| CP1 | Each non-trivial .mli docstring claim has an identified pinning test | PASS | score∈[0,1], 0=virgin → test_virgin_scores_zero; 1=heavy recent → test_heavy_recent_supply_saturates. recent = proximity-weighted mass saturated at saturation_bars → test_heavy (8→1.0), test_moderate (3→0.375=3/8), test_proximity_decay (bucket0 5→0.625=5/8; bucket4 5·0.7⁴/8=0.1500625). horizon floors bind ONLY when no in-band mass, selected by age → test_breakout_above_anchor_shifts_buckets (recent_far), test_stale_horizon_floors (mid/old). "in-band mass speaks for itself" is implicitly pinned: test_moderate scores 0.375 (mass), NOT recent_far_floor 0.4, despite max_high=150 proving recent overhead. virgin iff breakout>=max_high_520w, bit-equal v1 incl tie → test_virgin_parity_with_v1. Insufficient (non-finite / bars<min) w/ non-zero score → test_insufficient_history (asserts score=config.insufficient_score=0.5), test_nan_sketch_degrades_to_insufficient. grade derivation (Heavy/Moderate/Clean/Virgin) → all 4 grades covered. Nit (non-blocking): floor-magnitude defaults (0.4/0.25/0.1) and min_history_bars=0 are pinned by config-field name, not by literal — acceptable, since asserting a config default's literal restates the source; the behavioral selection logic (which floor under which age) is literally pinned. |
| CP2 | Each PR-body "Test plan" claim has a corresponding committed test | PASS | All 9 advertised tests exist in test_resistance_supply.ml: (1) virgin=0 → test_virgin_scores_zero; (2) heavy 8→1.0/Heavy → test_heavy_recent_supply_saturates; (3) moderate 3→0.375/Moderate → test_moderate_supply; (4) proximity decay 5·0.7⁴/8 vs 0.625 → test_proximity_decay_discounts_distant_supply; (5) k_min shift, mass→0, recent-far floor → test_breakout_above_anchor_shifts_buckets; (6) stale mid/old floors + Clean → test_stale_horizon_floors; (7) armed min_history_bars → Insufficient at configured score → test_insufficient_history; (8) NaN → Insufficient → test_nan_sketch_degrades_to_insufficient; (9) end-to-end virgin parity vs v1 through real Resistance_sketch builder, below/AT/above spike incl tie → test_virgin_parity_with_v1. No advertised-but-missing test. |
| CP3 | Identity/invariant tests pin identity, not just size | PASS | The v1↔v2 virgin-parity test uses List.count agreements + equal_to (List.length breakouts) (=3), pinning full per-case agreement across all 3 boundary breakouts (below/AT/above the spike) — the sanctioned List.count form, not a size_is-only assertion. Virgin & insufficient tests use whole-record equal_to (full value equality). |
| CP4 | Each guard named in code docstrings has a test exercising the guarded scenario | PASS | Documented degradation guards both exercised: non-finite sketch cell → test_nan_sketch_degrades_to_insufficient; bars_seen < min_history_bars → test_insufficient_history. k_min breakout<=anchor→0 branch exercised by the breakout_price=100.0=anchor cases. Nit (non-blocking): the extra defensive not (Float.is_finite breakout_price) branch (not called out in the .mli degradation clause) has no dedicated test — same _insufficient output is covered by the nan-cell case, so no behavioral gap. |
Behavioral Checklist (Weinstein domain rows)
Pure analysis-library PR with no strategy consumer (screener wiring + w_overhead_supply axis are PR-D, default-off per experiment-flag discipline; zero backtest/live behavior change). Per .claude/rules/qc-behavioral-authority.md §"When to skip", the S*/L*/C*/T* rows are NA.
One live domain claim — virgin-verdict fidelity to v1. weinstein-book-reference.md §resistance is the ultimate authority; the v1 Resistance module is the implementation authority being matched. Verified: v1 _is_virgin_territory uses strict > ("no bar high STRICTLY above breakout" = virgin), so v2's breakout >= max_high_520w is bit-equal including the tie. Pinned end-to-end through the real Resistance_sketch builder by test_virgin_parity_with_v1. Purity (claim 5) confirmed: lib dune depends only on core types weinstein_types — no snapshot-layer dependency; the module takes plain floats.
| # | Check | Status |
|---|---|---|
| A1 | Core module modification strategy-agnostic | NA — no core module touched (qc-structural did not flag A1) |
| S1–S6, L1–L4, C1–C3, T1–T4 | Stage / stop / cascade / domain-test rows | NA — pure scoring-primitive library, no strategy consumer; the sole domain claim (v1 virgin parity) is verified above via CP1/CP2/CP4 |
Quality Score
5 — Exemplary contract-pinning: literal arithmetic pins (3/8, 5·0.7⁴/8), whole-record equality on virgin/insufficient, a real-builder v1-parity test spanning the tie boundary, and all four grades covered. Two nits (config-default floor magnitudes pinned by-name; the breakout_price-nan defensive branch untested) touch no behavioral contract.
Verdict
APPROVED
Automated daily orchestrator run (run-4). Main GREEN (code) at 8c97077; no dispatch — every dispatchable surface blocked; the two open PRs (#1979, #1980) are fenced maintainer-LOCAL. See dev/daily/2026-07-15-run4.md for the full summary. 🤖 Dispatched by GHA orchestrator run [29421382522](https://github.com/dayfine/trading/actions/runs/29421382522) --------- Co-authored-by: claude-orchestrator <[email protected]> Co-authored-by: GitHub Actions Bot <bot@github-actions>
New track file for the resistance-v2 program (P0 of 2026-07-15 priorities): records PR-B #1975 merged / PR-C #1979 in QC, pins the PR-D screener-wiring design (config seams, Overlay_validator serialization rule, do-no-harm gates), and sequences PR-B2 deep-history feed → warehouse rebuild → PR-E WF-CV surface. Adds the index row (new-track exception per feat-agent-dispatch rules), fenced "in flight locally — do not dispatch". Docs-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…16 handoff (#1985) Session close-out for the 07-15 autonomous P0 run: - `dev/notes/next-session-priorities-2026-07-16.md` — supersedes 07-15: records the six merged resistance-v2 PRs (#1974/#1975/#1979/#1982/#1983/#1980), the end-of-session warehouse rebuild launch (`/tmp/snap_top3000_dedup_v3_sketch`), and pins next-session P0 = PR-E WF-CV score-weight surface (verify rebuild → do-no-harm cell → surface incl. weight 0 → ledger). New standing constraints: DIRTY-merge-state PRs run no pull_request workflows; detached-dune discipline for the container. - `dev/status/resistance-v2.md` — PR-D marked MERGED (#1983); Next-steps reordered (rebuild in flight → PR-E). Docs-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
PR-C of the resistance-v2 track (plan:
dev/plans/resistance-v2-supply-sketches-2026-07-15.md§D5; follows PR-B #1975).What it does
New pure module
Resistance_supply(analysis/weinstein/resistance): continuous overhead-supply score in [0,1] from the PR-B sketch cells, O(1) per query — the arbitration path that replaces the all-or-nothing virgin-grade flip with a searchable weight.breakout >= max_high_520w, bit-equal to v1's strict-exceedance test incl. the tie — same semantics pinned in PR-B): score 0.quality) for display back-compat: the score/display split from the plan.All constants live in
config(defaults mirror v1 where a v1 analog exists). Takes plain floats — no snapshot-layer dependency; the caller extracts sketch cells.What it does NOT do
No consumers: screener scoring, strategy config, and the
w_overhead_supplyaxis are PR-D (default-off per experiment-flag discipline). Zero behavior change to any backtest or the live report.Test plan
test_resistance_supply.ml(9 tests): virgin scores exactly 0; heavy 8-bar bucket saturates to 1.0 with Heavy grade; moderate 3 bars → 0.375/Moderate; proximity decay pins 5 bars at bucket 4 = 5·0.7⁴/8 vs 0.625 at bucket 0; breakout above the anchor's first band shiftsk_min(mass drops to 0, recent-far floor applies); stale mid/old floors with Clean grade; armedmin_history_bars→ Insufficient_history at the configured score; NaN sketch degrades to Insufficient_history; end-to-end virgin parity vs v1Resistance.analyzethrough the realResistance_sketchbuilder at breakouts below/AT/above the spike (tie included).Full
dune build @fmt && dune build && dune runtestgreen in container.🤖 Generated with Claude Code