feat(factors): reproduce peak/ridge traded-amount ratio (PR-M, closing factor — highest IC of the set) - #73
Merged
Conversation
… §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.
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
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
git diff main -- analytics/eval/ data/clean/intraday_volume_prv.pyEMPTY (both known frozen defects left untouched)