Giudizio spassionato sul codice e le idee che rappresenta:

#@title 28.0 - Il Ponte di Comando: Funzione main() | Rev. 15 (Con Helper Ripristinati)

# ==============================================================================
# FUNZIONI HELPER PER L'INTERAZIONE UTENTE
# Reinserite qui per garantire che siano sempre definite prima della chiamata a main()
# ==============================================================================
def _integrated_prompt_yes_no(question: str) -> bool:
    """Pone una domanda sì/no all'utente e restituisce un booleano."""
    print(f"\\n>>> DOMANDA: {question}")
    while True:
        response = input(">>> Risposta (s/n): ").lower().strip()
        if response in ['s', 'si', 'y', 'yes']: return True
        if response in ['n', 'no']: return False
        print("Input non valido. Per favore, rispondi 's' o 'n'.")

def _integrated_upload_and_unzip(target_dir: str) -> bool:
    """Gestisce il caricamento e l'estrazione di un file .zip."""
    print("\\n--- CARICAMENTO CHECKPOINT BANCHIERE ---")
    print("Seleziona il file 'banker_checkpoint.zip' dal tuo computer.")
    try:
        uploaded = files.upload()
        if not uploaded:
            print("Caricamento annullato.")
            return False
        zip_filename = list(uploaded.keys())[0]
        zip_filepath = os.path.join('/content/', zip_filename)
        
        if os.path.exists(target_dir): shutil.rmtree(target_dir)
        os.makedirs(target_dir)
        print(f"Estrazione di '{zip_filename}' in '{target_dir}'...")
        with zipfile.ZipFile(zip_filepath, 'r') as zip_ref:
            zip_ref.extractall(target_dir)
        print("Estrazione completata con successo.")
        return True
    except Exception as e:
        print(f"ERRORE durante il caricamento o l'estrazione: {e}")
        return False
    finally:
        if 'zip_filepath' in locals() and os.path.exists(zip_filepath):
             os.remove(zip_filepath)

# ==============================================================================
# FUNZIONE MAIN
# ==============================================================================
def main():
    fase_di_test = "Fase Sigma: Mercato Bipolare Azioni/Obbligazioni"
    try:
        config = SimulationConfig()
    except NameError:
        print("ERRORE: Esegui prima la cella 10.0 (Configurazione)!")
        return

    BANKER_CKPT_DIR = '/content/banker_checkpoint/'
    cb_trainer = None
    banker_is_ready = False

    # Pulisce l'ambiente per evitare errori da esecuzioni precedenti
    if os.path.exists(BANKER_CKPT_DIR):
        print(f"Pulizia della cartella checkpoint residua: {BANKER_CKPT_DIR}")
        shutil.rmtree(BANKER_CKPT_DIR)

    if config.enable_central_bank_agent:
        cb_trainer = CentralBankerOnline(config)
        
        if _integrated_prompt_yes_no("Vuoi caricare un checkpoint del Banchiere Centrale (.zip) dal tuo computer?"):
            if _integrated_upload_and_unzip(target_dir=BANKER_CKPT_DIR):
                if cb_trainer.load_pretrained_weights(ckpt_dir=BANKER_CKPT_DIR):
                    banker_is_ready = True
                    print("\\n--- Banchiere caricato con successo! ---")
                else:
                    print("\\nERRORE: I file caricati non sono un checkpoint valido.")
        
        if not banker_is_ready:
            if _integrated_prompt_yes_no("Banchiere non caricato. Vuoi procedere con pesi casuali?"):
                 print("\\nOK. Si procede con un banchiere non addestrato (casuale).")
            else:
                 print("\\nEsecuzione interrotta come richiesto.")
                 return
    else:
        print("\\nINFO: Agente Banca Centrale disattivato da config.")

    # --- Inizio Simulazione ---
    trainers = {}
    print_setup_report(config, phase_name=fase_di_test)
    print("!!! Attivazione cervelli per le tribù DRL !!!")
    for tribe_info in config.drl_tribes:
        tribe_name = tribe_info["name"]
        trainers[tribe_name] = PPOTrainer(config)
        print(f"  - Cervello per '{tribe_name}' attivato.")

    start_time = time.time()
    outputs = run_simulation(config, trainers, cb_trainer)
    print(f"\\nSimulazione completata in {time.time() - start_time:.2f} secondi.")

    # Destrutturazione completa dei risultati
    (prices, bond_prices, trades_history, volume, losses, agent_types, shock_log,
     wealth_history, interest_log, dividend_log, demand_history,
     private_info_log, pf_leak_log, macro_log, bankruptcy_log,
     fundamental_value_series, cb_loss_history, lambda_history,
     info_purchases_log, portfolio_history, innovation_shock_log) = outputs

    # Chiamata alle funzioni di analisi unificate e corrette
    print_final_report(config, prices, bond_prices, volume, agent_types, bankruptcy_log)
    
    plot_main_dashboard(
        prices, bond_prices, fundamental_value_series, macro_log, volume, 
        lambda_log, wealth_history, shock_log, private_info_log,
        innovation_shock_log
    )
    
    plot_portfolio_dashboard(portfolio_history, agent_types, config)
    
    plot_stylized_facts(np.array(prices))