Lección 4.1.6 · 75 min

Modelos factoriales: CAPM, Fama-French, PCA y atribución

Un modelo factorial separa lo que una cartera gana por exponerse a primas conocidas de lo que gana por sí misma. Esta lección regresa 20 fondos sintéticos con alfas y cargas publicadas sobre el CAPM y sobre seis factores con el formato de la biblioteca de Kenneth French, y comprueba qué se recupera, qué falla (el falso positivo, el market timing, la venta de puts, el cambio de estilo) y qué ven los componentes principales de un universo cuyos factores verdaderos se conocen.

Fig. — Alfa verdadero y alfa medido con seis factores de los 20 fondos sintéticos, con su intervalo del 95 % (errores de Newey-West), 2006-2025
Lecciones del nivel 4
  1. 4.1 · Teoría y optimización de carteras

    1. 4.1.1 Markowitz: frontera eficiente y restricciones reales
    2. 4.1.2 Estimación de covarianzas: shrinkage, modelos factoriales y denoising
    3. 4.1.3 Black-Litterman: del equilibrio del mercado a las visiones
    4. 4.1.4 Risk parity, mínima varianza, HRP y HERC
    5. 4.1.5 Optimización con CVaR, drawdown, rotación y costes
    6. 4.1.6 Modelos factoriales: CAPM, Fama-French, PCA y atribución
    7. 4.1.7 Rebalanceo y carteras de estrategias
    8. 4.1.8 Herramientas de carteras: Riskfolio-Lib, PyPortfolioOpt, skfolio y bt
  2. 4.2 · Gestión del riesgo y tamaño de posición

    1. 4.2.1 Riesgo por operación, stops y riesgo de ruina
    2. 4.2.2 Criterio de Kelly y volatility targeting: cuánto arriesgar
    3. 4.2.3 VaR, Expected Shortfall y backtesting del VaR
    4. 4.2.4 Stress testing y riesgos de correlación, liquidez, gap y contraparte
    5. 4.2.5 Control del drawdown y kill switch
    6. 4.2.6 Riesgo de derivados: griegas de cartera, margen y liquidaciones
  3. 4.3 · Microestructura y ejecución

    1. 4.3.1 Implementation shortfall, VWAP, TWAP y Almgren-Chriss
    2. 4.3.2 Order flow imbalance, señales de microestructura y latencia
  4. 4.4 · Brokers y APIs

    1. 4.4.1 Elegir bróker desde España o la UE: regulación, activos, costes y API
    2. 4.4.2 Interactive Brokers: TWS e IB Gateway, ib_async, contratos, datos y órdenes
    3. 4.4.3 Alpaca, Tradier, MetaTrader 5 con Python y OANDA v20
    4. 4.4.4 APIs de exchanges cripto: ccxt, Binance, Bybit, Kraken y Coinbase
    5. 4.4.5 Credenciales seguras: variables de entorno, gestores de secretos y permisos mínimos
  5. 4.5 · Sistema de trading en vivo

    1. 4.5.1 Arquitectura de un sistema en vivo: de los datos a la reconciliación
    2. 4.5.2 Gestión de órdenes: estados, fills parciales, cancelaciones y reconexiones
    3. 4.5.3 Paper trading, shadow trading y seguimiento del slippage
    4. 4.5.4 Programación de tareas y alertas: cron, APScheduler, Prefect, Telegram, Slack y email
    5. 4.5.5 Tolerancia a fallos: idempotencia, estado persistente y reconciliación al arrancar
    6. 4.5.6 NautilusTrader o LEAN en real: del backtest a producción con el mismo código

Del CAPM a seis factores

El CAPM (Sharpe, 1964) dice que, en equilibrio, el exceso de rendimiento esperado de cualquier activo es su beta por la prima del mercado. Aplicado a una cartera en serie temporal, la regresión

rt−rf,t=α+β (rm,t−rf,t)+εtr_t - r_{f,t} = \alpha + \beta\,(r_{m,t} - r_{f,t}) + \varepsilon_t

separa lo que la cartera gana por exponerse al mercado (β\beta) de lo que gana por sí misma (α\alpha). La lección 2.6.2 la usó para medir una estrategia frente a un índice. El problema es que el mercado no es la única prima por la que se cobra. Fama y French (1993) añadieron dos factores construidos como carteras largas y cortas de acciones: el tamaño (SMB, pequeñas menos grandes) y el valor (HML, cotización baja frente a su valor contable menos alta). Carhart (1997) añadió el momentum (los ganadores de los últimos doce meses, sin el último, menos los perdedores) y Fama y French (2015), la rentabilidad (RMW, empresas rentables menos poco rentables) y la inversión (CMA, empresas que invierten poco menos las que invierten mucho). Con los seis:

rt−rf,t=α+βm MKTt+s SMBt+h HMLt+r RMWt+c CMAt+m MOMt+εt.r_t - r_{f,t} = \alpha + \beta_m\,MKT_t + s\,SMB_t + h\,HML_t + r\,RMW_t + c\,CMA_t + m\,MOM_t + \varepsilon_t.
CAPM: mercadoFF3: + SMB, HMLCarhart: + MOMFF5: + RMW, CMASeis factores
Fuente Mermaid
flowchart TB
  C[CAPM: mercado] --> F3[FF3: + SMB, HML]
  F3 --> C4[Carhart: + MOM]
  F3 --> F5[FF5: + RMW, CMA]
  C4 --> F6[Seis factores]
  F5 --> F6

Un alfa en un modelo es un rendimiento que ese modelo no explica. Cada factor que se añade convierte en exposición algo que antes era alfa. Si las primas de los factores se pueden comprar barato (fondos indexados de estilos, futuros), un gestor que solo cobra primas de factores no aporta nada que no se pueda replicar. Cómo se construyen los factores con datos de acciones (ordenamientos por quintiles, Fama-MacBeth, IC) es la lección 2.3.5; aquí se usan ya construidos, para evaluar carteras.

Los datos son sintéticos y tienen la respuesta conocida. ff4_factores_mensuales.csv trae seis factores mensuales de 2006 a 2025 con el formato de la biblioteca de Kenneth French (en tanto por uno), coherentes con el universo de la lección 4.1.4. ff4_fondos_mensuales.csv trae 20 fondos ficticios generados con alfas y cargas publicadas en ff4_fondos_verdad.csv, entre ellos un fondo con alfa real, uno con alfa pequeño, varios sin alfa, dos con alfa negativo por comisiones, dos no lineales y uno que cambia de estilo. Ningún resultado de esta lección dice nada de fondos reales.

IN [01]python
import numpy as np
import pandas as pd
import statsmodels.api as sm

fac = pd.read_csv("ff4_factores_mensuales.csv", index_col="mes")
fondos = pd.read_csv("ff4_fondos_mensuales.csv").pivot(
    index="mes", columns="fondo", values="rendimiento")
verdad = pd.read_csv("ff4_fondos_verdad.csv", index_col="fondo")
exc = fondos.sub(fac["rf"], axis=0)       # exceso del tipo libre
F6 = ["mkt_rf", "smb", "hml", "rmw", "cma", "mom"]

print(fac.shape, exc.shape, fac.index[0], "a", fac.index[-1])
resumen = pd.DataFrame({
    "media_%": 1200 * fac[F6].mean(),
    "vol_%": 100 * np.sqrt(12) * fac[F6].std(),
    "peor_mes_%": 100 * fac[F6].min(),
})
resumen["sharpe"] = resumen["media_%"] / resumen["vol_%"]
resumen.round(2)
OUT [01]Factores y fondos
(240, 7) (240, 20) 2006-01 a 2025-12
        media_%  vol_%  peor_mes_%  sharpe
mkt_rf     9.27  22.22      -28.94    0.42
smb        0.18   8.01       -8.75    0.02
hml       -2.45   8.35       -7.77   -0.29
rmw        3.52   7.67       -6.15    0.46
cma        1.69   6.94       -7.41    0.24
mom        4.05  17.64      -33.13    0.23

El mercado sintético tiene una prima media del 9,27 % anual con un 22,2 % de volatilidad y un peor mes del −28,9 %. Valor (HML) tiene prima negativa en la muestra, rentabilidad (RMW) la más alta en Sharpe (0,46) y momentum un peor mes del −33,1 %, el crash de 2009 que imitan los datos.

La fuente primaria: la biblioteca de Kenneth French

Los factores reales se publican gratis en la biblioteca de datos de Kenneth French (Dartmouth), que es la fuente primaria: los artículos de Fama y French usan esas series y casi todo el sector las toma como referencia. Cada fichero es un ZIP con un CSV de formato propio. Consultada el 22 de octubre de 2026, F-F_Research_Data_5_Factors_2x3_CSV.zip tiene tres líneas de texto («This file was created using the 202608 CRSP database», de dónde sale el tipo libre: Ibbotson hasta mayo de 2024 y el índice ICE BofA de letras a un mes desde junio), una línea en blanco, la cabecera ,Mkt-RF,SMB,HML,RMW,CMA,RF, los meses en formato AAAAMM con los valores en porcentaje desde julio de 1963 hasta agosto de 2026, otra línea en blanco y una segunda tabla, Annual Factors: January-December, con los años. El momentum va en otro fichero, F-F_Momentum_Factor_CSV.zip, con una cabecera de texto más larga que avisa de que los datos que faltan se marcan con −99,99 o −999. Las líneas terminan en CRLF.

Un lector robusto no cuenta líneas de cabecera, porque cambian de un fichero a otro: busca la primera línea que empieza por coma y lee hasta la primera que no empieza por un año. La celda lo prueba con un texto que copia esa disposición con los valores sintéticos del curso:

IN [02]python
import io

# Un fichero con la misma disposición que F-F_Research_Data_5_Factors
# _2x3.csv: cabecera de texto, meses AAAAMM, valores en %, y después
# los factores anuales. Los valores son los sintéticos del curso.
cab = ",Mkt-RF,SMB,HML,RMW,CMA,RF"
filas = [f"{m.replace('-', '')},"
         + ",".join(f"{100 * fac.loc[m, c]:8.2f}"
                    for c in ["mkt_rf", "smb", "hml", "rmw", "cma",
                              "rf"])
         for m in fac.index[:3]]
texto = "\r\n".join(
    ["Fichero de ejemplo (formato de la biblioteca de K. French).",
     "", cab, *filas, "", " Annual Factors: January-December ",
     cab, "  2006,   10.00,    1.00,    2.00,    3.00,    4.00,"
     "    5.00"])

def leer_french(texto):
    """Primera tabla (mensual) de un CSV de la biblioteca de French."""
    lineas = texto.splitlines()
    ini = next(i for i, l in enumerate(lineas) if l.startswith(","))
    fin = next(i for i in range(ini + 1, len(lineas))
               if not lineas[i].strip()[:1].isdigit())
    df = pd.read_csv(io.StringIO("\n".join(lineas[ini:fin])),
                     index_col=0)
    df.index = pd.PeriodIndex(df.index.astype(str), freq="M")
    df.columns = (df.columns.str.strip().str.lower()
                  .str.replace("-", "_"))
    df = df.replace([-99.99, -999], np.nan)   # faltantes
    return df / 100                            # de % a tanto por uno

leer_french(texto)
OUT [02]El formato de la biblioteca de Kenneth French
         mkt_rf     smb     hml     rmw     cma      rf
2006-01  0.0080 -0.0235  0.0296  0.0151  0.0107  0.0034
2006-02 -0.0064 -0.0207  0.0091  0.0099  0.0265  0.0033
2006-03  0.0236 -0.0360 -0.0225  0.0560  0.0189  0.0041

Con red, el mismo lector sirve para los ficheros reales. El código descarga el ZIP con urllib (de la biblioteca estándar), lee el CSV de dentro y une los cinco factores con el momentum por mes; no se ejecuta en el curso:

IN [03]python
import io
import urllib.request
import zipfile

BASE = ("https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/"
        "ftp/")
def descargar_french(nombre):
    """Descarga un ZIP de la biblioteca y devuelve el CSV como texto."""
    with urllib.request.urlopen(BASE + nombre, timeout=30) as r:
        z = zipfile.ZipFile(io.BytesIO(r.read()))
    with z.open(z.namelist()[0]) as f:
        return f.read().decode("latin-1")

ff5 = leer_french(descargar_french(
    "F-F_Research_Data_5_Factors_2x3_CSV.zip"))
mom = leer_french(descargar_french("F-F_Momentum_Factor_CSV.zip"))
ff6 = ff5.join(mom, how="inner")

Veinte fondos con la respuesta conocida

La regresión es la misma para todos los fondos: mínimos cuadrados con constante, en datos mensuales, y errores de Newey-West con tres retardos (lección 2.1.1), que corrigen la heterocedasticidad y algo de autocorrelación de los residuos. El alfa mensual se multiplica por 12 para anualizarlo.

IN [04]python
def regresion(y, X, hac=True):
    """MCO con constante; errores de Newey-West (3 meses) si hac."""
    m = sm.OLS(y, sm.add_constant(X))
    if hac:
        return m.fit(cov_type="HAC", cov_kwds={"maxlags": 3})
    return m.fit()

filas = {}
for f in exc:
    capm = regresion(exc[f], fac[["mkt_rf"]])
    ff = regresion(exc[f], fac[F6])
    filas[f] = {
        "alfa_verdad_%": 100 * verdad.loc[f, "alfa_anual"],
        "alfa_capm_%": 1200 * capm.params.iloc[0],
        "beta_capm": capm.params.iloc[1],
        "alfa_ff6_%": 1200 * ff.params.iloc[0],
        "t_ff6": ff.tvalues.iloc[0],
        "r2_ff6": ff.rsquared,
    }
tab = pd.DataFrame(filas).T
tab.round(2)
OUT [04]CAPM y seis factores para los 20 fondos
     alfa_verdad_%  alfa_capm_%  beta_capm  alfa_ff6_%  t_ff6  r2_ff6
F01           -0.1         0.04       1.00        0.05   0.55    1.00
F02           -1.0        -0.24       1.02       -0.24  -0.56    0.99
F03            0.0        -1.60       1.01       -0.72  -0.74    0.97
F04            0.0        -1.18       1.07       -1.05  -1.02    0.97
F05            0.0         3.31       0.85        0.09   0.12    0.97
F06            0.0         1.72       0.95       -1.67  -1.52    0.97
F07            0.0        -0.65       1.28        0.64   0.46    0.96
F08            3.0         3.52       0.98        3.29   2.86    0.96
F09            1.5         3.04       1.00        2.90   2.57    0.95
F10            0.0         1.45       0.73       -0.09  -0.13    0.98
F11            2.0         3.21       0.03        2.24   2.05    0.38
F12            0.0         2.88       0.67        3.13   4.15    0.96
F13            6.0        -1.19       0.54       -1.21  -1.82    0.96
F14            0.0        -3.70       1.01       -2.79  -1.98    0.94
F15            1.0         2.24       0.57        0.25   0.32    0.94
F16           -2.5        -1.99       1.00       -2.18  -3.15    0.99
F17            0.0         0.42       0.78       -0.39  -0.55    0.97
F18            0.0        -1.17       1.08        2.06   2.55    0.98
F19           -1.5        -0.09       2.00       -0.27  -0.53    1.00
F20            0.0         0.99       0.99        1.12   0.76    0.94

La figura de la cabecera pone el alfa verdadero (cuadrado hueco) junto al medido con seis factores (cuadrado lleno) y su intervalo del 95 %. En la mayoría de fondos el intervalo contiene la verdad. Cuatro lecturas de la tabla:

  • El CAPM se equivoca con los fondos de estilo. F05 (calidad) mide un 3,31 % de alfa del CAPM y un 0,09 % con seis factores; F06 (momentum), un 1,72 % y un −1,67 %; F03 (valor profundo), un −1,60 % y un −0,72 %. El alfa del CAPM de un fondo de estilo es, sobre todo, la prima de su estilo en la muestra.
  • El R² separa fondos activos de indexados. F01 (indexado amplio) y F19 (apalancado 2x) tienen R² de 1,00; F11, un largo-corto neutral al mercado, 0,38: casi todo su riesgo es propio.
  • El alfa medido tiene un error grande. Con 20 años, el error típico del alfa anual es la volatilidad propia dividida por √20: para un fondo con un 5 % de volatilidad propia, 1,1 puntos. F09, con un 1,5 % verdadero, mide un 2,90 %; F19, con un −1,5 % verdadero, mide un −0,27 %.
  • Tres fondos salen mal por razones distintas. F12 y F13 son no lineales y F14 cambia de estilo; se ven más abajo.

Las cargas se recuperan mucho mejor que los alfas, porque los factores se mueven mucho más que su media:

IN [05]python
cargas = pd.DataFrame(
    {f: regresion(exc[f], fac[F6]).params[1:] for f in exc},
    index=F6).T
err = cargas - verdad[[f"b_{c}" for c in F6]].values
lineales = [f for f in exc if f not in ("F12", "F13", "F14")]
print("error absoluto medio de las cargas (17 fondos lineales):")
err.loc[lineales].abs().mean().round(3)
OUT [05]Cargas medidas frente a cargas verdaderas
error absoluto medio de las cargas (17 fondos lineales):
mkt_rf    0.007
smb       0.023
hml       0.040
rmw       0.028
cma       0.042
mom       0.008
dtype: float64

En los 17 fondos lineales, el error absoluto medio de la carga de mercado es de 0,007 y el de las demás, entre 0,008 y 0,042. Una regresión factorial es un buen instrumento para medir exposiciones y uno malo para medir alfas.

Atribución: de dónde sale el rendimiento

Con las cargas estimadas, el exceso medio de un fondo se descompone exactamente en alfa más carga por media de cada factor (la regresión con constante pasa por las medias):

r−rf‾=α^+∑kb^k fk‾.\overline{r - r_f} = \hat\alpha + \sum_k \hat b_k\, \overline{f_k}.

F10 es el caso de libro: un 1,45 % de alfa del CAPM.

IN [06]python
def atribucion(f, factores):
    """Rendimiento medio anual: alfa + carga × media de cada factor."""
    m = regresion(exc[f], fac[factores], hac=False)
    partes = 1200 * m.params[1:] * fac[factores].mean()
    partes["alfa"] = 1200 * m.params.iloc[0]
    partes["total"] = 1200 * exc[f].mean()
    return partes

pd.DataFrame({"capm": atribucion("F10", ["mkt_rf"]),
              "ff6": atribucion("F10", F6)}).round(2)
OUT [06]F10: el alfa del CAPM eran factores
        capm   ff6
alfa    1.45 -0.09
cma      NaN  0.44
hml      NaN -1.24
mkt_rf  6.79  7.51
mom      NaN  0.34
rmw      NaN  1.21
smb      NaN  0.07
total   8.25  8.25

El exceso medio del 8,25 % anual es el mismo en las dos columnas; cambia el reparto. Con el CAPM, 6,79 puntos son mercado y 1,45 alfa. Con seis factores, el mercado sube a 7,51 (la beta de mercado de F10 con los demás factores es mayor que su beta del CAPM), rentabilidad aporta 1,21, inversión 0,44, momentum 0,34, valor resta 1,24 y el alfa queda en −0,09. El fondo tenía exposiciones a primas que el CAPM no mide; no tenía alfa.

Muchos fondos, algunos con suerte

Probar el alfa de 20 fondos es hacer 20 contrastes. Con un nivel del 5 %, aunque ninguno tuviera alfa, se esperaría uno significativo por azar. La lección 2.1.8 explica las correcciones; aquí se aplican con la verdad a la vista:

IN [07]python
from statsmodels.stats.multitest import multipletests

p = pd.Series({f: regresion(exc[f], fac[F6]).pvalues.iloc[0]
               for f in exc})
holm = multipletests(p, alpha=0.05, method="holm")
bh = multipletests(p, alpha=0.05, method="fdr_bh")
sig = pd.DataFrame({"alfa_verdad_%": tab["alfa_verdad_%"],
                    "t": tab["t_ff6"], "p": p,
                    "holm": holm[0], "fdr_bh": bh[0]})
sig[sig["p"] < 0.05].round(4)
OUT [07]Veinte contrastes a la vez
     alfa_verdad_%       t       p   holm  fdr_bh
F08            3.0  2.8593  0.0042  False    True
F09            1.5  2.5705  0.0102  False    True
F11            2.0  2.0466  0.0407  False   False
F12            0.0  4.1459  0.0000   True    True
F14            0.0 -1.9828  0.0474  False   False
F16           -2.5 -3.1471  0.0016   True    True
F18            0.0  2.5541  0.0106  False    True

Siete fondos tienen p < 0,05. Con la verdad conocida:

FondoAlfa verdaderop¿Qué es?HolmBH
F1200,0000Market timing: alfa espurio de un modelo mal especificadosísí
F16−2,5 %0,0016Comisión alta: alfa negativo realsísí
F083 %0,0042Alfa realnosí
F091,5 %0,0102Alfa real pequeño, medido con suertenosí
F1800,0106Falso positivonosí
F112 %0,0407Alfa realnono
F1400,0474Cambio de estilo: alfa espurionono

Holm controla la probabilidad de cometer algún falso positivo entre los 20 y se queda con F12 y F16: elimina el falso positivo F18, pero también los alfas reales de F08, F09 y F11. Benjamini-Hochberg controla la proporción de falsos descubrimientos y acepta cinco: tres con alfa real o negativo real, el falso positivo F18 y el espurio F12. Ninguna corrección arregla F12: su t de 4,15 es real, pero mide un modelo mal planteado, no una habilidad.

F18 es un fondo «contrario» con una carga de −0,4 en momentum y cero alfa. En esta muestra su residuo tuvo una media positiva del orden de dos errores típicos. Con 20 fondos, que a uno le pase es lo esperable.

sínosínonosíAlfa con t 2¿Muchos fondos?Holm o BH¿No lineal?Añadir TM o HM¿Carga estable?Ventanas móvilesAlfa creíble
Fuente Mermaid
flowchart TB
  A["Alfa con t > 2"] --> M{¿Muchos fondos?}
  M -- sí --> C[Holm o BH]
  M -- no --> N{¿No lineal?}
  C --> N
  N -- sí --> T[Añadir TM o HM]
  N -- no --> E{¿Carga estable?}
  T --> E
  E -- no --> V[Ventanas móviles]
  E -- sí --> R[Alfa creíble]

Market timing y venta de opciones

Dos fondos tienen rendimientos que no son lineales en el mercado:

  • F12 hace market timing. Sube su exposición antes de las subidas y la baja antes de las caídas. El resultado es una relación convexa con el mercado; Treynor y Mazuy (1966) la modelaron con un término cuadrático: r−rf=α+β MKT+γ MKT2+…r - r_f = \alpha + \beta\,MKT + \gamma\,MKT^2 + \dots. Verdad: γ=0,5\gamma = 0,5, α=0\alpha = 0.
  • F13 vende puts. Cobra una prima fija y, cuando el mercado cae, pierde más que su beta normal. Henriksson y Merton (1981) lo modelaron con un término min⁡(MKT,0)\min(MKT, 0): una beta distinta en las caídas. Verdad: α=6 %\alpha = 6\,\% anual (la prima), beta normal 0,4 y beta extra de 0,25 en las caídas.
IN [08]python
X = fac[F6].copy()
X["mkt2"] = fac["mkt_rf"] ** 2                  # Treynor-Mazuy
X["mkt_neg"] = np.minimum(fac["mkt_rf"], 0)     # Henriksson-Merton

tm = regresion(exc["F12"], X[F6 + ["mkt2"]])
hm = regresion(exc["F13"], X[F6 + ["mkt_neg"]])
pd.DataFrame({
    "F12_lineal": [tab.loc["F12", "alfa_ff6_%"], np.nan],
    "F12_TM": [1200 * tm.params.iloc[0], tm.params.iloc[-1]],
    "F13_lineal": [tab.loc["F13", "alfa_ff6_%"], np.nan],
    "F13_HM": [1200 * hm.params.iloc[0], hm.params.iloc[-1]],
}, index=["alfa_%", "coef_no_lineal"]).round(2)
OUT [08]F12 y F13: market timing y venta de puts
                F12_lineal  F12_TM  F13_lineal  F13_HM
alfa_%                3.13    0.68       -1.21    5.83
coef_no_lineal         NaN    0.44         NaN    0.24

El modelo lineal se equivoca con los dos y en sentidos opuestos. A F12 le atribuye un 3,13 % de alfa: la media de MKT2MKT^2 es positiva y, sin el término cuadrático, acaba en la constante. Con Treynor-Mazuy el alfa baja al 0,68 % (no significativo) y γ\gamma sale 0,44, cerca de 0,5. A F13 le atribuye un −1,21 %, y con Henriksson-Merton el alfa sale 5,83 % y la beta extra 0,24: la verdad.

¿Qué es entonces el 6 % de F13? Lo que cobra por asegurar a otros contra las caídas. Lo que le cuesta:

IN [09]python
b_mkt = hm.params.iloc[1]
extra = hm.params.iloc[-1]
print(f"beta normal {b_mkt:.2f}, beta extra en caídas {extra:.2f}")
coste_put = 1200 * extra * X["mkt_neg"].mean()
alfa_hm = 1200 * hm.params.iloc[0]
print(f"alfa (prima cobrada):         {alfa_hm:6.2f} %")
print(f"media de extra·min(mkt, 0):   {coste_put:6.2f} %")
peores = fac["mkt_rf"].nsmallest(3).index
(100 * pd.DataFrame({"mkt_rf": fac.loc[peores, "mkt_rf"],
                     "F13": exc.loc[peores, "F13"]})).round(2)
OUT [09]Lo que cobra F13 y lo que le cuesta
beta normal 0.40, beta extra en caídas 0.24
alfa (prima cobrada):           5.83 %
media de extra·min(mkt, 0):    -5.68 %
         mkt_rf    F13
mes
2018-10  -28.94 -18.54
2011-09  -21.28 -12.79
2025-03  -20.74 -13.54
Fig. — Rendimiento mensual medio del fondo F13, que vende puts, por tramos del mercado, con el ajuste de Henriksson-Merton (2006-2025)

La beta extra de 0,24 en las caídas tiene un coste medio del 5,68 % anual, casi todo el alfa. En los tres peores meses del mercado sintético (−28,9 %, −21,3 % y −20,7 %), F13 pierde un 18,5 %, un 12,8 % y un 13,5 %. La figura muestra la forma: medias por tramos del mercado sobre una línea quebrada con pendiente 0,40 en las subidas y 0,64 en las caídas. Un «alfa» que se cobra a cambio de un riesgo de cola no es habilidad; es una prima de riesgo con otro nombre (lección 3.6.1). La regresión lineal, además, lo esconde.

Cambio de estilo

F14 era un fondo de valor (carga de 0,8 en HML) hasta diciembre de 2015 y desde enero de 2016 es de crecimiento (−0,3). Una sola regresión de toda la muestra promedia los dos:

IN [10]python
movil = {}
for i in range(36, len(exc) + 1):
    v = slice(i - 36, i)
    m = regresion(exc["F14"].iloc[v], fac[F6].iloc[v], hac=False)
    movil[exc.index[i - 1]] = m.params["hml"]
movil = pd.Series(movil)
mitades = {t: regresion(exc["F14"].loc[a:b], fac[F6].loc[a:b],
                        hac=False).params["hml"]
           for t, (a, b) in {"2006-2015": ("2006-01", "2015-12"),
                             "2016-2025": ("2016-01", "2025-12")
                             }.items()}
print("toda la muestra:", round(cargas.loc["F14", "hml"], 2),
      "· por mitades:", {k: round(float(v), 2)
                         for k, v in mitades.items()})
movil.iloc[::24].round(2)
OUT [10]F14: el estilo cambia en 2016
toda la muestra: 0.22 · por mitades: {'2006-2015': 0.85, '2016-2025': -0.35}
2008-12    0.94
2010-12    0.84
2012-12    0.86
2014-12    0.79
2016-12    0.51
2018-12   -0.39
2020-12   -0.35
2022-12   -0.36
2024-12   -0.35
dtype: float64
Fig. — Carga en valor (HML) del fondo F14, que cambia de estilo en enero de 2016: regresión móvil de 36 meses frente a la verdad

Con toda la muestra, la carga en valor sale 0,22: un fondo «ligeramente de valor» que nunca existió. Por mitades sale 0,85 y −0,35, cerca de la verdad. La regresión móvil de 36 meses ve el cambio, pero tarde: cruza el cero en septiembre de 2018, casi tres años después. Además, el promedio mal especificado deja un alfa de −2,79 % con t −1,98, que no está en el fondo: es la consecuencia de imponer una carga fija a un fondo que no la tiene. Ventanas móviles, regresiones por tramos o modelos de cargas variables (el filtro de Kalman de la lección 2.1.6) son las herramientas para esto.

Atribución del riesgo de una cartera

La atribución también sirve para el riesgo. Con un modelo factorial Σ=B Σf B′+D\Sigma = B\,\Sigma_f\,B' + D, la varianza de una cartera ww es x′Σfx+w′Dwx'\Sigma_f x + w'Dw con x=B′wx = B'w, la exposición de la cartera a cada factor. Por Euler, como en la lección 4.1.4, cada factor aporta xk(Σfx)k/σp2x_k(\Sigma_f x)_k / \sigma_p^2 y el resto es idiosincrático.

El universo de la lección 4.1.4 se generó con un modelo de diez factores publicado: cargas verdaderas en universo4_verdad.csv, volatilidades y correlaciones de los factores por régimen en universo4_factores_verdad.csv y los rendimientos diarios realizados de los factores en universo4_factores_diarios.parquet. Se puede calcular la atribución verdadera del régimen de calma y la estimada con regresiones diarias en las sesiones de calma, y compararlas:

IN [11]python
precios = pd.read_parquet("universo4_precios.parquet")
precios = precios.set_index("fecha")
diarios = pd.read_parquet("universo4_factores_diarios.parquet")
diarios = diarios.set_index("fecha")
r = precios.pct_change()
r.iloc[0] = precios.iloc[0] / 100 - 1
cols = [c for c in precios if c not in ("MONET", "RF_T13")]
ex_d = r[cols].sub(diarios["rf"], axis=0)
nombres = [c[2:] for c in diarios if c.startswith("f_")]
f_d = diarios[[f"f_{k}" for k in nombres]].set_axis(nombres, axis=1)

# verdad: B, covarianza de los factores y D del régimen de calma
uv = pd.read_csv("universo4_verdad.csv", index_col="activo").loc[cols]
B = uv[[f"b_{k}" for k in nombres]].values
fv = pd.read_csv("universo4_factores_verdad.csv")
fv = fv[fv["regimen"] == "calma"].set_index("factor").loc[nombres]
C = fv[[f"corr_{k}" for k in nombres]].values
Sf = np.outer(fv["vol_anual"], fv["vol_anual"]) * C
D = np.diag(uv["vol_idio"] ** 2)
cvr = pd.read_csv("universo4_cov_verdad.csv")
cvr = cvr[cvr["regimen"] == "calma"].set_index("activo")
dif = B @ Sf @ B.T + D - cvr.loc[cols, cols].values
print(f"máx. |BΣfB' + D − Σ_calma|: {np.abs(dif).max():.1e}")

# estimación: regresiones diarias en las sesiones de calma
calma = diarios["regimen"] == "calma"
Xf = sm.add_constant(f_d[calma])
coef = np.linalg.lstsq(Xf.values, ex_d[calma].values, rcond=None)[0]
B_e = coef[1:].T
res = ex_d[calma].values - Xf.values @ coef
Sf_e = 252 * np.cov(f_d[calma].values.T)
D_e = np.diag(252 * res.var(axis=0, ddof=Xf.shape[1]))

def riesgo_factores(w, B, Sf, D):
    x = B.T @ w                          # exposición a cada factor
    var = x @ Sf @ x + w @ D @ w
    rc = x * (Sf @ x) / var              # contribución de cada factor
    return pd.Series(np.r_[rc, w @ D @ w / var],
                     index=nombres + ["idiosincrático"])

w = pd.Series(0.0, index=cols)
w[["RV_EEUU", "RF_T710"]] = [0.6, 0.4]
w_n = np.full(len(cols), 1 / len(cols))
pd.DataFrame({
    "60/40_verdad": riesgo_factores(w.values, B, Sf, D),
    "60/40_estim": riesgo_factores(w.values, B_e, Sf_e, D_e),
    "1/N_verdad": riesgo_factores(w_n, B, Sf, D),
    "1/N_estim": riesgo_factores(w_n, B_e, Sf_e, D_e),
}).round(3)
OUT [11]Riesgo de una cartera por factores: verdad y estimación
máx. |BΣfB' + D − Σ_calma|: 5.0e-09
                60/40_verdad  60/40_estim  1/N_verdad  1/N_estim
mercado                0.976        0.974       0.786      0.777
tamano                -0.008       -0.006       0.007      0.006
valor                  0.002        0.003       0.004      0.002
tipos                 -0.000        0.002      -0.011     -0.010
credito                0.000       -0.000       0.021      0.021
materias               0.000        0.000       0.107      0.114
dolar                 -0.000       -0.001       0.052      0.055
emergentes             0.000       -0.001       0.019      0.021
europa                 0.000        0.000       0.001      0.000
japon                  0.000       -0.000       0.003      0.002
idiosincrático         0.030        0.029       0.011      0.011

Primero, la comprobación: B Σf B′+DB\,\Sigma_f\,B' + D con los ficheros publicados reproduce la covarianza verdadera de calma con un error de 5⋅10−95 \cdot 10^{-9}, es decir, el redondeo de los ficheros. Después, la atribución: en la 60/40, el factor de mercado explica el 97,6 % del riesgo (verdad) y el 97,4 % (estimado); los tipos, nada: la cartera tiene exposición a ellos, pero su covarianza con la cartera es casi nula, porque la varianza propia del factor se compensa con su correlación negativa con el mercado. En la 1/N de los 36 activos, el mercado explica el 78,6 %, las materias primas el 10,7 % y el dólar el 5,2 %; las estimaciones están a menos de un punto de la verdad en todos los factores. Con un modelo bien especificado y 20 años de datos diarios, la atribución del riesgo es fiable. La del rendimiento, como se vio con los alfas, no tanto.

PCA: factores sin nombre

Los factores de Fama-French son observados: carteras construidas con una regla económica. Los componentes principales son factores estadísticos: las direcciones de máxima varianza de los propios rendimientos, ortogonales entre sí, sin nombre ni signo fijado. Se calculan con la descomposición en autovalores de la matriz de correlación (lección 0.7.3); su uso para estimar covarianzas con pocos datos está en la lección 4.1.2. Aquí la pregunta es otra: ¿los componentes de un universo son sus factores?

IN [12]python
Z = (ex_d - ex_d.mean()) / ex_d.std()
autoval, autovec = np.linalg.eigh(np.corrcoef(Z.values.T))
autoval, autovec = autoval[::-1], autovec[:, ::-1]
pcs = Z.values @ autovec[:, :10]
print("autovalores:", autoval[:6].round(2))
print("varianza explicada por 1, 3 y 10:",
      (autoval.cumsum()[[0, 2, 9]] / autoval.sum()).round(3))

def r2(y, X):
    X = np.column_stack([np.ones(len(X)), X])
    e = y - X @ np.linalg.lstsq(X, y, rcond=None)[0]
    return 1 - e.var() / y.var()

pd.DataFrame({k: [r2(f_d[f].values, pcs[:, :k]) for f in nombres]
              for k in (1, 3, 5, 10)}, index=nombres).round(2)
OUT [12]Componentes principales del universo
autovalores: [20.15  6.11  2.68  1.06  0.95  0.74]
varianza explicada por 1, 3 y 10: [0.56  0.804 0.931]
              1     3     5     10
mercado     0.97  0.98  0.99  0.99
tamano      0.05  0.05  0.06  0.09
valor       0.00  0.06  0.08  0.78
tipos       0.05  0.96  0.98  0.98
credito     0.28  0.33  0.93  0.95
materias    0.21  0.85  0.89  0.93
dolar       0.16  0.35  0.65  0.91
emergentes  0.19  0.23  0.24  0.41
europa      0.00  0.00  0.01  0.03
japon       0.00  0.00  0.00  0.95

El primer componente explica el 56 % de la varianza de los 36 activos; los tres primeros, el 80 %, y los diez primeros, el 93 %. Que haya diez factores verdaderos no quiere decir que los diez primeros componentes sean esos diez factores. La tabla da el R² de la regresión de cada factor verdadero sobre los kk primeros componentes:

Fig. — Cuánto de cada factor verdadero del universo recuperan los componentes principales de los 36 activos (2006-2025)
  • Un componente ya es el mercado (R² 0,97). Con tres, aparecen los tipos (0,96) y las materias primas (0,85).
  • Con cinco, el crédito (0,93); con diez, el dólar (0,91), Japón (0,95) y el valor (0,78).
  • Tamaño, emergentes y Europa no aparecen ni con diez componentes (0,09, 0,41 y 0,03). El factor europeo, con un 6 % de volatilidad, solo carga en dos activos (la bolsa europea con y sin cobertura); el de Japón, con un 9 %, también en dos, y sí aparece. PCA ordena por varianza explicada: un factor estrecho y poco volátil queda por debajo de combinaciones de ruido idiosincrático de otros activos.
IN [13]python
cargas_pc = pd.DataFrame(autovec[:, :3], index=cols,
                         columns=["pc1", "pc2", "pc3"])
for c in cargas_pc:
    s = cargas_pc[c].sort_values()
    print(c, "extremos:", ", ".join(
        f"{k} {v:+.2f}" for k, v in pd.concat([s.head(2),
                                               s.tail(2)]).items()))
OUT [13]Qué activos cargan en cada componente
pc1 extremos: RF_T20 -0.05, RF_T710 -0.05, SC_IND +0.21, RV_EEUU +0.22
pc2 extremos: RF_T710 -0.38, RF_T37 -0.38, ES_VAL +0.05, SC_FIN +0.06
pc3 extremos: MP_AMP -0.48, MP_ENE -0.47, RF_CIG +0.10, RF_CHY +0.14

Las cargas de los tres primeros componentes se pueden interpretar porque el universo es sencillo: el primero carga positivo en todas las bolsas (mercado), el segundo carga casi solo en los bonos del Tesoro con el mismo signo (tipos; el signo negativo es arbitrario: un componente y su opuesto son el mismo) y el tercero, en las materias primas. En un universo real la interpretación es más difícil y cambia de una ventana a otra; a partir del cuarto componente casi nunca hay una lectura económica limpia.

Para qué sirve cada tipo de factor:

Factores observados (Fama-French)Componentes principales
Qué sonCarteras con una regla económicaDirecciones de máxima varianza
InterpretaciónDirecta (valor, tamaño…)Solo los primeros, y no siempre
Se pueden comprarSí, aproximadamenteNo como tales
Para quéEvaluar gestores, atribuir rendimientoReducir dimensión, estimar covarianzas (4.1.2), detectar cambios de régimen
RiesgoOlvidar un factor (el alfa lo absorbe)Sobreinterpretar los componentes

Errores típicos

  • Quedarse con el CAPM. Un alfa del CAPM en un fondo de estilo es la prima del estilo.
  • Factores de otra región o moneda. Factores de EE. UU. para un fondo europeo meten en el alfa el tipo de cambio y la diferencia entre mercados.
  • Leer el t de un fondo entre muchos. Entre 20 fondos, uno sin alfa sale con t > 2 (F18).
  • Modelos lineales para estrategias con opciones o con timing. La regresión lineal fabrica alfa (F12) o lo esconde (F13). Para estrategias de venta de volatilidad, el alfa es la prima de un seguro.
  • Cargas fijas para fondos que cambian. F14 sale con un estilo que nunca tuvo y un alfa negativo espurio.
  • Interpretar todos los componentes principales. El primero suele ser el mercado; a partir de ahí, cuidado.
  • Rendimientos logarítmicos. La regresión factorial de una cartera usa rendimientos simples en exceso, como los factores de French.

Ejercicios

  1. Repite la regresión de seis factores de F08 solo con los diez primeros años y solo con los diez últimos. ¿Cuántas veces sale significativo su alfa (verdadero: 3 %)? ¿Qué dice eso de evaluar a un gestor con diez años de datos?
Solución
IN [14]python
for a, b in [("2006-01", "2015-12"), ("2016-01", "2025-12")]:
    m = regresion(exc["F08"].loc[a:b], fac[F6].loc[a:b])
    print(f"{a[:4]}-{b[:4]}: alfa {1200 * m.params.iloc[0]:5.2f} %"
          f"  t {m.tvalues.iloc[0]:5.2f}")
OUT [14]El alfa de F08 por décadas
2006-2015: alfa  3.69 %  t  2.42
2016-2025: alfa  2.47 %  t  1.43

Con diez años, el error típico del alfa crece en un factor 2\sqrt{2} respecto a veinte, y un alfa real del 3 % con un 4,5 % de volatilidad propia tiene un t esperado de solo 3/(4,5/10)≈2,13 / (4,5/\sqrt{10}) \approx 2,1. Aproximadamente la mitad de las veces no saldrá significativo. En estos datos pasa exactamente eso: en 2006-2015 el alfa sale un 3,69 % con t = 2,42 y en 2016-2025 un 2,47 % con t = 1,43, que no es significativo. Diez años de historia no bastan para separar a un buen gestor de uno con suerte, y casi ningún gestor tiene veinte con la misma estrategia.

  1. Estima F13 con el CAPM y con el modelo de Henriksson-Merton solo con mercado (sin los otros cinco factores) y compara el alfa. ¿Cambia la conclusión respecto al modelo de seis factores?
Solución
IN [15]python
capm13 = regresion(exc["F13"], fac[["mkt_rf"]])
hm13 = regresion(exc["F13"], X[["mkt_rf", "mkt_neg"]])
print(f"CAPM: alfa {1200 * capm13.params.iloc[0]:.2f} %, "
      f"beta {capm13.params.iloc[1]:.2f}")
print(f"HM:   alfa {1200 * hm13.params.iloc[0]:.2f} %, "
      f"beta {hm13.params.iloc[1]:.2f}, "
      f"extra {hm13.params.iloc[2]:.2f}")
OUT [15]Henriksson-Merton con un solo factor
CAPM: alfa -1.19 %, beta 0.54
HM:   alfa 5.79 %, beta 0.40, extra 0.24

No cambia: F13 no tiene exposición a los otros factores, así que el mercado basta. Lo que decide es el término no lineal. Con el CAPM lineal, el alfa es del −1,19 % y la beta, 0,54, queda entre la normal y la de las caídas; con Henriksson-Merton, el alfa vuelve al 5,79 %, la beta normal a 0,40 y la extra a 0,24, cerca de la verdad. Elegir los factores importa; elegir la forma del modelo, también.

  1. Sin código: un gestor de una estrategia de venta de puts sobre un índice presenta un alfa de seis factores del −0,5 % y argumenta que «su estrategia no tiene alfa, pero tampoco cuesta nada, porque su beta es solo 0,5». Usa F13 para explicar qué está ocultando la regresión lineal y qué pedirías para evaluarlo.
Solución

La regresión lineal mezcla dos cosas que en F13 son de signo contrario: una prima que se cobra todos los meses (el alfa de Henriksson-Merton, 5,83 % anual) y una pérdida extra en las caídas (beta adicional de 0,24, que cuesta de media un 5,68 % al año y concentra las pérdidas en los peores meses: −18,5 % cuando el mercado cae un 28,9 %). La beta de 0,5 es un promedio de 0,40 en las subidas y 0,64 en las caídas: la exposición en el peor momento es mayor que la que se declara. Pediría la regresión con el término min⁡(MKT,0)\min(MKT, 0), los peores meses de la estrategia junto a los del mercado, su caída máxima y un análisis de estrés (lección 4.2.4) con caídas de mercado que no estén en su historial. La pregunta no es si tiene alfa, sino cuánto pierde cuando el mercado cae y si alguien le está pagando lo suficiente por asumirlo.

Respuesta plegada en cada pregunta

Autoevaluación

  1. F10 tiene un alfa del CAPM del 1,45 % anual y un alfa de seis factores del −0,09 %. ¿De dónde salía el alfa del CAPM?

    • ADe la habilidad del gestor, que el modelo de seis factores no sabe medir
    • BDe un error de los datos
    • CDe exposiciones a primas que el CAPM no incluye: rentabilidad (RMW, +1,21 puntos), inversión (CMA, +0,44) y momentum (+0,34), con HML restando 1,24
    • DDel tipo libre de riesgo
    Ver respuesta

    Respuesta C. De exposiciones a primas que el CAPM no incluye: rentabilidad (RMW, +1,21 puntos), inversión (CMA, +0,44) y momentum (+0,34), con HML restando 1,24

    La atribución con seis factores reparte el exceso medio del 8,25 % en mercado (7,51), los otros cinco factores y un alfa de −0,09. El fondo cobra primas que se pueden comprar con un índice de factores: no es alfa.

  2. Entre los 20 fondos, siete tienen p < 0,05 en el alfa de seis factores. Con la corrección de Holm, solo quedan F12 y F16. ¿Qué pasa con F08, que tiene un alfa verdadero del 3 %?

    • AQue Holm está mal aplicado
    • BQue con 20 años y su volatilidad propia, un 3 % de alfa da un t de 2,86 y p = 0,0042: no supera el umbral de Holm (0,05/20 = 0,0025 para el primero). Controlar los falsos positivos cuesta potencia
    • CQue F08 no tiene alfa
    • DQue hay que usar errores ordinarios en lugar de Newey-West
    Ver respuesta

    Respuesta B. Que con 20 años y su volatilidad propia, un 3 % de alfa da un t de 2,86 y p = 0,0042: no supera el umbral de Holm (0,05/20 = 0,0025 para el primero). Controlar los falsos positivos cuesta potencia

    Holm controla la probabilidad de algún falso positivo entre los 20; con eso pierde fondos con alfa real. Benjamini-Hochberg (FDR) sí acepta F08 y F09, pero también el falso positivo F18. No hay corrección sin coste.

  3. F12 tiene un alfa lineal del 3,13 % con t = 4,15, el más significativo de todos, y un alfa verdadero de cero. ¿Por qué?

    • APorque hace market timing: su rendimiento lleva un término 0,5·mkt_rf², siempre positivo, que la regresión lineal mete en el alfa. Con el término cuadrático de Treynor-Mazuy el alfa baja al 0,68 % y el coeficiente sale 0,44
    • BPor azar
    • CPorque su beta es baja
    • DPorque los errores de Newey-West inflan el t
    Ver respuesta

    Respuesta A. Porque hace market timing: su rendimiento lleva un término 0,5·mkt_rf², siempre positivo, que la regresión lineal mete en el alfa. Con el término cuadrático de Treynor-Mazuy el alfa baja al 0,68 % y el coeficiente sale 0,44

    La media de mkt_rf² es positiva; en una regresión lineal se convierte en constante. El modelo correcto es no lineal, y la significación del alfa lineal no dice nada de la habilidad.

  4. F13 vende puts: alfa verdadero del 6 % y beta extra de 0,25 cuando el mercado cae. ¿Qué alfa da la regresión lineal y por qué?

    • AUn 6 %, porque la regresión es insesgada
    • BCero
    • CUn 12 %
    • DUn −1,21 %: el término 0,25·min(mkt_rf, 0) tiene media negativa y la regresión lineal lo absorbe entre el alfa y la beta. Con el término de Henriksson-Merton el alfa sale 5,83 %
    Ver respuesta

    Respuesta D. Un −1,21 %: el término 0,25·min(mkt_rf, 0) tiene media negativa y la regresión lineal lo absorbe entre el alfa y la beta. Con el término de Henriksson-Merton el alfa sale 5,83 %

    El 6 % es la prima que cobra por vender seguro; la media de 0,24·min(mkt_rf, 0) le cuesta un 5,68 % al año. Lo que gana en los meses tranquilos lo devuelve en los de caída (−18,5 % en el peor mes). Separar los dos términos exige el modelo no lineal.

  5. PCA sobre los 36 activos del universo recupera el factor de mercado con un solo componente (R² 0,97) y no recupera el factor europeo ni con diez (R² 0,03). ¿Por qué?

    • APorque el factor europeo no existe
    • BPorque PCA necesita datos mensuales
    • CPorque PCA ordena por varianza: el mercado mueve casi todos los activos y domina, mientras que el factor europeo, de un 6 % de volatilidad, solo carga en dos activos y su efecto se confunde con otras combinaciones
    • DPorque los componentes principales son siempre los factores de Fama-French
    Ver respuesta

    Respuesta C. Porque PCA ordena por varianza: el mercado mueve casi todos los activos y domina, mientras que el factor europeo, de un 6 % de volatilidad, solo carga en dos activos y su efecto se confunde con otras combinaciones

    Los componentes son las direcciones de máxima varianza, ortogonales entre sí, no los factores económicos. Recuperan bien los factores grandes y anchos (mercado, tipos, materias primas) y mal los estrechos (Europa, tamaño, emergentes).

Ideas clave

  • Una regresión de series temporales de los excesos de una cartera sobre los factores da su alfa (la constante), sus cargas y su R². Con 240 meses, las cargas de los 17 fondos lineales se recuperan con un error medio de 0,01 a 0,04.
  • Un alfa del CAPM puede ser una exposición a otros factores: el de F10 (1,45 %) es rentabilidad, inversión y momentum, y desaparece con seis factores.
  • Entre 20 fondos sin corregir, un fondo sin alfa sale significativo (F18, t = 2,55). Holm lo elimina pero también elimina alfas reales; Benjamini-Hochberg conserva los reales y el falso.
  • Las estrategias no lineales engañan a la regresión lineal: el market timing parece alfa (F12) y la venta de puts parece no tenerlo (F13). Hay que añadir mkt_rf² o min(mkt_rf, 0).
  • Una carga estable en toda la muestra puede ser la media de dos estilos (F14: 0,22 frente a 0,85 y −0,35 por mitades); una ventana móvil de 36 meses tarda casi tres años en verlo.
  • PCA recupera los factores grandes y anchos (mercado, tipos, materias primas) y no los estrechos; sus componentes no son factores económicos, sino direcciones de varianza.

Recursos