---
title: "1.1.2 Sesiones, subastas, festivos y calendarios"
description: "Cuándo abre cada bolsa y cómo se fijan la apertura y el cierre: sesiones en UTC, subastas, festivos y medias sesiones con exchange_calendars."
url: "https://pythonparatrading.com/curso-python-trading/nivel-1/sesiones-subastas-y-calendarios-de-mercado/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-1/sesiones-subastas-y-calendarios-de-mercado.png"
---

# 1.1.2 · 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.

- Curso: [Nivel 1: Datos financieros, mercados y análisis](https://pythonparatrading.com/curso-python-trading/nivel-1/)
- Módulo: 1.1 · Cómo funcionan los mercados
- Duración: 55 min
- Requisitos: [1.1.1 · Participantes y centros de negociación](https://pythonparatrading.com/curso-python-trading/nivel-1/participantes-y-centros-de-negociacion/), [0.4.4 · Ficheros (CSV, JSON, YAML, TOML) y fechas con zonas horarias](https://pythonparatrading.com/curso-python-trading/nivel-0/ficheros-pathlib-y-zonas-horarias/)

## Al terminar esta lección

- Describir las fases de una sesión bursátil (preapertura, subasta de apertura, negociación continua, subasta de volatilidad, subasta de cierre) y cuándo se usa cada una.
- Obtener con `exchange_calendars` y `pandas_market_calendars` las horas de apertura y cierre, las pausas, los festivos y las medias sesiones de una bolsa, en UTC y en hora local.
- Calcular el precio de una subasta como el que maximiza el volumen ejecutable y comprobarlo contra la operación de apertura real de los datos.
- Explicar el perfil de volumen en U y el peso de la subasta de cierre, y evitar los errores de calendario más comunes en un backtest.

## 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.

```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:

| Fase | NYSE (Nueva York) | BME, mercado continuo (Madrid) |
|---|---|---|
| Entrada de órdenes | desde las 6:30 | — |
| Subasta de apertura | cruce a las 9:30 | de 8:30 a 9:00 |
| Negociación continua | de 9:30 a 16:00 | de 9:00 a 17:30 |
| Periodo de desequilibrio del cierre | de 15:50 a 16:00 | — |
| Subasta de cierre | cruce a las 16:00 | de 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
reduzcan[^1]. 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.

[^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).

## 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.

```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("")
```
```output
             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:

```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}")
```
```output
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:

```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()
```
```output
-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 diciembre[^2]. 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.

[^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.

```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")))
```
```output
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:

```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")
```
```output
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 nueva[^3]:

[^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.

```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"))
```
```output
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`):

```python
import pandas_market_calendars as mcal

nyse_m = mcal.get_calendar("NYSE")
nyse_m.schedule("2025-11-26", "2025-12-01")
```
```output
                         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:

```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())
```
```output
250 sesiones en 2025
Mismos cierres: True
```

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

El calendario de una bolsa no es el de su liquidación ni el de otro mercado del mismo país. Los futuros de un índice
pueden negociarse el día que la bolsa de contado cierra, y el depositario puede no liquidar un día en que la bolsa
abre. Para cada serie, usa el calendario del mercado que la produce (lecciones 1.1.5 y 1.2.3).

</aside>

## 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 $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)$ baja al subir el precio; la oferta $S(p)$ sube. El precio de la
subasta es el $p$ con mayor $V(p)$, y la diferencia $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:

```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"])
```
```output
139 órdenes
                 size    sum
lado   tipo
compra limitada    63  27400
       mercado      8  17000
venta  limitada    61  30600
       mercado      7  14600
```

```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]
```
```output
    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.* Dos curvas escalonadas entre 52,96 y 53,62 USD. La demanda acumulada baja de 44.400 acciones a 17.200 al subir el precio; la oferta sube de 15.600 a 45.200. Se cruzan en 53,35 USD (destacado), donde se pueden ejecutar 33.900 acciones, el máximo.

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:

```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"])
```
```output
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.

```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)
```
```output
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.* 78 barras de 9:30 a 16:00 de Nueva York. La primera reúne el 3,06 % del volumen del día y las siguientes bajan hasta rondar el 1 % a mediodía; en la última media hora suben de nuevo, y la última barra, la de las 15:55, que incluye la subasta de cierre, llega al 11,28 % (destacada).

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:

```python
vol_dia = op["tamano"].sum()
por_tipo = op.groupby("condicion")["tamano"].sum()
(100 * por_tipo / vol_dia).round(2)
```
```output
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.

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

Decidir con el cierre y ejecutar al mismo cierre es anticipación (lección 0.1.1): el precio de cierre no se conoce
hasta que se cruza la subasta, y para participar en ella la orden tenía que estar enviada diez minutos antes. Una
regla honesta decide con los datos de las 15:50 y ejecuta en la subasta, o decide con el cierre y ejecuta en la
apertura siguiente.

</aside>

## Lo que esto cambia en tus datos

```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.**

```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")
```
```output
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.

2. 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.**

```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()]
```
```output
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.

3. 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.

## Autoevaluación

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

   - A. Ninguna: Madrid cierra antes de que abra Nueva York
   - B. Dos horas, de 13:30 a 15:30 UTC
   - C. Seis horas y media
   - D. Depende del bróker

   *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?

   - A. El precio de la última operación del día anterior
   - B. El precio que maximiza el volumen que se puede ejecutar entre compras y ventas
   - C. El punto medio entre la mejor compra y la mejor venta
   - D. El precio de la orden más grande

   *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?

   - A. Porque no admite zonas horarias
   - B. Porque cuenta como hábiles los festivos y los cierres extraordinarios y no sabe nada de las medias sesiones
   - C. Porque empieza la semana en domingo
   - D. Sí sirve: da las mismas fechas

   *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?

   - A. Porque los creadores de mercado se retiran antes del cierre
   - B. Porque incluye la subasta de cierre, donde ejecutan los fondos indexados y muchas gestoras que se valoran al precio de cierre
   - C. Porque es un error del proveedor de datos
   - D. Porque a esa hora abre Europa

   *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?

   - A. Nada: la hora de cierre no influye
   - B. Trata como cierre de sesión precios de media hora antes en los datos antiguos, o busca barras que no existían
   - C. Solo afecta a los futuros
   - D. Los datos se ajustan solos

   *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

### Research

- [Estacionalidad en Python. Buscando Patrones Intradia](https://pythonparatrading.com/estacionalidad-intradia-preprocesado/) · Research-0024 · 9 min

### Documentación

- [NYSE: Holidays & Trading Hours (sesiones, subastas, festivos y medias sesiones)](https://www.nyse.com/markets/hours-calendars) · nyse.com
- [NYSE: Opening and Closing Auctions (fact sheet; MOC/LOC hasta las 15:50)](https://www.nyse.com/publicdocs/nyse/markets/nyse/NYSE_Opening_and_Closing_Auctions_Fact_Sheet.pdf) · nyse.com
- [BME Exchange: horario del mercado (SIBE, subastas y Trading At Last)](https://www.bolsasymercados.es/es/bme-exchange/horario-del-mercado.html) · bolsasymercados.es
- [JPX: Cash Market Trading Hours Extension Kicked Off Today (5 de noviembre de 2024)](https://www.jpx.co.jp/english/corporate/news/news-releases/1030/20241105-01.html) · jpx.co.jp
- [exchange_calendars (repositorio y documentación)](https://github.com/gerrymanoim/exchange_calendars) · github.com
- [pandas_market_calendars: documentación](https://pandas-market-calendars.readthedocs.io/en/latest/) · pandas-market-calendars.readthedocs.io

### Datos

- [Datos del nivel 1: libro de la subasta de apertura de NOVA (CSV)](https://pythonparatrading.com/curso/datos/nivel-1/nova_subasta_apertura.csv) · Descarga · nova_subasta_apertura.csv

Lección anterior: [1.1.1 · Participantes y centros de negociación](https://pythonparatrading.com/curso-python-trading/nivel-1/participantes-y-centros-de-negociacion/)

Lección siguiente: [1.1.3 · Libro de órdenes: bid, ask, spread y profundidad](https://pythonparatrading.com/curso-python-trading/nivel-1/libro-de-ordenes-bid-ask-y-profundidad/)
