diff --git a/Ottimizzatore ITA.py b/Ottimizzatore ITA.py index 795a82e..d34e7b5 100644 --- a/Ottimizzatore ITA.py +++ b/Ottimizzatore ITA.py @@ -125,6 +125,26 @@ def validate_returns_frame(df_returns: pd.DataFrame, threshold: float = 0.2): cols = ", ".join([f"{c} ({v:.0%})" for c, v in high_na.items()]) print(f"[warn] Colonne con >{threshold:.0%} di NaN prima del fill: {cols}") +# --------------------------------- +# Utility per mappare Asset Class -> NEO +# --------------------------------- +def map_neo_asset_class(asset_class: str) -> str: + """Mappa l'Asset Class in un valore compatibile NEO.""" + if not asset_class: + return "" + value = asset_class.strip().lower() + if "azion" in value: + return "Equity" + if "obblig" in value: + return "Fixed Income" + if "immobil" in value: + return "Real Estate" + if "metalli" in value or "materie" in value: + return "Commodities" + if "cript" in value or "crypto" in value: + return "Crypto" + return asset_class + # --------------------------------- # Utility per R^2 sull’equity line # --------------------------------- @@ -346,6 +366,8 @@ except SQLAlchemyError as e: # ========================= template_path = os.path.join(INPUT_DIR, 'Template_Guardian.xls') template_df = pd.read_excel(template_path) +template_neo_path = os.path.join(INPUT_DIR, 'Template NEO.csv') +template_neo_df = pd.read_csv(template_neo_path, sep=';', dtype=str).fillna("") file_path = os.path.join(INPUT_DIR, 'Universo per ottimizzatore.xlsx') df = pd.read_excel( @@ -402,6 +424,8 @@ riskfree_rate = 0.02 optimized_weights = pd.DataFrame() per_asset_metrics = {} export_rows = [] +export_rows_neo = [] +neo_portfolio_name = "VAR6_5Y" for (years, target_vol), name in volatility_targets.items(): period_start_date = end_date - pd.DateOffset(years=years) @@ -537,6 +561,33 @@ for (years, target_vol), name in volatility_targets.items(): output_df = results_full_df.reindex(columns=template_cols) export_rows.append(output_df) + # --- File CSV NEO (uno per portafoglio) --- + if name == neo_portfolio_name: + template_neo_cols = list(template_neo_df.columns) + if not template_neo_df.empty: + template_neo_defaults = template_neo_df.iloc[0].to_dict() + else: + template_neo_defaults = {col: "" for col in template_neo_cols} + neo_rows = [] + for isin, weight in weights.items(): + if weight > 0: + row = dict(template_neo_defaults) + row['Asset class'] = "Equity" + row['InstrumentType'] = row.get('InstrumentType') or "ISIN" + row['Instrument'] = isin + row['Currency'] = row.get('Currency') or "EUR" + row['Side'] = row.get('Side') or "Buy" + row['Quantity'] = round(float(weight * 0.95), 4) + row['Price'] = "" + neo_rows.append(row) + + neo_full_df = pd.DataFrame(neo_rows, columns=template_neo_cols) + if neo_full_df.empty: + output_neo_df = pd.DataFrame(columns=template_neo_cols) + else: + output_neo_df = neo_full_df.reindex(columns=template_neo_cols) + export_rows_neo.append(output_neo_df) + # --- Pie chart asset allocation: salva in Output senza mostrare --- asset_allocations = {asset: 0 for asset in asset_class_limits} for isin, weight in weights.items(): @@ -689,3 +740,14 @@ with pd.ExcelWriter(combined_path, engine='openpyxl', mode='w') as writer: combined_df = template_df.copy() combined_df.to_excel(writer, sheet_name='Pesi Ottimizzati', index=False) print(f"Pesi ottimizzati salvati in un unico file/sheet: '{combined_path}'.") + +# ========================= +# EXPORT CSV NEO +# ========================= +neo_combined_path = os.path.join(OUTPUT_DIR, f"{date_tag} Pesi ottimizzati NEO.csv") +if export_rows_neo: + neo_combined_df = pd.concat(export_rows_neo, ignore_index=True) +else: + neo_combined_df = pd.DataFrame(columns=template_neo_df.columns) +neo_combined_df.to_csv(neo_combined_path, sep=';', index=False) +print(f"Pesi ottimizzati NEO salvati in un unico CSV: '{neo_combined_path}'.")