La libreria CVXPY di Python è una libreria open-source per modellare e risolvere problemi matematici in modo naturale e leggibile meno "rigido" rispetto ad altri standard. Problemi di ottimizzazione, regressione e allocazione di risorse, si prestano bene alla risoluzione tramite questa libreria. L'attuale versione è 1.7.2 e si può installare tramite pip install cvxpy. Documentazione e vari esempi sul sito ufficiale cvxpy.org.
Caso di studio: ottimizzazione portafoglio finanziario
Supponiamo di voler allocare un capitale fra un numero N di diversi asset, con lo scopo di minimizzare il rischio (matematicamente, la varianza del portafoglio - teoria di Markowitz, frontiera efficiente, trade-off rischio-rendimento ovvero "il massimo rendimento atteso per un dato livello di rischio"), per questo esempio assumiamo:
- rendimento atteso minimo del 5%
- somma dei pesi unitaria (allocare esattamente il 100% del capitale)
- pesi non negativi (quindi solo semplici posizioni Long, evitiamo Short selling)
Di seguito tutto il codice Python (impostazione definita tramite IA), con i commenti al codice.
import cvxpy as cp
import numpy as np
# Dati del problema (fittizi per 3 asset)
# Rendimenti attesi degli asset (es. 0.07, 0.05, 0.10)
r = np.array([0.07, 0.05, 0.10])
# Matrice di covarianza (rappresenta il rischio; valori fittizi positivi definiti)
Sigma = np.array([
[0.02, 0.01, 0.005],
[0.01, 0.03, 0.01],
[0.005, 0.01, 0.04]
])
# Rendimento atteso minimo desiderato
mu = 0.05
# Numero di asset
n = len(r)
# Definizione della variabile di ottimizzazione: w è un vettore di dimensione n
w = cp.Variable(n)
# Funzione obiettivo: minimizzare la varianza del portafoglio (w^T * Sigma * w)
# CVXPY riconosce automaticamente questa espressione come convessa
objective = cp.Minimize(cp.quad_form(w, Sigma))
# Vincoli:
# 1. Rendimento atteso >= mu (prodotto scalare lineare)
constraints = [r @ w >= mu]
# 2. Somma dei pesi = 1 (somma lineare)
constraints += [cp.sum(w) == 1]
# 3. Pesi non negativi (vincolo element-wise)
constraints += [w >= 0]
# Creazione del problema: combina obiettivo e vincoli
problem = cp.Problem(objective, constraints)
# Risoluzione del problema (usa solver di default, es. ECOS)
problem.solve()
# Output dei risultati
print("Status dell'ottimizzazione:", problem.status)
print("Valore ottimale della funzione obiettivo (varianza):", problem.value)
print("Pesi ottimali del portafoglio:")
for i in range(n):
print(f"Asset {i+1}: {w.value[i]:.4f}")
# Calcolo del rendimento atteso effettivo (per verifica)
expected_return = r @ w.value
print(f"Rendimento atteso effettivo: {expected_return:.4f}")
- Dati di input: Definiamo r (rendimenti) e Sigma (covarianza) come array NumPy. mu è il target.
- Variabile: cp.Variable(n) crea un vettore libero di dimensione nn.
- Obiettivo: cp.quad_form(w, Sigma) calcola wTΣwwTΣw, minimizzato per ridurre il rischio.
- Vincoli: Aggiunti come lista; CVXPY li verifica per convessità (tutti lineari o affini qui).
- Risoluzione: problem.solve() chiama un solver interno; restituisce lo status (es. "optimal") e aggiorna w.value.
- Output: Stampa pesi, valore ottimale e verifica del rendimento.
I risultati dell'esecuzione, mostrati dall'output del programma, sono:
Status dell'ottimizzazione: optimal
Valore ottimale della funzione obiettivo (varianza): 0.01431372549019608
Pesi ottimali del portafoglio:
Asset 1: 0.5490
Asset 2: 0.2157
Asset 3: 0.2353
Rendimento atteso effettivo: 0.0727
Quindi ci viene mostrato semplicemente, con i dati di partenza, come allocare le risorse (54,9% del capitale per il primo asset, 21,57% per il secondo, 23,53% per il terzo) e il rendimento atteso definitivo in questo caso pari al +7,27%.
Il caso di studio tramite la libreria CVXPY ha semplificato molto le cose rispetto alla scrittura manuale di altre formule (matrici, ottimizzazione tramite annullamento del gradiente della funzione lagrangiana, ecc).
Un altro caso di studio: Calcolo soglia di probabilità di successo per la redditività nel trading