Skip to content

feat(factors): reproduce peak/ridge traded-amount ratio (PR-M, closing factor — highest IC of the set) - #73

Merged
StackOverFlow11 merged 2 commits into
mainfrom
feat/pr-m-peak-ridge-amount-ratio
Jul 20, 2026
Merged

feat(factors): reproduce peak/ridge traded-amount ratio (PR-M, closing factor — highest IC of the set)#73
StackOverFlow11 merged 2 commits into
mainfrom
feat/pr-m-peak-ridge-amount-ratio

Conversation

@StackOverFlow11

Copy link
Copy Markdown
Owner

Summary

Reproduce the peak/ridge traded-amount ratio — Kaiyuan Securities microstructure #27 §7.2, and the closing factor (10/10) of the report-reproduction loop. It completes the family map with the AMOUNT family, the last one untested.

Result: Watch/Watch, both assessed axes PASS, and the highest raw IC of the whole set. IC +0.0401, ICIR 0.488 (CI low +0.4225 > the 0.30 bar), NW-t 16.34, win rate 0.682, monotonicity 0.60, N_eff 1009; incremental ICIR +0.297 (CI low +0.239) — a genuinely new exposure beyond the value/low-vol book. OOS sign-consistent (train +0.0364 / test +0.0436; holdout subperiods +0.0488 / +0.0385).

What it settles for the loop: it is NOT only price information. This construction has zero price content, yet posts the set's highest IC and clears Incremental. Family map now reads — count weak, timing null, return rejected, price-ratio ×2 and price-position passing, amount passing with the strongest IC. The peak/ridge behavioural split itself carries signal.

But it does not displace the price factors as the best candidates, and the reason is instructive: monotonicity 0.60 (vs 1.00 for PR-I/PR-L) and turnover 2.13 (vs 0.47/0.68). High IC concentrated in the rank extremes, four times as expensive to hold, netting −0.001402 at 1× cost (2× −0.003529, 4× −0.007784). PR-I remains the strongest all-round factor of the loop.

The task card was WRONG and the report won — this is the process working. My card pinned mean of daily ratios; §7.2 says 「20 日量峰总成交额与量岭总成交额,二者做比」 — a ratio of sums. The report draws this distinction itself in adjacent sections (§7.1, PR-J's factor, says 「20 日价格比均值」). The card pre-authorized the report to override it. Critically: the prototype scored the card's (wrong) form BETTER (+0.0384 vs the shipped +0.0264), and the implementation did not switch — selecting an aggregation on prototype IC would be exactly the post-hoc selection this process exists to prevent. Both numbers are recorded in the prototype artifact; the report's form ships. Pre-registered sign +1 was read from §7.2 (RankIC +10.28%) before any code ran.

Every invariance test verified to have teeth — independently, twice. Per the PR-L lesson (a flagship anti-lookahead test that passed even under the buggy implementation), the implementer extracted defect-injection seams and showed each test FAILS under its defect. The reviewer then wrote its own from-scratch monkeypatches rather than reusing those helpers, and confirmed all four: PIT cutoff disabled → 2.000000 vs 0.001499; valley bars leaked into the peak leg → 2168.67 vs 33705.70; forward-looking rolling window → day-1 value 2.0 → 0.947; symbol frames mislabeled → the two symbols' ratios swap exactly.

A card premise corrected by measurement: I flagged peak scarcity as the risk (hence the ≥5 floor). Measured at full scale, the binding constraint is the ridge floor inherited from PR-J — 557,428 day-rejections vs 94,179 for peaks. Ridge median (10) is below peak median (13) despite a higher mean, because the ridge distribution is violently right-skewed (p10=0, p90=47, max 227). Day-validity is 50.4%, driven almost entirely by a gate that was argued for a price ratio and never separately justified for an amount ratio. Left unchanged (changing a gate after seeing IC is tuning) and recorded as an open concern.

Test plan

  • Full pytest: 1649 passed (baseline 1583 + 66); diff is 6 files, 0 deletions — the six prior factors' modules and tests are untouched by construction
  • ruff clean; phase0 ic 0.9600 / annual 0.8408 unchanged; 31 configs validate
  • git diff main -- analytics/eval/ data/clean/intraday_volume_prv.py EMPTY (both known frozen defects left untouched)
  • Independent review: APPROVE, no CRITICAL/HIGH/MEDIUM/LOW; read §7.2 itself and confirmed the aggregation override is correct; re-verified the sign source; independently re-derived all four teeth checks; confirmed prototype artifact predates the run by mtime
  • Secret scan: the real token value grepped across diff and every artifact — zero matches
  • Cross-factor claim corrected before it propagates: the earlier 'rank-IC/Pearson-IC divergence predicts monotonicity' pattern is falsified as a fine-grained predictor by this factor (PR-L 1.52×→1.00, PR-I 3.69×→1.00, PR-M 3.86×→0.60, PR-J 4.22×→0.90, PR-K 381×→0.20 — not monotonic; three factors cluster at 3.7–4.2× yet span 0.60–1.00). Only the extremes hold. To be stated as an inconclusive association, not an established regularity.

… §7.2)

The AMOUNT family -- the last untested one in this reproduction loop. The nine prior
factors covered a count (PR-F, weak), a timing moment (PR-H, null), two price ratios
(PR-I / PR-J, both passing), a price position (PR-L, passing and strongest) and a return
(PR-K, sign transferred but rejected). Every signal found so far has been a PRICE signal.
This factor carries none: both legs are pure traded value, so it is the one test that
separates "only price information survives in this taxonomy" from "the peak/ridge split
itself carries alpha".

DEFINITION FOLLOWS THE REPORT, NOT THE TASK CARD, on the aggregation. §7.2 says "计算 20
日量峰总成交额与量岭总成交额,二者做比" -- pool each leg over 20 valid days, then divide
ONCE (a RATIO OF SUMS). The task card pinned a mean of DAILY ratios. §7.1 (PR-J) does
specify a mean of ratios ("计算 20 日价格比均值"), so the report draws the distinction
itself in adjacent sections and each form is followed as written. The difference is
substantive: a ratio of sums is amount-weighted, and it is far better behaved here, since
one day with a nearly vanishing ridge amount would dominate a mean of daily ratios.

Pre-registered sign = +1, READ FROM THE REPORT before any code: §7.2 states "RankIC 均值
10.28%,RankICIR 4.07". The §1 taxonomy summary agrees independently ("对于量峰时点...成交额
类因子...为正向因子", with the ridge leg's amount factors marked negative-alpha).

The classification is REUSED verbatim from data/clean/intraday_volume_prv.py (zero diff):
PEAK = isolated eruption, RIDGE = eruptive and not isolated, and a VALLEY bar enters
NEITHER leg. Pinned: peak numerator / ridge denominator; amount-only positive-trade guard
(volume is deliberately excluded -- this factor never divides by volume); RAW amounts,
which need no adjustment argument at all since traded value in RMB is not rescaled by any
split/dividend factor; PIT-visible 09:31-14:50 window (disclosed deviation); an asymmetric
bar floor of >=5 peak vs >=10 ridge bars, the REVERSE of PR-J's asymmetry.

Test discipline (the PR-L lesson, applied throughout): every "perturbing X changes
nothing" test ships with a _defect_* context manager that monkeypatches the production
seam into the exact bug it guards, plus a companion test that runs the same assertions
under the defect and asserts they blow up. Three load-bearing steps are named functions
(_tradable_amount / _trailing_ratio_of_sums / _symbol_frames) so each property has a
substitutable seam. All four invariance tests were verified to FAIL under their defect.
… + config

run-eval-peak-ridge-amount-ratio drives the closing factor of the reproduction loop
through the FROZEN StandardFactorEvaluator on the eval cell shared by PR-C .. PR-L
(CSI500 PIT, 2021-07-01 .. 2026-06-30, daily rebalance, OOS split 2024-01-01, book =
value_ep/value_bp/volatility_20, fee 0.001), so this run is directly comparable to all
nine siblings. Cache-only: the minute read has no fetch closure, so stk_mins live calls
are provably zero.

PEAK SCARCITY IS MEASURED, NOT ASSUMED, and the asymmetry runs the opposite way from
PR-J's -- here a peak must erupt AND be isolated. PeakCoverage reports the realized
peak-bar distribution, both denominators for the day-validity rate, the per-gate failure
counts, and the COUNTERFACTUAL valid-day count at a peak floor of 10, so the lowered gate
is a number rather than a claim.

extract_metrics surfaces the same comparison quantities PR-K / PR-L did (turnover, net
long-short by cost scenario, lag-1 rank autocorrelation + half-life, cross-section size,
ic_pearson_mean alongside the rank ic_mean), which matters more for this run than any
other: the closing deliverable of the loop is the full ten-factor table.

The config header records the report-vs-card definitional deviation (ratio of sums, not
mean of daily ratios), the pre-registered sign and where in the report it was read, and
the seven pinned choices. sign=+1, so the frozen layer's aligned-spread cost-sign defect
does not apply to this factor.
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