Calcoli scientifici con Julia/Ottimizzazione del portafoglio in finanza
In finanza quando si vuole ottimizzare un portafoglio secondo Harry Markowitz, costituito per esempio da n azioni, si risolve la questione, minimizzando la seguente funzione obiettivo:
soggetto a:
dove è la varianza del portafoglio che si vuole minimizzare con rendimento atteso ≥ target. Se un titolo ha un rendimento atteso del 7%, ma a volte fa +20% , a volte fa –15% allora è rischioso non perché perda sempre, ma perché è imprevedibile. La misura matematica di questa imprevedibilità è la varianza del portafoglio che deve essere minimizzata, facendo in modo che il rendimento atteso sia maggiore o uguale al target, mentre la volatilità è la radice quadrata della varianza.
invece è la matrice di covarianza e w il vettore dei pesi che rappresenta la percentuale del portafoglio investita in ciascun asset, ad esempio:
- → 40% nell’asset 1
- → 35% nell’asset 2
- → 25% nell’asset 3
E valgono sempre:
e i pesi tutti positivi.
La matrice di covarianza è uno degli oggetti matematici più importanti nella finanza quantitativa.
Per un portafoglio con n asset, la matrice è:
Dove è la serie dei rendimenti dell’asset ( i ).
Sulla diagonale principale si trova la varianza dei vari asset per cui:
che misura la volatilità dell’asset (più è grande, più è rischioso)
Fuori diagonale la covarianza:
e misura se due asset si muovono insieme per cui se :
- Cov > 0 → si muovono nella stessa direzione
- Cov < 0 → si muovono in direzioni opposte
- Cov ≈ 0 → movimenti indipendenti
Se si hanno due titoli dove:
- uno sale quando l’altro scende → covarianza negativa → ottima diversificazione
- si muovono sempre insieme → covarianza positiva → poca diversificazione
- movimenti indipendenti → covarianza vicino a 0 → discreta diversificazione
Implementazione in Julia
[modifica | modifica sorgente]Innanzitutto installo le librerie di Julia che mi servono:
using Pkg
Pkg.add(["Convex", "SCS", "LinearAlgebra","YFinance", "DataFrames", "Statistics"])
Supponiamo di considerare tre titoli : Apple (AAPL), Microsoft (MSFT), Amazon (AMZN) . Tramite il package YFinance scarico i prezzi giornalieri degli ultimi 5 anni e trasformo i dati in un DataFrame:
using YFinance, DataFrames, Statistics
tickers = ["AAPL", "MSFT", "AMZN"]
data =get_prices.(tickers, range="5y", interval="1d")
data = data |> DataFrame
Creo un dizionario e poi un DataFrame con i prezzi di chiusura delle azioni e la rispettiva data:
prices = Dict()
for t in tickers
prices[t] = data[data.ticker.==t,"adjclose"] # colonna dei prezzi aggiustati
end
# Converti in DataFrame unico
df = DataFrame(Date = data[data.ticker.=="AMZN","timestamp"])
for t in tickers
df[!, t] = prices[t]
end
df
Si calcolano i rendimenti medi delle 3 azioni e si nota che Apple rende di più :
AAPL = reduce(vcat, df.AAPL)
MSFT = reduce(vcat, df.MSFT)
AMZN = reduce(vcat, df.AMZN)
# Calcola rendimenti giornalieri
rets = dropmissing(DataFrame(
AAPL = diff(log.(AAPL)),
MSFT = diff(log.(MSFT)),
AMZN = diff(log.(AMZN))
))
μ = mean(Matrix(rets), dims=1)'
println("Rendimento medio:")
println(μ)
Rendimento medio: [0.0006538039701943343; 0.0005005829765010237; 0.00029305866759470864;;]
Calcolo la matrice di covarianza:
Σ = cov(Matrix(rets))
3×3 Matrix{Float64}:
0.000299483 0.000171464 0.000210499
0.000171464 0.000272221 0.000233099
0.000210499 0.000233099 0.000493231
Per finire calcolo la varianza minima del portafoglio ed i relativi pesi:
#Target rendimento giornaliero:
r_target = 0.0004
#Variabili di decisione:
n = length(μ)
w = Variable(n)
#Problema di ottimizzazione:
# vincoli
constraints = [
sum(w) == 1,
μ' * w >= r_target,
w >= 0
]
# funzione obiettivo:
objective = quadform(w, Σ)
# costruzione del problema
problem = minimize(objective, constraints)
solve!(problem, SCS.Optimizer)
weights = evaluate(w)
println("Pesi ottimali = ", weights)
println("Varianza minima = ", evaluate(objective))
------------------------------------------------------------------
SCS v3.2.11 - Splitting Conic Solver
(c) Brendan O'Donoghue, Stanford University, 2012
------------------------------------------------------------------
problem: variables n: 5, constraints m: 12
cones: z: primal zero / dual free vars: 1
l: linear vars: 4
q: soc vars: 7, qsize: 2
settings: eps_abs: 1.0e-04, eps_rel: 1.0e-04, eps_infeas: 1.0e-07
alpha: 1.50, scale: 1.00e-01, adaptive_scale: 1
max_iters: 100000, normalize: 1, rho_x: 1.00e-06
acceleration_lookback: 10, acceleration_interval: 10
compiled with openmp parallelization enabled
lin-sys: sparse-direct-amd-qdldl
nnz(A): 22, nnz(P): 0
------------------------------------------------------------------
iter | pri res | dua res | gap | obj | scale | time (s)
------------------------------------------------------------------
0| 1.45e+01 1.00e+00 2.01e+01 -9.96e+00 1.00e-01 4.88e-04
75| 1.77e-04 7.28e-05 2.82e-05 2.60e-04 1.00e-01 6.02e-04
------------------------------------------------------------------
status: solved
timings: total: 6.04e-04s = setup: 2.04e-04s + solve: 4.00e-04s
lin-sys: 3.97e-05s, cones: 2.52e-05s, accel: 7.34e-06s
------------------------------------------------------------------
objective = 0.000260
------------------------------------------------------------------
Pesi ottimali = [0.35076741527347427, 0.37629612818017755, 0.27293645299415875]
Varianza minima = 0.00024558676226717267
# Rendimento giornaliero
portfolio_return_daily = dot(μ, weights)
# Volatilità giornaliera
portfolio_vol_daily = sqrt(weights' * Σ * weights)
# Rendimento mensile (%) : un mese di borsa ha circa 21 giorni.
portfolio_return_month = portfolio_return_daily * 21 * 100
# Rendimento annuale (%) : un anno di borsa ha circa 252 giorni.
portfolio_return_year = portfolio_return_daily * 252 * 100
# Volatilità mensile (%)
portfolio_vol_month = portfolio_vol_daily * sqrt(21) * 100
# Volatilità annuale (%)
portfolio_vol_year = portfolio_vol_daily * sqrt(252) * 100
println("Rendimento mensile (%) = ", portfolio_return_month )
println("Rendimento annuale (%) = ", portfolio_return_year )
println("Volatilità mensile (%) = ", portfolio_vol_month )
println("Volatilità annuale (%) = ", portfolio_vol_year )
Rendimento mensile (%) = 0.9913696368340023 Rendimento annuale (%) = 11.896435642008028 Volatilità mensile (%) = 7.163080305760683 Volatilità annuale (%) = 24.813638056547024
Conclusione:
Si ottiene una varianza minima di 0.00026 col 35,07% di Apple, il 37,63% di Microsoft e il 27,29% di Amazon nel portafoglio con rendimento atteso maggiore di 0.0004, ma dalla matrice di covarianza si vede che le covarianze sono tutte positive quindi c'è poca diversificazione, nel senso che se sale un titolo sale anche l'altro e viceversa, il che è normale perché tutti appartengono al settore tech USA.