Files
chinese-equity-quant/docs/reversal_5d_all_universe_pipeline_report.md
T
Yuxuan Yan 2c0ca53bd6 Document cost bps as one-way per-trade, not round-trip
The simulator charges (cost_bps + slippage_bps) on each fill, so a full
round trip is charged twice. Correct the cost-model doc, the reversal_5d
report, and the report generator to state the rate is one-way per-trade
(~20 bps round trip for 5+5), rather than mislabeling it round-trip.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 21:46:41 +08:00

8.8 KiB
Raw Blame History

5-Day Reversal — End-to-End Pipeline Report

Generated: 2026-06-11T17:17:34

This report runs the 5-day reversal signal end to end through the decoupled pipeline (data → alpha → combo → portfolio build → portfolio simulate/eval) on the full downloaded A-share universe, and answers the seven review questions: alpha storage, metric sanity, NaN/look-ahead handling, alpha↔portfolio closeness, alpha↔PnL closeness, per-phase timing, and visualizations.

Per this repo's convention an alpha is a signed cross-sectional position weight, not a return predictor, so evaluation is return / Sharpe / turnover / drawdown — there is deliberately no IC/IR anywhere.

TL;DR

The naive built-in reversal alpha (raw -pct_change(5) then cross-sectional z-score) loses -87.45% in costless research on the full ~5,200-name universe. That is not evidence the signal is bad — it is an artifact of z-score weighting on A-shares: a handful of newly listed / post-suspension / limit-up names produce huge pct_change outliers, and z-scoring pours the book into exactly those names (stored weights reach -52σ).

Switching only the weighting to a bounded cross-sectional rank (reversal_rank) and restricting to a per-date liquid, non-ST, tradable universe recovers a genuine reversal edge: 72.24% costless research cumulative return at Sharpe 0.73 with a 54.31% daily hit rate.

The binding constraint is cost, not signal: at ~148×/year turnover, a 10 bps one-way per-trade cost (5 bps commission + 5 bps slippage, charged on each leg — so ~20 bps per round trip) erases the edge — every variant is negative after costs. A tradable 5-day reversal needs turnover control, not a different signal.

Headline Metrics

run weighting research cum research Sharpe research turn/yr exec before cost exec net exec net Sharpe
naive z-score (full) z-score -87.45% -2.4515 160× 18.39% -111.94% -1.4508
rank (full) rank -3.48% -0.0198 143× 50.52% -66.61% -1.1839
rank (liquid subset) rank 72.24% 0.7310 148× 110.18% -17.16% -0.2226

Research = costless, no-look-ahead weights · next-day return. Execution = next-open fills on the discretized integer book under suspension / price-limit / volume-cap constraints, 5 bps commission + 5 bps slippage.

Research equity

1. Are Alpha Values Properly Stored?

All alpha artifacts conform to ALPHA_COLUMNS (symbol_id, date, alpha_name, weight), carry no null / non-finite weights, no duplicate (symbol_id, date) keys, and have numerically-zero daily cross-sectional means (weights are demeaned per date).

| run | schema ok | null w | non-finite w | dup keys | max |daily mean| | weight range | combo identity Δ | | --- | --- | --- | --- | --- | --- | --- | --- | | naive z-score (full) | True | 0 | 0 | 0 | 3.32e-16 | [-52.2, 19.2] | 0.00e+00 | | rank (full) | True | 0 | 0 | 0 | 0.00e+00 | [-2603.0, 2603.0] | 0.00e+00 | | rank (liquid subset) | True | 0 | 0 | 0 | 0.00e+00 | [-498.5, 498.5] | 0.00e+00 |

The decisive storage signal is the weight range. The naive z-score alpha stores weights as extreme as [-52, 19] — single names tens of sigma from the cross-section. Rank weighting is bounded by construction, so its stored weights are well-behaved. Same signal, completely different book.

Weight distributions

2. Do The Alpha Metrics Make Sense?

Yes, and they tell a coherent story:

  • The z-score full run is dominated by a few outlier names; its research Sharpe of -2.45 reflects a book that is effectively long/short a tiny set of extreme movers, which in A-shares keep trending — so the reversal bet loses.
  • Rank full (-3.48%) is roughly flat: the direction is right (hit rate 51.18%) but the long tail of illiquid / ST / freshly listed names adds noise.
  • Rank liquid is the clean result: a positive, monotone reversal premium (72.24%, Sharpe 0.73) once the investable universe is sane.

This matches the prior literature that short-horizon reversal is a real but liquidity- and cost-sensitive A-share effect.

3. NaN And Look-Ahead Handling

  • The raw signal uses close.pct_change(5, fill_method=None) — missing prices are not forward-filled, so a suspended name does not silently inherit a stale price.
  • Weights are formed at close t and earn the next close-to-close return t → t+1. Forward returns are computed on the full market calendar before selecting signal dates, so a sparse signal grid still earns the next available return rather than the next signal date. The final signal date, which has no forward return, is dropped from metrics (that is why the research day count is one less than the stored signal-date count).
  • The liquid-universe mask is applied to the signal, not to the price history: pct_change(5) is always computed on contiguous prices, and the mask only decides what is held. It uses tradestatus, isST, a ≥60-session seasoning rule, and a trailing-20-day liquidity rank — all backward-looking.

4. How Close Are Alpha And Constructed Portfolio?

portfolio build normalizes the alpha to target_weight = w / Σ|w| and scales by booksize. The continuous target portfolio is an exact normalization of the stored alpha (research return correlation ≈ 1.0); the integer book then diverges because small per-name targets are rounded away under A-share lot rules.

| run | target_value identity max|Δ| | alpha→target max|Δ| | research corr(alpha,portfolio) | mean integer gross | mean L1 tracking | | --- | --- | --- | --- | --- | --- | | naive z-score (full) | 0.0000 | 0.00e+00 | 1.000000 | 9,138,331 | 2,542,655 | | rank (full) | 0.0000 | 0.00e+00 | 1.000000 | 8,984,098 | 2,678,278 | | rank (liquid subset) | 0.0000 | 0.00e+00 | 1.000000 | 9,810,256 | 862,303 |

Portfolio tracking

5. How Close Are Alpha Metrics And Final PnL?

The costless research metric and the simulated net PnL diverge for two mechanical reasons, both quantified below: (a) execution friction — next-open fills, integer shares, and constraints; and (b) cost — the dominant term here.

run corr(alpha, exec net) PnL before cost total cost net PnL mean daily turnover
naive z-score (full) 0.9675 1,838,974 13,032,720 -11,193,746 0.5711
rank (full) 0.9613 5,052,067 11,713,451 -6,661,383 0.5133
rank (liquid subset) 0.9762 11,017,842 12,733,803 -1,715,960 0.5715

The research↔execution-net daily-return correlation stays high (the book does track the signal), but the level collapses after cost. For the liquid run, gross costless edge is real yet total cost (12,733,803) swamps it. This is the central finding: 5-day reversal is a signal you must trade slowly to monetize.

Execution vs research

6. Time Consumption By Phase

phase rank full (s) rank liquid (s)
alpha compute 108.3 116.1
alpha eval 98.0 118.7
combo combine 22.9 22.5
portfolio build 599.8 254.3
portfolio eval 94.2 90.0
portfolio simulate 168.7 163.3
total 1091.8 764.9

Phase timings

portfolio build dominates because it iterates per signal date and repairs a multi-thousand-name integer book under lot rules. The liquid run is faster across the board because it carries far fewer non-zero names per date.

7. Reproduce The Run

# naive z-score baseline (full universe) — the built-in alpha, unchanged
uv run python cli.py alpha compute --data-path data/daily_bars/all \
    --alpha-name reversal_5d_all --alpha-type reversal --lookback 5 --output-dir alphas

# robust rank weighting, full + liquid universe (one script, both runs)
bash scripts/run_reversal_rank_e2e.sh

# regenerate this report + figures
uv run python scripts/generate_reversal_5d_report.py

Interpretation & Next Steps

The pipeline is internally consistent end to end: storage validates, the trivial one-alpha combo is an exact identity, the continuous target portfolio matches the alpha, and the execution layer cleanly explains the gap to net PnL via friction and cost. The premise that 5-day reversal "produces not-bad PnL" holds at the signal level once weighting and universe are sane (rank + liquid), but fails net of cost at daily rebalance frequency.

Recommended next diagnostics:

  • Turnover control — the highest-leverage lever: hold bands / no-trade zones, weight smoothing, or longer rebalance spacing to cut the ~150×/yr turnover.
  • Volatility-scaled or decayed reversal to reduce churn.
  • Sweep the liquidity cutoff and lookback to map the cost/edge frontier.