# Selección óptima de portafolios I

En la clase pasada vimos que:
- La LAC describe las posibles selecciones de riesgo-rendimiento entre un activo libre de riesgo y un activo riesgoso.
- Su pendiente es igual al radio de Sharpe del activo riesgoso.
- La asignación óptima de capital para cualquier inversionista es el punto tangente de la curva de indiferencia del inversionista con la LAC (depende de las preferencias particulares - aversión al riesgo).

Para todo lo anterior, supusimos que ya teníamos el portafolio óptimo (activo riesgoso).

En el siguiente análisis:

**Objetivos:**
- ¿Cuál es el portafolio óptimo de activos riesgosos?
 - ¿Cuál es el mejor portafolio de activos riesgosos?
 - Es un portafolio eficiente en media-varianza.
- Problema: dado un conjunto de activos riesgosos, ¿cómo construimos la mejor combinación?

*Referencia:*
- Notas del curso "Portfolio Selection and Risk Management", Rice University, disponible en Coursera.
___

## 1. Maximizando el radio de Sharpe

### ¿Qué pasa si tenemos dos activos riesgosos?

Cuando tenemos dos o más activos riesgosos, tenemos disponibles diferentes LAC. ¿Qué significan sus pendientes?

 Ver en el tablero.

Pregunta:
- ¿Qué es lo que se quiere?

**Conclusión:**
- El mejor portafolio de activos no depende de las preferencias individuales, y por tanto va a ser igual para todos.
- Dicho mejor portafolio maximiza el radio de Sharpe.
- A este portafolio lo llamaremos el portafolio eficiente en media-varianza (EMV)

**Idea principal: el portafolio óptimo de activos riesgosos es independiente de las preferencias del inversionista.**
- El portafolio EMV determina el portafolio óptimo de activos riesgosos.
- Todos tendremos el mismo portafolio de activos riesgosos (EMV), y lo combinaremos con el activo libre de reisgo de acuerdo con las preferencias de cada uno de nosotros (aversión al riesgo).
- La LAC combinando el activo libre de riesgo y el portafolio EMV, se vuelve el conjunto de portafolios eficientes.

Entonces, se deben seguir los siguientes pasos:
1. Crear la frontera media-varianza.
2. Encontrar el portafolio que maximize el radio de Sharpe (portafolio EMV).
3. Construir la frontera eficiente (LAC) del punto $(0,r_f)$ al punto $(\sigma_s,E[r_s])$ del portafolio EMV.
4. Combinar de acuerdo a sus preferencias.
___

## 2. Solución analítica del portafolio EMV: caso con dos activos.

Queremos solucionar el siguiente problema:

\begin{align}
\max_{w_1,w_2} &\quad \frac{E[r_p]-r_f}{\sigma_p}\\
\text{s.a.}    &\quad E[r_p]=w_1E[r_1]+w_2E[r_2]\\
               &\quad \sigma_p=\sqrt{w_1^2\sigma_1^2+w_2^2\sigma_2^2+2w_1w_2\rho_{12}\sigma_1\sigma_2}\\
               &\quad w_1+w_2=1, \quad w_1,w_2\geq0
\end{align}

el cual es equivalente a

\begin{align}
\max_{w_1} &\quad \frac{w_1E[r_1]+(1-w_1)E[r_2]-r_f}{\sqrt{w_1^2\sigma_1^2+(1-w_1)^2\sigma_2^2+2w_1(1-w_1)\rho_{12}\sigma_1\sigma_2}}\\
\text{s.a.}    &\quad 0\leq w_1\leq1
\end{align}

**Actividad.**
El anterior es un problema de maximizar una función de una variable en un dominio cerrado. No debaría representar dificultad.

Encontrar la solución analítica a este problema.

Quien primero lo haga, y salga a explicarlo al tablero, le subo alguna tarea, quiz o el primer examen a 100.

Deben llegar a:

$$w_{1,EMV}=\frac{(E[r_1]-r_f)\sigma_2^2-(E[r_2]-r_f)\sigma_{12}}{(E[r_2]-r_f)\sigma_1^2+(E[r_1]-r_f)\sigma_2^2-((E[r_1]-r_f)+(E[r_2]-r_f))\sigma_{12}}.$$

Si nadie lo ha hecho en 30 min., procederé a hacerlo yo.

**Nota:**
- así como obtuvimos una expresión para el peso del portafolio de mínima varianza con dos activos, obtenemos una expresión para el peso del portafolio Eficiente en Media-Varianza.
- Estas actividades son sin duda un buen ejercicio, y se pueden replicar usando técnicas de varias variables (multiplicadores de Lagrange) cuando se tengan más de dos activos.
- Sin embargo, la complejidad del problema crece considerablemente con el número de variables, y la solución analítica deja de ser viable cuando mencionamos que un portafolio bien diversificado consta aproximadamente de 50-60 activos.
- En esos casos, este problema se soluciona con rutinas numéricas que hagan la optimización por nosotros.
- Por eso, les enseño cómo resolver este problema con optimizadores numéricos, porque son una solución viable y escalable a más variables.

## 3. Ejemplo ilustrativo.

Retomamos el ejemplo de mercados de acciones en los países integrantes del $G5$: EU, RU, Francia, Alemania y Japón.

```python
# Importamos pandas y numpy
import pandas as pd
import numpy as np
```

```python
# Resumen en base anual de rendimientos esperados y volatilidades
annual_ret_summ = pd.DataFrame(columns=['EU', 'RU', 'Francia', 'Alemania', 'Japon'], index=['Media', 'Volatilidad'])
annual_ret_summ.loc['Media'] = np.array([0.1355, 0.1589, 0.1519, 0.1435, 0.1497])
annual_ret_summ.loc['Volatilidad'] = np.array([0.1535, 0.2430, 0.2324, 0.2038, 0.2298])

annual_ret_summ.round(4)
```

      EU
      RU
      Francia
      Alemania
      Japon

      Media
      0.1355
      0.1589
      0.1519
      0.1435
      0.1497

      Volatilidad
      0.1535
      0.243
      0.2324
      0.2038
      0.2298

```python
# Matriz de correlación
corr = pd.DataFrame(data= np.array([[1.0000, 0.5003, 0.4398, 0.3681, 0.2663],
                                    [0.5003, 1.0000, 0.5420, 0.4265, 0.3581],
                                    [0.4398, 0.5420, 1.0000, 0.6032, 0.3923],
                                    [0.3681, 0.4265, 0.6032, 1.0000, 0.3663],
                                    [0.2663, 0.3581, 0.3923, 0.3663, 1.0000]]),
                    columns=annual_ret_summ.columns, index=annual_ret_summ.columns)
corr.round(4)
```

      EU
      RU
      Francia
      Alemania
      Japon

      EU
      1.0000
      0.5003
      0.4398
      0.3681
      0.2663

      RU
      0.5003
      1.0000
      0.5420
      0.4265
      0.3581

      Francia
      0.4398
      0.5420
      1.0000
      0.6032
      0.3923

      Alemania
      0.3681
      0.4265
      0.6032
      1.0000
      0.3663

      Japon
      0.2663
      0.3581
      0.3923
      0.3663
      1.0000

Supondremos, además, que la tasa libre de riesgo es $r_f=5\%$.

```python
# Tasa libre de riesgo
rf = 0.05
```

Entonces, supondremos que tenemos disponibles los activos correspondientes a los mercados de acciones de EU y Japón, y en adición el activo libre de riesgo.

#### 1. Construir la frontera de mínima varianza

```python
# Vector de w variando entre 0 y 1 con n pasos
n = 101
w = np.linspace(0, 1, n)
# Rendimientos esperados individuales
# Activo1: EU, Activo2:Japon
E1 = annual_ret_summ.loc['Media', 'EU']
E2 = annual_ret_summ.loc['Media', 'Japon']
# Volatilidades individuales
s1 = annual_ret_summ.loc['Volatilidad', 'EU']
s2 = annual_ret_summ.loc['Volatilidad', 'Japon']
# Correlacion
r12 = corr.loc['EU', 'Japon']
```

```python
# DataFrame de portafolios:
# 1. Índice: i
# 2. Columnas 1-2: w, 1-w
# 3. Columnas 3-4: E[r], sigma
# 4. Columna 5: Sharpe ratio
portafolios = pd.DataFrame(index=range(n),
                           data={'w':w,
                                 '1-w':1-w,
                                 'Media':w * E1 + (1 - w) * E2,
                                 'Vol':((w * s1)**2 + ((1 - w) * s2)**2 + 2 * w * (1 - w) * r12 * s1 * s2)**0.5
                                }
                          )
portafolios['RS'] = (portafolios['Media'] - rf) / portafolios['Vol']
portafolios
```

      w
      1-w
      Media
      Vol
      RS

      0
      0.00
      1.00
      0.149700
      0.229800
      0.433856

      1
      0.01
      0.99
      0.149558
      0.227916
      0.436820

      2
      0.02
      0.98
      0.149416
      0.226041
      0.439814

      3
      0.03
      0.97
      0.149274
      0.224176
      0.442839

      4
      0.04
      0.96
      0.149132
      0.222322
      0.445894

      5
      0.05
      0.95
      0.148990
      0.220478
      0.448979

      6
      0.06
      0.94
      0.148848
      0.218645
      0.452094

      7
      0.07
      0.93
      0.148706
      0.216823
      0.455238

      8
      0.08
      0.92
      0.148564
      0.215012
      0.458411

      9
      0.09
      0.91
      0.148422
      0.213213
      0.461613

      10
      0.10
      0.90
      0.148280
      0.211426
      0.464843

      11
      0.11
      0.89
      0.148138
      0.209651
      0.468101

      12
      0.12
      0.88
      0.147996
      0.207889
      0.471387

      13
      0.13
      0.87
      0.147854
      0.206139
      0.474698

      14
      0.14
      0.86
      0.147712
      0.204403
      0.478036

      15
      0.15
      0.85
      0.147570
      0.202680
      0.481398

      16
      0.16
      0.84
      0.147428
      0.200971
      0.484785

      17
      0.17
      0.83
      0.147286
      0.199277
      0.488195

      18
      0.18
      0.82
      0.147144
      0.197597
      0.491627

      19
      0.19
      0.81
      0.147002
      0.195932
      0.495080

      20
      0.20
      0.80
      0.146860
      0.194282
      0.498553

      21
      0.21
      0.79
      0.146718
      0.192648
      0.502044

      22
      0.22
      0.78
      0.146576
      0.191031
      0.505553

      23
      0.23
      0.77
      0.146434
      0.189429
      0.509076

      24
      0.24
      0.76
      0.146292
      0.187845
      0.512613

      25
      0.25
      0.75
      0.146150
      0.186279
      0.516162

      26
      0.26
      0.74
      0.146008
      0.184730
      0.519721

      27
      0.27
      0.73
      0.145866
      0.183199
      0.523288

      28
      0.28
      0.72
      0.145724
      0.181688
      0.526860

      29
      0.29
      0.71
      0.145582
      0.180195
      0.530435

      ...
      ...
      ...
      ...
      ...
      ...

      71
      0.71
      0.29
      0.139618
      0.142081
      0.630751

      72
      0.72
      0.28
      0.139476
      0.141924
      0.630452

      73
      0.73
      0.27
      0.139334
      0.141806
      0.629973

      74
      0.74
      0.26
      0.139192
      0.141729
      0.629313

      75
      0.75
      0.25
      0.139050
      0.141693
      0.628472

      76
      0.76
      0.24
      0.138908
      0.141697
      0.627451

      77
      0.77
      0.23
      0.138766
      0.141742
      0.626250

      78
      0.78
      0.22
      0.138624
      0.141828
      0.624872

      79
      0.79
      0.21
      0.138482
      0.141954
      0.623317

      80
      0.80
      0.20
      0.138340
      0.142120
      0.621587

      81
      0.81
      0.19
      0.138198
      0.142327
      0.619687

      82
      0.82
      0.18
      0.138056
      0.142574
      0.617618

      83
      0.83
      0.17
      0.137914
      0.142860
      0.615385

      84
      0.84
      0.16
      0.137772
      0.143187
      0.612990

      85
      0.85
      0.15
      0.137630
      0.143552
      0.610440

      86
      0.86
      0.14
      0.137488
      0.143957
      0.607737

      87
      0.87
      0.13
      0.137346
      0.144401
      0.604887

      88
      0.88
      0.12
      0.137204
      0.144883
      0.601894

      89
      0.89
      0.11
      0.137062
      0.145403
      0.598765

      90
      0.90
      0.10
      0.136920
      0.145960
      0.595505

      91
      0.91
      0.09
      0.136778
      0.146555
      0.592120

      92
      0.92
      0.08
      0.136636
      0.147186
      0.588615

      93
      0.93
      0.07
      0.136494
      0.147854
      0.584996

      94
      0.94
      0.06
      0.136352
      0.148557
      0.581270

      95
      0.95
      0.05
      0.136210
      0.149296
      0.577443

      96
      0.96
      0.04
      0.136068
      0.150070
      0.573521

      97
      0.97
      0.03
      0.135926
      0.150877
      0.569509

      98
      0.98
      0.02
      0.135784
      0.151719
      0.565415

      99
      0.99
      0.01
      0.135642
      0.152593
      0.561244

      100
      1.00
      0.00
      0.135500
      0.153500
      0.557003

101 rows × 5 columns

```python
portafolios.iloc[portafolios['RS'].idxmax()]
```

    w        0.70000
    1-w      0.30000
    Media    0.13976
    Vol      0.14228
    RS       0.63087
    Name: 70, dtype: float64

```python
# Importar librerías de gráficos
import matplotlib.pyplot as plt
%matplotlib inline
```

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a SR
plt.figure(figsize=(6, 4))
plt.scatter(portafolios['Vol'],
            portafolios['Media'],
            c=portafolios['RS'],
            cmap='magma'
           )
plt.colorbar()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.grid()
plt.axis([0.14, 0.18, 0.135, 0.145])
```

#### 2. Encontrar el portafolio que maximiza el radio de Sharpe (EMV)

Primero, encontramos este portafolio con la fórmula que obtuvimos:

```python
# Fórmula que obtuvimos
s12 = r12 * s1 * s2
wEMV = ((E1 - rf) * s2**2 - (E2 - rf) * s12) / ((E2 - rf) * s1**2 + (E1 - rf) * s2**2 - ((E1 -rf) + (E2 - rf)) * s12)
EEMV = wEMV * E1 + (1 - wEMV) * E2
sEMV = ((wEMV * s1)**2 + ((1 - wEMV) * s2)**2 + 2 * wEMV * (1 - wEMV) * s12)**0.5
RSmax = (EEMV - rf) / sEMV
wEMV, 1 - wEMV, EEMV, sEMV, RSmax
```

    (0.6983139170512034,
     0.3016860829487966,
     0.13978394237787292,
     0.14231708951606933,
     0.6308725303698346)

Ahora sí, con la función scipy.optimize.minimize

```python
# Importar el módulo optimize de la libreria scipy
import scipy.optimize as opt
```

```python
## Construcción de parámetros
## Activo 1: EU, Activo 2: Japon
# 1. Sigma: matriz de varianza-covarianza
#Sigma = np.array([[s1**2, r12 * s1 * s2],
#                  [r12 * s1 * s2, s2**2]]
#                )
Sigma = np.array([[s1**2, s12],
                  [s12, s2**2]]
                )
# 2. Eind: rendimientos esperados activos individuales
Eind = np.array([E1, E2])
```

```python
# Función objetivo (-SR)
def minus_SR(w, Eind, Sigma, rf):
    Ep = Eind.dot(w)
    sp = (w.dot(Sigma).dot(w))**0.5
    RS = (Ep - rf) / sp
    return -RS
```

```python
# Dato inicial
N = len(Eind)
w0 = np.ones((N,)) / N
#w0 = np.array([0.5, 0.5])
# Cotas de las variables
bnds = ((0, 1),) * N
#bnds = ((0,1), (0,1))
# Restricciones
cons = {'type': 'eq', 'fun': lambda w: w.sum() - 1}
```

```python
# Optimización numérica
EMV = opt.minimize(fun=minus_SR,
                   x0=w0,
                   args=(Eind, Sigma, rf),
                   bounds=bnds,
                   constraints=cons
                  )
# Resultado
EMV
```

         fun: -0.6308725303332082
         jac: array([-0.35171176, -0.3502962 ])
     message: 'Optimization terminated successfully.'
        nfev: 17
         nit: 4
        njev: 4
      status: 0
     success: True
           x: array([0.6983075, 0.3016925])

Con lo anterior, podemos obtener datos de rendimiento esperado y volatilidad del portafolio EMV

```python
# Rendimiento esperado y volatilidad del portafolio EMV
wEMV2 = EMV.x
Ep = Eind.dot(wEMV2)
sp = (wEMV2.dot(Sigma).dot(wEMV2))**0.5
RSmax = (Ep - rf) / sp
wEMV2, Ep, sp, RSmax
```

    (array([0.6983075, 0.3016925]),
     0.13978403352815136,
     0.14231723400720916,
     0.6308725303332082)

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a SR, y portafolio EMV
plt.figure(figsize=(6, 4))
plt.scatter(portafolios['Vol'],
            portafolios['Media'],
            c=portafolios['RS'],
            cmap='magma'
           )
plt.plot(sp, Ep, 'ko')
plt.colorbar()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.grid()
plt.axis([0.14, 0.18, 0.135, 0.145])
```

#### 3. Construir LAC

Ahora, dibujamos la LAC, combinando el portafolio EMV con el activo libre de riesgo:

```python
# Vector de wp variando entre 0 y 1.5 con n pasos
import numpy as np
n_p = 51
wp = np.linspace(0, 1.5, n_p)
```

```python
# DataFrame de CAL:
# 1. Índice: i
# 2. Columnas 1-2: wp, wrf
# 3. Columnas 3-4: E[r], sigma
# 4. Columna 5: Sharpe ratio
LAC = pd.DataFrame(index=range(n_p),
                   data={'wp': wp,
                         'wrf': 1 - wp,
                         'Media': wp * Ep + (1 - wp)*rf,
                         'Vol': wp * sp
                        }
                  )
LAC['RS'] = (LAC['Media'] - rf) / LAC['Vol']
LAC
```

      wp
      wrf
      Media
      Vol
      RS

      0
      0.00
      1.00
      0.050000
      0.000000
      NaN

      1
      0.03
      0.97
      0.052694
      0.004270
      0.630873

      2
      0.06
      0.94
      0.055387
      0.008539
      0.630873

      3
      0.09
      0.91
      0.058081
      0.012809
      0.630873

      4
      0.12
      0.88
      0.060774
      0.017078
      0.630873

      5
      0.15
      0.85
      0.063468
      0.021348
      0.630873

      6
      0.18
      0.82
      0.066161
      0.025617
      0.630873

      7
      0.21
      0.79
      0.068855
      0.029887
      0.630873

      8
      0.24
      0.76
      0.071548
      0.034156
      0.630873

      9
      0.27
      0.73
      0.074242
      0.038426
      0.630873

      10
      0.30
      0.70
      0.076935
      0.042695
      0.630873

      11
      0.33
      0.67
      0.079629
      0.046965
      0.630873

      12
      0.36
      0.64
      0.082322
      0.051234
      0.630873

      13
      0.39
      0.61
      0.085016
      0.055504
      0.630873

      14
      0.42
      0.58
      0.087709
      0.059773
      0.630873

      15
      0.45
      0.55
      0.090403
      0.064043
      0.630873

      16
      0.48
      0.52
      0.093096
      0.068312
      0.630873

      17
      0.51
      0.49
      0.095790
      0.072582
      0.630873

      18
      0.54
      0.46
      0.098483
      0.076851
      0.630873

      19
      0.57
      0.43
      0.101177
      0.081121
      0.630873

      20
      0.60
      0.40
      0.103870
      0.085390
      0.630873

      21
      0.63
      0.37
      0.106564
      0.089660
      0.630873

      22
      0.66
      0.34
      0.109257
      0.093929
      0.630873

      23
      0.69
      0.31
      0.111951
      0.098199
      0.630873

      24
      0.72
      0.28
      0.114645
      0.102468
      0.630873

      25
      0.75
      0.25
      0.117338
      0.106738
      0.630873

      26
      0.78
      0.22
      0.120032
      0.111007
      0.630873

      27
      0.81
      0.19
      0.122725
      0.115277
      0.630873

      28
      0.84
      0.16
      0.125419
      0.119546
      0.630873

      29
      0.87
      0.13
      0.128112
      0.123816
      0.630873

      30
      0.90
      0.10
      0.130806
      0.128086
      0.630873

      31
      0.93
      0.07
      0.133499
      0.132355
      0.630873

      32
      0.96
      0.04
      0.136193
      0.136625
      0.630873

      33
      0.99
      0.01
      0.138886
      0.140894
      0.630873

      34
      1.02
      -0.02
      0.141580
      0.145164
      0.630873

      35
      1.05
      -0.05
      0.144273
      0.149433
      0.630873

      36
      1.08
      -0.08
      0.146967
      0.153703
      0.630873

      37
      1.11
      -0.11
      0.149660
      0.157972
      0.630873

      38
      1.14
      -0.14
      0.152354
      0.162242
      0.630873

      39
      1.17
      -0.17
      0.155047
      0.166511
      0.630873

      40
      1.20
      -0.20
      0.157741
      0.170781
      0.630873

      41
      1.23
      -0.23
      0.160434
      0.175050
      0.630873

      42
      1.26
      -0.26
      0.163128
      0.179320
      0.630873

      43
      1.29
      -0.29
      0.165821
      0.183589
      0.630873

      44
      1.32
      -0.32
      0.168515
      0.187859
      0.630873

      45
      1.35
      -0.35
      0.171208
      0.192128
      0.630873

      46
      1.38
      -0.38
      0.173902
      0.196398
      0.630873

      47
      1.41
      -0.41
      0.176595
      0.200667
      0.630873

      48
      1.44
      -0.44
      0.179289
      0.204937
      0.630873

      49
      1.47
      -0.47
      0.181983
      0.209206
      0.630873

      50
      1.50
      -0.50
      0.184676
      0.213476
      0.630873

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a SR, portafolio EMV y LAC
plt.figure(figsize=(6, 4))
plt.scatter(portafolios['Vol'],
            portafolios['Media'],
            c=portafolios['RS'],
            cmap='magma'
           )
plt.plot(sp, Ep, 'ko')
plt.plot(LAC['Vol'], LAC['Media'], '-b')
plt.colorbar()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.grid()
#plt.axis([0.1, 0.18, 0.10, 0.145])
```

#### 4. Combinación óptima de acuerdo a preferencias

Con los datos anteriores, y la caracterización de aversión al riesgo, se escoge la combinación óptima entre el portafolio EMV y el activo libre de riesgo de acuerdo a:

$$w^\ast=\frac{E[r_s-r_f]}{\gamma\sigma_s^2}.$$

```python
# Para gamma=7
gamma = 20
wopt = (Ep - rf) / (gamma * sp**2)
wopt
```

    0.22164305494486033

```python
wopt * wEMV2, 1 - wopt
```

    (array([0.15477501, 0.06686805]), 0.7783569450551396)

# Anuncios parroquiales

- [Calificaciones](https://docs.google.com/spreadsheets/d/1AoEaDByByOPQLZfuZhRy3CIGB2UMr89PoJauRdRuG3o/edit?usp=sharing)

Created with Jupyter by Esteban Jiménez Rodríguez.