docs: document post-stop gate reset results

This commit is contained in:
2026-07-17 19:58:40 +02:00
parent 5155d00d9e
commit d858475ddb
3 changed files with 223 additions and 11 deletions
+44 -9
View File
@@ -2,13 +2,13 @@
Investing-signal platform for US equities. It runs one strategy, and it is a boring one: Investing-signal platform for US equities. It runs one strategy, and it is a boring one:
> **A long-only cross-sectional momentum book.** Buy the top quintile by beta-adjusted 12-1 month momentum, tilt toward higher volatility, hold at most 10 names, cut at 1.5× ATR, then trail at 3× ATR for up to 30 trading days. > **A long-only cross-sectional momentum book.** Buy the top quintile by beta-adjusted 12-1 month momentum, tilt toward higher volatility, hold at most 10 names, cut at 1.5× ATR, then trail at 3× ATR for up to 30 trading days. After an initial-stop exit, re-enter only after the gate has failed and subsequently qualified again.
**Philosophy:** don't predict price — rank it. The edge is *relative* strength across the universe, and the discipline is in the exit: cut losers fast, let winners run until the trail catches them. **Philosophy:** don't predict price — rank it. The edge is *relative* strength across the universe, and the discipline is in the exit: cut losers fast, let winners run until the trail catches them.
**What is NOT the edge — read this before trusting a number on screen.** The composite score, the 5 dimensions, sentiment, fundamentals, and Structural S/R are **display context**, not validated predictors. The Gate Target Ladder is screening machinery that preserves the production setup population; it is not a claim about true market structure. In particular: **What is NOT the edge — read this before trusting a number on screen.** The composite score, the 5 dimensions, sentiment, fundamentals, and Structural S/R are **display context**, not validated predictors. The Gate Target Ladder is screening machinery that preserves the production setup population; it is not a claim about true market structure. In particular:
- **The headline "target" is not an exit.** It comes from the internal **Gate Target Ladder** and exists only to compute the R:R and reach-probability used by the activation gate. Human-facing chart S/R is a separate model. The live exit reads neither. Across 320 backtested production trades the exit reasons were **144 initial stop, 98 trailing stop, 78 max hold — and 0 targets.** Honoring the target as a take-profit was tested and *halves CAGR* ([research](docs/research/sr-levels-and-exits.md)). - **The headline "target" is not an exit.** It comes from the internal **Gate Target Ladder** and exists only to compute the R:R and reach-probability used by the activation gate. Human-facing chart S/R is a separate model. The live exit reads neither. Across 472 trades in the current daily gate-reset replay, the exit reasons were **229 initial stop, 147 trailing stop, 96 max hold — and 0 targets.** Honoring the target as a take-profit was tested and *halves CAGR* ([research](docs/research/sr-levels-and-exits.md)).
- **The composite score does not select trades.** Residual momentum does. - **The composite score does not select trades.** Residual momentum does.
Full experiment log — everything tested, kept, and rejected: **[docs/research/](docs/research/README.md)**. Full experiment log — everything tested, kept, and rejected: **[docs/research/](docs/research/README.md)**.
@@ -36,19 +36,29 @@ flowchart TD
BOOK -->|yes| OPEN["OPEN — size at 1% account risk"] BOOK -->|yes| OPEN["OPEN — size at 1% account risk"]
OPEN --> EXIT{"Exit — whichever comes first"} OPEN --> EXIT{"Exit — whichever comes first"}
EXIT --> E1["Initial stop hit<br/>entry 1.5 × ATR → 1R<br/><b>45% of trades</b>"] EXIT --> E1["Initial stop hit<br/>entry 1.5 × ATR → 1R<br/><b>49% of trades</b>"]
EXIT --> E2["Trailing stop hit<br/>highest close 3 × ATR<br/><i>only binds once price is ~1R up</i><br/><b>31% of trades</b>"] EXIT --> E2["Trailing stop hit<br/>highest close 3 × ATR<br/><i>only binds once price is ~1R up</i><br/><b>31% of trades</b>"]
EXIT --> E3["Max hold reached<br/>30 trading days<br/><b>24% of trades</b>"] EXIT --> E3["Max hold reached<br/>30 trading days<br/><b>20% of trades</b>"]
EXIT -.->|"NEVER"| E4["Gate Target Ladder target<br/><b>0% of trades</b>"] EXIT -.->|"NEVER"| E4["Gate Target Ladder target<br/><b>0% of trades</b>"]
E1 --> LOCK["Re-entry locked"]
LOCK --> GF{"Later daily scan<br/>fails the gate?"}
GF -->|no| LOCK
GF -->|yes| GQ{"A subsequent daily scan<br/>qualifies again?"}
GQ -->|no| GQ
GQ -->|yes| RANK
style M fill:#1e3a5f,color:#fff style M fill:#1e3a5f,color:#fff
style OPEN fill:#1e4d2b,color:#fff style OPEN fill:#1e4d2b,color:#fff
style E4 fill:#2a2a2a,color:#888 style E4 fill:#2a2a2a,color:#888
style E1 fill:#4a1f1f,color:#fff style E1 fill:#4a1f1f,color:#fff
style E2 fill:#1e4d2b,color:#fff style E2 fill:#1e4d2b,color:#fff
style LOCK fill:#4a351f,color:#fff
``` ```
**How to read the exit box.** The initial stop is tight (1.5× ATR) and the trail is wide (3× ATR), so the trail sits *below* the initial stop at entry and only takes over once price has advanced roughly 1R. Cut fast when wrong; give room once right. That asymmetry is what produces the right-tailed return profile the strategy depends on — most trades lose a little (win rate ~37.5%), a few win big (best trade +12.9R), and *that is why there is no take-profit*. **How to read the exit box.** The initial stop is tight (1.5× ATR) and the trail is wide (3× ATR), so the trail sits *below* the initial stop at entry and only takes over once price has advanced roughly 1R. Cut fast when wrong; give room once right. That asymmetry is what produces the right-tailed return profile the strategy depends on — most trades lose a little (win rate 36.2%), a few win big (best trade +12.0R), and *that is why there is no take-profit*.
**What happens after an initial stop.** The stop always closes the trade and realizes its costs. The ticker is then locked until a successful daily full-universe scan first observes it outside the production gate and a later scan observes a fresh qualification. A continuously qualified ticker therefore cannot generate an immediate duplicate entry. Other exit reasons do not start this reset. See the [daily post-stop re-entry study](docs/research/post-stop-reentry.md).
## How It Works ## How It Works
@@ -121,7 +131,7 @@ Once a day (default 07:00). Steps run **in dependency order**, each consuming th
1. **OHLCV** — fetch the latest daily bars for every tracked ticker (Alpaca); new tickers backfill ~5 years. 1. **OHLCV** — fetch the latest daily bars for every tracked ticker (Alpaca); new tickers backfill ~5 years.
2. **Sentiment** — fetch sentiment for the names that matter and are stale (> 5 days): top-pick feeders (residual-momentum leaders with a tradeable long setup), the watchlist, and open paper trades, plus a top-N-by-composite discovery net. Runs *before* the scan so the scan sees fresh sentiment. 2. **Sentiment** — fetch sentiment for the names that matter and are stale (> 5 days): top-pick feeders (residual-momentum leaders with a tradeable long setup), the watchlist, and open paper trades, plus a top-N-by-composite discovery net. Runs *before* the scan so the scan sees fresh sentiment.
3. **R:R Scan** — persist clean Structural S/R for charts/alerts, recompute the 5-dimension scores, and build long/short setups from a transient Gate Target Ladder (ATR stops and nominal gate targets) for every ticker. Attach each ticker's residual 121 momentum activation percentile plus the promoted 80/20 production rank. 3. **R:R Scan** — persist clean Structural S/R for charts/alerts, recompute the 5-dimension scores, and build long/short setups from a transient Gate Target Ladder (ATR stops and nominal gate targets) for every ticker. Attach each ticker's residual 121 momentum activation percentile plus the promoted 80/20 production rank. The completed full-universe scan also advances post-stop locks from gate failure to later requalification; failed scans never count as a transition.
4. **Outcome Eval** — resolve setups that hit target/stop or expired (default 30 trading days) and auto-close paper trades per the exit policy (default: 3x ATR trail with a 30-trading-day max hold). 4. **Outcome Eval** — resolve setups that hit target/stop or expired (default 30 trading days) and auto-close paper trades per the exit policy (default: 3x ATR trail with a 30-trading-day max hold).
5. **Market Regime** — recompute the regime index (breadth/trend). 5. **Market Regime** — recompute the regime index (breadth/trend).
6. **Regime Monitor** — separate v2 State/Warning risk thermometer with fixed-basket breadth, VIX, credit, and point-in-time fundamentals; feeds no trades. 6. **Regime Monitor** — separate v2 State/Warning risk thermometer with fixed-basket breadth, VIX, credit, and point-in-time fundamentals; feeds no trades.
@@ -155,6 +165,7 @@ Fundamentals (weekly, early Monday) · Alerts (hourly, Telegram) · Backtest (we
|---|---|---| |---|---|---|
| **Residual 12-1 cross-sectional momentum** (the activation gate, long-only) | **Production gate — in-sample edge** | Promoted July 2026 after the portfolio variant beat raw 80 on CAGR, Sharpe and drawdown. Raw 12-1 remains a fallback only when benchmark data is unavailable | | **Residual 12-1 cross-sectional momentum** (the activation gate, long-only) | **Production gate — in-sample edge** | Promoted July 2026 after the portfolio variant beat raw 80 on CAGR, Sharpe and drawdown. Raw 12-1 remains a fallback only when benchmark data is unavailable |
| **3× ATR trailing exit** (+ 1.5× ATR initial stop, 30-day max hold) | **Production exit — best Sharpe of every exit tested** | Beat hold / SMA50 / 20-day-low / technical-40 and both take-profit variants (July 2026) | | **3× ATR trailing exit** (+ 1.5× ATR initial stop, 30-day max hold) | **Production exit — best Sharpe of every exit tested** | Beat hold / SMA50 / 20-day-low / technical-40 and both take-profit variants (July 2026) |
| **Post-stop gate reset** | **Production re-entry policy** | The initial stop always closes; the ticker must later fail the daily gate and subsequently qualify again. At the production capacity of 10: Sharpe 1.67 → 1.77, CAGR 45.2% → 48.3%, DD 24.3% → 21.6% versus immediate re-entry. [Full study](docs/research/post-stop-reentry.md) |
| **Structural S/R** | **Human-facing context only — not a gate and not an exit** | Clean, capped zones are persisted for charts and alerts. The scanner deliberately does not read them. | | **Structural S/R** | **Human-facing context only — not a gate and not an exit** | Clean, capped zones are persisted for charts and alerts. The scanner deliberately does not read them. |
| **Gate Target Ladder** | **Gate input only — not market structure and not an exit** | Volume-free range grid + pivots preserves the useful legacy screening behavior exactly: 1,086/1,086 qualified setups retained and identical Sharpe 2.03 / CAGR 50.0% / DD 21.4% / 321 trades. The exit never reads its target. [Full write-up](docs/research/sr-levels-and-exits.md#explicit-gate-target-ladder) | | **Gate Target Ladder** | **Gate input only — not market structure and not an exit** | Volume-free range grid + pivots preserves the useful legacy screening behavior exactly: 1,086/1,086 qualified setups retained and identical Sharpe 2.03 / CAGR 50.0% / DD 21.4% / 321 trades. The exit never reads its target. [Full write-up](docs/research/sr-levels-and-exits.md#explicit-gate-target-ladder) |
| Composite score + 5 dimensions | **Display/ranking only** | Sub-scores are hand-built heuristics; none has a measured IC. Note: the "momentum" *dimension* is 5/20-day ROC — NOT the validated 12-1 factor (that lives in `momentum_service`) | | Composite score + 5 dimensions | **Display/ranking only** | Sub-scores are hand-built heuristics; none has a measured IC. Note: the "momentum" *dimension* is 5/20-day ROC — NOT the validated 12-1 factor (that lives in `momentum_service`) |
@@ -167,11 +178,25 @@ Fundamentals (weekly, early Monday) · Alerts (hourly, Telegram) · Backtest (we
Caveats on the momentum result: in-sample, roughly one market regime, costs/slippage approximated at 0.1% per side, and residual momentum still needs SPY benchmark history to compute. The **out-of-sample proof is the forward paper-trade record**: Signals → Track Record compares live qualified expectancy against the backtest. Caveats on the momentum result: in-sample, roughly one market regime, costs/slippage approximated at 0.1% per side, and residual momentum still needs SPY benchmark history to compute. The **out-of-sample proof is the forward paper-trade record**: Signals → Track Record compares live qualified expectancy against the backtest.
### Current production baseline ### Daily post-stop re-entry decision (2026-07-17)
Use this as a regression guardrail for future strategy changes, not as a return promise. Backtest run: local production SQLite snapshot, 506 tickers, weekly cadence, 30-trading-day horizon, 2022-06-28 → 2026-07-02, 0.1% per-side costs, price-only SPY benchmark. Numbers below are the 2026-07-11 run (`reports/backtest-20260711-prod-baseline.json`) — measured *after* the primary-target probability floor shipped, which pruned lottery-target setups (1,428 → 1,089 qualified) and lifted Sharpe on all three promotion contenders. The production policy is **normal gate reset**, evaluated with daily setup opportunities and live-like full-universe ranking. An initial stop always closes. Re-entry unlocks only after a later successful daily scan observes the ticker failing the gate and a subsequent scan observes it qualifying again. The study replayed 1,011,248 point-in-time candidate observations across 505 tickers from 2022-06-24 through 2026-07-02, with the production GTL gate, 80/20 rank, exit, fees, sizing, and 10-position capacity.
| Item | Current baseline | | Re-entry policy | Total return | CAGR | Max DD | Sharpe | Trades |
|---|---:|---:|---:|---:|---:|
| Immediate | 348.4% | 45.2% | -24.3% | 1.67 | 489 |
| **Gate reset (production)** | **388.1%** | **48.3%** | **-21.6%** | **1.77** | **472** |
| Fixed five-session cooldown | 250.8% | 36.6% | -22.2% | 1.47 | 473 |
In the disjoint 2025+ book, gate reset also beat immediate re-entry (Sharpe 1.66 vs 1.55; CAGR 41.8% vs 39.3%) and the fixed five-session rule (Sharpe 1.43; CAGR 32.7%). Its lead over both survived costs of 0.2% and 0.3% per side. The result is capacity-specific: cooldown 5 won at capacity 5, while immediate had slightly higher return and Sharpe at capacity 15. Production uses capacity 10, so that is the portfolio for which this decision is valid.
`gate_reset` and a simple `next_session` block happened to produce the same executed live-universe portfolio in this sample. Their rules are still different: this establishes that same-day re-entry was harmful here, but does not isolate a separate historical return premium from the reset condition. Gate reset was promoted because it represents a genuinely new signal episode and did not sacrifice results in the production book. Full definitions, all nine policy arms, cost/capacity sensitivity, and legacy-rank results are in [docs/research/post-stop-reentry.md](docs/research/post-stop-reentry.md); source report: [`reports/daily_reentry_matrix.json`](reports/daily_reentry_matrix.json).
### Historical weekly production baseline (pre gate-reset)
Use this as the historical ranking/exit regression guardrail, not as a return promise or the current re-entry-policy result. This run predates the post-stop gate reset and uses weekly entry replay, so its portfolio headline is not directly comparable with the daily matrix above. Backtest run: local production SQLite snapshot, 506 tickers, weekly cadence, 30-trading-day horizon, 2022-06-28 → 2026-07-02, 0.1% per-side costs, price-only SPY benchmark. Numbers below are the 2026-07-11 run (`reports/backtest-20260711-prod-baseline.json`) — measured *after* the primary-target probability floor shipped, which pruned lottery-target setups (1,428 → 1,089 qualified) and lifted Sharpe on all three promotion contenders.
| Item | Historical weekly baseline |
|---|---| |---|---|
| Strategy version | `residual_highvol_80_20_atr_trail3_v1` | | Strategy version | `residual_highvol_80_20_atr_trail3_v1` |
| Production gate | Long-only, residual 12-1 momentum percentile >= 80, headline gate-target R:R >= 2.0 (live `activation_min_rr`; the code default is 1.2), primary-target reach-probability >= 20%, NEUTRAL excluded, confidence floor off (0) | | Production gate | Long-only, residual 12-1 momentum percentile >= 80, headline gate-target R:R >= 2.0 (live `activation_min_rr`; the code default is 1.2), primary-target reach-probability >= 20%, NEUTRAL excluded, confidence floor off (0) |
@@ -218,6 +243,7 @@ A systematic single-variable sweep (offline prod snapshot, production gate/rank/
| Momentum lookback: 6-1, 3-1, 12-7 (Novy-Marx), composites | **Keep residual 12-1** | 6-1/3-1 rank-IC ≈ 0; 12-7 IC 0.045 / t 1.58 — weaker than residual 12-1 (0.055 / t 1.98) | | Momentum lookback: 6-1, 3-1, 12-7 (Novy-Marx), composites | **Keep residual 12-1** | 6-1/3-1 rank-IC ≈ 0; 12-7 IC 0.045 / t 1.58 — weaker than residual 12-1 (0.055 / t 1.98) |
| Selection cutoff {70, 75, 85, 90} × book size {10, 15, 20} | **Keep 80 × 10** | Monotonically worse in both directions from 80; the 10-slot cap never binds (<10 concurrent) | | Selection cutoff {70, 75, 85, 90} × book size {10, 15, 20} | **Keep 80 × 10** | Monotonically worse in both directions from 80; the 10-slot cap never binds (<10 concurrent) |
| Position sizing: equal-weight, inverse-vol, risk-% sweep | **Keep 1% fixed-fractional** | See the inverse-vol warning below | | Position sizing: equal-weight, inverse-vol, risk-% sweep | **Keep 1% fixed-fractional** | See the inverse-vol warning below |
| Post-stop re-entry: immediate, fixed 25 sessions, gate resets, confirmation filters | **Keep normal gate reset for the 10-position production book** | Sharpe 1.77 vs 1.67 immediate and 1.47 cooldown 5; rerun before changing portfolio capacity |
| FIP path-smoothness as an in-book tie-breaker/filter | **Reject** (but see the lead below) | Non-monotonic across FIP quintiles within the qualified set; either half of a median split underperforms the full book — thinning the entry stream costs more compounding than the tilt returns | | FIP path-smoothness as an in-book tie-breaker/filter | **Reject** (but see the lead below) | Non-monotonic across FIP quintiles within the qualified set; either half of a median split underperforms the full book — thinning the entry stream costs more compounding than the tilt returns |
Two findings future sessions must not re-litigate: Two findings future sessions must not re-litigate:
@@ -450,6 +476,14 @@ python scripts/run_backtest_snapshot.py backtest_snapshots/prod.sqlite --workers
.venv\Scripts\python.exe scripts\run_backtest_snapshot.py backtest_snapshots\prod.sqlite --workers 6 --allow-spawn .venv\Scripts\python.exe scripts\run_backtest_snapshot.py backtest_snapshots\prod.sqlite --workers 6 --allow-spawn
``` ```
Weekly remains the resource-safe default. Add `--cadence daily` for live-like daily entry opportunities; this performs roughly five times as many setup evaluations. To generate the complete weekly/daily × immediate/gate-reset comparison in one invocation, use:
```bash
python scripts/run_backtest_cadence_comparison.py backtest_snapshots/prod.sqlite --workers 7
```
On Windows, add `--allow-spawn`. The comparison runner writes the two full cadence reports plus one compact four-arm report. For the larger nine-policy daily research matrix used in the post-stop decision, see `scripts/run_daily_reentry_matrix.py` and the [research record](docs/research/post-stop-reentry.md).
On an 8-thread machine, `--workers 6` is a good starting point: it leaves a On an 8-thread machine, `--workers 6` is a good starting point: it leaves a
couple of threads for Windows, the shell, and browser/UI work while still using couple of threads for Windows, the shell, and browser/UI work while still using
most of the CPU. most of the CPU.
@@ -490,6 +524,7 @@ matching decision. Every change still goes through the factor harness first (see
| `gate_ablation` | Net expectancy with each floor removed | Drop a floor only if removing it doesn't hurt net expectancy | | `gate_ablation` | Net expectancy with each floor removed | Drop a floor only if removing it doesn't hurt net expectancy |
| `time_exit_sweep` | Net avg R / net R-per-day by hold length | Whether a fixed time exit beats the promoted ATR trail | | `time_exit_sweep` | Net avg R / net R-per-day by hold length | Whether a fixed time exit beats the promoted ATR trail |
| `portfolio_monitor`, `portfolio_sim`, `strategy_variants` | CAGR, Sharpe, max drawdown, per-year returns | Promote a strategy only if it beats the current baseline on CAGR/Sharpe/DD | | `portfolio_monitor`, `portfolio_sim`, `strategy_variants` | CAGR, Sharpe, max drawdown, per-year returns | Promote a strategy only if it beats the current baseline on CAGR/Sharpe/DD |
| `production_cadence_comparison` | Immediate vs production gate reset at the selected weekly or daily cadence | Isolates the re-entry rule while keeping gate, rank, exit, fees, sizing, and capacity fixed |
| `signal_eval` | Mean IC, t-stat, IC>0 %, `reliable` | Iron rule: wire a new factor in only if \|IC\| ≳ 0.03 with a consistent sign and `reliable: true` | | `signal_eval` | Mean IC, t-stat, IC>0 %, `reliable` | Iron rule: wire a new factor in only if \|IC\| ≳ 0.03 with a consistent sign and `reliable: true` |
| `holdout` (opt-in) | Train vs test books, split by entry date | **The only honest OOS read.** Set `BACKTEST_HOLDOUT_SPLIT=YYYY-MM-DD` | | `holdout` (opt-in) | Train vs test books, split by entry date | **The only honest OOS read.** Set `BACKTEST_HOLDOUT_SPLIT=YYYY-MM-DD` |
| `recommendation`, `research_recommendation` | The report's own headline read | A starting point, not a substitute for the sections above | | `recommendation`, `research_recommendation` | The report's own headline read | A starting point, not a substitute for the sections above |
+10 -2
View File
@@ -8,7 +8,9 @@ was run and the data said no.** Detail lives in the linked docs and in
**The one-line summary of the whole platform:** it is a **long-only **The one-line summary of the whole platform:** it is a **long-only
cross-sectional momentum book** — buy the top quintile by beta-adjusted 12-1 cross-sectional momentum book** — buy the top quintile by beta-adjusted 12-1
momentum, tilt toward higher volatility, hold ≤ 10 names, cut at 1.5× ATR, then momentum, tilt toward higher volatility, hold ≤ 10 names, cut at 1.5× ATR, then
trail at 3× ATR for up to 30 trading days. Everything else in the app (composite trail at 3× ATR for up to 30 trading days. After an initial stop, require the
daily production gate to fail and subsequently qualify again before re-entry.
Everything else in the app (composite
score, Structural S/R, the Gate Target Ladder, sentiment, fundamentals) is score, Structural S/R, the Gate Target Ladder, sentiment, fundamentals) is
**display or screening**, not edge. **display or screening**, not edge.
@@ -22,6 +24,7 @@ score, Structural S/R, the Gate Target Ladder, sentiment, fundamentals) is
| 80/20 residual-momentum / 6m-volatility rank | Ranking tilt | Buys ~2pp CAGR over momentum-only; costs ~6pp drawdown | | 80/20 residual-momentum / 6m-volatility rank | Ranking tilt | Buys ~2pp CAGR over momentum-only; costs ~6pp drawdown |
| 1.5× ATR initial stop | Real exit | Cuts losers fast | | 1.5× ATR initial stop | Real exit | Cuts losers fast |
| 3× ATR trailing stop, 30-day max hold | Real exit | Best Sharpe of every exit tested | | 3× ATR trailing stop, 30-day max hold | Real exit | Best Sharpe of every exit tested |
| Post-stop normal gate reset | Re-entry policy | Stop always closes; a later gate failure and subsequent fresh qualification define the next signal episode. Sharpe 1.67 → 1.77 and CAGR 45.2% → 48.3% at production capacity 10. [Full study](post-stop-reentry.md) |
| Max 10 concurrent positions, 1% risk per trade | Sizing | Cap never binds in practice | | Max 10 concurrent positions, 1% risk per trade | Sizing | Cap never binds in practice |
| Structural S/R | Human-facing product context | Clean, capped zones for charts and alerts; not read by the scanner | | Structural S/R | Human-facing product context | Clean, capped zones for charts and alerts; not read by the scanner |
| Gate Target Ladder | Screening machinery | Volume-free transient proposals preserve the production candidate set exactly; never an exit | | Gate Target Ladder | Screening machinery | Volume-free transient proposals preserve the production candidate set exactly; never an exit |
@@ -63,6 +66,7 @@ invites overfitting.
| Primary-target R:R selector | **Keep 1.5** — target choice is intentionally independent of the later 2.0 activation floor | | Primary-target R:R selector | **Keep 1.5** — target choice is intentionally independent of the later 2.0 activation floor |
| Exit policy (hold / SMA50 / 20-day low / technical-40 / ATR trail) | **Keep 3× ATR trail** — best Sharpe (2.04) | | Exit policy (hold / SMA50 / 20-day low / technical-40 / ATR trail) | **Keep 3× ATR trail** — best Sharpe (2.04) |
| **Activation R:R floor `min_rr`** (swept 2026-07-12) | **Keep 2.0** — best in-sample *and* out-of-sample. But it is a **spike, not a plateau** — see below | | **Activation R:R floor `min_rr`** (swept 2026-07-12) | **Keep 2.0** — best in-sample *and* out-of-sample. But it is a **spike, not a plateau** — see below |
| Post-stop re-entry (nine daily policy arms) | **Keep normal gate reset at production capacity 10** — Sharpe 1.77 vs 1.67 immediate and 1.47 fixed cooldown 5. The result changes with book capacity; see [post-stop-reentry.md](post-stop-reentry.md) |
### The `min_rr` sweep (2026-07-12) ### The `min_rr` sweep (2026-07-12)
@@ -142,6 +146,10 @@ it is internal screening machinery whose broad historical-price-traffic behavior
was preserved explicitly and volume-free, with exact full-period parity. It is was preserved explicitly and volume-free, with exact full-period parity. It is
still neither market structure nor an exit. The one component that *does* have still neither market structure nor an exit. The one component that *does* have
measured predictive edge is the momentum gate, and every knob on it has been measured predictive edge is the momentum gate, and every knob on it has been
swept and confirmed. swept and confirmed. After an initial-stop exit, that same gate now also defines
when a new episode may begin: one later failed observation followed by a fresh
qualification. The [daily re-entry matrix](post-stop-reentry.md) supports this
for the current 10-position book, but not as a universal rule for other
portfolio capacities.
The next real evidence is **forward**, not backward: the live paper-trade record. The next real evidence is **forward**, not backward: the live paper-trade record.
+169
View File
@@ -0,0 +1,169 @@
# Post-stop re-entry: daily policy study and production decision
## Decision
Use a **normal gate reset** after an initial-stop exit:
1. The initial stop always closes the trade. It is never cancelled because the
ticker still passes the gate.
2. Re-entry remains locked until a later full-universe daily scan observes the
ticker **failing** the production activation gate.
3. The lock remains in place until a subsequent daily scan observes a **fresh
qualification**.
4. Only then may the ticker return to the actionable setup list or be opened
through `create_trade`.
Trailing-stop, time, target, and manual exits do not start this state machine.
Scanner errors do not count as a gate failure. The two transitions are persisted
on the latest initial-stop `PaperTrade`, so neither a service call nor a restart
can bypass the rule.
This replaces the previously proposed fixed five-session lockdown. The normal
reset counts an unqualified stop-day close when that close is observed after the
stop. The stricter experiment, which required a failed close on a later session,
was not promoted.
## Experiment design
Source: [`reports/daily_reentry_matrix.json`](../../reports/daily_reentry_matrix.json),
generated 2026-07-17.
| Input | Value |
|---|---|
| Snapshot | Production SQLite snapshot through 2026-07-02 |
| Period used by the all/5y rows | 2022-06-24 to 2026-07-02 |
| Tickers | 505 |
| Point-in-time candidate observations | 1,011,248 (492,850 long; 518,398 short) |
| Live-universe rank observations | 584,393 |
| Qualified candidates under `live_universe` ranking | 5,189 |
| Entry cadence | Daily |
| Selection and ordering | Production GTL gate; residual/high-vol 80/20 rank; long-only after ranking |
| Exit | 1.5× ATR initial stop; 3× ATR trailing stop; 30-session maximum hold |
| Portfolio | 10 positions; 1% risk per trade; $10,000 initial capital |
| Trading cost | 0.1% per side in the primary matrix; 0.10.3% robustness sweep |
| Holdout split | 2025-01-01 |
The expensive daily candidate replay was performed once. Every policy arm then
used the same candidates, prices, costs, position sizing, capacity, and exit
logic. `live_universe` ranks all eligible tickers once per session like the live
scanner. `backtest_legacy` retains the older candidate-only rank approximation as
a sensitivity check.
### Policies tested
| Arm | Rule after an initial stop |
|---|---|
| `immediate` | No memory; a same-day close re-entry is possible |
| `next_session` | Block only the stop session |
| `cooldown_2/3/5` | Re-entry allowed at wait-session N |
| `gate_reset` | Require a failed gate observation, then a later qualification; the stop-day close may establish the failure |
| `strict_gate_reset` | Ignore the stop-day failure; require a later failed close and then requalification |
| `gate_reset_improved` | Gate reset plus a higher new stop and non-weaker production rank |
| `two_session_confirmation` | Require two consecutive qualified post-stop closes |
## Primary result: production-like `live_universe` ranking
The available history is shorter than five years, so the report's `5y` and
`all` rows cover the same period.
| Policy | Total return | CAGR | Max DD | Sharpe | Trades | Win rate | Post-stop re-entries |
|---|---:|---:|---:|---:|---:|---:|---:|
| Immediate | 348.4% | 45.2% | 24.3% | 1.67 | 489 | 35.6% | 155 |
| Next session | 388.1% | 48.3% | 21.6% | 1.77 | 472 | 36.2% | 142 |
| Cooldown 2 | 343.5% | 44.8% | 23.4% | 1.68 | 472 | 35.8% | 146 |
| Cooldown 3 | 293.4% | 40.6% | 22.7% | 1.56 | 474 | 35.9% | 146 |
| Cooldown 5 | 250.8% | 36.6% | 22.2% | 1.47 | 473 | 35.9% | 145 |
| **Gate reset** | **388.1%** | **48.3%** | **21.6%** | **1.77** | **472** | **36.2%** | **142** |
| Strict gate reset | 342.7% | 44.8% | 23.4% | 1.68 | 471 | 35.9% | 144 |
| Gate reset + improved setup | 267.4% | 38.2% | 24.9% | 1.60 | 422 | 36.3% | 69 |
| Two-session confirmation | 296.1% | 40.8% | **17.6%** | 1.65 | 441 | **37.9%** | 96 |
At the production capacity, normal gate reset improved all four portfolio
objectives relative to immediate re-entry: higher total return, CAGR, and
Sharpe, with lower drawdown. The fixed five-session rule reduced churn but gave
up too many profitable re-entry opportunities.
`gate_reset` and `next_session` produced exactly the same executed portfolio in
the `live_universe` runs. Their rules are not equivalent. In this sample, the
portfolio-level candidate path happened to converge to the same trades. This is
evidence that blocking same-day re-entry helped; it does **not** isolate an
independent return premium for the reset condition itself.
## Disjoint 2025+ test window
These are separate books with entries on or after 2025-01-01. They are a useful
temporal sensitivity check, but not forward evidence: the policy was still
selected after the historical data existed.
| Policy | Total return | CAGR | Max DD | Sharpe | Trades |
|---|---:|---:|---:|---:|---:|
| Immediate | 64.1% | 39.3% | 19.6% | 1.55 | 181 |
| Next session | 68.5% | 41.8% | 19.2% | 1.66 | 183 |
| **Gate reset** | **68.5%** | **41.8%** | **19.2%** | **1.66** | **183** |
| Cooldown 5 | 52.7% | 32.7% | 19.8% | 1.43 | 175 |
| Strict gate reset | 52.9% | 32.9% | 21.0% | 1.38 | 181 |
| Two-session confirmation | 35.4% | 22.5% | **18.1%** | 1.02 | 183 |
The gate-reset result did not depend solely on the earlier training period: it
also beat immediate and the fixed five-session rule in the disjoint test book.
## Cost and capacity sensitivity
At the production capacity of 10, gate reset remained ahead of both immediate
and cooldown 5 as costs increased.
| Cost per side | Policy | Total return | CAGR | Max DD | Sharpe |
|---:|---|---:|---:|---:|---:|
| 0.1% | Immediate | 348.4% | 45.2% | 24.3% | 1.67 |
| 0.1% | **Gate reset** | **388.1%** | **48.3%** | **21.6%** | **1.77** |
| 0.1% | Cooldown 5 | 250.8% | 36.6% | 22.2% | 1.47 |
| 0.2% | Immediate | 296.3% | 40.8% | 25.2% | 1.54 |
| 0.2% | **Gate reset** | **333.0%** | **44.0%** | **22.9%** | **1.64** |
| 0.2% | Cooldown 5 | 209.9% | 32.5% | 23.4% | 1.33 |
| 0.3% | Immediate | 249.8% | 36.5% | 26.0% | 1.41 |
| 0.3% | **Gate reset** | **284.0%** | **39.7%** | **24.2%** | **1.51** |
| 0.3% | Cooldown 5 | 164.0% | 27.3% | 24.7% | 1.16 |
The capacity sweep is a real limitation, not a footnote:
| Capacity at 0.1% cost | Immediate Sharpe / CAGR / DD | Gate-reset Sharpe / CAGR / DD | Cooldown-5 Sharpe / CAGR / DD |
|---:|---|---|---|
| 5 | 1.33 / 31.9% / 16.8% | 1.37 / 32.8% / 17.5% | **1.46 / 35.8% / 18.3%** |
| **10 (production)** | 1.67 / 45.2% / 24.3% | **1.77 / 48.3% / 21.6%** | 1.47 / 36.6% / 22.2% |
| 15 | **1.66 / 44.8% / 24.3%** | 1.63 / 43.0% / **21.6%** | 1.33 / 32.8% / 22.2% |
The promotion is therefore specific to the actual 10-position production book.
At capacity 5, cooldown 5 ranked best; at capacity 15, immediate had slightly
higher return and Sharpe while gate reset retained the shallower drawdown. Do
not generalize the chosen rule to a differently sized portfolio without rerunning
the matrix.
## Legacy-rank sensitivity
The older candidate-only ranking approximation also favored normal gate reset
over immediate and cooldown 5, although `next_session` was slightly stronger.
| Policy | Total return | CAGR | Max DD | Sharpe | Trades |
|---|---:|---:|---:|---:|---:|
| Immediate | 357.4% | 45.9% | 17.9% | 1.73 | 480 |
| Next session | **421.5%** | **50.8%** | 18.5% | **1.86** | 466 |
| **Gate reset** | 408.3% | 49.8% | 18.3% | 1.84 | 464 |
| Cooldown 5 | 332.3% | 43.9% | 19.6% | 1.71 | 459 |
| Strict gate reset | 351.3% | 45.5% | 20.4% | 1.72 | 457 |
## Why gate reset was promoted
- It is tied to a new signal episode instead of an arbitrary elapsed time.
- At the production capacity, it beat immediate and five-session cooldown on
return, CAGR, drawdown, and Sharpe.
- The advantage survived costs of 0.2% and 0.3% per side and the disjoint 2025+
test book.
- It avoids cancelling a valid stop: the loss and transaction costs are always
realized before any later trade.
- It avoids the extra filters that weakened strict reset, improved-setup reset,
and two-close confirmation.
The correct interpretation is deliberately modest: **normal gate reset is the
best production rule among the tested policies for the current 10-position
book.** It is not proof that gate reset is a universal source of alpha. Forward
paper-trade monitoring is still the only genuinely new evidence.