---
title: "4.5.3 Paper trading, shadow trading y slippage"
description: "Backtest, paper y real del mismo sistema comparados ejecución a ejecución: el paper llena al medio y el real paga horquilla y deslizamiento medibles."
url: "https://pythonparatrading.com/curso-python-trading/nivel-4/paper-trading-shadow-trading-y-slippage/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-4/paper-trading-shadow-trading-y-slippage.png"
---

# 4.5.3 · Paper trading, shadow trading y seguimiento del slippage

> El paper trading promete lo que el backtest promete: ejecuciones al precio medio, sin horquilla y casi sin latencia. Esta lección compara ejecución a ejecución el backtest, el paper y la cuenta real de un mismo sistema, separa el retraso del deslizamiento, recupera los parámetros de deslizamiento plantados por tipo de orden y momento, y monta el seguimiento que habría avisado en la primera semana.

- Curso: [Nivel 4: Carteras, riesgo, ejecución y trading en real](https://pythonparatrading.com/curso-python-trading/nivel-4/)
- Módulo: 4.5 · Sistema de trading en vivo
- Duración: 70 min
- Requisitos: [4.5.1 · Arquitectura de un sistema en vivo: de los datos a la reconciliación](https://pythonparatrading.com/curso-python-trading/nivel-4/arquitectura-de-un-sistema-en-vivo/), [4.5.2 · Gestión de órdenes: estados, fills parciales, cancelaciones y reconexiones](https://pythonparatrading.com/curso-python-trading/nivel-4/gestion-de-ordenes-estados-fills-y-reconexiones/), [4.3.1 · Implementation shortfall, VWAP, TWAP y Almgren-Chriss](https://pythonparatrading.com/curso-python-trading/nivel-4/implementation-shortfall-vwap-twap-y-almgren-chriss/), [2.4.7 · Modelado de costes: comisiones, horquilla, impacto, financiación y préstamo de títulos](https://pythonparatrading.com/curso-python-trading/nivel-2/modelado-de-costes/)

## Al terminar esta lección

- Explicar qué simula y qué no un broker de paper trading, y por qué sus ejecuciones son optimistas.
- Descomponer la diferencia entre el precio del backtest y el de la ejecución real en retraso (el mercado se mueve antes de enviar) y deslizamiento (lo que cuesta ejecutar).
- Medir el deslizamiento por tipo de orden y momento del día, con su error típico, y contrastarlo con la verdad conocida de los datos.
- Montar un libro en sombra (shadow trading) con las reglas del paper sobre las órdenes reales y medir en dólares lo que el paper esconde.
- Vigilar el deslizamiento en vivo con un CUSUM frente al modelo de costes y saber cuándo un modelo está mal calibrado.

## Tres precios para la misma orden

El checklist de la lección 2.5.8 terminaba en el paper trading: la estrategia corre en tiempo real con órdenes
simuladas durante un periodo fijado de antemano, para comprobar que lo que supone el backtest ocurre. Esta lección lo
hace con números. El sistema de los datos del nivel (lecciones 4.5.1 y 4.5.2) operó en una cuenta **paper** del 2 de
septiembre al 10 de octubre de 2025 y en una cuenta **real** del 13 de octubre al 31 de diciembre, con las mismas
reglas y 1.000.000 USD en cada una. Para cada señal hay tres precios:

- **Backtest** (`vivo4_backtest_fills.csv`): lo que supone el backtest. El medio a la hora de la señal (15 minutos
  antes del cierre) para las órdenes a mercado y límite, el cierre oficial para las de cierre y la apertura de la
  sesión siguiente para las de los lunes que se ejecutan al abrir. Sin costes.
- **Medio al enviar** (`precio_medio_envio` en `vivo4_ordenes.csv`): el precio medio del mercado cuando el OMS envía
  la orden. Es la referencia del deslizamiento.
- **Ejecución**: el precio de cada ejecución que devolvió el broker.

```mermaid
flowchart TB
  B[Precio backtest] --> R[Retraso]
  R --> M[Medio al enviar]
  M --> D[Deslizamiento]
  D --> E[Ejecución]
```

La diferencia entre backtest y ejecución se parte en dos. El **retraso** es lo que se mueve el mercado entre la hora
que supone el backtest y la del envío: si la señal no tiene información de corto plazo, su media es cero, pero su
ruido es grande. El **deslizamiento** es lo que cuesta ejecutar frente al medio en el momento de enviar: media
horquilla, impacto, latencia. Es lo que un modelo de costes (lecciones 2.4.7 y 4.3.1) tiene que estimar, y lo que el
paper no tiene.

```python
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf

ordn = pd.read_csv("vivo4_ordenes.csv",
                   parse_dates=["ts_envio", "fecha"])
ev = pd.read_csv("vivo4_eventos_broker.csv")
bt = pd.read_csv("vivo4_backtest_fills.csv")
ej = (ev[ev["evento"].isin(["parcial", "ejecutada"])]
      .drop_duplicates("id_ejecucion"))          # lección 4.5.2
x = (ej[["id_orden_cliente", "id_ejecucion", "cantidad_ultima",
         "precio_ultimo", "comision", "ts_broker"]]
     .merge(ordn, on="id_orden_cliente")
     .merge(bt[["id_senal", "precio_backtest", "supuesto"]],
            on="id_senal"))
x["s"] = np.where(x["lado"] == "compra", 1, -1)
x["efectivo"] = x["cantidad_ultima"] * x["precio_ultimo"]
pb = lambda a, b: x["s"] * (a / b - 1) * 1e4    # + = peor
x["total"] = pb(x["precio_ultimo"], x["precio_backtest"])
x["retraso"] = pb(x["precio_medio_envio"], x["precio_backtest"])
x["desliz"] = pb(x["precio_ultimo"], x["precio_medio_envio"])
print(len(x), "ejecuciones;", x.groupby("modo").size().to_dict())

def resumen(g):
    w = g["efectivo"]
    return pd.Series({c: np.average(g[c], weights=w)
                      for c in ("total", "retraso", "desliz")}
                     | {"efectivo_musd": w.sum() / 1e6,
                        "comisiones": g["comision"].sum()})

x.groupby("modo").apply(resumen, include_groups=False).round(2)
```
```output
531 ejecuciones; {'paper': 183, 'real': 348}
       total  retraso  desliz  efectivo_musd  comisiones
modo
paper  -0.04     0.27   -0.31           5.21      303.30
real    0.97    -0.77    1.74          11.70      593.71
```

Ponderadas por efectivo, las ejecuciones de paper salen 0,04 pb **mejor** que el backtest y las reales 0,97 pb peor.
Pero mirar solo esa columna engaña: en la cuenta real el retraso aporta −0,77 pb (el mercado se movió a favor entre la
señal y el envío, por azar) y el deslizamiento de ejecución es de 1,74 pb. En paper, el deslizamiento es de −0,31 pb:
el broker de paper no cobra nada por ejecutar. Las órdenes al cierre tienen un deslizamiento sin sentido frente al
medio al enviar (se ejecutan diez minutos después, en la subasta), así que en adelante el deslizamiento se mide solo
con las órdenes a mercado.

## Lo que simula un broker de paper

El simulador de paper de los datos llena las órdenes a mercado **al precio medio** del instante de la ejecución, sin
horquilla y sin deslizamiento extra; la cuenta real paga media horquilla más un extra que depende del momento. Es una
simplificación, pero no una caricatura: los brokers lo documentan. Alpaca dice que su paper trading no tiene en
cuenta el impacto de las órdenes, la filtración de información, el deslizamiento por latencia, la posición en la cola
de las límite no ejecutables, la mejora de precio, las tasas regulatorias ni los dividendos, y que la cantidad de la
orden no se compara con la del mejor precio[^1]. Interactive Brokers advierte que las ejecuciones de su cuenta paper
pueden caer o no dentro del mercado del momento y que no son indicativas de lo que pasaría en el real[^2].

```python
mk = x[x["tipo"] == "mercado"].copy()
mk["momento"] = np.where(
    mk["motivo"] == "resto_de_limite", "resto_limite",
    np.where(mk["ts_envio"].dt.hour < 17, "apertura", "cierre"))

def media_et(g):
    return pd.Series({"n": len(g), "media": g.mean(),
                      "desv": g.std(),
                      "et": g.std() / np.sqrt(len(g))})

med = (mk.groupby(["modo", "momento"])["desliz"]
       .apply(media_et).unstack())
med.round(2)
```
```output
                        n  media  desv    et
modo  momento
paper cierre        108.0   0.02  0.56  0.05
      resto_limite   21.0   0.14  0.43  0.09
real  apertura       31.0   5.22  3.73  0.67
      cierre        141.0   1.49  1.55  0.13
      resto_limite   48.0   2.32  2.40  0.35
```

En paper, el deslizamiento medio frente al medio al enviar es prácticamente cero (0,02 pb al cierre, 0,14 pb en los
restos de las límite): lo único que queda es lo que se mueve el medio en los milisegundos entre el envío y la
ejecución. En real hay tres momentos: las órdenes del cierre (10 minutos antes), los restos de las límite que no se
llenaron (tres minutos antes del cierre) y las de los lunes que se ejecutan al abrir el martes. Abrir cuesta 5,2 pb,
más del triple que cerrar (1,5 pb). Y cada media lleva su error típico: con 31 ejecuciones de apertura, ±0,67 pb; con
141 de cierre, ±0,13 pb.

La latencia es la otra mitad de la promesa del paper. Desde que el OMS envía hasta que recibe la aceptación, y desde
la aceptación hasta la primera ejecución en el broker:

```python
ac = ev[ev["evento"] == "aceptada"].merge(
    ordn[["id_orden_cliente", "ts_envio", "tipo"]])
ac["ms_acuse"] = (pd.to_datetime(ac["ts_recibido"])
                  - ac["ts_envio"]).dt.total_seconds() * 1e3
prim = (ej.groupby("id_orden_cliente")["ts_broker"].min()
        .rename("primera"))
ac = ac.join(prim, on="id_orden_cliente")
ac["ms_ejec"] = (pd.to_datetime(ac["primera"])
                 - pd.to_datetime(ac["ts_broker"])
                 ).dt.total_seconds() * 1e3
q = lambda s: s.quantile([0.1, 0.5, 0.9]).round(1).tolist()
corte = ac["ms_acuse"] > 10_000      # acuse recibido tras un corte
print("acuses tras un corte:", ac[corte].groupby("modo").size()
      .to_dict())
for modo, g in ac[~corte].groupby("modo"):
    print(f"{modo:5s} acuse p10/p50/p90 ms:", q(g["ms_acuse"]),
          "| 1.ª ejecución a mercado:",
          q(g.loc[g["tipo"] == "mercado", "ms_ejec"]))
```
```output
acuses tras un corte: {'real': 42}
paper acuse p10/p50/p90 ms: [24.0, 31.3, 40.7] | 1.ª ejecución a mercado: [2.9, 5.0, 10.2]
real  acuse p10/p50/p90 ms: [69.8, 87.3, 110.0] | 1.ª ejecución a mercado: [19.4, 61.9, 167.9]
```

*Fig. — Latencias del sistema en vivo en paper y en real: del envío al acuse recibido y de la aceptación a la primera ejecución de una orden a mercado (mediana y percentiles 10 a 90, escala logarítmica).* Cuatro filas en escala logarítmica de milisegundos. Acuse en paper: mediana 31 ms (24 a 41). Acuse en real: mediana 87 ms (70 a 110), destacado. Primera ejecución en paper: 5 ms (2,9 a 10). Primera ejecución en real: 62 ms (19 a 168).

Antes, una exclusión: 42 acuses de la cuenta real llegaron más de 10 segundos después del envío. No es latencia sino
los cortes de conexión de la lección 4.5.2: las órdenes enviadas justo antes de un corte reciben su acuse al
reconectar, minutos después. Sin ellos, la mediana del acuse es de 31 ms en paper y de 87 ms en real; la primera
ejecución de una orden a mercado llega en 5 ms en paper y en 62 ms en real, con un percentil 90 de 168 ms. Para un sistema que opera al cierre, unas decenas de milisegundos no cambian nada; para uno que
reacciona a cada cotización, el paper falsea la parte más importante.

## Recuperar los parámetros plantados

Los datos tienen la respuesta conocida: el precio de cada ejecución real a mercado es el medio por
$1 \pm (h + \varepsilon)/10^4$, con $h$ la media horquilla del ETF y $\varepsilon$ un extra normal cuya media y
desviación dependen del momento (`vivo4_deslizamiento_verdad.csv`). Lo medido arriba es $h + \varepsilon$ más el
movimiento del medio durante la latencia. Para llegar a $\varepsilon$ hace falta $h$, y los datos no la dan
directamente. Se puede estimar con la regla de las órdenes límite del sistema, que se colocan a un cuarto de horquilla
del medio (media horquilla = 2 × la distancia del límite al medio):

```python
lim = ordn[ordn["tipo"] == "limite"]
s_l = np.where(lim["lado"] == "compra", 1, -1)
dist = s_l * (lim["precio_medio_envio"] - lim["precio_limite"]) \
    / lim["precio_medio_envio"] * 1e4
h = (2 * dist).groupby(lim["instrumento"]).agg(["mean", "count"])
print(h.round(2).T.to_string())
mk["h"] = np.where(mk["modo"] == "real",
                   mk["instrumento"].map(h["mean"]), 0.0)
```
```output
instrumento   ORAU  ORCM  OREM   OREU   ORIG  ORRE   ORSC  ORTL   ORTN   ORUS
mean          0.67  1.59   1.6   0.88   0.36  0.93   0.49  0.72   0.33   0.19
count        13.00  6.00   9.0  12.00  13.00  6.00  17.00  9.00  14.00  12.00
```

Las medias horquillas estimadas van de 0,2 pb (ORUS, el ETF de la renta variable de EE. UU.) a 1,6 pb (OREM y ORCM,
emergentes y materias primas). Son estimaciones ruidosas: el precio límite se redondea al céntimo, y en un ETF de 24
USD un céntimo son 4 pb, diez veces la distancia que se quiere medir; con seis órdenes, el error de ORCM es grande. En
un broker real, la horquilla se mide con las cotizaciones (lección 1.1.5); aquí la regla de las límites basta.

```python
verdad = pd.read_csv("vivo4_deslizamiento_verdad.csv")
mk["extra"] = mk["desliz"] - mk["h"]
est = (mk.groupby(["modo", "momento"])["extra"]
       .apply(media_et).unstack()[["n", "media", "et"]])
comp = est.join(verdad.set_index(["modo", "momento"]))
comp["z"] = (comp["media"] - comp["media_pb"]) / comp["et"]
comp.round(2)
```
```output
                        n  media    et  media_pb  desviacion_pb     z
modo  momento
paper cierre        108.0   0.02  0.05       0.0            0.0  0.31
      resto_limite   21.0   0.14  0.09       0.0            0.0  1.49
real  apertura       31.0   4.44  0.69       3.0            4.0  2.08
      cierre        141.0   0.85  0.13       1.0            1.5 -1.20
      resto_limite   48.0   1.61  0.34       1.5            2.0  0.32
```

La figura de la cabecera lo resume. Al cierre, el extra medido es de 0,85 ± 0,13 pb frente al 1,0 verdadero; en los
restos de las límite, 1,61 ± 0,34 frente a 1,5. Los dos están a menos de 1,2 errores típicos de la verdad. En la
apertura, 4,44 ± 0,69 frente a 3,0: a algo más de dos errores típicos, y con 31 ejecuciones de unos pocos lunes; es
el tipo de diferencia que en datos reales no se sabría si es azar o un cambio de régimen. El paper da exactamente 0:
es su regla. Y la columna de paper deja claro lo que no se puede hacer: calibrar el modelo de costes con el paper.

Las **diferencias** entre momentos se pueden medir sin conocer la horquilla. Con un efecto fijo por ETF, la horquilla
de cada uno queda absorbida y la regresión da cuánto más cuesta la apertura o el resto que el cierre. Las varianzas
son distintas por momento (casi 4 pb de desviación en la apertura, 1,5 al cierre), así que los errores tienen que ser
robustos:

```python
real = mk[mk["modo"] == "real"]
fit = smf.ols("desliz ~ C(instrumento) + "
              "C(momento, Treatment('cierre'))", data=real
              ).fit(cov_type="HC1")
fila = fit.params.filter(like="momento")
tabla = pd.DataFrame({"coef": fila,
                      "et": fit.bse.filter(like="momento")})
tabla.index = ["apertura - cierre", "resto - cierre"]
tabla["verdad"] = [3.0 - 1.0, 1.5 - 1.0]
tabla.round(2)
```
```output
                   coef    et  verdad
apertura - cierre  3.64  0.70     2.0
resto - cierre     0.87  0.35     0.5
```

La apertura cuesta unos 3,6 pb más que el cierre según la regresión, frente a 2,0 de verdad: el mismo exceso de las
aperturas de la tabla anterior, ahora con un error típico robusto de unos 0,7 pb. El resto cuesta 0,9 pb más que el
cierre, frente a 0,5. Con efectos fijos no hay nivel: la regresión no puede decir cuánto cuesta el cierre, solo
cuánto más cuesta lo demás. Para el nivel hace falta la horquilla.

## Un libro en sombra

Comparar el paper de septiembre con el real de noviembre mezcla dos cosas: la regla de ejecución y el mercado de
cada periodo. El **shadow trading** (operar en sombra) las separa: el mismo sistema lleva, junto al libro real, un
libro que aplica otra regla de ejecución a **las mismas órdenes, los mismos días**. Aquí la sombra es la regla del
paper aplicada a las órdenes reales: cada ejecución a mercado al medio del envío, las límite a su límite y las de
cierre al cierre. Es lo que habría dicho la cuenta paper si hubiera seguido en paralelo.

```mermaid
flowchart TB
  S[Señal] --> O[OMS]
  O --> BR[Broker real]
  O --> SB[Libro en sombra]
  BT[Backtest] --> C[Comparar]
  BR --> C
  SB --> C
```

Una variante habitual es la contraria: correr la estrategia nueva en sombra, sin enviar órdenes, junto a la que ya
opera, con los mismos datos y el mismo reloj, y comparar sus señales y sus ejecuciones supuestas antes de darle dinero.
La arquitectura de la lección 4.5.1 lo permite sin cambios: otra señal y otro OMS suscritos al mismo bus, con un
broker simulado.

```python
r = x[x["modo"] == "real"].copy()
r["px_sombra"] = np.where(r["tipo"] == "mercado",
                          r["precio_medio_envio"], r["precio_ultimo"])
usd = lambda px: r["s"] * r["cantidad_ultima"] * (
    px - r["precio_backtest"])
r["coste_real"] = usd(r["precio_ultimo"]) + r["comision"]
r["coste_sombra"] = usd(r["px_sombra"]) + r["comision"]
tot = r[["coste_real", "coste_sombra"]].sum()
print(tot.round(2).to_dict())
oculto = tot["coste_real"] - tot["coste_sombra"]
efec_mk = r.loc[r["tipo"] == "mercado", "efectivo"].sum()
print(f"Lo que el paper esconde: {oculto:,.2f} USD, "
      f"{oculto / efec_mk * 1e4:.2f} pb de lo ejecutado a mercado")
import exchange_calendars as xcals
dias = len(xcals.get_calendar("XNYS").sessions_in_range(
    "2025-10-13", "2025-12-31"))
print(f"{dias} sesiones; al año, {oculto / dias * 252:,.0f} USD, "
      f"un {oculto / dias * 252 / 1e6:.2%} del capital")
```
```output
{'coste_real': 1732.02, 'coste_sombra': 27.02}
Lo que el paper esconde: 1,705.00 USD, 2.49 pb de lo ejecutado a mercado
56 sesiones; al año, 7,672 USD, un 0.77% del capital
```

*Fig. — Coste acumulado frente al backtest (precio y comisiones) del sistema en vivo: cuenta paper hasta el 10 de octubre y cuenta real después, con el libro en sombra que aplica la regla del paper a las órdenes reales.* Tres líneas en dólares. En paper, del 2 de septiembre al 10 de octubre, el coste oscila entre −189 y 327 USD y acaba en 283. En la cuenta real, desde el 13 de octubre, la línea real (destacada) sube hasta 1.732 USD el 31 de diciembre; la sombra oscila entre −831 y 339 y acaba en 27. La distancia entre real y sombra crece casi sin interrupción.

En las 56 sesiones de la cuenta real, el libro real cuesta 1.732 USD frente al backtest (deslizamiento y comisiones)
y la sombra, 27 USD: las comisiones casi compensadas por el retraso, que esos días jugó a favor. La diferencia, 1.705
USD, es lo que el paper esconde: el deslizamiento de las órdenes a mercado, 2,49 pb de lo que se ejecutó a mercado.
Llevado a un año, unos 7.700 USD, un 0,77 % del capital, para una estrategia que espera unos pocos puntos al año.
La figura muestra el coste acumulado frente al backtest de los dos periodos. En paper, la curva sube y baja con el
retraso (cientos de dólares en un día) y termina en 283 USD, casi solo comisiones. En real, la curva real y la sombra
suben y bajan juntas, porque el retraso es el mismo para las dos, pero la distancia entre ellas crece casi sin
interrupción: es el deslizamiento, que se paga en cada orden. Ningún mes de paper habría anticipado esa distancia.

## Seguimiento del slippage en vivo

Medir el deslizamiento al final del trimestre no basta: hay que saber pronto si el modelo de costes está mal. La
herramienta clásica es un **CUSUM** de Page: acumula las desviaciones del deslizamiento observado frente al que dice
el modelo, menos una holgura, y avisa cuando la suma pasa un umbral.

$$
S_t = \max\!\left(0,\; S_{t-1} + \frac{d_t - \widehat{d}_t}{\sigma} - k\right),
\qquad \text{alarma si } S_t > h
$$

con $d_t$ el deslizamiento de la ejecución $t$, $\widehat{d}_t$ el del modelo, $\sigma$ la desviación esperada (1,5 pb
al cierre), $k = 0{,}5$ (detecta desvíos de un sigma) y $h = 5$ (pocas falsas alarmas). Se aplica a las ejecuciones de
cierre de la cuenta real, en orden, con tres modelos de coste: el del paper (0), solo la media horquilla y la media
horquilla más 1 pb.

```python
def cusum(d, modelo, sigma=1.5, k=0.5, h=5.0):
    s, camino = 0.0, []
    for v in (d - modelo) / sigma:
        s = max(0.0, s + v - k)
        camino.append(s)
    camino = np.array(camino)
    salta = np.argmax(camino > h) if (camino > h).any() else None
    return camino, salta

cie = mk[(mk["modo"] == "real") & (mk["momento"] == "cierre")] \
    .sort_values("ts_broker")
modelos = {"paper (0)": 0.0, "media horquilla": cie["h"],
           "horquilla + 1 pb": cie["h"] + 1.0}
for nombre, mod in modelos.items():
    camino, i = cusum(cie["desliz"], mod)
    cuando = ("nunca" if i is None else
              f"ejecución {i + 1}, {cie['ts_broker'].iloc[i][:10]}")
    print(f"{nombre:17s} alarma: {cuando}")

ape = mk[(mk["modo"] == "real") & (mk["momento"] == "apertura")] \
    .sort_values("ts_broker")
_, i = cusum(ape["desliz"], ape["h"] + 1.0)
print("aperturas con el modelo del cierre: alarma en la ejecución",
      i + 1, ape["ts_broker"].iloc[i][:10])
```
```output
paper (0)         alarma: ejecución 8, 2025-10-21
media horquilla   alarma: ejecución 9, 2025-10-21
horquilla + 1 pb  alarma: nunca
aperturas con el modelo del cierre: alarma en la ejecución 2 2025-10-14
```

Con el modelo del paper, la alarma salta en la octava ejecución de cierre, el 21 de octubre: seis sesiones después de
empezar en real. Con solo la media horquilla, una ejecución más tarde: el extra de 1 pb es pequeño pero constante.
Con horquilla más 1 pb no salta nunca en once semanas: es el modelo bueno. Si se aplicase el modelo del cierre a las
aperturas, saltaría en la segunda ejecución: los modelos de coste se calibran y se vigilan **por momento y tipo de
orden**, no en bloque.

```mermaid
flowchart TB
  E[Ejecución] --> D[Deslizamiento]
  D --> C[CUSUM por momento]
  C --> A{Pasa el umbral}
  A -- no --> E
  A -- sí --> AL[Alerta]
  AL --> R[Recalibrar modelo]
```

La alerta no corrige nada sola: dice que el modelo de costes con el que se aprobó la estrategia ya no describe la
realidad. Lo siguiente es recalibrarlo con las ejecuciones reales y repetir el backtest con él (lección 2.4.7); si la
estrategia ya no compensa sus costes, se reduce o se para (lección 4.2.5). El envío de la alerta es la lección 4.5.4.

## Lo que el paper sí comprueba

Nada de lo anterior hace inútil el paper trading. Las seis semanas de paper de los datos sirvieron para comprobar que
las señales en vivo coinciden con las del backtest (lección 4.5.1), que la máquina de estados aguanta los mensajes
reales del broker y una desconexión (lección 4.5.2), que los rechazos se tratan bien y que el sistema arranca y para
cada día sin intervención. Para lo que no sirvieron es para medir costes. La forma práctica de unirlo: paper para la
operativa, una cuenta real pequeña para los costes (con la sombra en paralelo), y el tamaño completo solo cuando el
CUSUM lleve semanas sin saltar con el modelo con el que se hizo el backtest.

## Ejercicios

1. Repite la descomposición del primer apartado solo con las órdenes a mercado del cierre. ¿Cuánto del 1,74 pb de
   deslizamiento real explica la media horquilla estimada?

**Solución.**

```python
c = mk[mk["momento"] == "cierre"]
for modo, g in c.groupby("modo"):
    w = g["efectivo"]
    print(modo, "deslizamiento", round(np.average(g["desliz"],
                                                  weights=w), 2),
          "| media horquilla", round(np.average(g["h"], weights=w), 2),
          "| retraso", round(np.average(g["retraso"], weights=w), 2))
```
```output
paper deslizamiento 0.03 | media horquilla 0.0 | retraso 1.67
real deslizamiento 1.4 | media horquilla 0.55 | retraso -0.43
```

Al cierre, la cuenta real paga 1,4 pb ponderado por efectivo, de los que la media horquilla estimada explica 0,55:
el resto es el extra. En paper, 0,03. El retraso, en cambio, cambia de signo entre periodos (+1,67 pb en paper, −0,43
en real): es el ruido del mercado en cinco minutos, no un coste, y cuatro meses no bastan para que su media se acerque
a cero.

2. ¿Depende el deslizamiento real a mercado del ETF? Compara el deslizamiento medio por ETF con su media horquilla
   estimada.

**Solución.**

```python
por = (mk[mk["modo"] == "real"].groupby("instrumento")
       .agg(n=("desliz", "size"), desliz=("desliz", "mean"),
            h=("h", "first")))
por["desliz - h"] = por["desliz"] - por["h"]
print(por.round(2).sort_values("h").to_string())
print("correlación desliz-h:", round(por["desliz"].corr(por["h"]), 2))
```
```output
              n  desliz     h  desliz - h
instrumento
ORUS         20    1.12  0.19        0.92
ORTN         25    2.85  0.33        2.53
ORIG         34    1.79  0.36        1.43
ORSC         28    2.10  0.49        1.61
ORAU         22    2.22  0.67        1.55
ORTL         27    1.36  0.72        0.64
OREU         19    3.58  0.88        2.70
ORRE         18    2.26  0.93        1.32
ORCM          9    3.04  1.59        1.45
OREM         18    2.68  1.60        1.08
correlación desliz-h: 0.53
```

Los ETF con más horquilla deslizan algo más (correlación de 0,53 entre las diez medias), pero lo que queda tras restar
la media horquilla va de 0,6 a 2,7 pb. Con unas 20 ejecuciones por ETF, mezclando momentos (cada ETF tiene una
proporción distinta de aperturas, que cuestan el triple) y horquillas estimadas con pocas órdenes, cada media tiene un
error de medio punto o más: los datos no permiten distinguir un extra por ETF, y en la verdad plantada no lo hay, solo
depende del momento. En datos reales, el extra también depende del tamaño de la
orden frente a la liquidez (impacto, lección 4.3.1); aquí las órdenes son pequeñas frente al volumen de los ETF y ese
efecto no está.

3. Una estrategia espera 3 pb de beneficio bruto por operación y opera al cierre con órdenes a mercado. Con lo
   medido en la cuenta real, ¿cuánto queda? ¿Y si la mitad de las operaciones se hicieran en la apertura?

**Solución.**

Al cierre, el deslizamiento real medio es de 1,5 pb por ejecución y las comisiones añaden algo más (0,0035 USD por
acción: 0,35 pb en una acción de 100 USD): quedan unos 1,1 pb, poco más de un tercio de lo esperado. Si la mitad se
ejecuta en la apertura (5,2 pb), el deslizamiento medio sube a unos 3,4 pb y la estrategia pierde dinero antes de
comisiones. El paper habría dicho 3 pb limpios en los dos casos.

[^1]: Alpaca, «Paper Trading» (consultado el 2026-10-22; enlace en los recursos).

[^2]: Interactive Brokers, «I Received an Execution in my Paper Trader Account that Did Not Correspond to the Market at
    the Time, What Happened?», actualizado el 11 de noviembre de 2024 (consultado el 2026-10-22; enlace en los
    recursos).

## Autoevaluación

1. ¿Por qué las ejecuciones a mercado de la cuenta paper de los datos salen a 0,02 pb del medio y las de la cuenta real a 1,49 pb?

   - A. Porque el broker de paper llena al precio medio, sin horquilla ni deslizamiento; la cuenta real paga media horquilla más un extra
   - B. Porque en paper el sistema operó mejor
   - C. Porque la cuenta real tenía más capital
   - D. Por azar: con más datos serían iguales

   *Respuesta A: Porque el broker de paper llena al precio medio, sin horquilla ni deslizamiento; la cuenta real paga media horquilla más un extra.* Es la regla del simulador de paper de los datos, y la de muchos brokers en alguna medida: Alpaca, por ejemplo, documenta que su paper no tiene en cuenta el impacto, el deslizamiento por latencia ni la posición en la cola. El 0,02 que queda en paper es el movimiento del medio en los milisegundos de latencia.

2. La cuenta real ejecuta 0,97 pb peor que el backtest. ¿Por qué no basta esa cifra para calibrar el modelo de costes?

   - A. Porque está en puntos básicos
   - B. Porque el backtest no tiene comisiones
   - C. Sí basta
   - D. Porque mezcla el retraso (el medio se mueve 5 minutos entre la señal y el envío, con media cero pero mucho ruido) con el deslizamiento de ejecución

   *Respuesta D: Porque mezcla el retraso (el medio se mueve 5 minutos entre la señal y el envío, con media cero pero mucho ruido) con el deslizamiento de ejecución.* Frente al medio al enviar, la cuenta real paga 1,74 pb; frente al backtest, 0,97 pb, porque el retraso aporta −0,77 pb de puro ruido en estos cuatro meses. El coste de ejecución se mide frente al medio en el momento de enviar; el retraso, aparte.

3. ¿Qué recupera la regresión del deslizamiento sobre efectos fijos de ETF y del momento, sin conocer las horquillas?

   - A. Las horquillas
   - B. Las diferencias de deslizamiento entre momentos (apertura frente a cierre), no su nivel
   - C. El deslizamiento total
   - D. Nada

   *Respuesta B: Las diferencias de deslizamiento entre momentos (apertura frente a cierre), no su nivel.* Con un efecto fijo por ETF, la horquilla de cada ETF y el nivel común se confunden: solo se identifican las diferencias. Para el nivel hace falta la horquilla, que en la lección se estima con las órdenes límite.

4. Un CUSUM vigila el deslizamiento al cierre frente a un modelo calibrado con el paper (coste esperado 0). ¿Cuándo salta?

   - A. Nunca
   - B. Al final de diciembre
   - C. En la octava ejecución al cierre de la cuenta real, el 21 de octubre, a la semana de empezar
   - D. Antes de empezar

   *Respuesta C: En la octava ejecución al cierre de la cuenta real, el 21 de octubre, a la semana de empezar.* El coste real al cierre es de 1,5 pb por ejecución y el modelo dice 0: la suma acumulada pasa el umbral en ocho ejecuciones. Con un modelo de horquilla más 1 pb no salta nunca: está bien calibrado.

5. ¿Qué aporta un libro en sombra frente a comparar el paper de septiembre con el real de noviembre?

   - A. Nada
   - B. Más rentabilidad
   - C. Evita pagar comisiones
   - D. Compara las mismas órdenes, los mismos días y los mismos precios con dos reglas de ejecución: la diferencia es solo la regla, no el mercado

   *Respuesta D: Compara las mismas órdenes, los mismos días y los mismos precios con dos reglas de ejecución: la diferencia es solo la regla, no el mercado.* Paper y real en periodos distintos mezclan la regla de ejecución con el mercado de cada periodo. La sombra aplica la regla del paper a las órdenes reales: en la lección, la diferencia son 1.705 USD en once semanas, 2,49 pb sobre lo ejecutado a mercado.

## Ideas clave

- El paper trading comprueba que el sistema funciona (señales, órdenes, estados, reconexiones); no mide costes. Su broker suele llenar al medio, sin cola ni impacto.
- La diferencia entre backtest y real se separa en retraso (ruido de media cero si la señal no tiene información de corto plazo) y deslizamiento de ejecución (el coste que se puede modelar).
- El deslizamiento depende del momento: en los datos, abrir a mercado el lunes cuesta unas tres veces más que ejecutar al cierre. Un modelo único se equivoca en los dos.
- Cada media lleva su error típico: con 31 ejecuciones, el deslizamiento de apertura se mide con ±0,7 pb; con 141, el de cierre con ±0,13.
- Un libro en sombra aplica otra regla de ejecución a las mismas órdenes y aísla lo que cuesta la regla.
- El seguimiento en vivo es un contraste secuencial (CUSUM) del deslizamiento frente al modelo, por momento: salta en días si el modelo es el del paper.

## Recursos

### Research

- [Modelos Cuantitativos y Ejecución Algorítmica](https://pythonparatrading.com/modelos-cuantitativos-ejecucion-algoritmica/) · Research-0009 · 11 min

### Documentación

- [Alpaca: paper trading (qué simula y qué no)](https://docs.alpaca.markets/docs/paper-trading) · docs.alpaca.markets
- [Interactive Brokers: ejecuciones de la cuenta paper que no corresponden al mercado](https://ibkb.interactivebrokers.com/article/252) · ibkb.interactivebrokers.com
- [statsmodels: OLS con errores robustos (cov_type)](https://www.statsmodels.org/stable/generated/statsmodels.regression.linear_model.OLS.fit.html) · statsmodels.org

### Datos

- [Datos del nivel 4: lo que supone el backtest para cada señal (CSV)](https://pythonparatrading.com/curso/datos/nivel-4/vivo4_backtest_fills.csv) · Descarga · vivo4_backtest_fills.csv
- [Datos del nivel 4: deslizamiento verdadero por modo y momento (CSV)](https://pythonparatrading.com/curso/datos/nivel-4/vivo4_deslizamiento_verdad.csv) · Descarga · vivo4_deslizamiento_verdad.csv

Lección anterior: [4.5.2 · Gestión de órdenes: estados, fills parciales, cancelaciones y reconexiones](https://pythonparatrading.com/curso-python-trading/nivel-4/gestion-de-ordenes-estados-fills-y-reconexiones/)

Lección siguiente: [4.5.4 · Programación de tareas y alertas: cron, APScheduler, Prefect, Telegram, Slack y email](https://pythonparatrading.com/curso-python-trading/nivel-4/programacion-de-tareas-y-alertas/)
