---
title: "4.1.7 Rebalanceo y carteras de estrategias"
description: "Rebalanceo por calendario y por bandas con horquilla, comisiones fijas y deriva de pesos, y una cartera de nueve estrategias con pesos sin anticipación."
url: "https://pythonparatrading.com/curso-python-trading/nivel-4/rebalanceo-y-carteras-de-estrategias/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-4/rebalanceo-y-carteras-de-estrategias.png"
---

# 4.1.7 · Rebalanceo y carteras de estrategias

> Unos pesos óptimos solo lo son el día en que se fijan: al día siguiente los precios ya los han movido. Esta lección mide qué cuesta devolverlos a su sitio, cada cuánto conviene hacerlo y qué se arriesga si no se hace, primero en una cartera de cinco ETF y después en una cartera de nueve estrategias, donde los pesos se estiman con datos del pasado y se rehacen cada mes.

- Curso: [Nivel 4: Carteras, riesgo, ejecución y trading en real](https://pythonparatrading.com/curso-python-trading/nivel-4/)
- Módulo: 4.1 · Teoría y optimización de carteras
- Duración: 65 min
- Requisitos: [2.4.7 · Modelado de costes: comisiones, horquilla, impacto, financiación y préstamo de títulos](https://pythonparatrading.com/curso-python-trading/nivel-2/modelado-de-costes/), [2.6.1 · CAGR, Sharpe, Sortino, Calmar, Omega, drawdown y Ulcer Index](https://pythonparatrading.com/curso-python-trading/nivel-2/metricas-de-rentabilidad-y-riesgo/), [3.1.4 · Sistemas de futuros diversificados: el enfoque de Carver](https://pythonparatrading.com/curso-python-trading/nivel-3/sistemas-de-futuros-diversificados-carver/)

## Al terminar esta lección

- Explicar por qué derivan los pesos de una cartera y cuánto se alejan del objetivo en 20 años si no se toca.
- Simular rebalanceo por calendario (diario a anual) y por bandas (absolutas y relativas) con horquilla normal y de estrés y comisión fija por orden.
- Comparar las reglas por órdenes, rotación, coste, error de seguimiento, caída máxima y comportamiento en las crisis, distinguiendo lo robusto del ruido.
- Construir una cartera de estrategias con pesos equiponderados, inversos a la volatilidad y de mínima varianza estimados sin anticipación, y medir su peor tramo.

## Los pesos se mueven solos

Las lecciones anteriores del módulo calculan pesos: de Markowitz (4.1.1), con covarianzas mejor estimadas (4.1.2),
con visiones (4.1.3), por riesgo (4.1.4), con restricciones de coste (4.1.5). Todos son pesos de un día. En cuanto los
precios se mueven, el peso de cada activo cambia sin que nadie opere:

$$
w_{i,t+1} = w_{i,t}\,\frac{1 + r_{i,t+1}}{1 + r_{p,t+1}}, \qquad r_{p,t+1} = \sum_j w_{j,t}\, r_{j,t+1}
$$

Lo que sube gana peso y lo que baja lo pierde. Una cartera que no se toca acaba siendo, con los años, una apuesta por
lo que más ha subido. **Rebalancear** es devolver los pesos a su objetivo, y la pregunta práctica tiene tres partes:
cuándo (por calendario o cuando la desviación pasa de un umbral), hasta dónde (al objetivo o al borde de la banda) y a
qué coste.

```mermaid
flowchart TB
  P[Precios del día] --> D[Pesos derivados]
  D --> C{¿Toca operar?}
  C -- calendario --> O[Operar al objetivo]
  C -- fuera de banda --> O
  C -- no --> E[Esperar]
  O --> K[Costes]
  K --> N[Pesos nuevos]
```

La cartera de la lección es una cartera clásica de cinco ETF del universo del nivel (README de los datos): 40 % de
renta variable de EE. UU. (`RV_EEUU`), 10 % de emergentes (`RV_EMG`), 30 % de bonos del Tesoro a 7-10 años
(`RF_T710`), 10 % de crédito con grado de inversión (`RF_CIG`) y 10 % de oro (`MP_ORO`). Los datos son sintéticos,
de 2006 a 2025, con las crisis del guion (2008, 2020, 2022) y una liquidez por activo que incluye la horquilla en
calma y en estrés:

```python
import numpy as np
import pandas as pd

precios = pd.read_parquet("universo4_precios.parquet").set_index(
    "fecha")
fac = pd.read_parquet("universo4_factores_diarios.parquet").set_index(
    "fecha")
liq = pd.read_csv("riesgo4_liquidez.csv", index_col="activo")
OBJ = pd.Series({"RV_EEUU": 0.40, "RV_EMG": 0.10, "RF_T710": 0.30,
                 "RF_CIG": 0.10, "MP_ORO": 0.10})
A = list(OBJ.index)
# rendimiento simple total (con el tipo libre); base 100 la víspera
R = precios[A] / precios[A].shift(1).fillna(100) - 1
rf = fac["rf"]
crisis = fac["regimen"].ne("calma").to_numpy()
crece = (1 + R).prod()
final = OBJ * crece / (OBJ * crece).sum()
pd.DataFrame({"objetivo": OBJ, "sin_tocar_2025": final,
              "media_horquilla_pb": liq.loc[A, "horquilla_pb"] / 2,
              "en_estres_pb": liq.loc[A, "horquilla_pb_estres"] / 2}
             ).round(3)
```
```output
         objetivo  sin_tocar_2025  media_horquilla_pb  en_estres_pb
RV_EEUU       0.4           0.554               0.435         1.745
RV_EMG        0.1           0.052               0.485         1.950
RF_T710       0.3           0.117               1.390        11.120
RF_CIG        0.1           0.054               1.355        10.835
MP_ORO        0.1           0.222               2.715        32.575
```

Sin tocarla en 20 años, la cartera terminaría con un 55,4 % en la bolsa de EE. UU. (frente al 40 % pedido), un 22,2 %
en oro (frente al 10 %) y solo un 11,7 % en el bono del Tesoro (frente al 30 %). Ya no sería la cartera que se eligió:
tendría más del doble de oro y casi un tercio de los bonos. La horquilla, en cambio, es pequeña: media horquilla de
0,435 pb en el ETF de la bolsa de EE. UU. y 2,7 pb en el de oro en calma. En los regímenes de estrés del guion se
multiplica por 4 en la renta variable, por 8 en los bonos y por 12 en el oro (de 2,7 a 32,6 pb): rebalancear en
plena crisis es más caro, y es justo cuando más se desvían los pesos.

## Un simulador con las dos familias de reglas

El simulador sigue la cartera día a día en dólares: aplica los rendimientos, calcula los pesos y, si la regla lo
pide, opera al cierre hasta el objetivo. Cada orden paga media horquilla sobre su importe (la de estrés fuera de los
tramos de calma) y, si se indica, una comisión fija por orden. Las reglas de **calendario** operan el último día de
cada periodo pase lo que pase; las de **banda** miran cada cierre y operan solo si algún activo se ha salido de su
banda, que puede ser absoluta (5 puntos: un 40 % puede ir de 35 % a 45 %) o relativa (20 % del peso: un 10 % puede ir
de 8 % a 12 %):

```python
def cortes(fechas, freq):
    """True en la última sesión de cada periodo (M, Q o Y)."""
    p = fechas.to_period(freq)
    return np.r_[p[1:] != p[:-1], False]

def rebalanceo(regla, capital=1e6, fijo=0.0, banda=0.05,
               relativa=False):
    """Cartera con pesos objetivo OBJ, decisiones al cierre.
    regla: 'diario', 'mensual', 'trimestral', 'anual', 'nunca' o
    'banda' (se mira cada cierre). Coste: media horquilla (la de
    estrés fuera de la calma) sobre el efectivo + `fijo` USD por
    orden. Devuelve el rendimiento diario y un resumen."""
    w = OBJ.to_numpy()
    hs = liq.loc[A, "horquilla_pb"].to_numpy() / 2e4
    he = liq.loc[A, "horquilla_pb_estres"].to_numpy() / 2e4
    toca = {"diario": np.ones(len(R), bool),
            "mensual": cortes(R.index, "M"),
            "trimestral": cortes(R.index, "Q"),
            "anual": cortes(R.index, "Y"),
            "nunca": np.zeros(len(R), bool)}.get(regla)
    lim = banda * w if relativa else np.full(len(w), banda)
    x = capital * w
    rend, peso_rv = np.empty(len(R)), np.empty(len(R))
    ordenes = giro = coste = desv = 0.0
    for t, r in enumerate(R.to_numpy()):
        v0 = x.sum()
        x = x * (1 + r)
        v = x.sum()
        peso = x / v
        desv = max(desv, np.abs(peso - w).max())
        hacer = toca[t] if toca is not None else (
            np.abs(peso - w) > lim).any()
        if hacer:
            orden = w * v - x
            n = (np.abs(orden) > 0.01).sum()
            c = (np.abs(orden) * (he if crisis[t] else hs)).sum()
            c += fijo * n
            ordenes += n
            giro += np.abs(orden).sum() / v / 2
            coste += c / v
            x = w * (v - c)
        rend[t] = x.sum() / v0 - 1
        peso_rv[t] = x[:2].sum() / x.sum()
    años = len(R) / 252
    exc = pd.Series(rend, R.index) - rf
    capital_t = (1 + pd.Series(rend, R.index)).cumprod()
    return exc, pd.Series(peso_rv, R.index), {
        "ordenes_año": ordenes / años,
        "giro_pct_año": 100 * giro / años,
        "coste_pb_año": 1e4 * coste / años,
        "exceso_pct": 100 * exc.mean() * 252,
        "vol_pct": 100 * exc.std() * np.sqrt(252),
        "sharpe": exc.mean() / exc.std() * np.sqrt(252),
        "caida_max_pct": 100 * (capital_t / capital_t.cummax()
                                - 1).min(),
        "desv_max_pp": 100 * desv}
```

Tres detalles del código que importan en un sistema real. El rendimiento de la cartera se calcula en dinero, con
rendimientos simples, no con una media ponderada de rendimientos logarítmicos (que infravalora el Sharpe, como se vio
en 3.1.4). La rotación (`giro`) cuenta la mitad del importe negociado, la convención habitual: vender un 5 % de un
activo y comprar un 5 % de otro es una rotación del 5 %. Y el coste se descuenta del valor antes de fijar los pesos
nuevos, así que una cartera que rebalancea mucho tiene algo menos de capital invertido que otra que no lo hace.

## Calendario frente a bandas

La referencia ideal es la cartera con los pesos exactos todos los días y sin costes, `R @ OBJ`: lo que se llama a
veces la cartera «de pesos constantes». Cada regla se compara con ella por el **error de seguimiento** (la volatilidad
anual de la diferencia de rendimientos):

```python
REGLAS = {"diario": {"regla": "diario"},
          "mensual": {"regla": "mensual"},
          "trimestral": {"regla": "trimestral"},
          "anual": {"regla": "anual"},
          "nunca": {"regla": "nunca"},
          "banda 5 pp": {"regla": "banda", "banda": 0.05},
          "banda 20 % rel.": {"regla": "banda", "banda": 0.20,
                              "relativa": True}}
ideal = R @ OBJ - rf          # pesos exactos cada día, sin costes
res, rend, pesos = {}, {}, {}
for nombre, kw in REGLAS.items():
    exc, peso_rv, m = rebalanceo(**kw)
    m["error_seg_pct"] = 100 * (exc - ideal).std() * np.sqrt(252)
    res[nombre], rend[nombre], pesos[nombre] = m, exc, peso_rv
tabla = pd.DataFrame(res).T
tabla.columns = ["ordenes", "giro_%", "coste_pb", "exceso_%", "vol_%",
                 "sharpe", "caida_%", "desv_pp", "error_%"]
tabla.drop(columns=["exceso_%", "vol_%"]).round(2)
```
```output
                 ordenes  giro_%  coste_pb  sharpe  caida_%  desv_pp  error_%
diario           1260.00   66.92      4.21    0.49   -33.39     2.40     0.01
mensual            59.86   16.65      1.00    0.51   -33.14     8.26     0.54
trimestral         19.79    9.98      0.59    0.52   -32.08     9.26     0.81
anual               4.76    5.38      0.31    0.53   -29.94    12.10     1.10
nunca               0.00    0.00      0.00    0.55   -29.64    20.33     2.31
banda 5 pp          5.76    8.06      0.81    0.56   -31.24     6.55     0.79
banda 20 % rel.     6.76    7.36      0.66    0.53   -32.44     8.39     0.69
```

Con un millón de USD en ETF muy líquidos, el coste no decide nada: rebalancear cada día, con 1.260 órdenes y una
rotación del 67 % anual, cuesta 4,2 pb al año; cada mes, 1,0 pb; una banda de 5 puntos, 0,8 pb. Lo que separa a las
reglas es el control de los pesos:

- **Por calendario**, cuanto más espaciado, menos rotación y más desviación: la desviación máxima de un activo pasa de
  2,4 puntos (diario) a 8,3 (mensual), 9,3 (trimestral) y 12,1 (anual), y el error de seguimiento de 0,01 % a 1,10 %.
  Sin rebalancear, la desviación llega a 20,3 puntos y el error a 2,31 %.
- **Por bandas**, la desviación queda acotada por construcción (6,55 puntos como máximo con la banda de 5: el exceso
  sobre 5 es el movimiento del último día) con muy pocas órdenes: 5,8 al año, menos que el rebalanceo anual. La banda
  de 5 puntos tiene un error de seguimiento (0,79 %) como el trimestral (0,81 %) con menos de un tercio de órdenes.
  La banda relativa del 20 % protege más los pesos pequeños (un 10 % no puede pasar del 12 %) y menos los grandes.

La banda gana porque solo opera cuando la desviación importa: en años tranquilos casi no toca la cartera y en una
crisis opera varias veces seguidas. El calendario opera igual en los dos casos.

<aside class="callout" data-kind="nota">

El buffer de Carver de la lección 3.1.4 operaba **hasta el borde** de la banda, no hasta el objetivo. Allí cada
mercado es independiente (un futuro no consume capital de los demás) y llevarlo al borde ahorra operaciones. En una
cartera de pesos que suman 1, llevar un activo al borde obliga a repartir la diferencia entre los demás, que se quedan
cerca de sus bordes y vuelven a salirse enseguida: en una variante de este simulador que operaba al borde, las órdenes
pasaban de 6 a 123 al año. Por eso aquí se opera hasta el objetivo.

</aside>

## Cuando la comisión es fija

Con una cuenta pequeña el problema cambia de naturaleza. Muchos brókeres minoristas cobran una comisión mínima o fija
por orden, y entonces el coste depende del número de órdenes y no de su tamaño. El plan fijo del bróker ficticio Brio
de los datos del nivel 2 (`costes2_brokers.csv`, lección 2.4.7) cobra 4,95 USD por orden:

```python
brokers = pd.read_csv("costes2_brokers.csv")
fijo = float(brokers.loc[brokers["plan"] == "fijo", "tarifa"].iloc[0])
pequena = {}
for nombre in ["mensual", "trimestral", "anual", "banda 5 pp",
               "banda 20 % rel."]:
    exc, _, m = rebalanceo(capital=25_000, fijo=fijo, **REGLAS[nombre])
    pequena[nombre] = {k: m[k] for k in ["ordenes_año",
                                         "coste_pb_año", "sharpe"]}
print(f"comisión fija: {fijo} USD por orden")
pd.DataFrame(pequena).T.round(2)
```
```output
comisión fija: 4.95 USD por orden
                 ordenes_año  coste_pb_año  sharpe
mensual                59.86         76.44    0.44
trimestral             19.79         24.21    0.50
anual                   4.76          5.75    0.53
banda 5 pp              5.76          7.19    0.55
banda 20 % rel.         6.76          8.83    0.52
```

*Fig. — Coste anual de rebalancear una cartera de 25.000 USD y cinco ETF con una comisión fija de 4,95 USD por orden más la media horquilla, según la regla (2006-2025).* Barras horizontales con el coste en puntos básicos al año y las órdenes al año. Rebalanceo mensual (destacado): 76,4 pb y 59,9 órdenes. Trimestral: 24,2 pb y 19,8 órdenes. Anual: 5,8 pb y 4,8 órdenes. Banda de 5 puntos: 7,2 pb y 5,8 órdenes. Banda del 20 % relativa: 8,8 pb y 6,8 órdenes.

Con 25.000 USD, el rebalanceo mensual cuesta 76 pb al año y baja el Sharpe de 0,51 a 0,44; el trimestral, 24 pb. La
banda de 5 puntos cuesta 7,2 pb con prácticamente el mismo Sharpe que sin costes (0,55 frente a 0,56). El rebalanceo
diario ni se incluye: 1.260 órdenes al año a 4,95 USD son 6.237 USD, una cuarta parte de la cuenta. El orden de
magnitud es lo que cuenta: con comisiones fijas, la frecuencia de rebalanceo tiene que bajar hasta que el número de
órdenes deje de pesar, y una banda es la forma natural de conseguirlo sin dejar los pesos a su suerte.

Fuera de la simulación hay un coste más que suele pesar más que todos estos: los **impuestos**. Vender lo que ha
subido para rebalancear realiza una ganancia, y en una cuenta sujeta a impuestos eso adelanta el pago. Las reglas
fiscales dependen del país, del vehículo (fondo, ETF, cuenta de pensiones) y cambian con el tiempo: hay que
consultarlas en la fuente oficial antes de elegir la regla. Una forma de reducir ventas es rebalancear con el dinero
que entra (aportaciones y dividendos) hacia lo que está por debajo de su peso.

## El riesgo deriva aunque la rentabilidad no lo diga

La figura de la cabecera dibuja el peso de la renta variable (bolsa de EE. UU. más emergentes, objetivo 50 %) sin
rebalancear y con la banda de 5 puntos. A fin de año:

```python
rv = pd.DataFrame(pesos)[["nunca", "anual", "banda 5 pp"]]
fin_año = rv.groupby(rv.index.year).last()
print(fin_año.loc[[2007, 2008, 2013, 2019, 2021, 2022, 2025]]
      .mul(100).round(1))
```
```output
       nunca  anual  banda 5 pp
fecha
2007    55.1   50.0        48.4
2008    38.8   50.0        47.6
2013    36.5   50.0        49.0
2019    52.6   50.0        49.8
2021    56.2   50.0        54.5
2022    53.0   50.0        46.5
2025    60.7   48.9        50.2
```

Sin tocarla, la cartera pasó de un 55,1 % de renta variable a finales de 2007 a un 38,8 % un año después,
seguía en un 36,5 % en 2013 y terminó en un 60,7 % en 2025; el mínimo mensual fue un 32,6 % en febrero de 2009 y el
máximo, un 65,2 % en noviembre de 2024. Es decir, la cartera sin rebalancear tuvo menos renta variable justo después
del desplome, cuando estaba barata, y más que nunca tras quince años de subidas. La regla anual vuelve al 50 % cada
diciembre (en 2025 marca 48,9 % porque el último día de la muestra no se rebalancea) y la banda se mueve entre el 41
% y el 56 %.

¿Qué hacen las reglas en una crisis? Rebalancear obliga a comprar lo que cae:

```python
TRAMOS = {"2008-09 a 2009-03": ("2008-09-02", "2009-03-09"),
          "rebote 2009": ("2009-03-10", "2009-12-31"),
          "2020-02 a 03": ("2020-02-20", "2020-03-23"),
          "2022": ("2022-01-04", "2022-10-12")}
total = {k: v + rf for k, v in rend.items()}
pd.DataFrame({t: {k: 100 * ((1 + s.loc[a:b]).prod() - 1)
                  for k, s in total.items()}
              for t, (a, b) in TRAMOS.items()}).round(1)
```
```output
                 2008-09 a 2009-03  rebote 2009  2020-02 a 03  2022
diario                       -25.5         30.1         -18.7 -20.8
mensual                      -25.5         30.7         -17.7 -20.8
trimestral                   -24.6         30.6         -17.9 -20.6
anual                        -21.9         29.4         -17.9 -20.5
nunca                        -21.0         24.3         -18.9 -19.9
banda 5 pp                   -23.9         31.3         -18.2 -20.4
banda 20 % rel.              -24.8         31.4         -18.4 -20.9
```

En la caída de septiembre de 2008 a marzo de 2009, la cartera rebalanceada a diario pierde un 25,5 % y la que no se
toca, un 21,0 %: la segunda llega con menos renta variable (la caída ya le había quitado peso) y no compra en la
bajada. En el rebote de 2009 pasa lo contrario: 30,1 % frente a 24,3 %. Es el **coste y el peor episodio** de la
regla: rebalancear empeora la caída de una crisis larga y mejora la recuperación; en una caída corta y seguida de
rebote (2020) apenas cambia nada (de −17,7 % a −18,9 %), y en 2022, con acciones y bonos cayendo a la vez, tampoco
(de −19,9 % a −20,9 %): cuando todo cae, no hay nada que vender caro para comprar barato.

## Lo que no se puede concluir: la rentabilidad

La tabla anterior da a «nunca» el mejor Sharpe por calendario (0,55). Antes de concluir nada, la diferencia frente al
mensual por quinquenios, y una comparación con la teoría:

```python
dif = pd.DataFrame({k: rend[k] - rend["mensual"]
                    for k in ["nunca", "anual", "banda 5 pp"]})
quinq = dif.groupby((dif.index.year - 2006) // 5).mean() * 252e4
quinq.index = ["2006-10", "2011-15", "2016-20", "2021-25"]
print(quinq.round(0))
años = len(R) / 252
crec = np.log1p(R).sum() / años              # crecimiento log anual
diario = np.log1p(R @ OBJ).sum() / años
nunca = np.log((OBJ * (1 + R).prod()).sum()) / años
bono = 0.5 * (OBJ @ R.var() - OBJ @ R.cov() @ OBJ) * 252
print(f"media ponderada de los activos {100 * (OBJ @ crec):.2f} %, "
      f"diario {100 * diario:.2f} %, nunca {100 * nunca:.2f} %")
print(f"½(Σ w·σ² − σ²_cartera) = {1e4 * bono:.0f} pb al año")
```
```output
         nunca  anual  banda 5 pp
2006-10   39.0   99.0        90.0
2011-15 -114.0  -37.0        12.0
2016-20 -165.0  -17.0        70.0
2021-25  248.0   18.0        28.0
media ponderada de los activos 5.75 %, diario 6.54 %, nunca 6.74 %
½(Σ w·σ² − σ²_cartera) = 79 pb al año
```

No rebalancear rinde 39 pb al año más que el mensual en 2006-2010, 114 y 165 menos en los dos quinquenios siguientes
y 248 más en el último. El signo cambia con quién gana en cada tramo. No hay una regla que rinda más de forma fiable;
hay reglas que controlan mejor el riesgo.

La segunda parte del resultado aclara un argumento que circula mucho, el «bono del rebalanceo». Booth y Fama (1992)
mostraron que el crecimiento compuesto de una cartera de pesos constantes supera a la media ponderada del crecimiento
de sus activos en aproximadamente $\tfrac12(\sum_i w_i\sigma_i^2 - \sigma_p^2)$: el **rendimiento de
diversificación**. En los datos, la media ponderada del crecimiento logarítmico de los cinco activos es un 5,75 %
anual y la cartera rebalanceada cada día crece un 6,54 %: 79 pb de diferencia, exactamente lo que da la fórmula. Pero
la cartera que no se rebalancea crece un 6,74 %, todavía más. No hay contradicción: la cartera sin tocar no crece como
la media ponderada de sus activos de partida, sino que se va cargando de los que más suben (aquí el oro, con un 10,7 %
anual de crecimiento, y la bolsa de EE. UU., con un 8,4 %). Si los ganadores hubieran sido otros, el resultado
sería el contrario. El rendimiento de diversificación existe, pero no es una ventaja garantizada frente a no hacer
nada.

## Una cartera de estrategias

El proyecto del nivel combina estrategias de los niveles 2 y 3, no activos. Una estrategia se puede tratar como un
activo más (su serie de rendimientos), con tres diferencias. Primera: sus rendimientos ya están dimensionados por la
propia estrategia (cada una con su volatilidad objetivo), así que el «peso» es la fracción del capital que se le
asigna. Segunda: rebalancear entre estrategias mueve capital entre sistemas que a su vez operan; el coste depende de
lo que negocia cada una. Tercera: las estrategias se degradan, y una cartera que asigna por el pasado sigue dando
capital a una estrategia que ha dejado de funcionar (lo trata 4.2.5).

```mermaid
flowchart TB
  S[Nueve estrategias] --> V[Vol. y covarianza]
  V --> W[Pesos a fin de mes]
  W --> C[Cartera con deriva]
  C --> T[Vol. objetivo]
  C --> K[Kill switch 4.2.5]
```

Los datos del nivel traen nueve estrategias parecidas a las de los niveles 2 y 3 (más una que deja de funcionar en
2019, `degradada`, que se deja para el segundo ejercicio), con rendimientos diarios simples en exceso del tipo libre
de 2006 a 2025 y eventos fechados: el crash de los quants de agosto de 2007 en pares, el desplome del carry en octubre
de 2008, el crash de momentum de 2009, el Volmageddon de febrero de 2018. La correlación verdadera es baja, y lo que
interesa es si se mantiene en los malos momentos:

```python
est = pd.read_parquet("estrategias4_diarias.parquet").set_index("fecha")
E = est.drop(columns="degradada")
regimen = fac["regimen"]
rho = {g: E[regimen == g].corr() for g in ["calma", "estres"]}
iu = np.triu_indices(E.shape[1], 1)
for g, c in rho.items():
    print(f"{g:7s} correlación media {c.to_numpy()[iu].mean():.3f}")
par = ["venta_puts", "venta_volatilidad", "carry_divisas"]
print(rho["calma"].loc[par, par].round(2))
print(rho["estres"].loc[par, par].round(2))
```
```output
calma   correlación media 0.082
estres  correlación media 0.084
                   venta_puts  venta_volatilidad  carry_divisas
venta_puts               1.00               0.57           0.33
venta_volatilidad        0.57               1.00           0.27
carry_divisas            0.33               0.27           1.00
                   venta_puts  venta_volatilidad  carry_divisas
venta_puts               1.00               0.61           0.56
venta_volatilidad        0.61               1.00           0.42
carry_divisas            0.56               0.42           1.00
```

La correlación media de las 36 parejas apenas se mueve entre la calma (0,082) y los regímenes de estrés del universo
(0,084). La media engaña: entre las tres estrategias que «venden seguros» (venta de puts, venta de volatilidad, carry
de divisas), la correlación sube en estrés (de 0,33 a 0,56 entre puts y carry de divisas; de 0,27 a 0,42 entre
volatilidad y carry), mientras otras parejas, con la tendencia, bajan. Las pérdidas se concentran.

Los pesos se calculan el último día de cada mes con datos hasta ese día y se aplican desde la sesión siguiente. Tres
asignaciones: **equiponderada**, **inversa a la volatilidad** de 63 sesiones (la risk parity ingenua, sin
correlaciones) y **mínima varianza** con solo largos y la covarianza de Ledoit-Wolf de un año (lección 4.1.2), optimizada con cvxpy. La risk parity completa, HRP y HERC son de la lección 4.1.4:

```python
import cvxpy as cp
from sklearn.covariance import LedoitWolf

def minima_varianza(S):
    w = cp.Variable(len(S))
    cp.Problem(cp.Minimize(cp.quad_form(w, cp.psd_wrap(S))),
               [cp.sum(w) == 1, w >= 0]).solve(solver="CLARABEL")
    return np.clip(w.value, 0, None) / np.clip(w.value, 0, None).sum()

f = E.index
fin_mes = np.flatnonzero(cortes(f, "M"))
PESOS = {"equiponderada": {}, "inversa_vol": {}, "min_varianza": {}}
for i in fin_mes[fin_mes >= 251]:
    iv = 1 / E.iloc[i - 62:i + 1].std()
    S = LedoitWolf().fit(E.iloc[i - 251:i + 1].to_numpy()).covariance_
    d = f[i + 1]                   # desde la sesión siguiente
    PESOS["equiponderada"][d] = np.full(E.shape[1], 1 / E.shape[1])
    PESOS["inversa_vol"][d] = (iv / iv.sum()).to_numpy()
    PESOS["min_varianza"][d] = minima_varianza(S)
ult = pd.DataFrame({k: v[max(v)] for k, v in PESOS.items()},
                   index=E.columns)
print("pesos desde", max(PESOS["inversa_vol"]).date())
ult.round(3)
```
```output
pesos desde 2025-12-01
                   equiponderada  inversa_vol  min_varianza
tendencia_futuros          0.111        0.086         0.169
carry_futuros              0.111        0.157         0.190
carry_divisas              0.111        0.121         0.071
venta_puts                 0.111        0.112         0.040
pares                      0.111        0.120         0.169
momentum_acciones          0.111        0.066         0.049
valor_acciones             0.111        0.076         0.109
venta_volatilidad          0.111        0.067         0.000
reversion_corto            0.111        0.195         0.201
```

Los últimos pesos (desde el 1 de diciembre de 2025) muestran la diferencia de criterio. La inversa de la volatilidad
da más capital a las estrategias tranquilas (reversión a corto plazo, 19,5 %; carry de futuros, 15,7 %) y menos a las
volátiles (momentum, 6,6 %; venta de volatilidad, 6,7 %). La mínima varianza, que además ve las correlaciones, deja
la venta de volatilidad a cero y reduce la venta de puts al 4 %: las dos se mueven juntas, y con una basta. Sube la
tendencia (16,9 %), que se correlaciona negativamente con casi todas.

```python
def cartera(nuevos, datos=E, coste_pb=2.0):
    """Rendimiento diario con pesos que derivan y se rehacen en las
    fechas de `nuevos`; coste por unidad de peso negociado."""
    w, rs, idx, giro = None, [], [], 0.0
    for d, r in zip(datos.index, datos.to_numpy()):
        c = 0.0
        if d in nuevos:
            obj = nuevos[d]
            if w is not None:
                giro += np.abs(obj - w).sum()
                c = np.abs(obj - w).sum() * coste_pb / 1e4
            w = obj
        if w is None:
            continue
        rs.append(w @ r - c)
        idx.append(d)
        w = w * (1 + r) / (w @ (1 + r))
    s = pd.Series(rs, idx)
    return s, giro / (len(s) / 252)

def resumen(s, giro=np.nan):
    eq = (1 + s).cumprod()
    dd = eq / eq.cummax() - 1
    m = {"sharpe": s.mean() / s.std() * np.sqrt(252),
         "vol_pct": 100 * s.std() * np.sqrt(252),
         "exceso_pct": 100 * s.mean() * 252,
         "caida_max_pct": 100 * dd.min(),
         "giro_pct_año": 100 * giro}
    return {**{k: round(v, 2) for k, v in m.items()},
            "fecha_peor": dd.idxmin().date()}

CARTERAS = {k: cartera(v) for k, v in PESOS.items()}
pd.DataFrame({k: resumen(*v) for k, v in CARTERAS.items()}).T
```
```output
              sharpe vol_pct exceso_pct caida_max_pct giro_pct_año  fecha_peor
equiponderada   0.84    4.47       3.77        -14.54        24.68  2009-05-05
inversa_vol      1.0    3.75       3.74        -10.48        142.7  2009-02-20
min_varianza    0.94    3.49       3.29         -8.58       111.26  2008-05-13
```

La deriva entre fines de mes se calcula con los rendimientos en exceso de cada estrategia (todas cobran el mismo
tipo libre sobre el capital, así que la aproximación es buena), y cada rebalanceo paga 2 pb por unidad de peso
negociado, un supuesto de la lección. De 2007 a 2025, la equiponderada da un Sharpe de 0,84 con un 4,5 % de
volatilidad, la inversa a la volatilidad 1,00 con un 3,75 % y la mínima varianza 0,94 con un 3,5 %. Las caídas máximas
son −14,5 %, −10,5 % y −8,6 %. Ninguna de estas carteras rinde mucho por sí sola (un 3,3 % a 3,8 % anual en exceso):
con correlaciones tan bajas su volatilidad es pequeña, y la forma de llevarla a un riesgo útil es apalancarla hasta
una volatilidad objetivo, que es la lección 4.2.2.

El precio de las carteras que miran la volatilidad es la rotación: 143 % al año la inversa a la volatilidad y 111 %
la mínima varianza, frente al 25 % de la equiponderada (que solo deshace la deriva). Y cabe preguntarse si conviene
recalcular más a menudo:

```python
diario = {}
vol = E.rolling(63).std()
for i in range(fin_mes[fin_mes >= 251][0], len(f) - 1):
    iv = 1 / vol.iloc[i]
    diario[f[i + 1]] = (iv / iv.sum()).to_numpy()
s_d, giro_d = cartera(diario)
pd.DataFrame({"diaria": resumen(s_d, giro_d),
              "mensual": resumen(*CARTERAS["inversa_vol"])}).T
```
```output
        sharpe vol_pct exceso_pct caida_max_pct giro_pct_año  fecha_peor
diaria    1.04    3.66       3.82         -10.1       359.95  2009-02-20
mensual    1.0    3.75       3.74        -10.48        142.7  2009-02-20
```

Con los pesos rehechos cada día, el Sharpe pasa de 1,00 a 1,04 (el README de los datos mide 1,10 con pesos diarios
sin costes y desde 2006) y la rotación de 143 % a 360 % al año. La mejora no viene de «rebalancear más», sino de que
la volatilidad de cada estrategia se agrupa en el tiempo: la inversa a la volatilidad recalculada a diario recorta
antes la estrategia que acaba de entrar en un tramo agitado, una forma de volatility targeting estrategia a
estrategia. Con 2 pb por unidad de peso compensa; con estrategias caras de mover (opciones, crédito), podría no hacerlo.

## El peor tramo de la cartera

```python
s = CARTERAS["inversa_vol"][0]
eq = (1 + s).cumprod()
dd = eq / eq.cummax() - 1
pico = eq.loc[:dd.idxmin()].idxmax()
print(f"de {pico.date()} a {dd.idxmin().date()}: "
      f"{100 * dd.min():.1f} %")
tramo = E.loc[pico:dd.idxmin()]
print((100 * ((1 + tramo).prod() - 1)).sort_values().round(1))
print(s.nsmallest(3).mul(100).round(2))
```
```output
de 2007-04-02 a 2009-02-20: -10.5 %
venta_puts          -45.0
venta_volatilidad   -37.2
carry_divisas       -22.1
reversion_corto     -21.0
carry_futuros       -15.6
pares               -13.2
valor_acciones       -4.9
momentum_acciones     4.7
tendencia_futuros    44.9
dtype: float64
2018-02-05   -1.35
2008-09-25   -1.02
2018-10-03   -0.94
dtype: float64
```

*Fig. — Peor tramo de la cartera de nueve estrategias con pesos inversos a la volatilidad (2007-04-02 a 2009-02-20): variación de cada estrategia y de la cartera.* Barras horizontales con la variación acumulada en porcentaje. La cartera (destacada) cae un 10,5 %. Venta de puts −45,0 %, venta de volatilidad −37,2 %, carry de divisas −22,1 %, reversión a corto −21,0 %, carry de futuros −15,6 %, pares −13,2 %, valor −4,9 %, momentum de acciones +4,7 % y tendencia en futuros +44,9 %.

La cartera inversa a la volatilidad cae un 10,5 % desde el 2 de abril de 2007 hasta el 20 de febrero de 2009. En ese
tramo, la venta de puts pierde un 45,0 %, la venta de volatilidad un 37,2 % y el carry de divisas un 22,1 %; la
tendencia gana un 44,9 % y compensa una parte. El peor día es el 5 de febrero de 2018 (−1,35 %), el del Volmageddon
de los datos. Una cartera de estrategias «poco correladas» no está diversificada contra lo que de verdad la amenaza:
que varias estrategias sean, en el fondo, la misma apuesta (vender seguros contra caídas), con sus pérdidas en los
mismos días. La lección 4.2.4 trata esos escenarios.

<aside class="callout" data-kind="riesgo">

Los datos son sintéticos. Las estrategias tienen su Sharpe objetivo plantado y el generador eligió una semilla con
medias realizadas cerca de la verdad (README de los datos): en una muestra real de 20 años los Sharpes medidos podrían
estar mucho más lejos de los verdaderos, que no se conocen. Un Sharpe de 1,0 en una cartera de estrategias es el
resultado de este experimento con estos supuestos, no una expectativa para estrategias reales.

</aside>

## Las mismas reglas en bt

bt (la librería de backtesting de carteras de la lección 2.4.6) trae las dos familias de reglas como bloques: los de
calendario (`RunMonthly`, `RunQuarterly`) y `RunIfOutOfBounds`, que en la versión 1.4.1 dispara cuando algún peso se
desvía de su objetivo más que una **tolerancia relativa** (0,2 = un 20 % del peso objetivo, la banda relativa de la
lección). bt no está en el entorno del curso (como en 2.4.6, su código va sin ejecutar), y el de abajo está comprobado
contra el código fuente de esa versión:

```python skip title="rebalanceo_bt.py"
import bt

precios = ...   # DataFrame de precios, una columna por activo
pesos = {"RV_EEUU": 0.40, "RV_EMG": 0.10, "RF_T710": 0.30,
         "RF_CIG": 0.10, "MP_ORO": 0.10}
trimestral = bt.Strategy("trimestral", [
    bt.algos.RunQuarterly(),
    bt.algos.SelectAll(),
    bt.algos.WeighSpecified(**pesos),
    bt.algos.Rebalance()])
bandas = bt.Strategy("bandas_20_rel", [
    bt.algos.SelectAll(),
    bt.algos.WeighSpecified(**pesos),
    bt.algos.Or([bt.algos.RunOnce(),
                 bt.algos.RunIfOutOfBounds(0.20)]),
    bt.algos.Rebalance()])
comision = lambda cantidad, precio: 4.95      # fija por orden
res = bt.run(bt.Backtest(trimestral, precios, commissions=comision,
                         initial_capital=25_000),
             bt.Backtest(bandas, precios, commissions=comision,
                         initial_capital=25_000))
res.display()
```

El orden de la pila importa: `RunIfOutOfBounds` necesita los pesos objetivo en `temp["weights"]`, así que va después
de `WeighSpecified`, y `RunOnce` compra la cartera el primer día. `Backtest` usa por defecto posiciones enteras
(`integer_positions=True`), lo que con 25.000 USD añade un pequeño error de redondeo que el simulador de la lección no
tiene.

## Errores típicos

- **Comparar reglas por su rentabilidad en un backtest.** La diferencia depende de qué ganó en la muestra; compara
  órdenes, costes, desviación y error de seguimiento.
- **Olvidar las comisiones fijas o mínimas.** Con cuentas pequeñas deciden la frecuencia.
- **Recalcular pesos con datos del día en que se aplican.** Los pesos de fin de mes se aplican desde la sesión
  siguiente.
- **Promediar rendimientos logarítmicos para la cartera**, o rebalancear «sin deriva» (pesos fijos cada día) en un
  backtest que dice rebalancear cada mes.
- **Fiarse de la correlación media.** Mira qué estrategias pierden juntas en los peores tramos.

## Ejercicios

1. Con la cuenta de 25.000 USD y la comisión fija, compara bandas de 2,5, 5 y 10 puntos: órdenes, coste, desviación
   máxima y error de seguimiento.

**Solución.**

```python
filas = {}
for b in (0.025, 0.05, 0.10):
    exc, _, m = rebalanceo("banda", capital=25_000, fijo=fijo, banda=b)
    filas[f"{100 * b:g} pp"] = {
        "ordenes_año": m["ordenes_año"],
        "coste_pb_año": m["coste_pb_año"],
        "desv_max_pp": m["desv_max_pp"],
        "error_seg_pct": 100 * (exc - ideal).std() * np.sqrt(252)}
pd.DataFrame(filas).T.round(2)
```
```output
        ordenes_año  coste_pb_año  desv_max_pp  error_seg_pct
2.5 pp        17.53         22.51         4.34           0.32
5 pp           5.76          7.19         6.55           0.78
10 pp          1.50          2.13        10.52           1.47
```

Al estrechar la banda a 2,5 puntos las órdenes se triplican (17,5 al año) y el coste también (22,5 pb), con un error
de seguimiento de 0,32 %; al ensancharla a 10 puntos bastan 1,5 órdenes al año (2,1 pb), pero la desviación máxima
llega a 10,5 puntos y el error a 1,47 %, peor que el rebalanceo anual. La anchura se elige como un compromiso entre
coste y control: con esta cuenta, 5 puntos es un punto razonable.

2. Añade la estrategia `degradada` a la cartera inversa a la volatilidad y compara el Sharpe antes y después del 1 de
   julio de 2019 con la cartera de nueve. ¿Cuánto capital recibe después de dejar de funcionar?

**Solución.**

```python
PESOS10 = {}
for i in fin_mes[fin_mes >= 251]:
    iv = 1 / est.iloc[i - 62:i + 1].std()
    PESOS10[f[i + 1]] = (iv / iv.sum()).to_numpy()
s10, _ = cartera(PESOS10, datos=est)
peso_deg = pd.Series({d: w[-1] for d, w in PESOS10.items()})
s9 = CARTERAS["inversa_vol"][0]
for a, b in [("2007", "2019-06"), ("2019-07", "2025")]:
    x10, x9 = s10.loc[a:b], s9.loc[a:b]
    print(f"{a} a {b}: con degradada "
          f"{x10.mean() / x10.std() * np.sqrt(252):.2f}, sin ella "
          f"{x9.mean() / x9.std() * np.sqrt(252):.2f}, peso medio "
          f"{100 * peso_deg.loc[a:b].mean():.1f} %")
```
```output
2007 a 2019-06: con degradada 1.10, sin ella 0.74, peso medio 11.6 %
2019-07 a 2025: con degradada 1.37, sin ella 1.50, peso medio 10.7 %
```

Hasta junio de 2019, la estrategia degradada (Sharpe 1,30 en ese tramo según el README) sube el Sharpe de la cartera
de 0,74 a 1,10. Después, con media cero, lo baja de 1,50 a 1,37, y la cartera le sigue dando un 10,7 % del capital
de media: la inversa a la volatilidad no mira la rentabilidad, solo el riesgo. Asignar por Sharpe pasado tampoco lo
arregla bien (un Sharpe de 3 años tiene un error típico cercano a 0,6). Detectar que una estrategia ha dejado de
funcionar y retirarla con reglas fijadas de antemano es el tema de la lección 4.2.5.

3. Un compañero propone rebalancear la cartera de cinco ETF «solo cuando el mercado ha caído un 20 %», para comprar
   barato. ¿Qué tiene de banda y qué no? ¿Qué pasaría en 2022?

**Solución.**

Tiene de banda que solo opera cuando algo se ha movido mucho, pero mira el mercado y no los pesos: una caída del 20 %
de la bolsa con los bonos subiendo (2008) desvía mucho la cartera y la regla opera; una subida larga de la bolsa
(2013-2021) también desvía la cartera, hacia arriba, y la regla no la corrige nunca, así que el riesgo crece sin
control. En 2022 la bolsa cayó un 26 % y los bonos un 15 % a la vez (README de los datos): los pesos se desviaron poco
y la regla operaría vendiendo bonos que también habían caído, sin «comprar barato» nada. La banda sobre los pesos
resuelve las dos cosas, porque opera cuando la cartera ya no es la elegida, en cualquier dirección.

## Autoevaluación

1. Una cartera 50/50 de acciones y bonos no se toca durante un año en el que las acciones suben un 30 % y los bonos un 0 %. ¿Qué peso tienen las acciones al final?

   - A. 50 %
   - B. Un 65 %
   - C. Depende de la volatilidad
   - D. Un 56,5 %: 0,5·1,3 / (0,5·1,3 + 0,5)

   *Respuesta D: Un 56,5 %: 0,5·1,3 / (0,5·1,3 + 0,5).* Cada peso se multiplica por (1 + su rendimiento) y se divide por (1 + el de la cartera): 0,65 / 1,15 = 0,565. La deriva no depende de la volatilidad sino de los rendimientos realizados.

2. Con 25.000 USD y una comisión fija de 4,95 USD por orden, el rebalanceo mensual de cinco ETF cuesta 76 pb al año en la lección. ¿Qué lo explica?

   - A. El número de órdenes: unas 60 al año, casi todas de importes pequeños, cada una con su comisión fija
   - B. La horquilla de los ETF
   - C. El impacto en el mercado
   - D. Los impuestos

   *Respuesta A: El número de órdenes: unas 60 al año, casi todas de importes pequeños, cada una con su comisión fija.* Rebalancear cada mes manda órdenes por desviaciones mínimas. Con una comisión fija, el coste depende del número de órdenes y no de su tamaño: 60 × 4,95 USD son 297 USD al año, un 1,2 % de 25.000 USD en los primeros años y algo menos según crece la cuenta. Con una banda de 5 puntos bastan unas 6 órdenes al año.

3. En la muestra, no rebalancear nunca da un Sharpe de 0,55 y rebalancear cada mes, 0,51. ¿Qué conclusión es correcta?

   - A. Que no hay que rebalancear
   - B. Que el rebalanceo mensual es demasiado caro
   - C. Ninguna sobre la rentabilidad: la diferencia cambia de signo por quinquenios (+39, −114, −165 y +248 pb al año) y depende de qué activos ganaron; lo que sí es robusto es que sin rebalancear el peso de la renta variable vagó entre el 33 % y el 65 %
   - D. Que el Sharpe está mal calculado

   *Respuesta C: Ninguna sobre la rentabilidad: la diferencia cambia de signo por quinquenios (+39, −114, −165 y +248 pb al año) y depende de qué activos ganaron; lo que sí es robusto es que sin rebalancear el peso de la renta variable vagó entre el 33 % y el 65 %.* La cartera sin tocar se carga de lo que más sube. En estos 20 años ganaron el oro y la bolsa de EE. UU., y por eso le fue algo mejor; con otro reparto de ganadores habría salido al revés. El riesgo, en cambio, deriva siempre: es la razón de fondo para rebalancear.

4. ¿Por qué la cartera de estrategias con pesos inversos a la volatilidad recalculados cada día (Sharpe 1,04) mejora a la misma cartera recalculada cada mes (1,00)?

   - A. Porque rebalancear cada día siempre es mejor
   - B. Porque la volatilidad de cada estrategia se agrupa en el tiempo: recalcular a diario recorta antes la estrategia cuya volatilidad acaba de subir, que es una forma de volatility targeting por estrategia, a cambio de una rotación de 360 % al año frente a 143 %
   - C. Por los costes
   - D. Por azar: la diferencia es enorme

   *Respuesta B: Porque la volatilidad de cada estrategia se agrupa en el tiempo: recalcular a diario recorta antes la estrategia cuya volatilidad acaba de subir, que es una forma de volatility targeting por estrategia, a cambio de una rotación de 360 % al año frente a 143 %.* Los rendimientos de las estrategias tienen GARCH: los días de volatilidad alta vienen juntos. Pesos que se adaptan rápido reducen la exposición en esos tramos. La ganancia (0,04 de Sharpe) es pequeña y cuesta rotación; con costes reales por estrategia altos, podría no compensar.

5. La correlación media entre las nueve estrategias es 0,082 en calma y 0,084 en estrés. ¿Significa que la diversificación aguanta en las crisis?

   - A. No: la media esconde que las estrategias que venden seguros (venta de puts, de volatilidad, carry de divisas) se correlacionan más entre sí en estrés (0,33 a 0,56 entre puts y carry de divisas) mientras la tendencia se mueve al revés; las pérdidas se concentran en el mismo grupo
   - B. Sí, la correlación no cambia
   - C. No, porque en estrés todas las correlaciones son 1
   - D. Sí, porque el Sharpe es alto

   *Respuesta A: No: la media esconde que las estrategias que venden seguros (venta de puts, de volatilidad, carry de divisas) se correlacionan más entre sí en estrés (0,33 a 0,56 entre puts y carry de divisas) mientras la tendencia se mueve al revés; las pérdidas se concentran en el mismo grupo.* Una media de 36 correlaciones puede quedarse quieta mientras unas suben y otras bajan. Lo que importa es quién pierde a la vez: en el peor tramo, venta de puts −45 %, venta de volatilidad −37 % y carry de divisas −22 %, compensados en parte por la tendencia (+45 %).

## Ideas clave

- Sin rebalancear, cada peso se multiplica por (1 + su rendimiento) / (1 + el de la cartera): en 2006-2025 la renta variable de la cartera de la lección (objetivo 50 %) fue del 33 % al 65 % y el oro del 10 % al 22 %.
- Con un millón de USD en ETF líquidos el coste de rebalancear es de 1 a 4 pb al año en cualquier regla; con 25.000 USD y comisión fija por orden manda el número de órdenes: 76 pb al año el mensual frente a 7 pb una banda de 5 puntos.
- Las bandas consiguen con 6 órdenes al año un error de seguimiento (0,79 %) como el del rebalanceo trimestral con 20, porque solo operan cuando la desviación importa.
- La diferencia de rentabilidad entre reglas es ruido de la trayectoria (cambia de signo por quinquenios); lo robusto es el control del riesgo. El rendimiento de diversificación medido, 79 pb al año, coincide con ½(Σw·σ² − σ²) de la teoría.
- Una cartera de nueve estrategias poco correladas (0,08 de media) da un Sharpe de 0,84 equiponderada, 1,00 con pesos inversos a la volatilidad y 0,94 con mínima varianza, todos estimados sin anticipación; su peor tramo, −10,5 %, llega cuando las tres estrategias que venden seguros pierden a la vez.

## Recursos

### Research

- [Volatility Targeting en Python](https://pythonparatrading.com/volatility-targeting/) · Research-0004 · 8 min

### Documentación

- [bt v1.4.1: algoritmos de rebalanceo (RunQuarterly, RunIfOutOfBounds, WeighSpecified)](https://github.com/pmorissette/bt/blob/v1.4.1/bt/algos.py) · github.com
- [scikit-learn: LedoitWolf](https://scikit-learn.org/stable/modules/generated/sklearn.covariance.LedoitWolf.html) · scikit-learn.org

### Libros

- [Booth y Fama (1992): Diversification Returns and Asset Contributions, Financial Analysts Journal 48(3)](https://doi.org/10.2469/faj.v48.n3.26) · doi.org

### Datos

- [Datos del nivel 4: precios del universo multiactivo (Parquet)](https://pythonparatrading.com/curso/datos/nivel-4/universo4_precios.parquet) · Descarga · universo4_precios.parquet
- [Datos del nivel 4: rendimientos diarios de las estrategias (Parquet)](https://pythonparatrading.com/curso/datos/nivel-4/estrategias4_diarias.parquet) · Descarga · estrategias4_diarias.parquet

Lección anterior: [4.1.6 · Modelos factoriales: CAPM, Fama-French, PCA y atribución](https://pythonparatrading.com/curso-python-trading/nivel-4/modelos-factoriales-capm-fama-french-y-pca/)

Lección siguiente: [4.1.8 · Herramientas de carteras: Riskfolio-Lib, PyPortfolioOpt, skfolio y bt](https://pythonparatrading.com/curso-python-trading/nivel-4/riskfolio-pyportfolioopt-skfolio-y-bt/)
