revisione generale per fix

This commit is contained in:
fredmaloggia
2026-05-24 20:46:43 +02:00
parent ebb8114879
commit c2f21fcb65
4 changed files with 4326 additions and 2217 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -651,9 +651,18 @@ def compute_current_capital_from_log(
def size_equal_weight(candidates: List[str]) -> Dict[str, float]:
"""
Equal-Weight sizing.
FIX v2.1.1: ogni candidato riceve peso 1/MAX_OPEN, NON 1/len(candidates).
Cosi' il peso massimo per asset e' sempre <= 1/MAX_OPEN, indipendentemente
dal numero di candidati effettivamente selezionati. Se il target ha meno
di MAX_OPEN candidati, la quota non investita resta implicitamente in cash
(il sistema non apre nuove posizioni per "riempire" l'esposizione).
"""
if not candidates:
return {}
w = 1.0 / max(1, len(candidates))
w = 1.0 / max(1, MAX_OPEN)
return {isin: w for isin in candidates}
@@ -663,7 +672,13 @@ def size_risk_parity(
asof_idx: int,
rp_max_weight: float,
) -> Dict[str, float]:
"""Risk Parity sui soli candidati con cap per singolo asset."""
"""
Risk Parity sui soli candidati con cap per singolo asset.
FIX v2.1.1: la normalizzazione finale rispetta il cap. Target di esposizione
totale = len(candidates)/MAX_OPEN (cosi' con MAX_OPEN candidati l'esposizione
e' ~100%, con meno candidati e' frazionaria e il resto va in cash).
"""
if not candidates:
return {}
L = RISK_PARITY_LOOKBACK
@@ -674,8 +689,14 @@ def size_risk_parity(
inv_vol = inv_vol.replace([np.inf, -np.inf], np.nan).fillna(0.0)
if inv_vol.sum() == 0:
return size_equal_weight(candidates)
w = (inv_vol / inv_vol.sum()).to_dict()
# Pesi normalizzati a sum=target_total invece di sum=1
target_total = len(candidates) / max(1, MAX_OPEN)
w_raw = (inv_vol / inv_vol.sum()) * target_total
w = w_raw.to_dict()
if rp_max_weight is not None:
# Cap per singolo asset (NON si rinormalizza: il deficit resta in cash)
w = {k: min(rp_max_weight, float(v)) for k, v in w.items()}
return w