Restore reversal tutorial wording

This commit is contained in:
Yuxuan Yan
2026-06-12 22:58:22 +08:00
parent 3c58a1372e
commit 17fa75495d
2 changed files with 112 additions and 40 deletions
@@ -1,7 +1,5 @@
# Tutorial: Testing a 5-Day Reversal Alpha
Generated: 2026-06-12T18:30:56
This document is a teaching walkthrough for someone who is new to this research
framework and only lightly familiar with quant research. We will use one
concrete experiment, a 5-day reversal alpha on the full downloaded Chinese
@@ -9,6 +7,7 @@ A-share universe, to learn how the framework defines an alpha, stores it, tests
it, turns it into a portfolio, and explains the gap between a research result
and simulated trading PnL.
This generated version was refreshed at 2026-06-12T22:52:56.
The important point is not the timestamp; it is the research method.
## The Research Question
@@ -131,6 +130,22 @@ evaluator tests the weight formed on date `t` over the tradable interval from
`open[t+1]` to `open[t+2]`; the later execution simulator is the separate layer
that trades the constructed integer book at the next open.
The code lives in `pipeline/alpha/library/reversal.py`:
```python
class ReversalAlpha(BaseAlpha):
name = "reversal"
def __init__(self, lookback: int = 5):
self.lookback = lookback
def signal(self, close: pd.DataFrame) -> pd.DataFrame:
return -close.pct_change(self.lookback, fill_method=None)
```
The alpha class only defines the raw signal. The base class then turns that
signal into weights.
## Step 2: Turn A Signal Into Cross-Sectional Weights
By default, `BaseAlpha.to_weights()` does a cross-sectional z-score each date:
@@ -219,6 +234,13 @@ That last phrase, **before trading costs**, is essential.
![Research equity](assets/reversal_5d_research_equity.png)
When reading this chart, focus on the shape and relative behavior:
- The naive z-score line shows why outlier-sensitive weighting is fragile.
- The rank full-universe line shows that robust weighting helps, but the full
universe still contains noisy and hard-to-trade names.
- The liquid rank line shows the signal-level edge before execution costs.
## Step 5: Check That The Alpha File Is Sane
Before trusting any metric, inspect the stored alpha artifact. The run checked:
@@ -244,6 +266,10 @@ raw size of an abnormal stock move.
![Weight distributions](assets/reversal_5d_weight_distributions.png)
This is a good habit: when a backtest looks strange, plot the weights before
debugging the PnL. A broken or concentrated weight distribution often explains
the result.
## Step 6: Understand The Alpha Evaluation Formula
The costless alpha evaluator now asks:
@@ -305,8 +331,18 @@ The continuous target portfolio matched the stored alpha almost exactly:
| 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 |
The integer book is not exact because small target positions can be rounded
away. The liquid subset has lower tracking error because it spreads the book
over fewer and more tradable names.
![Portfolio tracking](assets/reversal_5d_portfolio_tracking.png)
When you research a new alpha, ask two separate questions:
- Does the continuous target portfolio match the alpha? It should.
- Does the integer tradable portfolio still resemble the target? It may not,
especially for small books or very broad universes.
## Step 8: Simulate Execution And Costs
Research returns are not the same as tradable PnL. The simulator executes the
@@ -335,9 +371,9 @@ The execution results explain the final research conclusion:
| rank (liquid subset) | 0.8884 | 11,017,842 | 12,733,803 | -1,715,960 | 0.5715 |
For the liquid rank run, simulated PnL before cost is about
11,017,842, but total
cost is about 12,733,803. That is why
the final net PnL is weak or negative.
11,017,842, but total cost is about
12,733,803. That is why the final net PnL is
weak or negative.
This is not a contradiction. It is exactly what a research pipeline should show:
@@ -378,25 +414,7 @@ researcher should be more precise:
> The raw 5-day reversal idea has signal value in a liquid universe, but the
> current daily trading rule has too much turnover for the assumed cost model.
## Step 10: Time Consumption By Phase
| phase | rank full (s) | rank liquid (s) |
| --- | --- | --- |
| alpha compute | 94.1 | 107.8 |
| alpha eval | 93.3 | 96.9 |
| combo combine | 21.6 | 21.7 |
| portfolio build | 537.6 | 236.7 |
| portfolio eval | 95.1 | 88.3 |
| portfolio simulate | 139.6 | 139.1 |
| total | 981.3 | 690.5 |
![Phase timings](assets/reversal_5d_phase_timings.png)
`portfolio build` usually dominates because it iterates per signal date and
repairs a multi-thousand-name integer book under lot rules. The liquid run is
faster because it carries fewer non-zero names per date.
## Step 11: Reproduce The Experiment
## Step 10: Reproduce The Experiment
These commands reproduce the important artifacts, assuming the full daily-bar
dataset already exists at `data/daily_bars/all`.
@@ -471,3 +489,21 @@ The natural next experiments are:
The most important habit is to keep the layers separate. A good alpha research
workflow does not stop at a single performance number; it explains how the idea
travels from hypothesis, to signal, to weights, to portfolio, to executable PnL.
## Appendix: Phase Timings From This Rerun
| phase | rank full (s) | rank liquid (s) |
| --- | --- | --- |
| alpha compute | 94.1 | 107.8 |
| alpha eval | 93.3 | 96.9 |
| combo combine | 21.6 | 21.7 |
| portfolio build | 537.6 | 236.7 |
| portfolio eval | 95.1 | 88.3 |
| portfolio simulate | 139.6 | 139.1 |
| total | 981.3 | 690.5 |
![Phase timings](assets/reversal_5d_phase_timings.png)
`portfolio build` usually dominates because it iterates per signal date and
repairs a multi-thousand-name integer book under lot rules. The liquid run is
faster because it carries fewer non-zero names per date.