# 2026-06-13 — Equity gonfiata dal ribilancio: +4.77 fantasma (causa + fix)

## Sintomo

Report "dall'ultimo reset": equity 2020.53 → 2025.80 (+5.27), ma un solo trade
chiuso (MR02_BTC 15m, reale +0.50). Il +4.77 residuo è comparso di colpo a
00:01:52 (primo poll dopo mezzanotte UTC = ribilancio giornaliero), senza alcuna
chiusura dietro.

## Causa — doppio conteggio del capitale degli in-position al ribilancio

`rebalance_allocations` faceva:
1. `total_capital = Σ capital di tutti i worker`
2. `alloc[sid] = peso × total_capital` per OGNI sid
3. ai worker in posizione NON applicava l'alloc (la posizione tiene il suo notional)
4. ai flat: `capital = alloc[sid]`

`update_equity` (ogni poll) = Σ capital. Dopo il ribilancio la somma diventa:

    Σcapital = Σ(peso_i × total) [flat] + Σ(capital_j) [in-pos]
             = total − Σalloc_j + Σcapital_j
             = total + Σ(capital_j − alloc_j)   ← inflazione

Cioè i flat si dividevano l'INTERO total (che includeva anche il capitale degli
in-position), e gli in-position lo tenevano in più → doppio conteggio.

Al ribilancio delle 00:01, **MR02_BTC 15m era in posizione** col capitale seedato
**181.19** (eredità del gemello 1h, INIT_LINEAGE di ieri) mentre la sua allocazione
era ~176.42 → **+4.77** di equity dal nulla. Lo scatenante è stato il seed (capitale
ben sopra l'allocazione media) combinato con la posizione aperta al ribilancio; ma
il bug era latente da sempre (ogni worker in-pos che devia dall'alloc gonfiava/sgonfiava
l'equity al ribilancio successivo).

## Fix — conservazione (ledger.allocate `reserved`)

`allocate(weights, reserved={sid: capitale})`: i worker in posizione TRATTENGONO il
loro capitale (deployato, non spostabile); i flat si dividono `total − Σreserved`
per peso **rinormalizzato sui soli flat**. Così `Σalloc == total_capital` sempre →
**l'equity è conservata dal ribilancio** (un ribilancio sposta capitale fra sleeve,
non crea valore). `runner.rebalance_allocations` calcola `reserved` dai worker
`in_position` e lo passa. Senza `reserved` (default) = comportamento storico
(corretto solo a worker tutti flat, es. allocazione iniziale).
Test: `tests/portfolio/test_ledger.py::test_alloc_conserves_total_with_reserved`.

## Correzione dello stato (rimozione del +4.77 già accumulato)

Equity riportata a **2021.03** = baseline post-seed verificata (2020.53) + unico
trade reale (MR02_BTC 15m +0.50). Capitali dei worker del pool scalati ×
(2021.03/2025.79) e peak del ledger resettato a 2021.03 (il 2025.80 non è mai
stato un massimo legittimo → niente DD fantasma). L'orfano MR02_BTC 1h chiuso a
parte (+0.85) resta sul worker ritirato, fuori dal pool.

## Esito

Equity = soldi veri: dal reset il trading ha prodotto **+0.50** (1 trade), non +5.27.
Deploy con codice fixato (rebuild) così il prossimo ribilancio conserva.
