# Execution-Fidelity Fix Spec — unify backtest & live construction

> Authored 2026-05-29 (II) AFK. Operationalizes the session's #1 finding
> (RESEARCH.md top banner): the live execution path (`execution/daily.py`) and
> the backtest (`backtest/engine.py`) have **diverged into two different
> strategies**. This spec is the concrete plan to unify them. It is the #1
> priority before any real-money deployment.

## The problem (verified this session)
The two paths share only **signal + selection** (252/5 momentum + smoothness,
top-50 by rank + hysteresis). Everything else diverges:

| component | backtest (validated) | live (daily.py, active) |
|---|---|---|
| Weighting | HRP (keystone) | **equal-weight 1/N** |
| Sizing | half-Kelly (keystone) | **none** |
| Vol control | symmetric vol-target (12%) | **one-sided vol-delever only** |
| Drawdown control | 20% kill-switch (keystone) | **none** |
| Sector cap | 35% (binds ~20% of rebals) | **none** |
| Cadence | monthly (first-of-month) | **weekly (every Tuesday)** |
| Emergency exits | none | rank>200 / zero-vol |

Root cause: the construction/risk overlays are **implemented twice** — inlined in
`engine.py` (backtest) and partially in `portfolio/constructor.py:construct_portfolio`
(used only by the inactive `pipeline.compute_targets`) — and the active live path
(`daily.py:compute_diff`) reimplements a third, stripped version (equal-weight).
Three implementations → guaranteed drift.

## The fix (recommended)
**Extract ONE shared construction function and route BOTH the backtest engine and
the live executor through it.**

```
# portfolio/construct.py (new or extend constructor.py)
def build_target_weights(
    selected: list[str],          # the top-N names (post-selection/hysteresis)
    returns_window: ndarray,      # per-name returns for HRP + Kelly + vol-target
    equity_history: list[float],  # for the kill-switch
    sector_map, config,
) -> dict[str, float]:
    # 1. kill-switch (drawdown) -> {} (cash) if triggered
    # 2. HRP weights on selected (compute_hrp_weights)
    # 3. half-Kelly portfolio scalar (compute_kelly_weights -> scale HRP)
    # 4. position + sector limits
    # 5. symmetric vol-target (vol_target_scalar, max_leverage)
    # return final target weights
```
- **engine.py**: replace its inlined block (lines ~590–680: Kelly scalar, limits,
  vol-target) with a call to `build_target_weights`. Verify the backtest is
  byte-identical after (final equity 3,419,610.237847) — the inlined logic and the
  function must match exactly. This is the safe first step (refactor, no behavior
  change).
- **daily.py**: replace `compute_diff`'s equal-weight target_weights with
  `build_target_weights(selected_names, ...)`. The selection/hysteresis stays;
  only the sizing changes. Add the kill-switch + sector-cap (now inside the shared
  fn). Decide cadence: monthly stock-selection (gate the alpha rebalance to the
  monthly turn) to match validation, keeping the daily vol-delever as an
  additional intra-month safety overlay.
- Lock it with a test: `build_target_weights` output for a fixed input must equal
  the engine's pre-refactor weights AND be what daily.py applies (one source of
  truth, no drift).

## Sequencing / caveats
1. **Refactor engine first (byte-identical), THEN route daily.py through it** — so
   the live change is "adopt the already-validated construction", not new code.
2. **The equal-vs-HRP and weekly-vs-monthly are DELIBERATE tradeoffs** (RESEARCH
   entries): equal-weight outperformed HRP in 2017+ (1.24 vs 0.995) but is
   CPCV-REJECTED/crisis-fragile; weekly is benign-now but whipsaw-prone in crises.
   Unifying to the validated (HRP, monthly) gives up recent return for crisis
   robustness — make the call on purpose.
3. **Re-baseline after**: once live = validated, the backtest numbers finally
   describe the live strategy; quote net walk-forward ~0.80.
4. **Then** the 30-day live paper-trade A/B (the only true OOS test) is meaningful
   — currently it's testing a strategy the backtest never validated.

## Why this is #1
Every CPCV/walk-forward/keystone result in this repo describes the
HRP+Kelly+vol-target+kill-switch+monthly strategy. The live trades a bare
equal-weight top-N book with NO crisis controls. Recent paper-trading success is
not evidence of safety — the crisis protections are all absent. Unify before real
money.
