Guest User

Untitled

a guest
Jan 4th, 2026
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.45 KB | None | 0 0
  1. import numpy as np
  2. import pandas as pd
  3. import matplotlib.pyplot as plt
  4.  
  5. def run_simulation_final(n_sim=10000):
  6. # --- 1. PARAMETRI DI CONFIGURAZIONE ---
  7. ANNI = 10
  8. RAL = 35000
  9.  
  10. # Percentuali Contributive
  11. PERC_TFR = 0.0691
  12. PERC_DIP = 0.012
  13. PERC_DAT = 0.022
  14.  
  15. # Fiscalità
  16. TAX_TFR_REND = 0.17
  17. TAX_FONDO_REND = 0.20
  18. TAX_ETF_GAIN = 0.26
  19. TAX_BOND_GAIN = 0.125
  20. TAX_FONDO_EXIT = 0.23
  21. ALIQUOTA_MEDIA_TFR = 0.25
  22. ALIQUOTA_MARGINALE = 0.35
  23. BOLLO = 0.002
  24.  
  25. # Costi Strumenti (TER / ISC)
  26. COSTO_COMETA = 0.001 # ~0.10% (Negoziale)
  27. COSTO_FPA = 0.012 # ~1.20% (Fondo Aperto Azionario)
  28.  
  29. # Scenari di Mercato (Media, Deviazione Standard)
  30. INFLAZIONE_PARAMS = (0.02, 0.015)
  31. COMETA_PARAMS = (0.040, 0.06) # Profilo Bilanciato
  32. AZIONARIO_PARAMS = (0.080, 0.14) # Azionario Globale
  33. BOND_PARAMS = (0.030, 0.04) # Titoli di Stato Euro
  34.  
  35. print(f"Esecuzione di {n_sim} simulazioni Monte Carlo... (Orizzonte {ANNI} anni)")
  36.  
  37. # --- 2. MOTORE MONTE CARLO ---
  38. inflazione = np.random.normal(*INFLAZIONE_PARAMS, (n_sim, ANNI))
  39. r_cometa = np.random.normal(*COMETA_PARAMS, (n_sim, ANNI))
  40. r_azionario = np.random.normal(*AZIONARIO_PARAMS, (n_sim, ANNI))
  41. r_bond = np.random.normal(*BOND_PARAMS, (n_sim, ANNI))
  42.  
  43. # Inizializzazione vettori
  44. cometa_m = np.zeros(n_sim)
  45. cometa_vers = np.zeros(n_sim)
  46. fpa_m = np.zeros(n_sim)
  47. fpa_vers = np.zeros(n_sim)
  48. tfr_m = np.zeros(n_sim)
  49. tfr_cap = np.zeros(n_sim)
  50. etf_m = np.zeros(n_sim)
  51. etf_inv = np.zeros(n_sim)
  52. bond_m = np.zeros(n_sim)
  53. bond_inv = np.zeros(n_sim)
  54.  
  55. # Ciclo annuale
  56. for t in range(ANNI):
  57. q_tfr = RAL * PERC_TFR
  58. q_dip_lorda = RAL * PERC_DIP
  59. q_dat = RAL * PERC_DAT
  60.  
  61. # A. COMETA
  62. ing_cometa = q_tfr + q_dip_lorda + q_dat
  63. cometa_vers += ing_cometa
  64. gain_cometa = cometa_m * r_cometa[:, t]
  65. tax_cometa = np.maximum(0, gain_cometa * TAX_FONDO_REND)
  66. cometa_m = cometa_m + gain_cometa - tax_cometa - (cometa_m * COSTO_COMETA) + ing_cometa
  67.  
  68. # B. FPA AZIONARIO
  69. ing_fpa = q_tfr + q_dip_lorda
  70. fpa_vers += ing_fpa
  71. gain_fpa = fpa_m * r_azionario[:, t]
  72. tax_fpa = np.maximum(0, gain_fpa * TAX_FONDO_REND)
  73. fpa_m = fpa_m + gain_fpa - tax_fpa - (fpa_m * COSTO_FPA) + ing_fpa
  74.  
  75. # C. TFR AZIENDA
  76. coeff_riv = 0.015 + (0.75 * np.maximum(inflazione[:, t], 0))
  77. riv_netta = (tfr_m * coeff_riv) * (1 - TAX_TFR_REND)
  78. tfr_m += riv_netta + q_tfr
  79. tfr_cap += q_tfr
  80.  
  81. # D. INVESTIMENTI PRIVATI
  82. netto_inv = q_dip_lorda * (1 - ALIQUOTA_MARGINALE)
  83. etf_inv += netto_inv
  84. etf_m = etf_m * (1 + r_azionario[:, t]) * (1 - BOLLO) + netto_inv
  85. bond_inv += netto_inv
  86. bond_m = bond_m * (1 + r_bond[:, t]) * (1 - BOLLO) + netto_inv
  87.  
  88. # --- 3. CALCOLO VALORI NETTI DI LIQUIDAZIONE ---
  89. def get_netto_fondo(montante, versato):
  90. perc_rend = np.maximum(0, (montante - versato) / montante)
  91. imponibile = montante * (1 - perc_rend)
  92. return montante - (imponibile * TAX_FONDO_EXIT)
  93.  
  94. netto_totale_cometa = get_netto_fondo(cometa_m, cometa_vers)
  95. netto_totale_fpa = get_netto_fondo(fpa_m, fpa_vers)
  96. netto_totale_tfr = tfr_m - (tfr_cap * ALIQUOTA_MEDIA_TFR)
  97. netto_totale_etf = etf_m - (np.maximum(0, etf_m - etf_inv) * TAX_ETF_GAIN)
  98. netto_totale_bond = bond_m - (np.maximum(0, bond_m - bond_inv) * TAX_BOND_GAIN)
  99.  
  100. # --- 4. COSTRUZIONE DATAFRAME DETTAGLIATI ---
  101.  
  102. # Rinomino le chiavi per renderle univoche nella mega tabella finale
  103. df_parziale = pd.DataFrame({
  104. 'Cometa 75% (Parz)': netto_totale_cometa * 0.75,
  105. 'FPA 75% (Parz)': netto_totale_fpa * 0.75,
  106. 'No Fondo ETF (Parz)': (netto_totale_tfr * 0.70) + netto_totale_etf,
  107. 'No Fondo Bond (Parz)': (netto_totale_tfr * 0.70) + netto_totale_bond
  108. })
  109.  
  110. df_totale = pd.DataFrame({
  111. 'Cometa 100% (Tot)': netto_totale_cometa,
  112. 'No Fondo ETF (Tot)': netto_totale_tfr + netto_totale_etf,
  113. 'No Fondo Bond (Tot)': netto_totale_tfr + netto_totale_bond
  114. })
  115.  
  116. # --- 5. OUTPUT TABELLARE CON PROBABILITÀ ---
  117.  
  118. def print_stats_with_prob(df, benchmark_col, title):
  119. stats = df.describe(percentiles=[0.05, 0.5, 0.95]).T
  120. stats = stats[['mean', '5%', '50%', '95%']]
  121. stats.columns = ['Media (€)', 'Pessimo 5% (€)', 'Mediana (€)', 'Ottimo 95% (€)']
  122.  
  123. probs = []
  124. benchmark_vec = df[benchmark_col].values
  125. for col in df.columns:
  126. if col == benchmark_col:
  127. probs.append("-")
  128. else:
  129. win_rate = np.mean(benchmark_vec > df[col].values) * 100
  130. probs.append(f"{win_rate:.1f}%")
  131. stats['Prob. Cometa Vince'] = probs
  132.  
  133. print("\n" + "="*90)
  134. print(title)
  135. print("="*90)
  136. pd.set_option('display.float_format', '{:,.0f}'.format)
  137. print(stats.sort_values(by='Media (€)', ascending=False))
  138.  
  139. print_stats_with_prob(df_parziale, 'Cometa 75% (Parz)',
  140. "SCENARIO A: ANTICIPO CASA (Mantengo Lavoro - Liquidità Parziale)")
  141.  
  142. print_stats_with_prob(df_totale, 'Cometa 100% (Tot)',
  143. "SCENARIO B: CAMBIO LAVORO (Riscatto Totale - Liquidità Totale)")
  144.  
  145. # --- 6. PLOTTING (GRAFICI) ---
  146. fig, axes = plt.subplots(1, 2, figsize=(14, 6))
  147.  
  148. # Plot 1: Parziale
  149. df_parziale.boxplot(ax=axes[0], showfliers=False, patch_artist=True,
  150. boxprops=dict(facecolor="lightblue"))
  151. axes[0].set_title('Scenario A: Anticipo Casa (Parziale)')
  152. axes[0].set_ylabel('Liquidità Netta (€)')
  153. axes[0].grid(True, linestyle='--', alpha=0.7)
  154. axes[0].tick_params(axis='x', rotation=45)
  155.  
  156. # Plot 2: Totale
  157. df_totale.boxplot(ax=axes[1], showfliers=False, patch_artist=True,
  158. boxprops=dict(facecolor="lightgreen"))
  159. axes[1].set_title('Scenario B: Cambio Lavoro (Totale)')
  160. axes[1].grid(True, linestyle='--', alpha=0.7)
  161. axes[1].tick_params(axis='x', rotation=45)
  162.  
  163. # Nota rossa in alto
  164. axes[1].text(0.5, 0.95, "Nota: FPA escluso (non riscattabile)",
  165. transform=axes[1].transAxes, ha='center', va='top',
  166. color='red', fontsize=10, weight='bold',
  167. bbox=dict(facecolor='white', alpha=0.8, edgecolor='none'))
  168.  
  169. plt.tight_layout()
  170. plt.show()
  171.  
  172. # --- 7. MEGA TABELLA RIASSUNTIVA (RANKING ASSOLUTO) ---
  173.  
  174. # Unione dei dataframe per classifica globale
  175. df_mega = pd.concat([df_parziale, df_totale], axis=1)
  176.  
  177. stats_mega = df_mega.describe(percentiles=[0.05, 0.5, 0.95]).T
  178. stats_mega = stats_mega[['mean', '5%', '50%', '95%']]
  179. stats_mega.columns = ['Media (€)', 'Pessimo 5% (€)', 'Mediana (€)', 'Ottimo 95% (€)']
  180.  
  181. # Aggiungo colonna "Tipo Ritiro" per chiarezza
  182. stats_mega['Tipo'] = ['Parziale']*len(df_parziale.columns) + ['Totale']*len(df_totale.columns)
  183.  
  184. print("\n" + "="*90)
  185. print("🏆 CLASSIFICA GENERALE LIQUIDITÀ (RANKING ASSOLUTO)")
  186. print("Confronto unificato di tutti gli scenari (Parziali e Totali)")
  187. print("="*90)
  188. print(stats_mega.sort_values(by='Media (€)', ascending=False))
  189.  
  190. if __name__ == "__main__":
  191. run_simulation_final()
Advertisement
Add Comment
Please, Sign In to add comment