Lección 0.7.3 · 50 min

Álgebra lineal intuitiva: vectores y matrices como preparación para carteras

Una cartera son unos pesos; un día de mercado, unas rentabilidades; cinco años, una tabla. Con tres operaciones de álgebra lineal (producto escalar, producto de matrices y forma cuadrática) se calcula la rentabilidad y el riesgo de cualquier cartera, y se entiende por qué diversificar reduce el riesgo y por qué repartir el capital a partes iguales no reparte el riesgo.

Fig. — Volatilidad anual de los cuatro activos y de dos carteras: con pesos iguales (1/N) y con pesos inversos a la volatilidad (1/σ). El tramo claro es lo que la suma ponderada de volatilidades exagera
Lecciones del nivel 0
  1. 0.1 · Bienvenida y expectativas

    1. 0.1.1 Trading sistemático, algorítmico y cuantitativo frente al discrecional
    2. 0.1.2 Expectativas realistas: costes, tiempo y riesgo de pérdida
    3. 0.1.3 Estructura del curso, tests de entrada, repositorio personal y aprendizaje con IA
  2. 0.2 · Entorno de trabajo moderno

    1. 0.2.1 Instalación de Python con uv: versiones y ciclo de soporte
    2. 0.2.2 Entornos virtuales y dependencias: uv init, uv add, pyproject.toml y lockfiles
    3. 0.2.3 Editores (VS Code, Cursor, PyCharm) y notebooks (Jupyter, JupyterLab, marimo)
    4. 0.2.4 Terminal básica, rutas, variables de entorno y ficheros .env
    5. 0.2.5 Git y GitHub: commits, ramas, pull requests y .gitignore
    6. 0.2.6 Introducción a los asistentes de código con IA: prompts, revisión y tests
  3. 0.3 · Fundamentos del lenguaje

    1. 0.3.1 Variables, tipos y operadores; coma flotante y decimal para precios y ticks
    2. 0.3.2 Cadenas, f-strings y formato de números y fechas
    3. 0.3.3 Listas, tuplas, diccionarios, conjuntos y comprensiones
    4. 0.3.4 Control de flujo: if, elif, else, for, while y match-case
    5. 0.3.5 Funciones: argumentos, *args y **kwargs, lambdas y ámbito
    6. 0.3.6 Excepciones; módulos y paquetes, con los imports perezosos de Python 3.15
  4. 0.4 · Python intermedio

    1. 0.4.1 Programación orientada a objetos: clases, herencia, composición y dataclasses
    2. 0.4.2 Tipado: type hints, mypy o ty y Pydantic para configuraciones
    3. 0.4.3 Iteradores, generadores, decoradores y context managers
    4. 0.4.4 Ficheros (CSV, JSON, YAML, TOML) y fechas con zonas horarias
    5. 0.4.5 Logging profesional y testing con pytest
    6. 0.4.6 Concurrencia: threading, multiprocessing, asyncio y free-threading
  5. 0.5 · Computación numérica y datos

    1. 0.5.1 NumPy 2: arrays, broadcasting, vectorización, álgebra lineal y generadores aleatorios
    2. 0.5.2 pandas 3.0: Series, DataFrame, índices temporales, Copy-on-Write, tipo str y pd.col()
    3. 0.5.3 Selección, filtrado, groupby, merge, pivot, resample y ventanas móviles
    4. 0.5.4 Polars: API lazy frente a eager, expresiones, rendimiento y cuándo elegirlo frente a pandas
    5. 0.5.5 DuckDB y SQL esencial: SELECT, JOIN, GROUP BY y funciones de ventana sobre Parquet
  6. 0.6 · Visualización

    1. 0.6.1 Matplotlib orientado a objetos, Seaborn y Plotly: gráficos de precios, drawdown y velas
    2. 0.6.2 Dashboards rápidos con Streamlit: un panel de seguimiento en un solo script
  7. 0.7 · Matemáticas y estadística mínimas

    1. 0.7.1 Rentabilidades simples y logarítmicas, interés compuesto y estadística descriptiva
    2. 0.7.2 Probabilidad y distribuciones, correlación frente a causalidad y regresión simple
    3. 0.7.3 Álgebra lineal intuitiva: vectores y matrices como preparación para carteras

Una cartera es un vector

La lección 0.5.1 usó @ y np.cov para calcular la volatilidad de una cartera. Esta lección explica por qué esas cuentas funcionan, con la menor cantidad posible de álgebra lineal, porque es el lenguaje en que están escritos todos los métodos de construcción de carteras del curso.

Un vector es una lista ordenada de números. Una cartera de cuatro activos es un vector de pesos: la fracción del capital en cada uno. Las rentabilidades de los cuatro activos en un día son otro vector, en el mismo orden:

w=(w1w2w3w4)r=(r1r2r3r4)w⋅r=∑i=14wi riw = \begin{pmatrix} w_1 \\ w_2 \\ w_3 \\ w_4 \end{pmatrix} \qquad r = \begin{pmatrix} r_1 \\ r_2 \\ r_3 \\ r_4 \end{pmatrix} \qquad w \cdot r = \sum_{i=1}^{4} w_i\, r_i

El producto escalar w⋅rw \cdot r multiplica elemento a elemento y suma. Es exactamente la rentabilidad simple de la cartera ese día (lección 0.7.1: entre activos, la simple de la cartera es la media ponderada de las simples). Con los cuatro activos ficticios del curso y una cartera 40/30/10/20, en el peor día de RVAR:

IN [01]python
import numpy as np
import pandas as pd

cartera = pd.read_csv("cartera_sint.csv", parse_dates=["fecha"],
                      index_col="fecha")
rc = cartera.pct_change().dropna()
activos = list(rc.columns)
w = np.array([0.40, 0.30, 0.10, 0.20])
dia = rc["RVAR"].idxmin()
r_dia = rc.loc[dia].to_numpy()
print(f"{dia:%Y-%m-%d} {activos}")
print(f"Rentabilidades: {r_dia.round(4)}")
bucle = sum(wi * ri for wi, ri in zip(w, r_dia))
print(f"Bucle: {bucle:.4%} · w @ r: {w @ r_dia:.4%}")
OUT [01]El producto escalar es la rentabilidad de la cartera
2025-01-23 ['RVAR', 'RFIJ', 'ORO', 'TECH']
Rentabilidades: [-0.0374  0.0004 -0.0071 -0.0459]
Bucle: -2.4725% · w @ r: -2.4725%

El bucle y w @ r_dia dan lo mismo: @ es el producto escalar cuando sus dos operandos son vectores. El día en que RVAR cae un 3,74 %, la cartera pierde un 2,47 %: el 30 % en renta fija, que apenas se mueve, y el 10 % en oro, que cae mucho menos, amortiguan la caída; TECH, con un −4,59 %, la agrava. Esa compensación entre activos es, en un solo día, la idea de la diversificación.

De un día a toda la serie: matrices y formas

Una matriz es una tabla de números con filas y columnas. Las rentabilidades de cinco años son una matriz RR con una fila por día y una columna por activo. Multiplicar la matriz por el vector de pesos hace el producto escalar de cada fila con los pesos: la rentabilidad de la cartera cada día, de una vez y sin bucles.

R · 1302×4R @ ww · 4cartera · 1302
Fuente Mermaid
flowchart LR
  R[R · 1302×4] --> M[R @ w]
  W[w · 4] --> M
  M --> S[cartera · 1302]

La regla que decide si un producto de matrices es válido es la forma (shape): en A BA\,B, el número de columnas de AA tiene que ser igual al de filas de BB, y esa dimensión desaparece del resultado. (1302×4)(1302 \times 4) por (4)(4) da (1302)(1302):

IN [02]python
R = rc.to_numpy()
serie = R @ w
print(f"R {R.shape} @ w {w.shape} = {serie.shape}")
print(f"Primer día: {serie[0]:.6f} = {w @ R[0]:.6f}")
print(f"Media anual: {serie.mean() * 252:.2%} · "
      f"volatilidad: {serie.std(ddof=1) * np.sqrt(252):.2%}")
OUT [02]Formas
R (1302, 4) @ w (4,) = (1302,)
Primer día: -0.005898 = -0.005898
Media anual: 7.00% · volatilidad: 12.00%

Una serie con pesos constantes cada día supone que la cartera se rebalancea a diario para volver a los pesos iniciales. Sin rebalanceo, los pesos derivan con los precios: el activo que sube gana peso. Es una aproximación habitual que se precisará cuando el curso llegue a los backtests de carteras.

Con el orden cambiado, las dimensiones interiores ya no coinciden y NumPy lo dice:

IN [03]python
w @ R
OUT [03]Las dimensiones interiores no coinciden
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 1302 is different from 4)

Ese error es una suerte. Lo peligroso es lo contrario: un producto que sí funciona con las formas equivocadas. Con cuatro activos y cuatro días, R sería 4×44 \times 4 y w @ R daría un resultado sin sentido sin protestar. Antes de multiplicar, imprime las formas y di en voz alta qué es cada eje (filas: días; columnas: activos).

ObjetoFormaQué es
w(4,)Pesos de la cartera
R(1302, 4)Rentabilidades: días × activos
R @ w(1302,)Rentabilidad diaria de la cartera
Sigma(4, 4)Covarianzas entre activos
Sigma @ w(4,)Covarianza de cada activo con la cartera
w @ Sigma @ w()Varianza de la cartera, un número

La matriz de covarianzas, pieza a pieza

La lección 0.5.1 calculó la matriz de covarianzas Σ\Sigma con np.cov. Su contenido se entiende mejor construyéndola con lo que ya se conoce: la volatilidad de cada activo y las correlaciones de la lección 0.7.2. Cada elemento es

Σij=σi σj ρij\Sigma_{ij} = \sigma_i\,\sigma_j\,\rho_{ij}

y en forma de matrices, con DD la matriz diagonal de volatilidades y CC la de correlaciones, Σ=D C D\Sigma = D\,C\,D:

IN [04]python
vols = rc.std().to_numpy() * np.sqrt(252)
C = rc.corr().to_numpy()
D = np.diag(vols)
Sigma = D @ C @ D
print(np.allclose(Sigma, rc.cov().to_numpy() * 252))
print(Sigma.round(4))
OUT [04]Σ = D C D
True
[[ 0.0283 -0.0016  0.0031  0.0404]
 [-0.0016  0.0025  0.0014 -0.002 ]
 [ 0.0031  0.0014  0.0228  0.001 ]
 [ 0.0404 -0.002   0.001   0.0799]]

La diagonal son las varianzas anuales (σi2\sigma_i^2: 0,0283 para RVAR, un 16,8 % de volatilidad al cuadrado). Fuera de la diagonal, las covarianzas: positivas y grandes entre TECH y RVAR, negativas entre RFIJ y los dos, casi cero entre ORO y TECH. Es simétrica, porque la covarianza de A con B es la de B con A.

La volatilidad de una cartera

La varianza de la cartera es la forma cuadrática w⊤Σ ww^\top \Sigma\, w: cada par de activos aporta su peso por su peso por su covarianza. Con dos activos se ve entera:

σp2=w12σ12+w22σ22+2 w1w2 σ1σ2 ρ12\sigma_p^2 = w_1^2\sigma_1^2 + w_2^2\sigma_2^2 + 2\,w_1 w_2\,\sigma_1\sigma_2\,\rho_{12}

El último término es el que diversifica: con ρ12<1\rho_{12} < 1, la varianza de la cartera es menor que la de la suma ponderada. Si todas las correlaciones fueran 1, la volatilidad de la cartera sería exactamente la suma ponderada de volatilidades. La celda lo comprueba cambiando CC por una matriz de unos y por la identidad (correlación cero):

IN [05]python
def vol_cartera(w, Sigma):
    return np.sqrt(w @ Sigma @ w)


w_eq = np.full(4, 0.25)
unos = D @ np.ones((4, 4)) @ D
cero = D @ np.eye(4) @ D
print(f"Suma ponderada w @ vols:  {w_eq @ vols:.2%}")
print(f"Correlaciones todas 1:    {vol_cartera(w_eq, unos):.2%}")
print(f"Correlaciones reales:     {vol_cartera(w_eq, Sigma):.2%}")
print(f"Correlaciones todas 0:    {vol_cartera(w_eq, cero):.2%}")
OUT [05]El papel de las correlaciones
Suma ponderada w @ vols:  16.29%
Correlaciones todas 1:    16.29%
Correlaciones reales:     11.68%
Correlaciones todas 0:    9.14%

La suma ponderada (16,29 %) coincide con el caso de correlación 1: es el riesgo de una cartera que no diversifica nada. Con las correlaciones reales la volatilidad baja al 11,68 %, y con correlaciones nulas bajaría al 9,14 %. La diferencia entre la suma ponderada y la volatilidad real es lo que aporta la diversificación, y depende solo de las correlaciones.

Pesos iguales en capital, no en riesgo

La cartera 1/N pone el mismo capital en cada activo. Es simple y difícil de batir, pero tiene un problema que la volatilidad total no muestra: los activos volátiles dominan el riesgo. La contribución al riesgo de cada activo es su peso por su covarianza con la cartera, dividida entre la varianza de la cartera, y las cuatro suman 1:

CRi=wi (Σw)iw⊤Σ w\text{CR}_i = \frac{w_i\,(\Sigma w)_i}{w^\top \Sigma\, w}

Una alternativa sencilla son los pesos inversos a la volatilidad (1/σ): cada activo pesa en proporción inversa a su volatilidad, así que el más tranquilo recibe más capital. Las dos carteras, lado a lado:

IN [06]python
def contribuciones(w, Sigma):
    return w * (Sigma @ w) / (w @ Sigma @ w)


w_iv = (1 / vols) / (1 / vols).sum()
tabla = pd.DataFrame({
    "peso_1N": w_eq, "riesgo_1N": contribuciones(w_eq, Sigma),
    "peso_1σ": w_iv, "riesgo_1σ": contribuciones(w_iv, Sigma),
}, index=activos)
print(tabla.round(3))
for nombre, pesos in (("1/N", w_eq), ("1/σ", w_iv)):
    print(f"{nombre:<4} volatilidad {vol_cartera(pesos, Sigma):.2%}"
          f" · suma ponderada {pesos @ vols:.2%}")
OUT [06]1/N frente a 1/σ
      peso_1N  riesgo_1N  peso_1σ  riesgo_1σ
RVAR     0.25      0.322    0.164      0.313
RFIJ     0.25      0.001    0.556      0.150
ORO      0.25      0.130    0.182      0.233
TECH     0.25      0.547    0.098      0.303
1/N  volatilidad 11.68% · suma ponderada 16.29%
1/σ  volatilidad 6.58% · suma ponderada 11.03%

Con pesos iguales, TECH aporta el 54,7 % del riesgo con el 25 % del capital, y RFIJ prácticamente nada: la cartera 1/N es, en riesgo, sobre todo una apuesta por TECH y RVAR. Con pesos 1/σ, RFIJ recibe más de la mitad del capital y las contribuciones quedan mucho más repartidas, entre el 15 % y el 31 %. No son iguales porque los pesos 1/σ ignoran las correlaciones: TECH y RVAR, muy correlacionados, siguen sumando más riesgo del que su volatilidad sugiere.

La figura de la cabecera resume las dos carteras: cada barra es la volatilidad real y el tramo claro, lo que la suma ponderada de volatilidades exagera. La cartera 1/σ es mucho menos volátil (6,58 % frente a 11,68 %), pero eso no la hace mejor: también rinde menos, porque pesa más la renta fija. Comparar carteras exige mirar rentabilidad y riesgo a la vez, y eso se hace en los niveles de construcción de carteras.

Por qué la mínima varianza es tan inestable

La lección 0.5.1 calculó la cartera de mínima varianza, con pesos proporcionales a Σ−11\Sigma^{-1}\mathbf{1}, y dejó una pregunta abierta: ¿por qué estas carteras son tan sensibles a la estimación de Σ\Sigma? Porque usan la inversa. Invertir una matriz amplifica sus errores, y una Σ\Sigma estimada con pocos datos tiene muchos. La prueba: estimar Σ\Sigma con ventanas consecutivas de 60 sesiones (unos tres meses), calcular en cada una los pesos de mínima varianza y los 1/σ, y ver cuánto se mueven:

IN [07]python
mv, iv = [], []
for inicio in range(0, len(R) - 59, 60):
    S = np.cov(R[inicio:inicio + 60], rowvar=False)
    x = np.linalg.solve(S, np.ones(4))
    mv.append(x / x.sum())
    v = np.sqrt(np.diag(S))
    iv.append((1 / v) / (1 / v).sum())
mv, iv = np.array(mv), np.array(iv)
print(f"Ventanas: {len(mv)}")
for nombre, P in (("mín. varianza", mv), ("1/σ", iv)):
    rango = P.max(axis=0) - P.min(axis=0)
    rotacion = np.abs(np.diff(P, axis=0)).sum(axis=1).mean()
    print(f"{nombre:<14} rango de pesos {rango.round(2)} · "
          f"rotación media {rotacion:.0%}")
OUT [07]Pesos con ventanas de 60 sesiones
Ventanas: 21
mín. varianza  rango de pesos [0.26 0.15 0.19 0.17] · rotación media 21%
1/σ            rango de pesos [0.06 0.14 0.05 0.03] · rotación media 8%

Los datos de cartera_sint.csv se generaron con correlaciones y volatilidades constantes: toda la variación de los pesos es error de estimación. Aun así, los pesos de mínima varianza cambian mucho más de una ventana a otra que los 1/σ, pasan de largos a cortos en ORO y TECH, y obligan a rotar más del doble de la cartera en cada rebalanceo, con sus costes (lección 0.1.2). Los pesos 1/σ solo usan la diagonal de Σ\Sigma (las volatilidades), que se estima mucho mejor que las correlaciones.

Las soluciones habituales —prohibir posiciones cortas, limitar el peso de cada activo, «encoger» la matriz de covarianzas hacia una más simple, usar ventanas más largas— son todas formas de no fiarse del todo de Σ−1\Sigma^{-1}, y se verán con las técnicas de construcción de carteras. De esta lección basta quedarse con la idea: cuanto más usa un método las correlaciones estimadas, más sensible es a sus errores.

Ejercicios

  1. Calcula la volatilidad de una cartera 60/40 de RVAR y RFIJ con la fórmula de dos activos (escrita a mano, con vols y C) y compruébala con vol_cartera. ¿Cuánto aporta la correlación negativa frente a una correlación cero?
Solución
IN [08]python
w1, w2 = 0.6, 0.4
s1, s2, rho = vols[0], vols[1], C[0, 1]
var_formula = (w1**2 * s1**2 + w2**2 * s2**2
               + 2 * w1 * w2 * s1 * s2 * rho)
w_6040 = np.array([w1, w2, 0, 0])
print(f"Fórmula:          {np.sqrt(var_formula):.3%}")
print(f"w @ Sigma @ w:    {vol_cartera(w_6040, Sigma):.3%}")
sin_corr = np.sqrt(w1**2 * s1**2 + w2**2 * s2**2)
print(f"Con correlación 0: {sin_corr:.3%}")
OUT [08]Cartera 60/40
Fórmula:          9.921%
w @ Sigma @ w:    9.921%
Con correlación 0: 10.291%

Las dos vías coinciden. La correlación negativa de RFIJ con RVAR (−0,19) quita casi cuatro décimas de volatilidad frente a una correlación cero (9,92 % frente a 10,29 %). Los pesos de ORO y TECH a cero hacen que la forma cuadrática de cuatro activos se reduzca a la de dos.

  1. Comprueba que las contribuciones al riesgo de la cartera 1/N suman 1 y que w * (Sigma @ w) suma la varianza de la cartera. ¿Qué activo reduce el riesgo de la cartera si se le da un poco más de peso?
Solución
IN [09]python
cr = contribuciones(w_eq, Sigma)
print(f"Suma de contribuciones: {cr.sum():.6f}")
print(np.isclose((w_eq * (Sigma @ w_eq)).sum(),
                 w_eq @ Sigma @ w_eq))
for nombre, c in zip(activos, Sigma @ w_eq):
    print(f"{nombre:<5}{c:8.4f}")
OUT [09]Contribuciones que suman el total
Suma de contribuciones: 1.000000
True
RVAR   0.0176
RFIJ   0.0001
ORO    0.0071
TECH   0.0298

Las contribuciones reparten la varianza total sin que sobre ni falte nada. Sigma @ w es la covarianza de cada activo con la cartera: el que la tiene más baja es el que menos riesgo añade por cada euro más. RFIJ tiene una covarianza prácticamente nula con la cartera 1/N, así que darle más peso (a costa de los demás) reduce la volatilidad: es lo que hacen, llevado al extremo, los pesos 1/σ y la mínima varianza.

  1. Calcula la volatilidad de dos carteras 50/50: TECH con RVAR y RVAR con RFIJ. Para cada una, la suma ponderada de volatilidades y la diferencia. ¿Qué pareja diversifica más y por qué?
Solución
IN [10]python
parejas = {"TECH+RVAR": [0.5, 0, 0, 0.5],
           "RVAR+RFIJ": [0.5, 0.5, 0, 0]}
for nombre, pesos in parejas.items():
    p = np.array(pesos)
    real, suma = vol_cartera(p, Sigma), p @ vols
    print(f"{nombre}: real {real:.2%} · suma {suma:.2%} · "
          f"diferencia {suma - real:.2%}")
OUT [10]Dos parejas
TECH+RVAR: real 21.74% · suma 22.55% · diferencia 0.81%
RVAR+RFIJ: real 8.32% · suma 10.89% · diferencia 2.58%

TECH y RVAR, con una correlación de 0,85, apenas diversifican: la volatilidad real (21,74 %) queda a menos de un punto de la suma ponderada. RVAR y RFIJ, con correlación negativa, quedan 2,58 puntos por debajo: casi una cuarta parte de su suma ponderada. Lo que diversifica no es el número de activos, sino lo poco que se parecen sus movimientos.

Respuesta plegada en cada pregunta

Autoevaluación

  1. R tiene forma (1302, 4) (días × activos) y w forma (4,). ¿Qué devuelve R @ w?

    • AUn número: la rentabilidad total de la cartera
    • BUn array de forma (1302,): la rentabilidad de la cartera cada día
    • CUn array de forma (4,): la rentabilidad media de cada activo
    • DUn error, porque las formas no coinciden
    Ver respuesta

    Respuesta B. Un array de forma (1302,): la rentabilidad de la cartera cada día

    En un producto de matrices, la dimensión interior (4 activos) desaparece y quedan las exteriores: 1302 días. Cada elemento es el producto escalar de los pesos con las rentabilidades de un día.

  2. Una cartera tiene un 50 % en dos activos con un 20 % de volatilidad cada uno. ¿En qué caso su volatilidad es exactamente del 20 %?

    • ASiempre: la media de 20 % y 20 % es 20 %
    • BSolo si la correlación entre los dos es 1
    • CSi la correlación es 0
    • DNunca
    Ver respuesta

    Respuesta B. Solo si la correlación entre los dos es 1

    La suma ponderada de volatilidades es la volatilidad de la cartera solo con correlación 1. Con correlación 0 sería 20 % / √2 ≈ 14,1 %; con −1, cero.

  3. En la cartera con un 25 % en cada activo, TECH aporta en torno al 55 % del riesgo. ¿Por qué?

    • APorque tiene más peso que los demás
    • BPorque es el más volátil y está muy correlacionado con RVAR: su peso en capital es el mismo, pero su peso en riesgo es mucho mayor
    • CPorque su rentabilidad es la más alta
    • DEs un error de cálculo: con pesos iguales, cada activo aporta un 25 %
    Ver respuesta

    Respuesta B. Porque es el más volátil y está muy correlacionado con RVAR: su peso en capital es el mismo, pero su peso en riesgo es mucho mayor

    La contribución al riesgo de cada activo es su peso por su covarianza con la cartera. Un activo volátil y correlacionado con el resto pesa en riesgo mucho más que en capital.

  4. ¿Por qué los pesos de mínima varianza estimados con ventanas de 60 sesiones cambian tanto de una ventana a otra?

    • APorque la cartera de mínima varianza está mal definida
    • BPorque resuelven un sistema con la inversa de una matriz de covarianzas estimada con pocos datos, y amplifican su error de estimación
    • CPorque los precios cambian
    • DPorque NumPy redondea
    Ver respuesta

    Respuesta B. Porque resuelven un sistema con la inversa de una matriz de covarianzas estimada con pocos datos, y amplifican su error de estimación

    Con 60 datos, covarianzas y correlaciones tienen mucho error, y resolver el sistema con la inversa amplifica las diferencias pequeñas. Los pesos 1/σ solo usan las volatilidades, que se estiman mejor, y cambian mucho menos.

Ideas clave

  • Una cartera es un vector de pesos; su rentabilidad, el producto escalar w @ r con las rentabilidades simples del periodo.
  • En un producto de matrices, la dimensión interior tiene que coincidir y desaparece: (1302, 4) @ (4,) da (1302,). Comprobar shape evita errores silenciosos.
  • La matriz de covarianzas es Σ = D C D: volatilidades en la diagonal de D y correlaciones en C. La varianza de la cartera es w′Σw.
  • La suma ponderada de volatilidades solo es la volatilidad de la cartera si todas las correlaciones son 1; la diferencia es la diversificación.
  • Pesos iguales en capital no son pesos iguales en riesgo. Los pesos 1/σ reparten mejor el riesgo y, frente a la mínima varianza, son mucho más estables.

Recursos

Documentación