---
title: "5.8.1 Qué son los modelos fundacionales de series"
description: "Preentrenamiento masivo, tokens y parches, y una primera previsión zero-shot de Chronos-Bolt sobre la volatilidad realizada, frente a referencias ingenuas."
url: "https://pythonparatrading.com/curso-python-trading/nivel-5/que-son-los-modelos-fundacionales-de-series/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-5/que-son-los-modelos-fundacionales-de-series.png"
---

# 5.8.1 · Modelos fundacionales de series: preentrenar una vez, prever sin entrenar

> Un modelo fundacional de series se entrena una sola vez con decenas de miles de millones de observaciones de dominios muy distintos y después se usa, sin reentrenar, sobre una serie que nunca ha visto. Esta lección explica qué se preentrena, cómo se convierte una serie en tokens o en parches y qué significa «zero-shot», y hace la primera previsión con Chronos-Bolt sobre la volatilidad realizada del nivel 5, con dos referencias ingenuas al lado.

- Curso: [Nivel 5: Machine learning y deep learning (Keras/PyTorch)](https://pythonparatrading.com/curso-python-trading/nivel-5/)
- Módulo: 5.8 · Modelos fundacionales de series temporales
- Duración: 60 min
- Requisitos: [5.7.2 · Arquitecturas de series: TCN, N-BEATS, PatchTST e iTransformer](https://pythonparatrading.com/curso-python-trading/nivel-5/tcn-n-beats-patchtst-e-itransformer/), [5.7.3 · Modelos probabilísticos: pérdida cuantílica, DeepAR e intervalos](https://pythonparatrading.com/curso-python-trading/nivel-5/modelos-probabilisticos-cuantiles-y-deepar/), [2.1.4 · Modelos de volatilidad: ARCH, GARCH, EGARCH y GJR con la librería arch](https://pythonparatrading.com/curso-python-trading/nivel-2/volatilidad-arch-garch-egarch-gjr/)

## Al terminar esta lección

- Explicar qué es un modelo fundacional de series: preentrenamiento con un corpus masivo y heterogéneo, y uso zero-shot sobre series nuevas.
- Reproducir a mano la tokenización de Chronos (escala media y cuantización uniforme) y los parches de Chronos-Bolt, y comprobarlos contra la librería.
- Hacer una previsión zero-shot con Chronos-Bolt-small sin red ni reentrenamiento, y leer sus cuantiles.
- Evaluarla con origen móvil frente a referencias ingenuas, con diferencias pareadas y por año, y situarla frente al HAR y el oráculo del README.

## De un modelo por serie a un modelo para todas

Hasta aquí, cada modelo del curso se ha estimado con los datos de su problema: un GARCH con los rendimientos de un
activo (lección 2.1.4), un HAR o una red con la volatilidad de seis activos (lección 5.7.2). Un **modelo fundacional
de series** invierte el orden. Alguien lo entrena una vez, con un corpus enorme de series de dominios muy distintos
(energía, tráfico, ventas, tiempo, visitas a webs, finanzas, series sintéticas), publica los pesos y cualquiera los usa
después sobre una serie que el modelo nunca ha visto, **sin reentrenar**. A eso se le llama previsión **zero-shot**:
la serie nueva entra solo como contexto, en la pasada hacia delante, y los pesos no cambian.

```mermaid
flowchart TB
  A[Corpus masivo] --> B[Preentrenamiento]
  B --> C[Pesos publicados]
  C --> D[Contexto]
  D --> E[Zero-shot]
  E --> F[Ajuste fino]
```

La idea viene de los modelos de lenguaje: un transformer que ha visto suficientes textos completa uno nuevo sin que
nadie lo entrene para ese texto. En series, la apuesta es que hay regularidades comunes a casi todas (tendencias,
estacionalidades, persistencia, escalones, ruido con estructura) y que un modelo grande puede aprenderlas y
reconocerlas en el contexto que recibe. Las cifras de los corpus lo dicen todo:

| Modelo | Corpus de preentrenamiento (según sus autores) |
|---|---|
| Chronos (Amazon, 2024) | 28 conjuntos públicos, unos 890 000 series y unos 84 000 millones de observaciones, más 10 millones de series aumentadas con TSMixup y 1 millón de series sintéticas de procesos gaussianos (KernelSynth) |
| Chronos-Bolt (Amazon, 2024) | «casi 100 000 millones de observaciones» |
| TimesFM (Google, 2024) | del orden de 100 000 millones de puntos: Google Trends, visitas de Wikipedia y series sintéticas |

En la lección 5.8.2 están las familias con sus arquitecturas, tamaños y licencias. Esta lección se queda en lo que
tienen en común: cómo convierten una serie en algo que un transformer pueda leer y qué se obtiene al usarlas sin
entrenar.

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

Los datos son sintéticos y la respuesta es conocida: `vol5_diario.parquet` sale de un HAR en el logaritmo de la
varianza integrada (README del nivel 5). Tiene una ventaja que los datos reales no tienen: el modelo no puede haberlos
visto en su preentrenamiento, porque se generaron después. Con series de mercado públicas esa garantía no existe (la
lección 5.8.4 vuelve sobre ello).

</aside>

## La serie: la varianza realizada del índice

El banco de pruebas es el de las lecciones 5.7.2 y 5.7.3: el activo `indice` de `vol5_diario.parquet`, 4 529 sesiones
de 2008 a 2025, con su varianza realizada de cada día (`rv_5min`, en rendimiento al cuadrado). Se prevé su logaritmo,
que es casi gaussiano y es la escala en la que trabaja el generador.

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

vol = pd.read_parquet("vol5_diario.parquet")
ind = vol[vol["activo"] == "indice"].set_index("fecha")
log_rv = np.log(ind["rv_5min"].to_numpy())
vol_anual = np.sqrt(252 * ind["rv_5min"].to_numpy())
fechas = pd.DatetimeIndex(ind.index)
print(f"{len(log_rv)} sesiones, {fechas[0].date()} a "
      f"{fechas[-1].date()}")
print(f"log RV: media {log_rv.mean():.2f}, desv. {log_rv.std():.2f}")
print(f"vol. anual: mediana {np.median(vol_anual):.1%}, "
      f"p99 {np.quantile(vol_anual, 0.99):.1%}")
```
```output
4529 sesiones, 2008-01-02 a 2025-12-31
log RV: media -9.75, desv. 0.64
vol. anual: mediana 12.1%, p99 25.6%
```

La log RV vale en torno a −9,75 con una desviación de 0,64: una serie lejos del cero y con poca variación relativa.
Ese detalle importa enseguida.

## De números a tokens: la receta de Chronos

Un transformer de lenguaje trabaja con un vocabulario finito. Chronos (Ansari et al., 2024) hace lo mínimo para que una
serie lo parezca: **escala** el contexto dividiéndolo por la media de sus valores absolutos y **cuantiza** el
resultado en cajones uniformes. Con un contexto $x_1, \dots, x_C$:

$$
s = \frac{1}{C}\sum_{i=1}^{C} |x_i|, \qquad \tilde x_i = \frac{x_i}{s}, \qquad
\text{token}_i = \text{cajon}(\tilde x_i)
$$

Según el artículo y el `config.json` de los modelos, los cajones son 4 093 centros equiespaciados entre −15 y +15 (un
vocabulario de 4 096 con los tokens especiales de relleno y de fin), el contexto es de 512 observaciones y el
horizonte, de 64. El modelo (un T5 de 20 a 710 millones de parámetros) se entrena con entropía cruzada para predecir el
token siguiente, como un modelo de lenguaje, y la previsión probabilística sale de **muestrear** trayectorias de
tokens y deshacer la escala. Cada cajón mide $30/4\,092 \approx 0{,}0073$ en unidades escaladas.

La librería `chronos-forecasting` trae el tokenizador, que no necesita pesos: basta con la configuración de
`amazon/chronos-t5-small`. La celda lo reproduce a mano y lo aplica a dos versiones de la misma información, la
volatilidad anual y el logaritmo de la varianza:

```python
import torch
from chronos import ChronosConfig

# La configuración de amazon/chronos-t5-small (config.json del repo)
cfg = ChronosConfig(
    tokenizer_class="MeanScaleUniformBins",
    tokenizer_kwargs={"low_limit": -15.0, "high_limit": 15.0},
    n_tokens=4096, n_special_tokens=2, pad_token_id=0,
    eos_token_id=1, use_eos_token=True, model_type="seq2seq",
    context_length=512, prediction_length=64, num_samples=20,
    temperature=1.0, top_k=50, top_p=1.0)
tok = cfg.create_tokenizer()
centros = np.linspace(-15, 15, 4096 - 2 - 1)

def tokens_a_mano(x):
    """Escala por la media de |x| y redondea al centro más cercano.
    Los ids 0 y 1 son PAD y EOS; los cajones empiezan en el 3."""
    escala = np.abs(x).mean()
    ids = np.abs(x[:, None] / escala - centros).argmin(1) + 3
    return ids, centros[ids - 3] * escala

for nombre, x in (("vol. anual", vol_anual[-512:]),
                  ("log RV", log_rv[-512:])):
    ids, x_rec = tokens_a_mano(x)
    ids_chr, _, escala = tok.context_input_transform(
        torch.tensor(x[None, :], dtype=torch.float32))
    igual = (ids == ids_chr[0, :512].numpy()).mean()
    print(f"{nombre:<10} escala {escala.item():8.4f}  "
          f"tokens distintos {len(np.unique(ids)):4d}  "
          f"error máx. {np.abs(x - x_rec).max():.4f}  "
          f"igual que chronos {igual:.0%}")
```
```output
vol. anual escala   0.1405  tokens distintos  168  error máx. 0.0005  igual que chronos 100%
log RV     escala   9.5590  tokens distintos   46  error máx. 0.0350  igual que chronos 100%
```

El tokenizador de la librería y el de la celda coinciden. La comparación entre las dos series enseña la debilidad de
la escala media. La volatilidad anual (positiva, con una media de 0,14) usa 168 cajones distintos y el redondeo cuesta
como mucho 0,0005, cinco centésimas de punto de volatilidad. El logaritmo de la varianza, en cambio, vale siempre en
torno a −9,6: dividido por su media absoluta queda casi siempre cerca de −1, solo usa **46 cajones** y el redondeo llega
a 0,035 en log, un 3,6 % de la varianza. La escala media no resta la media, así que una serie lejos del cero pierde
resolución. El ejercicio 2 lo mide con la serie tipificada.

## Parches: lo que hacen Chronos-Bolt y TimesFM

La segunda generación abandona el token por dato. **Chronos-Bolt** tipifica el contexto (resta la media y divide por
la desviación típica, lo que el código llama `InstanceNorm`), lo corta en **parches** de 16 observaciones y proyecta
cada parche a un vector con una red pequeña; al final añade un token de regresión. El codificador T5 lee esos vectores y
el decodificador produce de una vez los **cuantiles 0,1…0,9 de los 64 pasos siguientes** (previsión directa
multipaso), sin muestrear. **TimesFM** hace algo parecido con parches de 32 y un transformer solo decodificador (lección
5.8.2). Es la misma idea que PatchTST en la lección 5.7.2, a escala de preentrenamiento.

```mermaid
flowchart LR
  S[Contexto] --> M[Escala media]
  M --> Q[Un token por dato]
  S --> N[Tipificar]
  N --> P[Parches de 16]
  Q --> T[Transformer]
  P --> T
```

La consecuencia es de coste. La atención compara cada token con todos los demás, así que su coste crece con el cuadrado
del número de tokens:

*Fig. — Cuántos tokens procesa el transformer con un contexto de 512 observaciones: uno por dato en Chronos (más el de fin), un parche de 16 más el token de regresión en Chronos-Bolt y un parche de 32 en TimesFM-2.5.* Tres barras en escala logarítmica. Chronos: 513 tokens. Chronos-Bolt, destacado: 33. TimesFM-2.5: 16. Como la atención compara cada token con todos, su coste crece con el cuadrado: unas 260 000 parejas en Chronos frente a unas 1 100 en Bolt.

Los pesos de Chronos-Bolt-small están en la caché del entorno `ml`, fijados por commit (README del entorno). La celda
los carga sin red y comprueba los parches a mano contra los del modelo:

```python
from chronos import BaseChronosPipeline

torch.manual_seed(0)
torch.use_deterministic_algorithms(True)
bolt = BaseChronosPipeline.from_pretrained(
    "amazon/chronos-bolt-small", device_map="cpu",
    dtype=torch.float32)
cc = bolt.model.chronos_config
print(f"contexto máx. {cc.context_length}, "
      f"parche {cc.input_patch_size}, "
      f"horizonte {cc.prediction_length}, cuantiles {cc.quantiles}")

x = torch.tensor(log_rv[-512:], dtype=torch.float32)[None, :]
z = (x - x.mean()) / x.std(correction=0)       # tipificar
parches = z.unfold(-1, 16, 16)                    # (1, 32, 16)
z_bolt, _ = bolt.model.instance_norm(x)
print("parches:", tuple(parches.shape),
      "| iguales a los del modelo:",
      torch.allclose(parches, bolt.model.patch(z_bolt), atol=1e-5))
print(f"valores tipificados: de {z.min():.2f} a {z.max():.2f}")
```
```output
contexto máx. 2048, parche 16, horizonte 64, cuantiles [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
parches: (1, 32, 16) | iguales a los del modelo: True
valores tipificados: de -2.45 a 2.57
```

Contexto máximo de 2 048 observaciones, parches de 16, horizonte de 64 pasos y nueve cuantiles. Tipificada, la log RV
ocupa de −2,45 a 2,57: el problema de resolución de la escala media desaparece, porque aquí no hay redondeo (cada parche
entra con sus valores reales) y la media se resta antes.

## Una primera previsión

Con el modelo cargado, prever es una llamada: se le da el contexto (las últimas 512 sesiones) y el horizonte. El
origen elegido es el viernes 6 de marzo de 2020, la víspera de uno de los choques plantados en los datos (el README
documenta +1,8 en log-varianza el 9 de marzo). La salida se pasa a volatilidad anual para leerla mejor:

```python
i0 = fechas.get_loc(pd.Timestamp("2020-03-06")) + 1
q, _ = bolt.predict_quantiles(
    inputs=torch.tensor(log_rv[i0 - 512:i0], dtype=torch.float32),
    prediction_length=5, quantile_levels=[0.1, 0.5, 0.9])
a_vol = lambda l: np.sqrt(252 * np.exp(l))       # log RV a vol. anual
tabla = pd.DataFrame(
    a_vol(q[0].numpy().astype(float)) * 100,
    index=fechas[i0:i0 + 5].date, columns=["q10", "q50", "q90"])
tabla["realizada"] = vol_anual[i0:i0 + 5] * 100
tabla.round(1)
```
```output
             q10   q50   q90  realizada
2020-03-09  12.0  15.5  20.2       52.8
2020-03-10  12.0  16.0  21.4       28.6
2020-03-11  12.2  16.5  22.1       31.0
2020-03-12  12.4  16.9  22.6       24.9
2020-03-13  12.5  17.1  22.9       23.3
```

El modelo prevé una volatilidad mediana del 15,5 % para el lunes, con una banda del 10 al 90 % de 12,0 a 20,2 %, y una
ligera subida en los días siguientes. La realizada del lunes es del 52,8 %: el choque no estaba en el pasado de la
serie y nadie podía preverlo con ella. A partir del martes la realizada (28,6 %, 31,0 %…) sigue por encima de la banda,
porque esta previsión es a cinco días desde el viernes y no se actualiza. La figura de la cabecera muestra lo que pasa
cuando se vuelve a prever cada día con el dato nuevo: la banda salta tras el choque y persigue a la realizada.

## Zero-shot a un día con origen móvil

Una previsión suelta no evalúa nada. La evaluación honesta es la de la lección 5.4.3 sin el reentrenamiento: para cada
sesión de 2018 a 2025, se prevé el día siguiente con las 512 sesiones anteriores y se compara con lo que pasó. El
modelo no se ajusta nunca, así que no hay fuga posible por entrenamiento; la única regla es que el contexto acabe en la
víspera. Las referencias son la previsión **ingenua** (la log RV de ayer) y la **media de los últimos 22 días**.

```python
prueba = np.flatnonzero(fechas.year >= 2018)
C = 512

def zero_shot(serie, origenes, contexto=C, lote=256):
    """Cuantiles 0,1…0,9 de mañana para cada origen (sin reentrenar)."""
    salida = []
    for k in range(0, len(origenes), lote):
        ventanas = np.stack([serie[i - contexto:i]
                             for i in origenes[k:k + lote]])
        q, _ = bolt.predict_quantiles(
            inputs=torch.tensor(ventanas, dtype=torch.float32),
            prediction_length=1)
        salida.append(q[:, 0, :].numpy().astype(float))
    return np.concatenate(salida)

q_bolt = zero_shot(log_rv, prueba)
real = log_rv[prueba]
previsiones = {
    "ingenua (ayer)": log_rv[prueba - 1],
    "media de 22 días": np.array([log_rv[i - 22:i].mean()
                                  for i in prueba]),
    "Chronos-Bolt (mediana)": q_bolt[:, 4],
}

def metricas(f):
    e2 = (real - f) ** 2
    return {"ECM": e2.mean(), "R²": np.corrcoef(real, f)[0, 1] ** 2}

tabla = pd.DataFrame({k: metricas(f) for k, f in previsiones.items()})
tabla.T.round(3)
```
```output
                          ECM     R²
ingenua (ayer)          0.233  0.503
media de 22 días        0.237  0.418
Chronos-Bolt (mediana)  0.181  0.551
```

Chronos-Bolt, sin haber visto nunca la serie, da un error cuadrático medio de 0,181 frente a 0,233 de la ingenua y
0,237 de la media de 22 días, y un R² de 0,551 (el R² es el de la regresión de la log RV sobre la previsión, la misma
medida que el README). Para situarlo: el README publica, en el mismo periodo, un R² de **0,57 para un HAR**
reestimado cada enero y de **0,61 para el oráculo**, la esperanza exacta de la varianza con la información de la
víspera. El modelo fundacional queda entre la referencia ingenua y el HAR, más cerca del HAR. Si eso basta o no lo
decide el laboratorio de la lección 5.8.5, con GARCH y LightGBM en la misma prueba.

La previsión que se compara es la **mediana** (el cuantil 0,5), no la media. En log da igual porque la distribución es
casi simétrica; en varianza no daría igual (la exponencial de la mediana del log infravalora la media de la varianza),
y la lección 5.8.3 lo trata.

## ¿Es la mejora más grande que el ruido?

Las diferencias de error se leen con la diferencia pareada día a día, como en la lección 5.4.3: los días difíciles
lo son para todos los modelos, y la diferencia elimina ese ruido común.

```python
def pareada(a, b):
    """Diferencia media de errores cuadráticos (a − b) y su t."""
    d = (real - previsiones[a]) ** 2 - (real - previsiones[b]) ** 2
    return d.mean(), d.mean() / (d.std(ddof=1) / np.sqrt(len(d)))

for ref in ("ingenua (ayer)", "media de 22 días"):
    dif, t = pareada("Chronos-Bolt (mediana)", ref)
    print(f"Bolt − {ref:<16} {dif:+.4f}  t = {t:+.1f}")
dentro = (real >= q_bolt[:, 0]) & (real <= q_bolt[:, 8])
print(f"cobertura del intervalo 10-90 %: {dentro.mean():.1%} "
      f"({len(real)} días)")
```
```output
Bolt − ingenua (ayer)   -0.0516  t = -9.8
Bolt − media de 22 días -0.0557  t = -10.4
cobertura del intervalo 10-90 %: 76.4% (2011 días)
```

Las dos mejoras están muy por encima del ruido: t = −9,8 frente a la ingenua y −10,4 frente a la media de 22 días. Los
días de prueba se solapan en el contexto pero no en el error que se mide (cada día se prevé una vez), así que el error
típico de la diferencia es razonable; si los errores tuvieran autocorrelación habría que corregirlo (Newey-West), pero
con un t de −10 haría falta una autocorrelación muy fuerte para cambiar la conclusión. El intervalo del 10 al 90 % cubre el **76,4 %** de los días, no el 80 %: los
cuantiles son algo estrechos. La lección 5.8.3 mide la calibración cuantil a cuantil y la corrige.

El resultado se mantiene año a año:

```python
anio = fechas.year[prueba]
por_anio = pd.DataFrame({k: (real - f) ** 2 for k, f in
                         previsiones.items()}).groupby(anio).mean()
por_anio.round(3)
```
```output
       ingenua (ayer)  media de 22 días  Chronos-Bolt (mediana)
fecha
2018            0.202             0.163                   0.147
2019            0.229             0.194                   0.166
2020            0.288             0.285                   0.224
2021            0.214             0.301                   0.181
2022            0.247             0.261                   0.197
2023            0.201             0.216                   0.159
2024            0.233             0.224                   0.182
2025            0.249             0.251                   0.194
```

*Fig. — Error cuadrático medio de la previsión a un día del logaritmo de la varianza realizada del índice sintético, por año: ingenua, media de 22 días y Chronos-Bolt zero-shot.* Tres líneas de 2018 a 2025. Chronos-Bolt (destacada) tiene el error más bajo todos los años, entre 0,147 y 0,224 (el máximo, en 2020). La ingenua va de 0,201 a 0,288 y la media de 22 días, de 0,163 a 0,301, con su peor año en 2021, tras el choque de 2020.

Chronos-Bolt tiene el error más bajo los ocho años. El peor es 2020 para todos (el año de los choques de marzo). La
media de 22 días sufre más en 2021: arrastra durante semanas los días de volatilidad extrema del año anterior, que el
modelo fundacional descuenta antes.

## Lo que el zero-shot no hace

Una previsión zero-shot buena sobre la volatilidad no dice nada de otras cosas, y conviene tenerlo claro antes de
seguir:

| El modelo hace | El modelo no hace |
|---|---|
| Extraer de la historia reciente persistencia, nivel y dispersión | Conocer el activo, el calendario de eventos ni las noticias |
| Dar cuantiles sin entrenar nada | Garantizar que esos cuantiles están calibrados (aquí, 76,4 % frente a 80 %) |
| Funcionar en series con estructura (la volatilidad tiene mucha) | Predecir lo que no tiene estructura: los rendimientos de `vol5` son impredecibles por construcción |
| Ahorrar el trabajo de estimar un modelo por serie | Superar al proceso generador: el oráculo es el suelo |

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

Que un modelo prevea bien la volatilidad no autoriza a usarlo para predecir la dirección del precio. La volatilidad es
persistente y casi cualquier modelo razonable la sigue; el rendimiento de mañana no lo es. La lección 5.8.4 revisa la
evidencia publicada y, en estos datos, la media condicional del rendimiento es constante: cualquier «acierto» ahí es
ruido.

</aside>

La lección 5.8.2 compara las familias (Chronos, Chronos-Bolt, Chronos-2, TimesFM-2.5, Moirai-2.0, TiRex, TimeGPT y
Kronos) y pone a TimesFM-2.5 al lado de Chronos-Bolt; la 5.8.3 entra en las decisiones prácticas (longitud del
contexto, cuantiles, covariables y ajuste fino).

## Ejercicios

1. Repite la evaluación zero-shot con el activo `cripto` (usa un origen de cada cuatro para que sea rápido). ¿Mejora
   Chronos-Bolt a las dos referencias? Compara su R² con el del HAR del README para ese activo (0,40).

**Solución.**

```python
vc = vol[vol["activo"] == "cripto"].set_index("fecha")
log_rv_c = np.log(vc["rv_5min"].to_numpy())
orig = prueba[::4]
q_c = zero_shot(log_rv_c, orig)
real_c = log_rv_c[orig]
for nombre, f in (("ingenua", log_rv_c[orig - 1]),
                  ("media 22", np.array([log_rv_c[i - 22:i].mean()
                                         for i in orig])),
                  ("Bolt", q_c[:, 4])):
    print(f"{nombre:<9} ECM {np.mean((real_c - f) ** 2):.3f}  "
          f"R² {np.corrcoef(real_c, f)[0, 1] ** 2:.3f}")
```
```output
ingenua   ECM 0.652  R² 0.252
media 22  ECM 0.427  R² 0.328
Bolt      ECM 0.395  R² 0.393
```

En cripto la ingenua es mucho peor (ECM 0,652): la RV diaria de un activo con saltos frecuentes es muy ruidosa y
el dato de ayer es mala previsión. Chronos-Bolt baja a 0,395, mejor que la media de 22 días (0,427), y su R² de 0,393
queda casi en el del HAR del README (0,40), con el oráculo en 0,46. Con 503 orígenes el R² tiene un error de unas
centésimas: la comparación con el HAR se lee como «parecido», no como una ordenación.

2. Tipifica el contexto de log RV (resta la media y divide por la desviación típica) antes de tokenizarlo con la receta
   de Chronos. ¿Cuántos cajones usa ahora? ¿Qué te dice sobre la escala media del Chronos original?

**Solución.**

```python
x = log_rv[-512:]
z = (x - x.mean()) / x.std()
for nombre, serie in (("log RV tal cual", x), ("log RV tipificada", z)):
    ids, rec = tokens_a_mano(serie)
    print(f"{nombre:<18} tokens distintos {len(np.unique(ids)):4d}")
```
```output
log RV tal cual    tokens distintos   46
log RV tipificada  tokens distintos  353
```

Tipificada, la misma información usa 353 cajones en lugar de 46: casi ocho veces más resolución. La escala media del
Chronos original está pensada para series positivas cerca de su nivel típico (ventas, demanda, visitas), donde dividir
por la media es natural. Para series lejos del cero o con signo, la segunda generación (Chronos-Bolt, Chronos-2) pasó a
tipificar, y además no cuantiza. Si usas un modelo que cuantiza, la transformación previa de la serie forma parte del
modelo.

3. Un artículo evalúa en 2025 un modelo fundacional sobre rentabilidades diarias de acciones del S&P 500 de 2015 a
   2023, y el modelo se preentrenó con un corpus publicado en 2024 que incluye series financieras públicas. ¿Qué
   problema tiene la evaluación? ¿Por qué los datos del curso no lo tienen?

**Solución.**

El riesgo es la **contaminación del preentrenamiento**: si las series de prueba (o series muy correlacionadas con
ellas, como otras acciones del mismo índice en las mismas fechas) estaban en el corpus, el modelo puede haber
memorizado parte de lo que luego «prevé», y la evaluación ya no es fuera de muestra. Es la misma fuga de la lección
5.2.2, pero escondida en los pesos de otro. Los autores serios lo controlan: la ficha de Chronos-2 dice que excluye
las partes de prueba de los conjuntos que solapan con el benchmark GIFT-Eval, y la de Moirai-2.0 habla de subconjuntos
«non-leaking». Para una evaluación propia, lo seguro es probar con datos posteriores a la fecha de corte del corpus o con
datos que no pueden estar en él, como los sintéticos de este curso, generados después de publicarse los modelos.

## Autoevaluación

1. ¿Qué significa que una previsión de Chronos-Bolt sobre `vol5` es zero-shot?

   - A. Que el modelo no tiene parámetros
   - B. Que la previsión no tiene error en el primer día
   - C. Que el modelo no se ha entrenado ni ajustado con esa serie: los pesos son los del preentrenamiento y la serie solo entra como contexto
   - D. Que el modelo se reentrena desde cero con cada serie

   *Respuesta C: Que el modelo no se ha entrenado ni ajustado con esa serie: los pesos son los del preentrenamiento y la serie solo entra como contexto.* El modelo aprende una vez, con su corpus, y después recibe la historia reciente de la serie como contexto. Lo que «aprende» de la serie nueva lo hace en la pasada hacia delante, sin tocar ningún peso.

2. Chronos tokeniza el logaritmo de la varianza realizada del índice (valores en torno a −9,8 con una desviación de 0,6) con escala media y 4 093 cajones uniformes en [−15, 15]. ¿Qué pasa?

   - A. La serie escalada vale casi siempre en torno a −1, así que solo usa unos 46 cajones y el redondeo llega a 0,035 en log (un 3,6 % de la varianza)
   - B. Nada: la escala media es invariante a cualquier transformación
   - C. Se sale del rango [−15, 15] y el modelo falla
   - D. Usa todos los cajones por igual

   *Respuesta A: La serie escalada vale casi siempre en torno a −1, así que solo usa unos 46 cajones y el redondeo llega a 0,035 en log (un 3,6 % de la varianza).* La escala media divide por la media de |x| pero no resta la media. En una serie lejos del cero y con poca variación relativa, todos los valores escalados caen en una franja estrecha. Tipificada (restando la media), la misma serie usa 353 cajones.

3. ¿Por qué Chronos-Bolt es mucho más rápido que el Chronos original del mismo tamaño?

   - A. Porque tiene menos capas
   - B. Porque no usa atención
   - C. Porque solo predice la media
   - D. Porque agrupa 16 observaciones en cada token (512 datos son 33 tokens en vez de 513) y predice todos los cuantiles del horizonte de una vez, en lugar de muestrear token a token

   *Respuesta D: Porque agrupa 16 observaciones en cada token (512 datos son 33 tokens en vez de 513) y predice todos los cuantiles del horizonte de una vez, en lugar de muestrear token a token.* Menos tokens abaratan la atención, cuyo coste crece con el cuadrado de su número, y la salida directa de cuantiles evita generar muchas trayectorias autorregresivas. Amazon cifra la ganancia en hasta 250 veces en velocidad.

4. Chronos-Bolt zero-shot da un ECM de 0,181 en log RV frente a 0,233 de la previsión ingenua, con t = −9,8 en la diferencia pareada. ¿Qué se puede concluir?

   - A. Que Chronos-Bolt es el mejor modelo de volatilidad posible
   - B. Que mejora con claridad a la ingenua en estos datos, pero queda por debajo del HAR del README (R² 0,551 frente a 0,57) y lejos del oráculo (0,61): falta compararlo con modelos estimados sobre la serie (lección 5.8.5)
   - C. Que también predecirá bien los rendimientos
   - D. Nada: 2 011 días son pocos

   *Respuesta B: Que mejora con claridad a la ingenua en estos datos, pero queda por debajo del HAR del README (R² 0,551 frente a 0,57) y lejos del oráculo (0,61): falta compararlo con modelos estimados sobre la serie (lección 5.8.5).* La referencia ingenua es fácil de batir. El listón serio es un HAR, un GARCH o un LightGBM estimados con la propia serie, y el suelo es el oráculo. Y la volatilidad tiene estructura; los rendimientos de `vol5`, por construcción, no.

5. El 9 de marzo de 2020 la volatilidad realizada del índice sintético salta al 52,8 % y la banda 10-90 % prevista la víspera va de 12,0 a 20,2 %. ¿Qué indica?

   - A. Que el modelo está roto
   - B. Que hay que entrenar más el modelo
   - C. Que el choque es una innovación sin aviso en los datos: ningún modelo que solo vea el pasado de la serie podía preverlo, y la banda reacciona después
   - D. Que el contexto era demasiado corto

   *Respuesta C: Que el choque es una innovación sin aviso en los datos: ningún modelo que solo vea el pasado de la serie podía preverlo, y la banda reacciona después.* El README del nivel lo documenta como un choque de +1,8 en log-varianza ese día. Un modelo de series, fundacional o no, solo puede anticipar lo que el pasado contiene; los saltos se gestionan con riesgo, no con previsión.

## Ideas clave

- Un modelo fundacional se preentrena una vez con un corpus enorme y variado (Chronos: unos 84 000 millones de observaciones de 28 conjuntos, más datos aumentados y sintéticos) y se usa zero-shot: la serie nueva entra como contexto, sin tocar los pesos.
- Chronos convierte cada dato en un token: lo divide por la media de |x| y lo redondea a uno de 4 093 cajones en [−15, 15]. En una serie lejos del cero, como el log de la varianza, eso deja pocos cajones útiles.
- Chronos-Bolt y TimesFM tipifican la serie y la cortan en parches (16 y 32 datos): 512 observaciones son 33 o 16 tokens, y la atención cuesta mucho menos.
- Sobre la log RV del índice, 2018-2025, Chronos-Bolt-small zero-shot da un ECM de 0,181 frente a 0,233 de la ingenua (t = −9,8) y un R² de 0,551, por debajo del HAR del README (0,57) y del oráculo (0,61).
- Sus cuantiles son útiles pero no exactos: el intervalo 10-90 % cubre el 76,4 % de los días. Los choques, como el del 9 de marzo de 2020, no se prevén.

## Recursos

### Research

- [Estimadores de volatilidad para trading](https://pythonparatrading.com/estimadores-de-volatilidad-para-trading/) · Research-0002 · 4 min

### Documentación

- [Hugging Face: ficha de amazon/chronos-bolt-small](https://huggingface.co/amazon/chronos-bolt-small) · huggingface.co
- [chronos-forecasting (GitHub de Amazon Science)](https://github.com/amazon-science/chronos-forecasting) · github.com

### Libros

- [Ansari, A. F. et al. (2024): Chronos: Learning the Language of Time Series. arXiv:2403.07815](https://arxiv.org/abs/2403.07815) · arxiv.org
- [Das, A., Kong, W., Sen, R. y Zhou, Y. (2024): A decoder-only foundation model for time-series forecasting. arXiv:2310.10688](https://arxiv.org/abs/2310.10688) · arxiv.org

### Datos

- [Datos del nivel 5: rendimientos y varianza realizada de seis activos (Parquet)](https://pythonparatrading.com/curso/datos/nivel-5/vol5_diario.parquet) · Descarga · vol5_diario.parquet

Lección anterior: [5.7.5 · Optimización de carteras end-to-end: capas diferenciables y pérdida de Sharpe](https://pythonparatrading.com/curso-python-trading/nivel-5/optimizacion-de-carteras-end-to-end/)

Lección siguiente: [5.8.2 · Las familias de modelos fundacionales: arquitectura, datos y licencia](https://pythonparatrading.com/curso-python-trading/nivel-5/familias-chronos-timesfm-moirai-y-kronos/)
