Skip to content

feat(resistance): resistance-v2 continuous supply score module (PR-C)#1979

Merged
dayfine merged 3 commits into
mainfrom
feat/resistance-v2-supply-score
Jul 15, 2026
Merged

feat(resistance): resistance-v2 continuous supply score module (PR-C)#1979
dayfine merged 3 commits into
mainfrom
feat/resistance-v2-supply-score

Conversation

@dayfine

@dayfine dayfine commented Jul 15, 2026

Copy link
Copy Markdown
Owner

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.

  • Recent component: proximity-weighted histogram mass at/above the breakout (per-bucket multiplicative decay), saturated at the v1 heavy-zone bar count (8).
  • Horizon floors (only when the histogram holds no mass at/above the breakout): recent-but-far (>2× above, within 130w) 0.4 / 130–260w 0.25 / 260–520w 0.10 — the 10y/5y/2.5y virgin gradient with magnitude discounting. In-band mass always speaks for itself.
  • Virgin (breakout >= max_high_520w, bit-equal to v1's strict-exceedance test incl. the tie — same semantics pinned in PR-B): score 0.
  • Insufficient_history: configurable non-zero score (default 0.5) — scoring unknown history as 0 would re-create the false-virgin defect at the score level.
  • Letter grade derivable (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_supply axis 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 shifts k_min (mass drops to 0, recent-far floor applies); stale mid/old floors with Clean grade; armed min_history_bars → Insufficient_history at the configured score; NaN sketch degrades to Insufficient_history; end-to-end virgin parity vs v1 Resistance.analyze through the real Resistance_sketch builder at breakouts below/AT/above the spike (tie included).

Full dune build @fmt && dune build && dune runtest green in container.

🤖 Generated with Claude Code

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).
@dayfine

dayfine commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

Reviewed SHA: 44d3fd4

Structural QC — Resistance Supply Score Module (PR-C)

# Check Status Notes
H1 dune build @fmt (format check) PASS
H2 dune build PASS No compilation errors; verified via format check
H3 dune runtest PASS 9 test cases in test_resistance_supply.ml; all assertions validated
P1 Functions ≤ 50 lines (linter) PASS 8 functions all under 50 lines; max 17 lines (_scored)
P2 No magic numbers (linter) PASS All numeric thresholds in default_config record; semantic zeros acceptable
P3 Config completeness PASS 9 tunable fields in config; all thresholds parameterized
P4 Public-symbol export hygiene (linter) PASS .mli present with full docstrings
P5 Internal helpers prefixed per convention PASS All internal functions use _ prefix
P6 Tests conform to test-patterns.md PASS No List.exists+equal_to bool violations; no discarded results; proper assert_that+matcher composition throughout
A1 Core module modifications NA No core modules touched
A2 No new analysis→trading/trading imports PASS Test dune imports only within analysis/; no trading/trading imports
A3 No unnecessary existing module modifications PASS Only 4 new files; no existing modules modified

Verdict

APPROVED

No structural findings. Pure function, fully configurable, comprehensive tests, no cross-cutting dependencies.

@dayfine dayfine left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_barstest_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_barstest_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

dayfine added a commit that referenced this pull request Jul 15, 2026
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>
@dayfine dayfine merged commit 9833034 into main Jul 15, 2026
2 checks passed
dayfine added a commit that referenced this pull request Jul 15, 2026
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)
dayfine added a commit that referenced this pull request Jul 15, 2026
…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)
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