From d5b66f79dc373aacdc3a5630af2e5e5f435c1b56 Mon Sep 17 00:00:00 2001 From: fredmaloggia Date: Mon, 1 Dec 2025 07:52:38 +0100 Subject: [PATCH] fixato problema interno di CVXPY/ARPACK durante ccertificazione matrice PSD --- Ottimizzatore ITA.py | 59 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/Ottimizzatore ITA.py b/Ottimizzatore ITA.py index bce4893..795a82e 100644 --- a/Ottimizzatore ITA.py +++ b/Ottimizzatore ITA.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Ottimizzatore Lite +Ottimizzatore ITA """ # ========================= @@ -22,6 +22,26 @@ from pypfopt import risk_models from pypfopt.efficient_frontier import EfficientFrontier from pypfopt.exceptions import OptimizationError +# --------------------------------------------------- +# Patch PyPortfolioOpt: usa cp.psd_wrap sulla covarianza +# --------------------------------------------------- +import cvxpy as cp +from pypfopt import objective_functions as _obj + +def portfolio_variance_psdwrap(w, cov_matrix): + """ + Versione patchata di portfolio_variance: + usa cp.psd_wrap(cov_matrix) per evitare che CVXPY + faccia il controllo spettrale con ARPACK. + Comportamento identico all'originale, ma più robusto. + """ + variance = cp.quad_form(w, cp.psd_wrap(cov_matrix)) + return _obj._objective_value(w, variance) + +# Monkey patch globale: da qui in poi EfficientFrontier usa questa versione +_obj.portfolio_variance = portfolio_variance_psdwrap + + # Cartelle di input/output/plot OUTPUT_DIR = "Output" INPUT_DIR = "Input" @@ -256,6 +276,37 @@ def h_min_100(returns: pd.Series, month_len: int = 21): return np.nan, np.nan +# --------------------------------- +# Utility per rendere PSD/robusta la covarianza +# --------------------------------- +def regularize_covariance(cov_df: pd.DataFrame, ridge_factor: float = 1e-6) -> pd.DataFrame: + """ + Rende la matrice di covarianza numericamente piu' robusta: + - la simmetrizza + - aggiunge un piccolo termine di ridge sulla diagonale + Ritorna un nuovo DataFrame con stessa index/columns. + """ + if cov_df.empty: + return cov_df + + Sigma = cov_df.values.astype(float) + + # simmetrizza (elimina piccole asimmetrie numeriche) + Sigma = 0.5 * (Sigma + Sigma.T) + + # piccolo ridge proporzionato alla scala media delle varianze + n = Sigma.shape[0] + trace = np.trace(Sigma) + if np.isfinite(trace) and n > 0: + eps = ridge_factor * (trace / n) + else: + eps = ridge_factor + + Sigma_reg = Sigma + eps * np.eye(n) + + return pd.DataFrame(Sigma_reg, index=cov_df.index, columns=cov_df.columns) + + # --- Lettura parametri dal file connection.txt --- params = {} with open("connection.txt", "r") as f: @@ -359,6 +410,9 @@ for (years, target_vol), name in volatility_targets.items(): daily_returns_mean = period_df.mean() annual_returns_mean = daily_returns_mean * days_per_year annual_covariance_matrix = risk_models.sample_cov(period_df, returns_data=True) + # --- Regularizzazione covarianza per l'ottimizzatore --- + annual_covariance_matrix = regularize_covariance(annual_covariance_matrix) + # ---------- PER-ASSET METRICS ---------- n_days = int(period_df.shape[0]) @@ -519,6 +573,9 @@ for (years, target_vol), name in volatility_targets.items(): annual_returns_mean = daily_returns_mean * days_per_year annual_covariance_matrix = risk_models.sample_cov(period_df, returns_data=True) + # --- Regularizzazione covarianza per l'ottimizzatore --- + annual_covariance_matrix = regularize_covariance(annual_covariance_matrix) + w_series = optimized_weights[name].reindex(period_df.columns).fillna(0.0) w_vec = w_series.values