---
title: "5.8.2 Familias: Chronos, TimesFM, Moirai y Kronos"
description: "Arquitecturas, datos, tamaños y licencias de los modelos fundacionales de series, y Chronos-Bolt frente a TimesFM-2.5 sobre la volatilidad realizada."
url: "https://pythonparatrading.com/curso-python-trading/nivel-5/familias-chronos-timesfm-moirai-y-kronos/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-5/familias-chronos-timesfm-moirai-y-kronos.png"
---

# 5.8.2 · Las familias de modelos fundacionales: arquitectura, datos y licencia

> En dos años han salido una docena de modelos fundacionales de series de Amazon, Google, Salesforce, Nixtla, NX-AI y grupos académicos. Se parecen más de lo que sugieren sus nombres: casi todos son transformers sobre parches que devuelven cuantiles. Esta lección los ordena por arquitectura, datos de preentrenamiento, tamaño y licencia, con las cifras de sus artículos y fichas, y pone a Chronos-Bolt y TimesFM-2.5 a prever lo mismo; del resto, el código real queda sin ejecutar.

- 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: 70 min
- Requisitos: [5.8.1 · Modelos fundacionales de series: preentrenar una vez, prever sin entrenar](https://pythonparatrading.com/curso-python-trading/nivel-5/que-son-los-modelos-fundacionales-de-series/), [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/)

## Al terminar esta lección

- Situar cada familia (Chronos, Chronos-Bolt, Chronos-2, TimesFM-2.5, Moirai-2.0, TiRex, TimeGPT y Kronos) por arquitectura, datos de preentrenamiento, tamaño, salida y licencia, con fuentes primarias.
- Cargar Chronos-Bolt-small y TimesFM-2.5 sin red, contar sus parámetros y leer su configuración.
- Compararlos en la misma prueba de volatilidad con origen móvil: error cuadrático, pérdida pinball, cobertura y diferencias pareadas.
- Comprobar la equivarianza a escala y desplazamiento de los dos modelos y qué hace la opción `force_flip_invariance` de TimesFM.
- Leer el código de Chronos-2, Moirai-2.0, TiRex, TimeGPT y Kronos, y saber por qué no se ejecuta aquí.

## Un mapa de familias

La lección 5.8.1 explicó lo común: preentrenar una vez con un corpus enorme y prever zero-shot a partir de un contexto
tipificado y cortado en parches. Lo que distingue a cada familia es la arquitectura del transformer, qué datos vio,
qué devuelve, cuánto mide y con qué licencia se publica. La tabla resume lo que dicen sus artículos y fichas (las de
Hugging Face y los README de sus repositorios, consultados en octubre de 2026):

| Modelo | Quién y cuándo | Arquitectura | Salida | Tamaño | Licencia de los pesos |
|---|---|---|---|---|---|
| Chronos | Amazon, mar. 2024 | T5 codificador-decodificador, un token por dato | muestras de trayectorias | 20-710 M | Apache-2.0 |
| Chronos-Bolt | Amazon, nov. 2024 | T5 codificador-decodificador, parches de 16 | 9 cuantiles, 64 pasos de una vez | 9-205 M | Apache-2.0 |
| Chronos-2 | Amazon, oct. 2025 | T5 solo codificador, atención de tiempo y de grupo | 21 cuantiles, covariables | 120 M | Apache-2.0 |
| TimesFM-2.5 | Google, sep. 2025 | solo decodificador, parches de 32 | media y 9 cuantiles | 200 M | Apache-2.0 |
| Moirai-2.0 | Salesforce, nov. 2025 | solo decodificador, un tamaño de parche | cuantiles, varios tokens por paso | 11,4 M (small) | CC-BY-NC-4.0 |
| TiRex | NX-AI, may. 2025 | xLSTM (sLSTM), no transformer | cuantiles | 35 M | comunitaria NXAI |
| TimeGPT-1 | Nixtla, oct. 2023 | transformer codificador-decodificador | puntual e intervalos conformes | no publicado | cerrado (API de pago) |
| Kronos | académico, ago. 2025 | solo decodificador sobre velas OHLCV tokenizadas | velas futuras muestreadas | 4,1-102,3 M abiertos | MIT |

La figura de la cabecera pone los tamaños en escala logarítmica. Van de 11 a 710 millones de parámetros, diminutos
frente a un modelo de lenguaje, y el más pequeño de la figura, Moirai-2.0 small, es el que sus autores defienden: en su
artículo entrenan variantes de 8 y 30 veces más parámetros y ninguna lo mejora en el benchmark GIFT-Eval («we do not
observe scaling benefits»). Con los datos de preentrenamiento que hay, más parámetros no compran más precisión.

La cronología cuenta otra historia, la de la velocidad del campo: casi todas las familias sacan una segunda versión en
menos de dos años, y la última de TimesFM, la 3.0 de agosto de 2026, cambia la licencia de los pesos.

*Fig. — Cronología de los modelos fundacionales de series de la lección, por familia (fecha del primer artículo en arXiv o del anuncio en el repositorio).* Seis filas. Chronos: marzo de 2024, Chronos-Bolt en noviembre de 2024 y Chronos-2 en octubre de 2025. TimesFM: octubre de 2023, la versión 2.5 en septiembre de 2025 y la 3.0, destacada, en agosto de 2026, con pesos bajo una licencia no comercial. Moirai: febrero de 2024 y Moirai 2.0 en noviembre de 2025. TiRex: mayo de 2025. TimeGPT: octubre de 2023. Kronos, preentrenado con velas OHLCV: agosto de 2025.

```mermaid
flowchart TB
  C[Chronos] --> B[Chronos-Bolt]
  B --> C2[Chronos-2]
  T1[TimesFM] --> T25[TimesFM-2.5]
  T25 --> T3[TimesFM 3.0]
  M1[Moirai] --> M2[Moirai-2.0]
```

## Chronos: del token al parche

El **Chronos original** (Ansari et al., 2024) es el que la lección 5.8.1 desmontó: escala media, 4 093 cajones, un T5
que predice el token siguiente con entropía cruzada y trayectorias muestreadas. Se preentrenó con 28 conjuntos públicos
(unos 84 000 millones de observaciones), 10 millones de series aumentadas con TSMixup (mezclas de series) y 1 millón de
series sintéticas de procesos gaussianos (KernelSynth). Sus pesos no están en la caché del curso; el código, con la
misma librería, sería este:

```python skip
from chronos import ChronosPipeline

chronos = ChronosPipeline.from_pretrained(
    "amazon/chronos-t5-small", device_map="cpu", dtype=torch.float32)
# 20 trayectorias muestreadas: (series, muestras, horizonte)
muestras = chronos.predict(torch.tensor(series["indice"][-512:]),
                           prediction_length=5, num_samples=20)
q10, q50, q90 = np.quantile(muestras[0].numpy(), [0.1, 0.5, 0.9],
                            axis=0)
```

**Chronos-Bolt** (noviembre de 2024) cambia el token por el parche y el muestreo por los cuantiles directos. Según su
ficha, se entrenó con «casi 100 000 millones de observaciones» y es hasta 250 veces más rápido y 20 veces más eficiente
en memoria que el Chronos original del mismo tamaño; el README del repositorio añade un 5 % menos de error. Es el modelo
de la lección 5.8.1. Para comparar, primero los datos: los mismos de la lección anterior, en dos activos, con un origen
de cada diez sesiones para que TimesFM, más lento, quepa en el tiempo de la lección.

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

torch.manual_seed(0)
torch.use_deterministic_algorithms(True)
vol = pd.read_parquet("vol5_diario.parquet")
series = {}
for a in ("indice", "cripto"):
    d = vol[vol["activo"] == a].set_index("fecha")
    series[a] = np.log(d["rv_5min"].to_numpy())
fechas = pd.DatetimeIndex(d.index)
C = 512
origenes = np.flatnonzero(fechas.year >= 2018)[::10]
print(f"{len(origenes)} orígenes por activo, del "
      f"{fechas[origenes[0]].date()} al {fechas[origenes[-1]].date()}")
```
```output
202 orígenes por activo, del 2018-01-02 al 2025-12-31
```

```python
from chronos import BaseChronosPipeline

bolt = BaseChronosPipeline.from_pretrained(
    "amazon/chronos-bolt-small", device_map="cpu",
    dtype=torch.float32)
n_bolt = sum(p.numel() for p in bolt.model.parameters())
print(f"Chronos-Bolt-small: {n_bolt / 1e6:.1f} M parámetros, "
      f"T5 de {bolt.model.config.num_layers} + "
      f"{bolt.model.config.num_decoder_layers} capas, "
      f"d_model {bolt.model.config.d_model}")

def prev_bolt(y):
    v = np.stack([y[i - C:i] for i in origenes])
    q, _ = bolt.predict_quantiles(
        inputs=torch.tensor(v, dtype=torch.float32),
        prediction_length=1)
    return q[:, 0, :].numpy().astype(float)     # cuantiles 0,1…0,9

q_bolt = {a: prev_bolt(y) for a, y in series.items()}
```
```output
Chronos-Bolt-small: 47.7 M parámetros, T5 de 6 + 6 capas, d_model 512
```

47,7 millones de parámetros contados en los pesos, los «48 M» de la ficha: un T5 pequeño con seis capas en el
codificador y seis en el decodificador.

## Chronos-2: covariables y atención de grupo

**Chronos-2** (octubre de 2025) es el salto conceptual de la familia. Según su artículo, es un transformer **solo
codificador** de 120 millones de parámetros que alterna dos tipos de atención: la de **tiempo**, entre parches de una
misma serie, y la de **grupo**, entre series relacionadas y covariables del mismo ejemplo. Con eso hace, sin
reentrenar, previsión univariante, multivariante y con **covariables** pasadas o conocidas en el futuro (numéricas o
categóricas); llega a un contexto de 8 192 pasos, a un horizonte de 1 024 y devuelve 21 cuantiles (del 0,01 al 0,99).
Sus autores atribuyen esas capacidades a un preentrenamiento con datos sintéticos que imponen estructuras multivariantes
sobre series univariantes. En su benchmark propio (fev-bench) queda primero, por delante de TiRex y TimesFM-2.5.

La librería instalada (`chronos-forecasting` 2.3.2) trae `Chronos2Pipeline`, pero los pesos de `amazon/chronos-2` no
están en la caché del entorno, así que su código va sin ejecutar. La forma de las entradas es la de su documentación
(un diccionario por serie); la lección 5.8.3 lo usa con covariables:

```python skip
from chronos import Chronos2Pipeline

chronos2 = Chronos2Pipeline.from_pretrained("amazon/chronos-2",
                                            device_map="cpu")
entradas = [{"target": series[a][-2048:]} for a in series]
cuantiles, media = chronos2.predict_quantiles(
    entradas, prediction_length=1, quantile_levels=[0.1, 0.5, 0.9])
# una lista: un tensor por serie (variables, horizonte, cuantiles)
```

## TimesFM-2.5: solo decodificador

**TimesFM** (Das et al., 2024, Google) es un transformer **solo decodificador**: lee el contexto en parches de 32
puntos con atención causal, como un modelo de lenguaje, y cada token de salida predice un bloque de 128 pasos. Se
preentrenó con del orden de 100 000 millones de puntos de Google Trends, visitas de Wikipedia y series sintéticas. La
versión **2.5** (septiembre de 2025) baja de 500 a 200 millones de parámetros, sube el contexto de 2 048 a 16 384 pasos,
añade una cabeza opcional de cuantiles continuos de unos 30 millones de parámetros (hasta 1 024 pasos) y quita el
indicador de frecuencia que pedía la versión anterior, según el README del proyecto. Su ficha añade al corpus el
conjunto de preentrenamiento de GIFT-Eval. Los pesos de la caché permiten comprobar las cifras:

```python
import timesfm

tfm = timesfm.TimesFM_2p5_200M_torch.from_pretrained(
    "google/timesfm-2.5-200m-pytorch", torch_compile=False)
for nombre, m in tfm.model.named_children():
    n = sum(p.numel() for p in m.parameters())
    print(f"{nombre:<28} {n / 1e6:6.1f} M")
n_tfm = sum(p.numel() for p in tfm.model.parameters())
cfg = tfm.model.config
print(f"{'total':<28} {n_tfm / 1e6:6.1f} M | contexto máx. "
      f"{cfg.context_limit}, parche {cfg.input_patch_len}, salida "
      f"{cfg.output_patch_len}, cuantiles hasta "
      f"{cfg.output_quantile_len}")
```
```output
tokenizer                       1.8 M
stacked_xf                    196.7 M
output_projection_point         4.9 M
output_projection_quantiles    27.9 M
total                         231.3 M | contexto máx. 16384, parche 32, salida 128, cuantiles hasta 1024
```

El transformer (`stacked_xf`) y sus proyecciones de entrada y salida puntual suman unos 203 millones, los «200 M» del
nombre; la cabeza de cuantiles continuos añade 27,9 millones, la cabeza de unos 30 millones del README. En total,
231,3 millones de parámetros.

Antes de prever hay que **compilar** la configuración: el contexto máximo, el horizonte máximo y varias opciones. Dos
de ellas importan aquí. `torch_compile=False` evita `torch.compile`, que en CPU necesita un compilador de C (README del
entorno `ml`). Y `force_flip_invariance`, activada por defecto, ejecuta el modelo sobre la serie y sobre la serie
cambiada de signo y combina las dos; garantiza una propiedad que la sección de equivarianza comprueba, a costa de
**doblar el cómputo**. La lección la desactiva para la comparación (en una prueba previa, con ella activada el ECM
cambió en la tercera cifra decimal):

```python
def compilar(flip):
    tfm.compile(timesfm.ForecastConfig(
        max_context=C, max_horizon=128, normalize_inputs=True,
        use_continuous_quantile_head=True, fix_quantile_crossing=True,
        force_flip_invariance=flip, per_core_batch_size=64))

compilar(flip=False)

def prev_tfm(y):
    punto, q = tfm.forecast(horizon=1,
                            inputs=[y[i - C:i] for i in origenes])
    return punto[:, 0], q[:, 0, :]     # q: media y deciles 0,1…0,9

r_tfm = {a: prev_tfm(y) for a, y in series.items()}
punto, q = r_tfm["indice"]
print("columnas de cuantiles:", q.shape[1],
      "| ¿punto = cuantil 0,5?", np.allclose(punto, q[:, 5]))
```
```output
columnas de cuantiles: 10 | ¿punto = cuantil 0,5? True
```

La salida de cuantiles tiene diez columnas, la media y los deciles del 0,1 al 0,9, y la previsión puntual que devuelve
`forecast` es la del **cuantil 0,5**, la mediana. Las dos familias comparan, por tanto, lo mismo: medianas. Otro
detalle: `per_core_batch_size` es el tamaño del lote; por defecto vale 1 y TimesFM prevé serie a serie.

## Cara a cara sobre la volatilidad

Mismos orígenes, mismo contexto de 512 sesiones, mismo objetivo (la log RV de mañana). Además del error cuadrático y el
R², la tabla da la **pérdida pinball** media de los nueve deciles (la regla de puntuación de la lección 5.7.3: premia
la distribución entera, no solo la mediana) y la cobertura del intervalo del 10 al 90 %.

```python
tau = np.arange(1, 10) / 10

def pinball(y, q):
    u = y[:, None] - q
    return np.maximum(tau * u, (tau - 1) * u).mean()

filas = {}
for a, y in series.items():
    real = y[origenes]
    cand = {"ingenua": (y[origenes - 1], None),
            "Chronos-Bolt": (q_bolt[a][:, 4], q_bolt[a]),
            "TimesFM-2.5": (r_tfm[a][0], r_tfm[a][1][:, 1:])}
    for m, (f, qs) in cand.items():
        fila = {"ECM": np.mean((real - f) ** 2),
                "R²": np.corrcoef(real, f)[0, 1] ** 2}
        if qs is not None:
            fila["pinball"] = pinball(real, qs)
            fila["cob. 10-90"] = np.mean((real >= qs[:, 0])
                                         & (real <= qs[:, 8]))
        filas[(a, m)] = fila
tabla = pd.DataFrame(filas).T
tabla.round(3)
```
```output
                       ECM     R²  pinball  cob. 10-90
indice ingenua       0.241  0.511      NaN         NaN
       Chronos-Bolt  0.205  0.543    0.140       0.762
       TimesFM-2.5   0.210  0.532    0.141       0.782
cripto ingenua       0.621  0.303      NaN         NaN
       Chronos-Bolt  0.370  0.514    0.167       0.807
       TimesFM-2.5   0.378  0.505    0.169       0.787
```

*Fig. — Error cuadrático medio de Chronos-Bolt-small y TimesFM-2.5 zero-shot en la previsión a un día de la log RV, relativo al de la previsión ingenua, en 202 orígenes de 2018-2025.* Cuatro barras. Índice: Chronos-Bolt 0,85 y TimesFM-2.5 0,87 del error de la ingenua. Cripto: Chronos-Bolt 0,60 (destacado) y TimesFM-2.5 0,61. Las diferencias entre los dos modelos no son significativas (t de 1,0 y 1,2).

Los dos modelos mejoran a la ingenua y quedan muy juntos: en el índice, ECM de 0,205 (Bolt) y 0,210 (TimesFM) frente a
0,241; en cripto, 0,370 y 0,378 frente a 0,621. La pinball también es casi igual (0,140 frente a 0,141 en el índice) y
la cobertura del 10-90 % va del 76 al 81 %, alrededor del 80 % nominal. Las cifras del índice no coinciden con las de
la lección 5.8.1 porque aquí se usa un origen de cada diez (202 días en lugar de 2 011). Con tan pocos días, las
diferencias hay que medirlas:

```python
def pareada(y, f1, f2):
    real = y[origenes]
    d = (real - f1) ** 2 - (real - f2) ** 2
    t = d.mean() / d.std(ddof=1) * np.sqrt(len(d))
    return f"{d.mean():+.4f} (t = {t:+.1f})"

for a, y in series.items():
    fb, ft = q_bolt[a][:, 4], r_tfm[a][0]
    print(f"{a:<7} Bolt − ingenua {pareada(y, fb, y[origenes - 1])}  "
          f"TimesFM − ingenua {pareada(y, ft, y[origenes - 1])}  "
          f"TimesFM − Bolt {pareada(y, ft, fb)}")
```
```output
indice  Bolt − ingenua -0.0366 (t = -2.0)  TimesFM − ingenua -0.0316 (t = -1.7)  TimesFM − Bolt +0.0050 (t = +1.0)
cripto  Bolt − ingenua -0.2503 (t = -2.6)  TimesFM − ingenua -0.2423 (t = -2.6)  TimesFM − Bolt +0.0080 (t = +1.2)
```

Frente a la ingenua, t entre −1,7 y −2,6 con 202 días (en la lección 5.8.1, con todos los días del índice, Bolt daba
t = −9,8: la muestra manda). **Entre los dos modelos, t = 1,0 y 1,2**: no se distinguen. Un modelo de 47,7 millones de
parámetros y otro de 231,3 millones, de dos empresas, con datos y arquitecturas distintos, dan aquí casi la misma
previsión. Es lo esperable en una serie con una estructura sencilla (un HAR en logaritmos): cualquier modelo competente
extrae casi toda la información del contexto, y el resto es ruido que nadie puede prever.

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

El resultado vale para esta serie y este horizonte. Los benchmarks públicos (GIFT-Eval, fev-bench) promedian cientos de
series de muchos dominios y sí separan a los modelos; y cada familia gana en el benchmark que publica. Lo que importa en
un caso concreto es una prueba propia, con origen móvil y diferencias pareadas, como esta. La lección 5.8.4 revisa la
evidencia en finanzas.

</aside>

## Equivarianza: lo que la arquitectura garantiza

Una propiedad útil de un modelo de previsión: si se multiplica la serie por $a$ y se le suma $b$, la previsión debería
transformarse igual, $f(ax + b) = a\,f(x) + b$. Si no la cumple, el resultado depende de las unidades (porcentaje o tanto
por uno, dólares o euros). Los dos modelos tipifican el contexto antes de procesarlo, así que la cumplen por
construcción para $a > 0$. Para $a < 0$ (darle la vuelta a la serie) no hay garantía: la serie tipificada cambia de
forma.

```python
x = series["indice"][-C:]

def una_bolt(v):
    q, _ = bolt.predict_quantiles(
        inputs=torch.tensor(v, dtype=torch.float32),
        prediction_length=1)
    return q[0, 0, 4].item()

def una_tfm(v):
    return tfm.forecast(horizon=1, inputs=[v])[0][0, 0]

for nombre, f in (("Bolt", una_bolt), ("TimesFM, sin flip", una_tfm)):
    base = f(x)
    e1 = abs(f(2 * x + 3) - (2 * base + 3))
    e2 = abs(f(-x) - (-base))
    print(f"{nombre:<18} |f(2x+3) − (2f(x)+3)| = {e1:.4f}   "
          f"|f(−x) − (−f(x))| = {e2:.4f}")
compilar(flip=True)
base = una_tfm(x)
print(f"{'TimesFM, con flip':<18} |f(−x) − (−f(x))| = "
      f"{abs(una_tfm(-x) + base):.4f}")
```
```output
Bolt               |f(2x+3) − (2f(x)+3)| = 0.0000   |f(−x) − (−f(x))| = 0.0931
TimesFM, sin flip  |f(2x+3) − (2f(x)+3)| = 0.0000   |f(−x) − (−f(x))| = 0.0982
TimesFM, con flip  |f(−x) − (−f(x))| = 0.0000
```

Escalar por 2 y sumar 3 no cambia nada en ninguno de los dos. Darle la vuelta a la serie, sí: unas 0,09 unidades de
log RV en los dos, del orden de un 10 % de la varianza. La volatilidad tiene picos hacia arriba y no hacia abajo; la
serie invertida tiene valles en su lugar, y el modelo no tiene por qué tratarla como el espejo de la original. Con
`force_flip_invariance=True`, TimesFM promedia f(x) y −f(−x), y la propiedad se cumple exacta. Para una serie con signo
(rendimientos, diferencias de tipos) puede ser deseable; para la volatilidad, que solo tiene picos hacia arriba, no
está claro que lo sea, y cuesta el doble.

## Moirai-2.0, TiRex, TimeGPT y Kronos

Las otras cuatro familias del temario no se ejecutan en el curso, cada una por un motivo (README del entorno `ml`). Su
código es el de su documentación oficial.

**Moirai** (Woo et al., 2024, Salesforce) se presentó como un transformer de **codificador enmascarado** con varios
tamaños de parche, atención «any-variate» (cualquier número de variables en una misma secuencia) y una salida de mezcla
de distribuciones (t de Student, log-normal, binomial negativa, normal de varianza baja), preentrenado con LOTSA, más de
27 000 millones de observaciones de nueve dominios. **Moirai-2.0** (noviembre de 2025) lo simplifica todo: solo
decodificador, un único tamaño de parche, pérdida cuantílica y varios tokens de salida por paso, con un corpus nuevo de
36 millones de series (unos 295 000 millones de observaciones). Sus autores lo describen como el doble de rápido y 30
veces más pequeño que Moirai 1.0-Large, y mejor. Su librería, `uni2ts`, exige `numpy~=1.26` y `torch<2.5`, que no
existen para Python 3.14, y sus pesos son **CC-BY-NC-4.0**, solo para investigación. El código del README:

```python skip
from uni2ts.model.moirai2 import Moirai2Forecast, Moirai2Module

modelo = Moirai2Forecast(
    module=Moirai2Module.from_pretrained(
        "Salesforce/moirai-2.0-R-small"),
    prediction_length=100,
    context_length=1680,
    target_dim=1,
    feat_dynamic_real_dim=0,
    past_feat_dynamic_real_dim=0,
)
predictor = modelo.create_predictor(batch_size=32)
# datos_gluonts: entradas de un PandasDataset de GluonTS
previsiones = predictor.predict(datos_gluonts)
```

**TiRex** (Auer et al., 2025, NX-AI; NeurIPS 2025) es la excepción arquitectónica: no es un transformer sino una
**xLSTM** (en concreto, con bloques sLSTM), una red recurrente moderna que según sus autores conserva la capacidad de
seguir el estado de la serie, útil en horizontes largos. Tiene 35 millones de parámetros, se entrenó con 47,5 millones
de series (de Chronos, aumentadas con TSMixup, sintéticas y parte del preentrenamiento de GIFT-Eval) y en su artículo
supera a modelos mayores en GIFT-Eval y en el benchmark de Chronos. Su licencia es la **NXAI community license**: una
empresa con más de 100 millones de euros de facturación anual que lo integre en un producto o servicio comercial
necesita una licencia comercial. Se instala con `pip install tirex-ts`; no está en el entorno del curso. El ejemplo de
su README:

```python skip
from tirex import load_model, ForecastModel

modelo: ForecastModel = load_model("NX-AI/TiRex")
contexto = torch.tensor(np.stack([series[a][-512:] for a in series]))
cuantiles, media = modelo.forecast(context=contexto,
                                   prediction_length=1)
```

**TimeGPT-1** (Garza, Challu y Mergenthaler-Canseco, 2023, Nixtla) fue de los primeros: un transformer
codificador-decodificador preentrenado con «más de 100 000 millones de puntos». Es **cerrado**: no hay pesos, se usa
mediante una API de pago con el SDK `nixtla` (Apache-2.0) y sus intervalos salen de **predicción conforme** sobre los
errores históricos, no del modelo. Para un fondo, el coste no es solo la cuota: la serie viaja a un servidor ajeno. La
firma es la de la referencia del SDK (la clave, de una variable de entorno, nunca en el código):

```python skip
import os
from nixtla import NixtlaClient

cliente = NixtlaClient(api_key=os.environ["NIXTLA_API_KEY"])
df = pd.DataFrame({"unique_id": "indice", "ds": fechas,
                   "y": series["indice"]})
prevision = cliente.forecast(df=df, h=5, freq="B", level=[80])
```

**Kronos** (Shi et al., 2025; aceptado en AAAI 2026) es el único de la lista específico de mercados. Se preentrena con
**más de 12 000 millones de velas** (registros OHLCV) de 45 mercados y siete frecuencias, y su tokenizador convierte cada
vela multivariante en dos subtokens jerárquicos (uno grueso y uno fino) con una cuantización esférica binaria; un
transformer solo decodificador predice las velas siguientes por muestreo. Hay versiones abiertas de 4,1 (mini), 24,7
(small) y 102,3 millones (base), con un contexto de 512 velas en small y base, y una large de 499,2 millones sin
publicar; licencia MIT. No es un paquete de PyPI (el paquete `kronos` de PyPI es otra cosa): se clona el repositorio y
se importa desde su carpeta `model`. El ejemplo de su README, con las columnas que espera:

```python skip
from model import Kronos, KronosTokenizer, KronosPredictor

tokenizer = KronosTokenizer.from_pretrained(
    "NeoQuasar/Kronos-Tokenizer-base")
modelo = Kronos.from_pretrained("NeoQuasar/Kronos-small")
predictor = KronosPredictor(modelo, tokenizer, max_context=512)

columnas = ["open", "high", "low", "close", "volume", "amount"]
x_df = velas.loc[:399, columnas]
pred_df = predictor.predict(
    df=x_df,
    x_timestamp=velas.loc[:399, "timestamps"],
    y_timestamp=velas.loc[400:519, "timestamps"],
    pred_len=120,
    T=1.0,          # temperatura del muestreo
    top_p=0.9,      # muestreo de núcleo
    sample_count=1, # trayectorias que se promedian
)
```

Sin el argumento `device`, el código del repositorio elige CUDA, después MPS (Apple) y si no, CPU.

## Qué mirar para elegir

| Criterio | Pregunta | Ejemplo |
|---|---|---|
| Licencia de los pesos | ¿Permite el uso comercial que tengo en mente? | Moirai-2.0 no; TiRex con límites; TimesFM 3.0, pesos no comerciales |
| Dónde se ejecuta | ¿Los datos salen de casa? | TimeGPT: API externa |
| Entradas | ¿Necesito covariables o varias series a la vez? | Chronos-2 y TimesFM-2.5 (XReg, lección 5.8.3) |
| Salida | ¿Cuantiles, muestras o solo un punto? | Bolt y TimesFM: cuantiles; Kronos: muestras de velas |
| Coste en CPU | ¿Cuántas previsiones por día y en qué máquina? | Bolt-small es varias veces más rápido que TimesFM-2.5 en esta prueba |
| Dependencias | ¿Se instala en mi versión de Python? | `uni2ts` exige numpy 1.26 y torch < 2.5 |
| Evidencia | ¿Gana en mis datos, con una prueba honesta? | lecciones 5.8.4 y 5.8.5 |

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

Las fichas y los README cambian. Las cifras de esta lección (tamaños, corpus, licencias) son las publicadas por los
autores a fecha de octubre de 2026; antes de usar un modelo en producción, vuelve a leer su ficha y su licencia en la
revisión exacta de los pesos que descargas, y fija esa revisión, como hace el entorno del curso.

</aside>

## Ejercicios

1. Con las previsiones ya calculadas, mide la calibración cuantil a cuantil de los dos modelos en cripto: la fracción
   de días en que la realizada queda por debajo de cada cuantil previsto. ¿Cuál se acerca más a la diagonal?

**Solución.**

```python
real = series["cripto"][origenes]
frec = pd.DataFrame({
    "nominal": tau,
    "Chronos-Bolt": (real[:, None] <= q_bolt["cripto"]).mean(0),
    "TimesFM-2.5": (real[:, None] <= r_tfm["cripto"][1][:, 1:]).mean(0),
})
print(frec.round(3).to_string(index=False))
print("desviación media absoluta:",
      (frec.iloc[:, 1:].sub(frec["nominal"], axis=0)).abs().mean()
      .round(3).to_dict())
```
```output
 nominal  Chronos-Bolt  TimesFM-2.5
     0.1         0.104        0.094
     0.2         0.168        0.173
     0.3         0.277        0.287
     0.4         0.386        0.391
     0.5         0.475        0.505
     0.6         0.559        0.569
     0.7         0.644        0.644
     0.8         0.767        0.752
     0.9         0.911        0.881
desviación media absoluta: {'Chronos-Bolt': 0.026, 'TimesFM-2.5': 0.024}
```

Las dos columnas siguen la diagonal con una desviación media de 2,6 puntos (Bolt) y 2,4 (TimesFM). Con 202 días, cada frecuencia tiene un error típico
de unos 2-3 puntos (√(0,5·0,5/202) ≈ 0,035 en el centro), así que diferencias de ese tamaño entre los dos modelos no
dicen nada. La lección 5.8.3 lo repite con todos los días y por tramos de volatilidad, que es donde aparecen los
problemas.

2. TimesFM-2.5 admite contextos de hasta 16 384 pasos. Repite su previsión del índice con un contexto de 128 sesiones
   (recompila con `max_context=128`). ¿Cuánto empeora el ECM?

**Solución.**

```python
tfm.compile(timesfm.ForecastConfig(
    max_context=128, max_horizon=128, normalize_inputs=True,
    use_continuous_quantile_head=True, fix_quantile_crossing=True,
    force_flip_invariance=False, per_core_batch_size=64))
y = series["indice"]
p128, _ = tfm.forecast(horizon=1,
                       inputs=[y[i - 128:i] for i in origenes])
real = y[origenes]
print(f"ECM con 512: {np.mean((real - r_tfm['indice'][0]) ** 2):.3f}"
      f"   con 128: {np.mean((real - p128[:, 0]) ** 2):.3f}")
print("diferencia pareada:",
      pareada(y, p128[:, 0], r_tfm["indice"][0]))
```
```output
ECM con 512: 0.210   con 128: 0.213
diferencia pareada: +0.0034 (t = +0.5)
```

Con 128 sesiones el ECM pasa de 0,210 a 0,213 y la diferencia pareada da t = 0,5: no es significativa. En una serie cuya memoria útil es
de semanas (el HAR usa medias de 1, 5 y 22 días), medio año de contexto ya contiene casi toda la información para el
día siguiente. La lección 5.8.3 barre la longitud del contexto de forma sistemática.

3. Un fondo con 500 millones de euros de facturación quiere integrar un modelo fundacional en un producto de
   previsión que venderá a clientes, sin enviar datos fuera. ¿Qué modelos de la lección descartarías por licencia o por
   despliegue, y cuáles quedarían?

**Solución.**

Descartaría **TimeGPT** (API externa: los datos salen de casa y el modelo no se puede desplegar dentro), **Moirai-2.0**
(pesos CC-BY-NC-4.0, «research purposes only») y **TimesFM 3.0** (pesos con licencia no comercial). **TiRex** no queda
descartado de entrada, pero la NXAI community license exige una licencia comercial a empresas de más de 100 millones de
euros que lo integren en un producto comercial, que es el caso: habría que negociarla. Quedan **Chronos-Bolt,
Chronos-2 y TimesFM-2.5** (pesos Apache-2.0) y **Kronos** (MIT). La licencia es la condición necesaria; la suficiente es
que el elegido gane en una prueba honesta con los datos del fondo, que es lo que hacen las lecciones 5.8.4 y 5.8.5.

## Autoevaluación

1. ¿Qué diferencia de arquitectura hay entre Chronos-Bolt y TimesFM-2.5?

   - A. Chronos-Bolt es una red recurrente y TimesFM una convolución
   - B. Chronos-Bolt es un T5 codificador-decodificador que produce de una vez los cuantiles del horizonte; TimesFM es un transformer solo decodificador, con atención causal entre parches, que genera parches de salida de 128 pasos
   - C. Ninguna: son el mismo modelo con otro nombre
   - D. TimesFM cuantiza cada dato en un token, como el Chronos original

   *Respuesta B: Chronos-Bolt es un T5 codificador-decodificador que produce de una vez los cuantiles del horizonte; TimesFM es un transformer solo decodificador, con atención causal entre parches, que genera parches de salida de 128 pasos.* Los dos trabajan con parches (16 y 32 datos), pero Bolt codifica el contexto y decodifica todos los cuantiles en un paso, y TimesFM lee los parches con atención causal, como un modelo de lenguaje, y predice el siguiente bloque.

2. En la prueba sobre `vol5` (202 orígenes por activo), TimesFM-2.5 tiene un ECM 0,005 mayor que Chronos-Bolt en el índice, con t = 1,0. ¿Qué se concluye?

   - A. Que Chronos-Bolt es mejor modelo
   - B. Que TimesFM-2.5 está mal configurado
   - C. Que hay que usar el modelo con más parámetros
   - D. Que con esta muestra no se distinguen: la diferencia es del orden de su error típico, y los dos mejoran a la ingenua de forma parecida

   *Respuesta D: Que con esta muestra no se distinguen: la diferencia es del orden de su error típico, y los dos mejoran a la ingenua de forma parecida.* Un t de 1,0 es compatible con que no haya diferencia. Para ordenar dos modelos tan parecidos haría falta mucha más muestra (o más activos), y aun así la conclusión valdría para estos datos.

3. Moirai-2.0 se publica con licencia CC-BY-NC-4.0 y TiRex con la «NXAI community license». ¿Qué implica para un fondo que quiera usarlos en producción?

   - A. Moirai-2.0 no admite uso comercial («research purposes only» en su ficha) y TiRex exige licencia comercial a empresas de más de 100 millones de euros de facturación que lo integren en un producto comercial; Chronos-Bolt, Chronos-2 y TimesFM-2.5 son Apache-2.0
   - B. Nada: los pesos publicados en Hugging Face son siempre de uso libre
   - C. Que hay que pagar a Hugging Face
   - D. Que solo se pueden usar en GPU

   *Respuesta A: Moirai-2.0 no admite uso comercial («research purposes only» en su ficha) y TiRex exige licencia comercial a empresas de más de 100 millones de euros de facturación que lo integren en un producto comercial; Chronos-Bolt, Chronos-2 y TimesFM-2.5 son Apache-2.0.* La licencia de los pesos es tan importante como la del código. uni2ts (el código de Moirai) es Apache-2.0, pero sus pesos de la versión 2.0 son CC-BY-NC-4.0. Y TimesFM 3.0, de agosto de 2026, ya publica sus pesos con una licencia no comercial.

4. La celda de equivarianza da |f(2x+3) − (2f(x)+3)| = 0 en los dos modelos, pero |f(−x) − (−f(x))| ≈ 0,09 sin la opción `force_flip_invariance`. ¿Por qué?

   - A. Porque los modelos tienen un error de programación
   - B. Porque la serie es negativa
   - C. Porque los dos tipifican el contexto (restan la media y dividen por la desviación), lo que garantiza la equivarianza para a > 0; darle la vuelta a la serie cambia su forma (la asimetría de los picos), y el modelo no tiene por qué devolver la previsión invertida, salvo que se le obligue promediando f(x) y −f(−x)
   - D. Porque el horizonte es de un día

   *Respuesta C: Porque los dos tipifican el contexto (restan la media y dividen por la desviación), lo que garantiza la equivarianza para a > 0; darle la vuelta a la serie cambia su forma (la asimetría de los picos), y el modelo no tiene por qué devolver la previsión invertida, salvo que se le obligue promediando f(x) y −f(−x).* Con la tipificación, f(ax + b) = a·f(x) + b para cualquier a > 0. Con a < 0 la serie tipificada es otra (los picos de volatilidad se vuelven valles). TimesFM lo fuerza con `force_flip_invariance=True`, que ejecuta el modelo dos veces; por eso la lección lo desactiva para medir.

5. ¿Qué hace distinto a Kronos frente al resto de familias?

   - A. Que no usa transformers
   - B. Que es el único con licencia comercial de pago
   - C. Que predice solo la volatilidad
   - D. Que se preentrena con velas OHLCV (más de 12 000 millones de registros de 45 mercados) y tokeniza cada vela entera, en lugar de una serie univariante cualquiera

   *Respuesta D: Que se preentrena con velas OHLCV (más de 12 000 millones de registros de 45 mercados) y tokeniza cada vela entera, en lugar de una serie univariante cualquiera.* Kronos es específico de mercados financieros: su tokenizador convierte la vela multivariante (apertura, máximo, mínimo, cierre, volumen, importe) en tokens jerárquicos. Que sea específico no lo hace capaz de predecir rendimientos: la lección 5.8.4 revisa la evidencia.

## Ideas clave

- Casi todas las familias son transformers sobre parches que devuelven cuantiles: Chronos-Bolt (T5 codificador-decodificador, parches de 16), TimesFM-2.5 (solo decodificador, parches de 32), Chronos-2 (solo codificador, con covariables), Moirai-2.0 (solo decodificador). TiRex usa xLSTM y Kronos tokeniza velas OHLCV.
- El tamaño no ordena la calidad: Chronos-Bolt-small (47,7 M parámetros contados) iguala aquí a TimesFM-2.5 (231,3 M en el fichero, 200 M según la ficha), y los autores de Moirai-2.0 encuentran que sus versiones grandes rinden menos que la pequeña.
- En `vol5`, los dos ejecutables reducen el ECM de la ingenua un 13-15 % en el índice y un 39-40 % en cripto; entre ellos, diferencias de t ≈ 1: no se distinguen.
- Los dos son equivariantes a escala y desplazamiento positivos por construcción; frente a darle la vuelta a la serie, solo TimesFM con `force_flip_invariance=True`, que dobla el cómputo.
- La licencia de los pesos cuenta: Apache-2.0 en Chronos-Bolt, Chronos-2 y TimesFM-2.5; CC-BY-NC-4.0 en Moirai-2.0; comunitaria con límites en TiRex; MIT en Kronos; API de pago en TimeGPT (los datos salen de casa).

## Recursos

### Documentación

- [google-research/timesfm (GitHub): TimesFM 2.5 y 3.0, novedades y licencias](https://github.com/google-research/timesfm) · github.com

### Libros

- [Ansari, A. F. et al. (2025): Chronos-2: From Univariate to Universal Forecasting. arXiv:2510.15821](https://arxiv.org/abs/2510.15821) · arxiv.org
- [Liu, C., Aksu, T. et al. (2025): Moirai 2.0: When Less Is More for Time Series Forecasting. arXiv:2511.11698](https://arxiv.org/abs/2511.11698) · arxiv.org
- [Auer, A. et al. (2025): TiRex: Zero-Shot Forecasting Across Long and Short Horizons with Enhanced In-Context Learning. arXiv:2505.23719](https://arxiv.org/abs/2505.23719) · arxiv.org
- [Garza, A., Challu, C. y Mergenthaler-Canseco, M. (2023): TimeGPT-1. arXiv:2310.03589](https://arxiv.org/abs/2310.03589) · arxiv.org
- [Shi, Y. et al. (2025): Kronos: A Foundation Model for the Language of Financial Markets. arXiv:2508.02739](https://arxiv.org/abs/2508.02739) · 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.8.1 · Modelos fundacionales de series: preentrenar una vez, prever sin entrenar](https://pythonparatrading.com/curso-python-trading/nivel-5/que-son-los-modelos-fundacionales-de-series/)

Lección siguiente: [5.8.3 · Uso práctico: contexto, cuantiles, covariables y ajuste fino](https://pythonparatrading.com/curso-python-trading/nivel-5/uso-practico-zero-shot-covariables-y-fine-tuning/)
