Lección 1.1.2 · 55 min

Sesiones, subastas, festivos y calendarios

Un mercado abre y cierra a horas que cambian con la zona horaria, el horario de verano, los festivos y las medias sesiones. Esta lección calcula las sesiones de seis bolsas con exchange_calendars, reconstruye una subasta de apertura orden a orden y mide por qué el cierre concentra tanto volumen.

Fig. — Sesiones de seis bolsas el 2 de junio de 2025 en hora UTC, con las pausas de mediodía de Tokio y Hong Kong
Lecciones del nivel 1
  1. 1.1 · Cómo funcionan los mercados

    1. 1.1.1 Participantes y centros de negociación
    2. 1.1.2 Sesiones, subastas, festivos y calendarios
    3. 1.1.3 Libro de órdenes: bid, ask, spread y profundidad
    4. 1.1.4 Tipos de órdenes
    5. 1.1.5 Costes, liquidación, apalancamiento y margen
  2. 1.2 · Clases de activos

    1. 1.2.1 Acciones: splits, dividendos y eventos corporativos
    2. 1.2.2 ETFs: réplica, tracking error, apalancados y UCITS
    3. 1.2.3 Futuros: especificaciones, margen, roll y contango
    4. 1.2.4 Forex: pares, pips, lotes, rollover y CFD
    5. 1.2.5 Opciones: call, put, valor intrínseco y valor temporal
    6. 1.2.6 Cripto: spot, perpetuos, funding y custodia
    7. 1.2.7 Renta fija y tipos de interés; CFDs y productos apalancados
  3. 1.3 · Fuentes de datos de mercado

    1. 1.3.1 Tipos de datos de mercado: EOD, barras, ticks, libro y referencia
    2. 1.3.2 Fuentes de datos gratuitas
    3. 1.3.3 APIs de datos retail y prosumer
    4. 1.3.4 Proveedores de datos profesionales
    5. 1.3.5 Datos de cripto y on-chain
    6. 1.3.6 Brókeres como fuente de datos
    7. 1.3.7 Licencias y términos de uso de datos
    8. 1.3.8 Cómo elegir proveedor de datos
  4. 1.4 · Ingeniería de datos financieros

    1. 1.4.1 Descarga por REST y websockets: paginación, límites y reintentos
    2. 1.4.2 Limpieza de datos: faltantes, ticks erróneos, huecos y zonas horarias
    3. 1.4.3 Ajustes por splits y dividendos: precios ajustados y sin ajustar
    4. 1.4.4 Universos sin sesgo de supervivencia: deslistados, fusiones y cambios de símbolo
    5. 1.4.5 Construcción de barras: tiempo, ticks, volumen, dólar y desequilibrio
    6. 1.4.6 Series continuas de futuros: reglas de roll, ajuste por diferencia y ajuste por ratio
    7. 1.4.7 Almacenamiento: CSV, Parquet, particionado, DuckDB, TimescaleDB y ArcticDB
    8. 1.4.8 Pipeline reproducible: descarga, validación, actualización incremental y tests de calidad
  5. 1.5 · Datos fundamentales y macro

    1. 1.5.1 Estados financieros, ratios, XBRL y SEC EDGAR
    2. 1.5.2 Datos macro, calendarios económicos, estimaciones y transcripciones
  6. 1.6 · Análisis exploratorio financiero

    1. 1.6.1 Rentabilidades, volatilidad, correlaciones móviles y drawdown
    2. 1.6.2 Hechos estilizados: colas gruesas, agrupamiento, autocorrelación y apalancamiento
    3. 1.6.3 Estacionalidad: día de la semana, mes, cambio de mes y sesiones intradía
  7. 1.7 · Análisis técnico programado

    1. 1.7.1 Indicadores técnicos en Python
    2. 1.7.2 Implementación propia frente a TA-Lib: validar resultados
    3. 1.7.3 Patrones de velas y soportes y resistencias
    4. 1.7.4 Multi-timeframe sin anticipación

Las fases de una sesión

En una bolsa de acciones no se negocia de la misma forma a todas horas. La sesión se divide en fases y cada una tiene sus reglas para cruzar órdenes. Las dos formas básicas son la negociación continua, en la que una orden que llega se ejecuta al momento contra las que ya esperan en el libro (lección 1.1.3), y la subasta, en la que las órdenes se acumulan durante un tiempo sin ejecutarse y al final se cruzan todas a un único precio.

subasta aperturaprecio fuera rangofin de subastahora de cierreprecio de cierrePreaperturaContinuaVolatilidadCierrePoscierre
Fuente Mermaid
stateDiagram-v2
  [*] --> Preapertura
  Preapertura --> Continua: subasta apertura
  Continua --> Volatilidad: precio fuera rango
  Volatilidad --> Continua: fin de subasta
  Continua --> Cierre: hora de cierre
  Cierre --> Poscierre: precio de cierre
  Poscierre --> [*]
  • Preapertura y subasta de apertura. Se pueden enviar, modificar y cancelar órdenes, pero no hay operaciones. El sistema publica un precio teórico de equilibrio que se va moviendo con las órdenes. Al final, todas las órdenes compatibles se cruzan al mismo precio: el de apertura.
  • Negociación continua. El libro está abierto: cada orden agresiva se ejecuta contra la mejor contraria.
  • Subasta de volatilidad. Si una operación fuera a producirse demasiado lejos del último precio o del precio de referencia, el mercado suspende la negociación continua de ese valor y abre una subasta corta. Sirve para que una noticia o un error no se conviertan en una cascada de operaciones a precios absurdos. Los umbrales y la duración los fija cada bolsa en su reglamento, y en un backtest aparecen como huecos de unos minutos sin operaciones.
  • Subasta de cierre. Igual que la de apertura, al final del día. Fija el precio de cierre oficial.
  • Poscierre. En algunas bolsas, un rato en el que se puede operar solo al precio de cierre.

Dos bolsas que se usan a menudo en el curso, con sus horarios oficiales en hora local:

FaseNYSE (Nueva York)BME, mercado continuo (Madrid)
Entrada de órdenesdesde las 6:30—
Subasta de aperturacruce a las 9:30de 8:30 a 9:00
Negociación continuade 9:30 a 16:00de 9:00 a 17:30
Periodo de desequilibrio del cierrede 15:50 a 16:00—
Subasta de cierrecruce a las 16:00de 17:30 a 17:35
Negociación al precio de cierre—Trading At Last, de 17:35 a 17:45

En la NYSE, las órdenes al cierre (MOC y LOC, lección 1.1.4) se pueden enviar, modificar o cancelar hasta las 15:50. A partir de ahí la bolsa publica el desequilibrio entre compras y ventas al cierre y solo admite órdenes que lo reduzcan1Nota 1 · Horarios de la NYSE en su página «Holidays & Trading Hours» y en el folleto «NYSE opening and closing auctions»; los de BME, en la página «Horario Bolsa Española» de BME Exchange (consultadas el 21 de octubre de 2026).. En BME, el Trading At Last permite seguir comprando y vendiendo diez minutos más, pero solo al precio que acaba de fijar la subasta de cierre.

Calendarios de bolsa en Python

Escribir las horas a mano («la NYSE abre a las 15:30 de Madrid») funciona hasta el primer cambio de hora. Un calendario de bolsa sabe, para cada fecha, si hay sesión, a qué hora abre y cierra en UTC, si hay pausa de mediodía y si es una media sesión. Las dos librerías de referencia en Python son exchange_calendars (códigos ISO 10383 de mercado, como XNYS; 71 calendarios sin contar alias en la versión 4.13.2) y pandas_market_calendars, que reutiliza esos calendarios y añade muchos más, sobre todo de futuros (211 nombres, alias incluidos, en la versión 5.5.0). Ambas están instaladas en el entorno del curso.

IN [01]python
import exchange_calendars as xcals
import pandas as pd

bolsas = {"XTKS": "Tokio", "XHKG": "Hong Kong", "XLON": "Londres",
          "XETR": "Xetra", "XMAD": "Madrid", "XNYS": "Nueva York"}
dia = pd.Timestamp("2025-06-02")
filas = {}
for codigo, nombre in bolsas.items():
    cal = xcals.get_calendar(codigo)
    filas[nombre] = {
        "abre": cal.session_open(dia),
        "pausa": cal.session_break_start(dia),
        "reanuda": cal.session_break_end(dia),
        "cierra": cal.session_close(dia),
    }
sesiones = pd.DataFrame(filas).T
hhmm = lambda c: pd.to_datetime(c).dt.strftime("%H:%M")
sesiones.apply(hhmm).fillna("")
OUT [01]Sesiones de seis bolsas el 2 de junio de 2025, en UTC
             abre  pausa reanuda cierra
Tokio       00:00  02:30   03:30  06:30
Hong Kong   01:30  04:00   05:00  08:00
Londres     07:00                 15:30
Xetra       07:00                 15:30
Madrid      07:00                 15:30
Nueva York  13:30                 20:00

Todas las horas salen en UTC, que es como hay que guardarlas (lección 0.4.4). Tokio y Hong Kong tienen pausa de mediodía: una hora sin negociación continua en la que el calendario devuelve pausa y reanuda. En las bolsas sin pausa esas columnas quedan vacías. En hora local las cosas se reconocen mejor:

IN [02]python
for codigo, nombre in bolsas.items():
    cal = xcals.get_calendar(codigo)
    abre = cal.session_open(dia).tz_convert(cal.tz)
    cierra = cal.session_close(dia).tz_convert(cal.tz)
    print(f"{nombre:<11} {abre:%H:%M}-{cierra:%H:%M}  {cal.tz}")
OUT [02]Las mismas sesiones en hora local
Tokio       09:00-15:30  Asia/Tokyo
Hong Kong   09:30-16:00  Asia/Hong_Kong
Londres     08:00-16:30  Europe/London
Xetra       09:00-17:30  Europe/Berlin
Madrid      09:00-17:30  Europe/Madrid
Nueva York  09:30-16:00  America/New_York

La figura de la cabecera dibuja las seis sesiones sobre el mismo eje UTC. Tres cosas se ven a simple vista. Tokio ha cerrado cuando abre Europa, y Hong Kong solo coincide con ella una hora. Londres, Xetra y Madrid abren y cierran a la misma hora UTC ese día. Y entre Europa y Nueva York solo hay dos horas de solape, de 13:30 a 15:30 UTC: el único tramo en el que una estrategia puede comprar en Madrid y vender en Nueva York con los dos libros abiertos.

Ese solape no es fijo. Estados Unidos y Europa cambian la hora en fechas distintas, así que durante unas semanas al año la diferencia entre Madrid y Nueva York es de cinco horas en lugar de seis (la figura de la lección 0.4.4 lo muestra con las aperturas de 2026). El calendario lo resuelve sin que haya que pensarlo:

IN [03]python
mad = xcals.get_calendar("XMAD")
nys = xcals.get_calendar("XNYS")
rango = ("2025-01-02", "2025-12-31")
comunes = mad.sessions_in_range(*rango).intersection(
    nys.sessions_in_range(*rango))
solape = mad.closes[comunes] - nys.opens[comunes]
(solape.dt.total_seconds() / 3600).value_counts().sort_index()
OUT [03]Horas de solape Madrid-Nueva York en cada sesión de 2025
-1.5      2
 2.0    225
 3.0     20
Name: count, dtype: int64

De las 247 sesiones comunes de 2025, 225 tienen dos horas de solape, 20 tienen tres (las semanas de desfase del horario de verano, en marzo y a finales de octubre) y 2 tienen un valor negativo: son las medias sesiones de Madrid del 24 y el 31 de diciembre, en las que el mercado continuo cierra a las 14:00, hora y media antes de que abra la NYSE. Un sistema que asuma «siempre dos horas de solape» se equivoca en 22 sesiones al año.

Festivos, cierres extraordinarios y medias sesiones

Los festivos de cada bolsa no coinciden con los de su país ni con los de las demás bolsas, y cambian cada año: un festivo que cae en sábado puede trasladarse al viernes, o no. Además hay cierres que nadie preveía: la NYSE cerró el 9 de enero de 2025 por el día de luto nacional por el expresidente Carter, anunciado el 30 de diciembre2Nota 2 · Calendario de 2025: comunicado de NYSE Group «NYSE Group Announces 2025, 2026 and 2027 Holiday and Early Closings Calendar»; cierre del 9 de enero, comunicado de ICE del 30 de diciembre de 2024. Calendario de 2026: página «Holidays & Trading Hours» de la NYSE.. Y hay medias sesiones: en la NYSE, el 3 de julio, el viernes siguiente a Acción de Gracias y el 24 de diciembre suelen cerrar a las 13:00.

IN [04]python
nyse = xcals.get_calendar("XNYS")

def cerrados(cal, desde, hasta):
    laborables = pd.bdate_range(desde, hasta)
    return laborables.difference(cal.sessions)

for anio in ("2025", "2026"):
    dias = cerrados(nyse, f"{anio}-01-01", f"{anio}-12-31")
    print(anio, len(dias), "días:", ", ".join(dias.strftime("%d/%m")))
OUT [04]Días laborables sin sesión en la NYSE
2025 11 días: 01/01, 09/01, 20/01, 17/02, 18/04, 26/05, 19/06, 04/07, 01/09, 27/11, 25/12
2026 10 días: 01/01, 19/01, 16/02, 03/04, 25/05, 19/06, 03/07, 07/09, 26/11, 25/12

Comparado con lo que publica la NYSE, el calendario acierta. Para 2026 se puede comprobar contra la lista de su página de festivos, copiada a mano:

IN [05]python
nyse_com = pd.to_datetime([
    "2026-01-01", "2026-01-19", "2026-02-16", "2026-04-03",
    "2026-05-25", "2026-06-19", "2026-07-03", "2026-09-07",
    "2026-11-26", "2026-12-25"])
medias_nyse_com = pd.to_datetime(["2026-11-27", "2026-12-24"])

libreria = cerrados(nyse, "2026-01-01", "2026-12-31")
medias = nyse.early_closes[nyse.early_closes.year == 2026]
print("Festivos iguales:", libreria.equals(pd.DatetimeIndex(nyse_com)))
print("Medias sesiones iguales:", medias.equals(medias_nyse_com))
nyse.closes[medias].dt.tz_convert("America/New_York").dt.strftime(
    "%Y-%m-%d %H:%M")
OUT [05]Contraste con nyse.com: festivos y medias sesiones de 2026
Festivos iguales: True
Medias sesiones iguales: True
2026-11-27    2026-11-27 13:00
2026-12-24    2026-12-24 13:00
Name: close, dtype: str

En 2026 el 4 de julio cae en sábado y el festivo se traslada al viernes 3, así que ese año no hay media sesión el día antes de la fiesta. Es el tipo de detalle que un calendario hecho a mano se salta. El calendario tampoco es eterno: se actualiza con cada versión de la librería. Un cierre extraordinario como el del 9 de enero de 2025 solo aparece en las versiones publicadas después del anuncio, así que conviene fijar la versión en el proyecto (lección 0.2.2) y actualizarla a propósito.

Los cambios de horario también quedan registrados. La Bolsa de Tokio alargó su sesión de tarde media hora el 5 de noviembre de 2024, con una subasta de cierre nueva3Nota 3 · JPX, «Cash Market Trading Hours Extension Kicked Off Today», 5 de noviembre de 2024. El horario vigente, de 9:00 a 11:30 y de 12:30 a 15:30, está en la página «Trading Hours» de JPX.:

IN [06]python
tks = xcals.get_calendar("XTKS")
for d in ("2024-11-01", "2024-11-05"):
    cierre = tks.session_close(d).tz_convert("Asia/Tokyo")
    print(d, "cierra a las", cierre.strftime("%H:%M"))
OUT [06]El cierre de Tokio antes y después del 5 de noviembre de 2024
2024-11-01 cierra a las 15:00
2024-11-05 cierra a las 15:30

pandas_market_calendars ofrece lo mismo con otra interfaz: un schedule con una fila por sesión. Es cómodo para generar rejillas de barras, y trae calendarios separados para cada grupo de futuros (CME_Equity, CME_Rate…), donde exchange_calendars tiene uno genérico por bolsa (CMES):

IN [07]python
import pandas_market_calendars as mcal

nyse_m = mcal.get_calendar("NYSE")
nyse_m.schedule("2025-11-26", "2025-12-01")
OUT [07]pandas_market_calendars: Acción de Gracias de 2025
                         market_open              market_close
2025-11-26 2025-11-26 14:30:00+00:00 2025-11-26 21:00:00+00:00
2025-11-28 2025-11-28 14:30:00+00:00 2025-11-28 18:00:00+00:00
2025-12-01 2025-12-01 14:30:00+00:00 2025-12-01 21:00:00+00:00

El jueves 27 no aparece y el viernes 28 cierra a las 18:00 UTC, las 13:00 de Nueva York. Las dos librerías deberían dar las mismas sesiones; comprobarlo es barato y detecta versiones desfasadas:

IN [08]python
sched = nyse_m.schedule("2025-01-02", "2025-12-31")
x_cierres = nyse.closes["2025-01-02":"2025-12-31"]
print(len(sched), "sesiones en 2025")
print("Mismos cierres:", (sched["market_close"] == x_cierres).all())
OUT [08]Las dos librerías coinciden en 2025
250 sesiones en 2025
Mismos cierres: True

La subasta de apertura, orden a orden

En la subasta no hay agresor ni libro que se recorra: hay una lista de órdenes y un único precio para todas. La regla general es maximizar el volumen ejecutable. Para cada precio candidato pp:

D(p)=∑compras con limite≥pqi,S(p)=∑ventas con limite≤pqj,V(p)=min⁡(D(p),S(p))D(p) = \sum_{\text{compras con limite} \ge p} q_i, \qquad S(p) = \sum_{\text{ventas con limite} \le p} q_j, \qquad V(p) = \min\big(D(p), S(p)\big)

Las órdenes a mercado cuentan a cualquier precio: una compra a mercado está dispuesta a pagar lo que sea, y una venta a mercado, a vender a lo que sea. La demanda D(p)D(p) baja al subir el precio; la oferta S(p)S(p) sube. El precio de la subasta es el pp con mayor V(p)V(p), y la diferencia D(p)−S(p)D(p) - S(p) a ese precio es el desequilibrio: órdenes que quedan sin ejecutar y pasan a la sesión continua. Si varios precios empatan en volumen, cada bolsa tiene sus reglas de desempate (menor desequilibrio, cercanía al precio de referencia…), publicadas en su reglamento.

El fichero nova_subasta_apertura.csv es el libro de la subasta de apertura de NOVA del 2 de junio de 2025 en el instante del cruce:

IN [09]python
import numpy as np

sub = pd.read_csv("nova_subasta_apertura.csv")
print(len(sub), "órdenes")
sub.groupby(["lado", "tipo"])["tamano"].agg(["size", "sum"])
OUT [09]El libro de la subasta
139 órdenes
                 size    sum
lado   tipo
compra limitada    63  27400
       mercado      8  17000
venta  limitada    61  30600
       mercado      7  14600
IN [10]python
compra = sub[sub["lado"] == "compra"]
venta = sub[sub["lado"] == "venta"]

def demanda(p):  # compras dispuestas a pagar p o más
    ok = compra["tipo"].eq("mercado") | compra["precio_limite"].ge(p)
    return compra.loc[ok, "tamano"].sum()

def oferta(p):   # ventas dispuestas a vender a p o menos
    ok = venta["tipo"].eq("mercado") | venta["precio_limite"].le(p)
    return venta.loc[ok, "tamano"].sum()

curva = pd.DataFrame({"precio": np.sort(sub["precio_limite"].dropna()
                                        .unique())})
curva["demanda"] = curva["precio"].map(demanda)
curva["oferta"] = curva["precio"].map(oferta)
curva["ejecutable"] = curva[["demanda", "oferta"]].min(axis=1)
curva["deseq"] = curva["demanda"] - curva["oferta"]
mejor = curva["ejecutable"].idxmax()
curva.loc[mejor - 3:mejor + 3]
OUT [10]Curvas de oferta y demanda y precio de equilibrio
    precio  demanda  oferta  ejecutable  deseq
19   53.32    35200   30200       30200   5000
20   53.33    34200   32200       32200   2000
21   53.34    34200   33200       33200   1000
22   53.35    33900   34500       33900   -600
23   53.36    31400   34500       31400  -3100
24   53.37    29900   35800       29900  -5900
25   53.38    29900   36800       29900  -6900
Fig. — Subasta de apertura de NOVA del 2 de junio de 2025: demanda y oferta acumuladas por precio y precio de equilibrio

Las dos curvas se cruzan entre 53,34 y 53,35. A 53,34 habría 34.200 acciones de demanda y 33.200 de oferta: se ejecutarían 33.200. A 53,35 la demanda baja a 33.900 y la oferta sube a 34.500: se ejecutan 33.900, el máximo. A 53,36 la demanda cae a 31.400. El precio de apertura es 53,35 USD, con 33.900 acciones y un desequilibrio de −600: sobran 600 acciones a la venta, que pasan al libro de la sesión continua. La comprobación final es contra la cinta:

IN [11]python
op = pd.read_parquet("nova_operaciones.parquet")
apertura = op.iloc[0]
print(apertura[["ts", "precio", "tamano", "condicion"]].to_string())
print("Coincide:", apertura["precio"] == curva.loc[mejor, "precio"]
      and apertura["tamano"] == curva.loc[mejor, "ejecutable"])
OUT [11]La apertura de la cinta es la subasta
ts           2025-06-02 13:30:00+00:00
precio                           53.35
tamano                           33900
condicion             subasta_apertura
Coincide: True

La primera operación del día es exactamente la subasta reconstruida: mismo precio, mismo volumen, a las 13:30:00 UTC y sin agresor. Es también el precio de apertura que dará cualquier proveedor de datos diarios. La primera operación de la sesión continua, 0,3 segundos después, ya fue a 53,34 (lección 1.1.1).

El volumen en U y la subasta de cierre

El volumen no se reparte por igual a lo largo del día. Las barras de 5 minutos de NOVA de 2024 y 2025 permiten medir la forma típica: para cada sesión completa, qué porcentaje del volumen del día cae en cada tramo, y luego la media de todas las sesiones.

IN [12]python
barras = pd.read_parquet("nova_barras_5min.parquet")
ny = barras["ts"].dt.tz_convert("America/New_York")
barras["sesion"] = ny.dt.date
barras["hora"] = ny.dt.strftime("%H:%M")

n_barras = barras.groupby("sesion")["ts"].size()
print(n_barras.value_counts().to_string())

normales = barras[barras["sesion"].map(n_barras) == 78].copy()
total = normales.groupby("sesion")["volumen"].transform("sum")
normales["cuota"] = 100 * normales["volumen"] / total
perfil = normales.groupby("hora")["cuota"].mean()
perfil.iloc[[0, 1, 2, 3, 38, 39, 74, 75, 76, 77]].round(2)
OUT [12]Perfil de volumen intradía de NOVA, 2024-2025
ts
78    496
42      6
hora
09:30     3.06
09:35     2.43
09:40     2.08
09:45     1.79
12:40     1.04
12:45     1.04
15:40     1.40
15:45     1.56
15:50     1.82
15:55    11.28
Name: cuota, dtype: float64
Fig. — Perfil de volumen intradía de NOVA: porcentaje medio del volumen del día en cada barra de 5 minutos, 496 sesiones completas de 2024 y 2025

Hay 496 sesiones completas de 78 barras y 6 medias sesiones de 42 (de 9:30 a 13:00), que se dejan fuera para no mezclar perfiles. La primera barra, de 9:30 a 9:35, reúne de media el 3,06 % del volumen del día; a mediodía cada barra ronda el 1 %; en la última media hora el volumen vuelve a subir, y la última barra, la de las 15:55, salta al 11,28 % porque incluye la subasta de cierre. Es la «U» clásica de las acciones, con la pata derecha mucho más alta que la izquierda.

¿Por qué el cierre? Porque es el precio con el que se valoran los fondos. Un fondo indexado que tiene que comprar las acciones que entran en su índice, o que recibe suscripciones, busca ejecutar exactamente al precio de cierre: así replica el índice sin error. La forma de conseguirlo es enviar órdenes a la subasta de cierre (órdenes MOC). Lo mismo hacen muchas gestoras que se miden contra el cierre. La sesión del 2 de junio de 2025 lo muestra con una cifra:

IN [13]python
vol_dia = op["tamano"].sum()
por_tipo = op.groupby("condicion")["tamano"].sum()
(100 * por_tipo / vol_dia).round(2)
OUT [13]Peso de las subastas en la sesión del 2 de junio de 2025
condicion
bloque               3.66
regular             86.00
subasta_apertura     0.38
subasta_cierre       9.96
Name: tamano, dtype: float64

Casi el 10 % del volumen del día se cruza en un solo instante, en la subasta de cierre, y la de apertura apenas llega al 0,4 %. Para un sistema de trading esto tiene dos caras. A favor: si la estrategia opera al cierre, la subasta ofrece muchísima liquidez a un precio conocido de antemano por todos. En contra: el precio de la última operación continua (15:59:59) no es el cierre oficial, y un backtest que «compra al cierre» con la barra de las 15:55 debe decidir si participa en la subasta (y entonces enviar la orden antes de las 15:50 en la NYSE) o en la sesión continua previa.

Lo que esto cambia en tus datos

nosínosísínoDatos crudos UTC¿Hay sesión?Descartar fila¿Dentro de horas?Pre o poscierreBarra de sesión¿Media sesión?42 barras78 barras
Fuente Mermaid
flowchart TB
  D[Datos crudos UTC] --> C{¿Hay sesión?}
  C -- no --> X[Descartar fila]
  C -- sí --> H{¿Dentro de horas?}
  H -- no --> E[Pre o poscierre]
  H -- sí --> B[Barra de sesión]
  B --> M{¿Media sesión?}
  M -- sí --> Q[42 barras]
  M -- no --> R[78 barras]
  • Fechas de barra diaria. La barra diaria de una sesión lleva su fecha local, que no siempre es la fecha UTC de sus barras. Tokio abre a las 00:00 UTC, justo en el cambio de día, y en los mercados que abren la tarde anterior (muchos futuros) las primeras barras de una sesión tienen fecha UTC del día previo. Agrupa por sesión del calendario, no por fecha UTC.
  • Barras que faltan. Una media sesión tiene 42 barras de 5 minutos, no 78. Un resample a 5 minutos sin calendario rellena con vacíos las horas en que la bolsa estaba cerrada (o se las salta si eran festivos), y un indicador de «N barras» mezcla días.
  • Subastas dentro de las barras. Muchos proveedores meten la subasta de cierre en la última barra, como aquí. Un filtro de «volumen anómalo» la tomará por una señal cada día.
  • Huecos de volatilidad. Unos minutos sin operaciones en mitad de la sesión pueden ser una subasta de volatilidad, no un fallo de datos (lección 1.4.2).
  • Rendimientos entre sesiones. El rendimiento de un lunes incluye el fin de semana y, tras un festivo, dos o tres días. Para comparar días, cuenta sesiones con el calendario y no días naturales (lección 1.6.3).

Ejercicios

  1. Calcula las horas de solape entre Madrid y Nueva York el 12 de marzo y el 2 de junio de 2025 y explica la diferencia.
Solución
IN [14]python
for d in ("2025-03-12", "2025-06-02"):
    h = (mad.session_close(d) - nys.session_open(d)).total_seconds()
    print(d, "NYSE abre", nys.session_open(d).strftime("%H:%M"),
          "UTC; Madrid cierra", mad.session_close(d).strftime("%H:%M"),
          f"UTC; solape {h / 3600:.0f} h")
OUT [14]Solape en dos fechas de 2025
2025-03-12 NYSE abre 13:30 UTC; Madrid cierra 16:30 UTC; solape 3 h
2025-06-02 NYSE abre 13:30 UTC; Madrid cierra 15:30 UTC; solape 2 h

El 12 de marzo Estados Unidos ya está en horario de verano (desde el 9 de marzo) y Europa todavía no (hasta el 30 de marzo): Nueva York abre a las 13:30 UTC y Madrid cierra a las 16:30 UTC. Tres horas de solape. El 2 de junio los dos están en horario de verano y Madrid cierra una hora antes en UTC: dos horas.

  1. Llega a la subasta de apertura de NOVA una compra a mercado de 5.000 acciones. Recalcula el precio, el volumen ejecutable y el desequilibrio.
Solución
IN [15]python
extra = pd.DataFrame([{"lado": "compra", "tipo": "mercado",
                       "precio_limite": np.nan, "tamano": 5000}])
compra = pd.concat([compra, extra], ignore_index=True)
curva["demanda"] = curva["precio"].map(demanda)
curva["ejecutable"] = curva[["demanda", "oferta"]].min(axis=1)
curva["deseq"] = curva["demanda"] - curva["oferta"]
curva.loc[curva["ejecutable"].idxmax()]
OUT [15]La subasta con una compra a mercado más
precio           53.37
demanda       34900.00
oferta        35800.00
ejecutable    34900.00
deseq          -900.00
Name: 24, dtype: float64

La demanda sube 5.000 acciones a cualquier precio, así que el cruce se desplaza hacia arriba: hacen falta precios más altos para que aparezca oferta suficiente. El precio pasa a 53,37 USD, dos céntimos más, con 34.900 acciones ejecutables y un desequilibrio de −900. Es lo que ocurre cuando una noticia de la noche anterior llena la subasta de compras: la apertura salta por encima del cierre previo y queda un hueco en el gráfico.

  1. Explica por qué una estrategia que calcula su señal con el precio de cierre y «compra al cierre» del mismo día tiene un sesgo de anticipación, y propón dos formas de corregirlo con los datos de esta lección.
Solución

El precio de cierre es el de la subasta de cierre, que se cruza a las 16:00, y en la NYSE las órdenes MOC solo pueden enviarse hasta las 15:50. La señal usa un dato que no existía cuando había que enviar la orden. Correcciones: calcular la señal con la barra que termina a las 15:50 (las barras de 5 minutos de nova_barras_5min.parquet lo permiten) y ejecutar en la subasta de cierre; o calcularla con el cierre y ejecutar en la subasta de apertura siguiente, cuyo precio se reconstruye como en esta lección. La segunda paga el hueco de la noche; la primera, el riesgo de los últimos diez minutos.

Footnotes

  1. Horarios de la NYSE en su página «Holidays & Trading Hours» y en el folleto «NYSE opening and closing auctions»; los de BME, en la página «Horario Bolsa Española» de BME Exchange (consultadas el 21 de octubre de 2026). ↩

  2. Calendario de 2025: comunicado de NYSE Group «NYSE Group Announces 2025, 2026 and 2027 Holiday and Early Closings Calendar»; cierre del 9 de enero, comunicado de ICE del 30 de diciembre de 2024. Calendario de 2026: página «Holidays & Trading Hours» de la NYSE. ↩

  3. JPX, «Cash Market Trading Hours Extension Kicked Off Today», 5 de noviembre de 2024. El horario vigente, de 9:00 a 11:30 y de 12:30 a 15:30, está en la página «Trading Hours» de JPX. ↩

Respuesta plegada en cada pregunta

Autoevaluación

  1. El 2 de junio de 2025, ¿cuántas horas coinciden abiertas la Bolsa de Madrid y la NYSE?

    • ANinguna: Madrid cierra antes de que abra Nueva York
    • BDos horas, de 13:30 a 15:30 UTC
    • CSeis horas y media
    • DDepende del bróker
    Ver respuesta

    Respuesta B. Dos horas, de 13:30 a 15:30 UTC

    La NYSE abre a las 9:30 de Nueva York (13:30 UTC en horario de verano) y el mercado continuo de Madrid cierra a las 17:30 de Madrid (15:30 UTC). En 2025, las semanas en que Estados Unidos ya ha cambiado de hora y Europa no, el solape sube a tres horas.

  2. En una subasta, ¿qué precio se elige?

    • AEl precio de la última operación del día anterior
    • BEl precio que maximiza el volumen que se puede ejecutar entre compras y ventas
    • CEl punto medio entre la mejor compra y la mejor venta
    • DEl precio de la orden más grande
    Ver respuesta

    Respuesta B. El precio que maximiza el volumen que se puede ejecutar entre compras y ventas

    Para cada precio candidato se suman las compras dispuestas a pagar ese precio o más y las ventas dispuestas a vender a ese precio o menos. El volumen ejecutable es el menor de los dos; el precio de la subasta es el que lo maximiza. Si hay empate, cada bolsa aplica sus reglas de desempate.

  3. ¿Por qué no sirve pd.bdate_range para generar las sesiones de la NYSE?

    • APorque no admite zonas horarias
    • BPorque cuenta como hábiles los festivos y los cierres extraordinarios y no sabe nada de las medias sesiones
    • CPorque empieza la semana en domingo
    • DSí sirve: da las mismas fechas
    Ver respuesta

    Respuesta B. Porque cuenta como hábiles los festivos y los cierres extraordinarios y no sabe nada de las medias sesiones

    bdate_range solo quita sábados y domingos. En 2025 la NYSE cerró once días laborables (diez festivos y el cierre extraordinario del 9 de enero) y tuvo tres medias sesiones que terminan a las 13:00. Un calendario de bolsa los incluye.

  4. ¿Por qué la última barra de 5 minutos de la sesión de NOVA tiene unas diez veces más volumen que las del mediodía?

    • APorque los creadores de mercado se retiran antes del cierre
    • BPorque incluye la subasta de cierre, donde ejecutan los fondos indexados y muchas gestoras que se valoran al precio de cierre
    • CPorque es un error del proveedor de datos
    • DPorque a esa hora abre Europa
    Ver respuesta

    Respuesta B. Porque incluye la subasta de cierre, donde ejecutan los fondos indexados y muchas gestoras que se valoran al precio de cierre

    Un fondo que replica un índice se valora al cierre, así que quiere comprar y vender a ese precio exacto. Lo consigue enviando órdenes a la subasta de cierre. En NOVA, el 2 de junio de 2025, la subasta de cierre cruzó 885.500 acciones, el 10 % del volumen del día, en un único precio.

  5. La Bolsa de Tokio cerraba a las 15:00 y desde el 5 de noviembre de 2024 cierra a las 15:30. ¿Qué pasa si un backtest usa la hora nueva para toda la historia?

    • ANada: la hora de cierre no influye
    • BTrata como cierre de sesión precios de media hora antes en los datos antiguos, o busca barras que no existían
    • CSolo afecta a los futuros
    • DLos datos se ajustan solos
    Ver respuesta

    Respuesta B. Trata como cierre de sesión precios de media hora antes en los datos antiguos, o busca barras que no existían

    Un calendario de bolsa guarda la historia de los horarios. exchange_calendars da 15:00 para el 1 de noviembre de 2024 y 15:30 para el 5. Con un horario fijo, las señales «al cierre» de los datos antiguos se calcularían a una hora en la que el mercado ya estaba cerrado.

Ideas clave

  • Una sesión tiene fases: preapertura con subasta, negociación continua, subastas de volatilidad cuando el precio se mueve demasiado y subasta de cierre. Cada una genera datos distintos.
  • Las horas de los mercados se guardan en UTC y se calculan con un calendario de bolsa: la zona horaria, el horario de verano, las pausas de mediodía y los cambios de horario hacen inútil una hora fija.
  • Los festivos, los cierres extraordinarios y las medias sesiones cambian de un año a otro. exchange_calendars y pandas_market_calendars los traen; pd.bdate_range no.
  • El precio de una subasta es el que maximiza el volumen ejecutable. En NOVA, 53,35 USD con 33.900 acciones, idéntico a la primera operación del día.
  • El volumen intradía tiene forma de U, y la subasta de cierre es la operación más grande del día porque fija el precio con el que se valoran los fondos.

Recursos