Files
signal-platform/docs/research/execution-recovery.md
T
dennisthiessen 5a61b164f6 docs: lock execution-recovery decisions and ops constraints
Record monotone fill-timing gradient, live [1.57,1.77] bracket, recover-flag
interpretation, gap-cap as third tail-trim, and pre-scheduler ops checklist.
2026-07-18 17:07:22 +02:00

147 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Execution recovery (2026-07-18) — when you fill is the edge you leave on the table
Report: `reports/execution_recovery_matrix.json` / `.md`
Follows Phase A A4 ([phase-a-matrix.md](phase-a-matrix.md)).
Arms: `close_control` · `next_open` · `stale_close` · `next_open_gap2`.
Mechanics verified before sign-off: `stale_close` re-anchors stop to
fill 1.5×ATR(signal day); no same-day stop after a close fill; each t1
candidate keeps its own gate/rank cross-section (no lookahead). Numbers trusted.
---
## Results
| Arm | Full Sharpe | Full CAGR | Full DD | Train Sharpe | Val Sharpe |
|---|---:|---:|---:|---:|---:|
| **close_control** | **1.77** | **48.3%** | **21.6%** | 1.75 | 1.68 |
| **stale_close** | **1.57** | **40.6%** | **21.8%** | 1.36 | **1.74** |
| next_open | 1.20 | 30.0% | 28.2% | 0.94 | 1.44 |
| next_open_gap2 | 1.06 | 25.2% | 27.8% | 0.72 | 1.41 |
### Monotonicity (the strongest evidence)
In every window, Sharpe recovers as the fill moves **toward** the signal:
| Window | next_open → stale_close → close |
|---|---|
| Train | 0.94 → 1.36 → 1.75 |
| Validation | 1.44 → 1.74 → 1.68* |
| Full | 1.20 → 1.57 → 1.77 |
\*Val close 1.68 is within SE of stale 1.74 — not a break of the story.
A dead edge does **not** produce a monotone gradient in fill timing. A live edge
that is progressively surrendered to execution delay does. Combined with full-period
**DD recovery** (28.2% → 21.8% ≈ closes 21.6%), this confirms the diagnosis:
**when you fill**, not decaying alpha.
### Auto-flag `recover: False` is not a null
Pre-registered recovery applied the “≥ close 0.5 SE” bar to **`stale_close`**,
the **lower-bound** arm (one full session of lag). Flags:
| Flag | Result |
|---|---|
| near_close_control (val) | **True** (Δ +0.06) |
| beats_next_open | **True** (val Δ +0.30) |
| train_ok | **False** (1.36 vs need ≥ ~1.39 — miss by ~0.03) |
The floor missed “≥ close 0.5 SE” by 0.03 while the live design is expected to
sit **above** the floor. The flag worked correctly on the wrong object.
**Log sentence:** *Partial recovery proven; full recovery needs same-day fill.*
### Live outcome bracket
| Bound | Arm | Meaning |
|---|---|---|
| Floor | stale_close ~1.57 full | One-session-stale close fill (conservative) |
| Ceiling | close_control ~1.77 full | Same-day close; optimistic only by final ~15 min of signal info |
Real near-close execution (scan ~15:3015:40 ET on a ~99% complete bar, MOC by
15:50/15:55) is signal-at-partial-bar filled at the same close the control uses.
**Live truth is bracketed [1.57, 1.77]** with residual uncertainty of ~15 minutes
of staleness, not 24 hours — expect near the **top** of the bracket.
`stale_close` alone already justifies the schedule change. **No further fill-timing
simulation on this snapshot** — the bracket is the result. You cannot simulate
15:45 partial bars from daily data, and you do not need to.
### Gap-cap — dead (third tail-trim instance)
`next_open_gap2` worse than plain next_open on every window (full Sharpe 1.06 vs
1.20). **262** full-period gap-ups skipped — they were continuations.
This is the **third independent instance** of the same lesson:
1. Take-profit exits (gate target as TP)
2. FIP as an in-book filter
3. **Gap-up entry caps**
Any rule that trims the right tail improves cosmetic quality metrics and destroys
P&L. **Standing method rule:** momentum filters must be presumed guilty of
tail-trimming until shown otherwise.
---
## Decisions locked
1. **Biggest lever is execution scheduling**, not a strategy rewrite.
2. **Until near-close execution is live:** grade strategy promotion under
`fill_mode=next_open`; keep close-fill as historical control.
3. **After near-close ships:** grade under a close-like / near-close fill mode
(actual live path).
4. **No more sim arms** on fill timing for this snapshot.
5. **Gap-cap:** do not ship.
6. **Strategy work** (nasdaq_all, fip_id, sector) waits until the execution path
is decided — those experiments must be graded under the fill mode you will trade.
---
## Ops design — settle before touching the scheduler
### 1. One canonical qualifying scan per day (non-negotiable)
Post-stop gate-reset was validated on **one full-universe observation per day**.
Adding a near-close scan *alongside* the 07:00 scan would let fail→qualify
transitions complete twice as fast and **silently change** the validated re-entry
policy.
**Move** the R:R scan to near-close. **Leave** sentiment / fundamentals /
OHLCV-backfill at 07:00 (or existing early slots). Preserve **scan before
Outcome Eval** (scan late session, eval after close) so the documented
strict-gate-reset live analogue is unchanged.
### 2. Cron in `America/New_York`, not `Europe/Berlin`
DST offsets shift on different dates. A Berlin-fixed wall time drifts ~1 hour
off the US close for a week or two twice a year.
### 3. Fill mechanics and feed honesty
- NYSE MOC cutoff **15:50 ET**; Nasdaq **15:55 ET**.
- Scan ~**15:3015:40 ET** on a latest-price snapshot; place entries by 15:50.
- For now: paper-trade entries marked at the **actual close**.
- Document Alpaca entitlement: if only 15-minute-delayed SIP, a 15:35 scan sees
~15:20 prices — immaterial for a 12-month signal, but write it down so nobody
treats it as a bug.
### 4. Partial-bar plumbing
- Scan synthesizes day-ts in-progress bar from the snapshot.
- Nightly OHLCV job overwrites with the final bar via existing idempotent upsert.
- Forward paper record marks entries at the actual near-close fill so the live
track measures the new execution honestly.
---
## Implementation status
| Item | Status |
|---|---|
| Research evidence | **Closed** — this doc + matrix report |
| Scheduler move (R:R scan → NY near-close) | **Not started** — blocked on ops design above |
| Partial-bar scan path | **Not started** |
| Paper fill-at-close marking | **Not started** (may already mark at close; verify when shipping) |