---
title: "7.4.5 Abuso de mercado: spoofing, layering y bots"
description: "MAR y sus indicadores de manipulación con fuentes primarias, un detector de spoofing sobre un log de órdenes y el bot sin mala intención que lo dispara."
url: "https://pythonparatrading.com/curso-python-trading/nivel-7/abuso-de-mercado-spoofing-y-bots/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-7/abuso-de-mercado-spoofing-y-bots.png"
---

# 7.4.5 · Abuso de mercado: spoofing, layering y el bot que los imita sin querer

> El Reglamento sobre abuso de mercado prohíbe las órdenes que dan una imagen falsa de la oferta o la demanda, y no pregunta si quien las envía es una persona o un programa. Esta lección lee los artículos 12 y 15 de MAR y los indicadores del Reglamento Delegado 2016/522, construye un detector de spoofing como los de vigilancia sobre un log de órdenes con cinco operadores y comprueba que un bot con un manejador de ejecuciones descuidado deja la misma huella que un manipulador. No es asesoramiento jurídico.

- Curso: [Nivel 7: Producción, MLOps, regulación, carrera y capstone](https://pythonparatrading.com/curso-python-trading/nivel-7/)
- Módulo: 7.4 · Regulación, ética y fiscalidad
- Duración: 65 min
- Requisitos: [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/), [1.1.4 · Tipos de órdenes](https://pythonparatrading.com/curso-python-trading/nivel-1/tipos-de-ordenes/), [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/), [6.4.5 · Guardarraíles para agentes: el agente propone, el motor decide](https://pythonparatrading.com/curso-python-trading/nivel-6/guardarrailes-para-agentes/)

## Al terminar esta lección

- Situar el spoofing, el layering, el quote stuffing y las operaciones ficticias en los artículos 12 y 15 de MAR y en el anexo II del Reglamento Delegado (UE) 2016/522, con la modificación de 2026.
- Construir un log de órdenes sintético con creadores de mercado, un manipulador y dos bots, y calcular por operador altas, cancelaciones y ejecuciones.
- Programar un detector de spoofing al estilo de la vigilancia de mercado (órdenes grandes cerca del mejor precio, ejecución pequeña en el lado contrario y cancelación inmediata) y medir su sensibilidad a los umbrales.
- Reconocer los patrones de diseño de un bot (cancelar todo tras una ejecución, perseguir el precio con cancelar y reponer) que producen la misma huella que la manipulación, y corregirlos.
- Calcular la proporción entre órdenes no ejecutadas y operaciones del RTS 9 y saber quién vigila, qué notifica y con qué consecuencias.

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

Esta lección explica una norma con sus fuentes primarias para que un sistema de trading no la incumpla por diseño. No
es asesoramiento jurídico: el detector de la lección es didáctico, sus umbrales son ejemplos y una alerta no es una
calificación jurídica. Para un caso concreto hay que leer el texto vigente y, si hace falta, consultar a un profesional.
Textos consultados el 23 de octubre de 2026.

</aside>

## Qué prohíbe MAR

El Reglamento (UE) n.º 596/2014 sobre el abuso de mercado (MAR) cabe en una frase: «Ninguna persona manipulará o
intentará manipular el mercado» (art. 15)[^1]. El artículo 12 dice qué es manipular. Su apartado 1, letra a), incluye
ejecutar una operación, dar una orden de negociación o cualquier otra conducta que «transmita o pueda transmitir señales
falsas o engañosas en cuanto a la oferta, la demanda o el precio» de un instrumento financiero, o que fije o pueda fijar
su precio en un nivel anormal o artificial, «a menos que la persona […] demuestre que esa operación, orden o conducta
se han efectuado por razones legítimas y de conformidad con una práctica de mercado aceptada».

Tres detalles del texto importan a quien programa un sistema:

- **Habla de órdenes, no solo de operaciones.** Una orden que nunca se ejecuta puede ser manipulación. El apartado 2,
  letra c), lo concreta para la negociación electrónica: «la formulación de órdenes en un centro de negociación,
  incluidas la cancelación o modificación de las mismas, […] como las estrategias de negociación algorítmica y de alta
  frecuencia», cuando perturben el mecanismo de negociación, dificulten a otros identificar las órdenes auténticas
  («introduciendo órdenes que den lugar a la sobrecarga o a la desestabilización del carné de órdenes») o creen una
  señal falsa sobre la oferta, la demanda o el precio.
- **Basta con que la orden pueda transmitir una señal falsa.** El texto no exige que el precio se mueva ni menciona la
  intención; la salida que ofrece es demostrar razones legítimas. Para un bot, «fue un fallo de programación» es una
  explicación, no una prueba de que la orden no podía engañar.
- **Se aplica a cualquier persona.** No solo a bancos o fondos: también a un particular con un bot conectado a su
  bróker. En criptoactivos, el título VI de MiCA tiene una prohibición equivalente (art. 91; lección 3.7.5).

El anexo I de MAR da indicadores «que no pueden considerarse por sí mismos como constitutivos de manipulación de
mercado», pero que los participantes y las autoridades deben tener en cuenta al examinar órdenes y operaciones[^1]. Dos
describen la huella de esta lección: la letra d), órdenes u operaciones con «revocaciones de posición en un período
corto», y la letra f), órdenes que «cambian los mejores precios de demanda u oferta […] o en general la configuración del
carné de órdenes […] y se retiran antes de ser ejecutadas».

Las consecuencias son serias. El artículo 30 obliga a los Estados a prever multas máximas de al menos 5 millones de
euros para una persona física y de 15 millones o el 15 % del volumen de negocios para una persona jurídica por
infringir los artículos 14 y 15, o el triple del beneficio obtenido si se puede calcular[^1]. En España, el
incumplimiento del artículo 15 de MAR es una de las infracciones del artículo 297 de la Ley 6/2023 de los Mercados de
Valores y de los Servicios de Inversión[^2].

## Spoofing, layering y compañía en el Reglamento Delegado 2016/522

El Reglamento Delegado (UE) 2016/522 desarrolla los indicadores del anexo I con una lista de prácticas (su anexo
II)[^3]. El Reglamento Delegado (UE) 2026/788, publicado en el Diario Oficial el 16 de julio de 2026 y en vigor a los
veinte días, la ha retocado: reescribe la descripción del layering y el spoofing y añade un punto 10 según el cual los
indicadores «también podrán evaluarse y calcularse a lo largo de un período de tiempo superior o inferior a un día o a
una sesión de negociación», algo «especialmente pertinente […] en el contexto de la negociación algorítmica»[^4]. Las
prácticas que más se parecen a lo que hace un programa:

| Práctica (anexo II, sección 1) | Qué describe | Cómo puede caer en ella un bot |
|---|---|---|
| Layering y spoofing (punto 5, e) | Órdenes múltiples o grandes en un lado del libro para ejecutar en el otro; una vez hecha la operación, se retiran | Un manejador que cancela todo tras cada ejecución mientras mantiene órdenes grandes en el otro lado |
| Órdenes sin intención de ejecutarlas (punto 6, a) | Órdenes que se retiran antes de ejecutarse y dan la impresión de demanda u oferta a ese precio | Órdenes «de apoyo» o de cebo que el algoritmo nunca piensa dejar ejecutar |
| Quote stuffing (punto 4, e) | Gran número de órdenes, cancelaciones o modificaciones que crean incertidumbre o ralentizan a otros | Un bucle que reenvía la orden cada pocos milisegundos aunque nada cambie |
| Momentum ignition (punto 4, f) | Órdenes que inician o exageran una tendencia para cerrar o abrir posición a buen precio; se ilustra con una ratio alta de órdenes canceladas | Una estrategia de ruptura que compra agresivamente y vende sobre el movimiento que ella misma provoca |
| Operaciones ficticias e «improper matched orders» (punto 3, a y c) | Compras y ventas sin cambio de titularidad final, o casi simultáneas, del mismo precio y cantidad, por la misma parte | Dos estrategias de la misma cuenta que se cruzan entre sí sin prevención de autocruce |

```mermaid
flowchart TB
  A[Capas de compra] --> B[Libro inclinado]
  B --> C[Venta pequeña]
  C --> D[Ejecución]
  D --> E[Cancela capas]
  E --> F[Libro vuelve]
```

El diagrama es el ciclo del spoofing de compra: el operador muestra mucho interés comprador cerca del mejor precio,
otros participantes leen el libro inclinado y compran o suben sus precios, su venta pequeña se ejecuta arriba y, acto
seguido, las capas desaparecen. La figura de la cabecera es un episodio concreto del log de esta lección: 12.000
acciones de compra durante menos de un segundo para vender 300.

## Un log de órdenes con cinco operadores

Los centros de negociación y los brókeres vigilan con logs de órdenes: cada alta, cancelación y ejecución, con su
hora, su operador, su lado, su precio y su tamaño. Para trabajar sin datos reales, la celda siguiente construye uno
sintético de media hora de sesión. El mejor precio de compra sigue un paseo aleatorio en céntimos (10.000 céntimos son
100 euros) y la clase `Registro` anota los eventos con la cotización vigente en cada instante:

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

DUR = 30 * 60 * 1000          # media hora de sesión, en ms
PASO = 250                    # la mejor compra se mueve cada 250 ms
rng = np.random.default_rng(7)
mov = rng.choice([-1, 0, 0, 0, 1], size=DUR // PASO)
BID = 10_000 + np.cumsum(mov)  # mejor compra en céntimos

def touch(t):
    """Mejor compra y mejor venta (céntimos) en el instante t."""
    b = int(BID[min(t // PASO, len(BID) - 1)])
    return b, b + 1

class Registro:
    """Log de órdenes al estilo de un centro de negociación."""
    def __init__(self):
        self.filas, self.vivas, self.n = [], {}, 0

    def _fila(self, t, quien, oid, tipo, o, qty):
        b, a = touch(t)
        self.filas.append((t, quien, oid, tipo, o["lado"],
                           o["precio"], qty, b, a))

    def alta(self, t, quien, lado, precio, qty):
        self.n += 1
        o = {"quien": quien, "lado": lado, "precio": precio,
             "qty": qty}
        self.vivas[self.n] = o
        self._fila(t, quien, self.n, "alta", o, qty)
        return self.n

    def cancela(self, t, oid):
        o = self.vivas.pop(oid, None)
        if o is not None:
            self._fila(t, o["quien"], oid, "cancelada", o, o["qty"])

    def ejecuta(self, t, oid):
        o = self.vivas.pop(oid)
        self._fila(t, o["quien"], oid, "ejecutada", o, o["qty"])

    def de(self, quien):
        return [i for i, o in self.vivas.items() if o["quien"] == quien]

    def tabla(self):
        cols = ["ts", "quien", "oid", "evento", "lado", "precio",
                "qty", "bid", "ask"]
        return (pd.DataFrame(self.filas, columns=cols)
                  .sort_values(["ts", "oid"], kind="stable")
                  .reset_index(drop=True))
```

Los operadores son cinco. Dos **creadores de mercado** (MM1 y MM2) cotizan las dos puntas con 300 acciones y renuevan
la cotización cada 300 a 900 ms; a veces les ejecutan una de las dos. Un **manipulador** (S1) hace 20 veces el ciclo del
diagrama: tres capas de 4.000 acciones en el mejor precio y los dos ticks siguientes, una orden real de 300 en el lado
contrario y, en cuanto se ejecuta, la cancelación de las capas:

```python
def creador(reg, nombre, rng, qty=300):
    """Cotiza las dos puntas con el mismo tamaño y las renueva."""
    t, puntas = 0, []
    while t < DUR - 1000:
        if puntas and rng.random() < 0.12:
            reg.ejecuta(t, puntas.pop(rng.integers(2)))
        for oid in puntas:
            reg.cancela(t + 1, oid)
        b, a = touch(t + 2)
        puntas = [reg.alta(t + 2, nombre, "compra", b, qty),
                  reg.alta(t + 2, nombre, "venta", a, qty)]
        t += int(rng.integers(300, 900))
    for oid in puntas:
        reg.cancela(DUR - 1, oid)

def spoofer(reg, nombre, rng, n=20, espera=(30, 150)):
    """Capas grandes en un lado; ejecuta poco en el otro y cancela."""
    inicios = np.sort(rng.choice(np.arange(5, DUR // 1000 - 10),
                                 n, replace=False)) * 1000
    for k, t0 in enumerate(inicios):
        t0 = int(t0)
        b, a = touch(t0)
        if k % 2 == 0:       # capas de compra para vender arriba
            capas = [reg.alta(t0, nombre, "compra", b - d, 4000)
                     for d in range(3)]
            lado, precio = "venta", a
        else:                # capas de venta para comprar abajo
            capas = [reg.alta(t0, nombre, "venta", a + d, 4000)
                     for d in range(3)]
            lado, precio = "compra", b
        t1 = t0 + int(rng.integers(400, 900))
        real = reg.alta(t1, nombre, lado, precio, 300)
        t2 = t1 + int(rng.integers(50, 400))
        reg.ejecuta(t2, real)
        t3 = t2 + int(rng.integers(*espera))
        for oid in capas:
            reg.cancela(t3, oid)
```

Y dos bots escritos sin ninguna intención de engañar. **BOT_A** quiere acumular: mantiene una compra de 3.000 acciones
un tick por debajo del mejor precio y la recoloca cada vez que el precio se mueve («perseguir el precio»); de vez en
cuando vende 200 de lo que ya tenía. Su manejador de ejecuciones tiene una regla que parece prudente: tras cualquier
ejecución, cancelar todo y esperar dos segundos. **BOT_B** quiere comprar 100 acciones al mejor precio y, para no
perder la cola, cancela y reenvía su orden cada 50 ms, cambie o no el precio:

```python
def bot_acumulador(reg, nombre, rng, cancelar_todo=True,
                   persigue=True):
    """Compra 3000 un tick por debajo del mejor precio y vende 200 de
    lo que ya tenía cuando puede. Tras cada ejecución, «por
    seguridad», cancela todo y espera dos segundos."""
    t, grande, venta = 1000, None, None
    while t < DUR - 3000:
        b, a = touch(t)
        lejos = (grande is not None and
                 abs(reg.vivas[grande]["precio"] - (b - 1))
                 > (0 if persigue else 5))
        if grande is None or lejos:
            if grande is not None:
                reg.cancela(t, grande)
            grande = reg.alta(t, nombre, "compra", b - 1, 3000)
        if venta is None and rng.random() < 0.003:
            venta = reg.alta(t, nombre, "venta", a, 200)
        elif venta is not None and rng.random() < 0.03:
            reg.ejecuta(t, venta)
            venta = None
            if cancelar_todo:
                for oid in reg.de(nombre):
                    reg.cancela(t + 5, oid)
                grande = None
                t += 2000
                continue
        t += 50
    for oid in reg.de(nombre):
        reg.cancela(DUR - 1, oid)

def bot_bucle(reg, nombre, rng, cada=50, solo_si_cambia=False):
    """Compra 100 en el mejor precio y reenvía la orden cada 50 ms."""
    t, viva = 0, None
    while t < DUR - 1000:
        b, _ = touch(t)
        if viva is not None and rng.random() < 0.002:
            reg.ejecuta(t, viva)
            viva = None
        if viva is not None and solo_si_cambia and \
                reg.vivas[viva]["precio"] == b:
            t += cada
            continue
        if viva is not None:
            reg.cancela(t, viva)
        viva = reg.alta(t, nombre, "compra", b, 100)
        t += cada
    if viva is not None:
        reg.cancela(DUR - 1, viva)
```

Las ejecuciones son una simplificación: no hay un motor de casación, cada orden se ejecuta con una probabilidad fija.
Para estudiar la huella de las órdenes en el log basta. La sesión completa:

```python
def sesion(**kw):
    reg = Registro()
    g = [np.random.default_rng(s) for s in range(1, 6)]
    creador(reg, "MM1", g[0])
    creador(reg, "MM2", g[1])
    spoofer(reg, "S1", g[2])
    bot_acumulador(reg, "BOT_A", g[3], **kw)
    bot_bucle(reg, "BOT_B", g[4])
    return reg.tabla()

log = sesion()
print(len(log), "eventos")
log.groupby(["quien", "evento"]).size().unstack(fill_value=0)
```
```output
101652 eventos
evento   alta  cancelada  ejecutada
quien
BOT_A    2762       2676         86
BOT_B   35980      35893         87
MM1      6026       5672        354
MM2      5978       5614        364
S1         80         60         20
```

El primer episodio de S1, con las horas en milisegundos desde el inicio de la sesión y los precios en céntimos:

```python
cols = ["ts", "oid", "evento", "lado", "precio", "qty", "bid", "ask"]
log.loc[log["quien"] == "S1", cols].head(8)
```
```output
         ts    oid     evento    lado  precio   qty    bid    ask
3552  63000  12005       alta  compra   10014  4000  10014  10015
3553  63000  12006       alta  compra   10013  4000  10014  10015
3554  63000  12007       alta  compra   10012  4000  10014  10015
3591  63546  12008       alta   venta   10015   300  10014  10015
3616  63924  12008  ejecutada   venta   10015   300  10014  10015
3619  63954  12005  cancelada  compra   10014  4000  10014  10015
3620  63954  12006  cancelada  compra   10013  4000  10014  10015
3621  63954  12007  cancelada  compra   10012  4000  10014  10015
```

Tres compras de 4.000 en el mejor precio y los dos ticks inferiores; 546 ms después, una venta de 300 en el mejor
precio de venta; se ejecuta a los 378 ms y, 30 ms más tarde, las tres compras desaparecen. Es lo que dibuja la figura
de la cabecera.

## Un detector al estilo de la vigilancia de mercado

Un sistema de vigilancia no lee intenciones: busca huellas. Para el layering, la huella es la del punto 5, letra e),
del anexo II: órdenes grandes en un lado, ejecución en el otro, retirada después. El detector de la lección la traduce
en una regla que se evalúa en cada ejecución de cada operador:

- se buscan sus órdenes **del lado contrario** que estaban vivas en el momento de la ejecución, se habían introducido
  como mucho 2 segundos antes y estaban **cerca del mejor precio** (a menos de 3 ticks);
- de ellas, las que se **cancelaron** en el segundo siguiente a la ejecución;
- la ejecución es una alerta si ese volumen cancelado es al menos **5 veces** el ejecutado.

En notación, para una ejecución de tamaño $q$ en el instante $t$ y las órdenes contrarias $C$ que cumplen las
condiciones de tiempo y precio:

$$
\text{alerta} \iff \sum_{o \in C,\ t < t^{\text{cancel}}_o \le t + 1\,\text{s}} q_o \ \ge\ 5\,q
$$

```python
def ordenes(log):
    """Una fila por orden: alta, fin y cómo terminó."""
    alta = log[log["evento"] == "alta"].set_index("oid")
    fin = log[log["evento"] != "alta"].set_index("oid")
    o = alta[["quien", "lado", "precio", "qty", "ts"]]
    o = o.rename(columns={"ts": "t_alta"})
    o["t_fin"] = fin["ts"]
    o["fin"] = fin["evento"]
    return o

def episodios(log, ventana=2000, tras=1000, ticks=3, ratio=5):
    o = ordenes(log)
    grupos = dict(tuple(o.groupby(["quien", "lado"])))
    filas = []
    for f in log[log["evento"] == "ejecutada"].itertuples():
        otro = "compra" if f.lado == "venta" else "venta"
        m = grupos.get((f.quien, otro))
        vol, lat = 0, np.nan
        if m is not None:
            viva = m[(m["t_alta"] <= f.ts) & (m["t_fin"] > f.ts)
                     & (f.ts - m["t_alta"] <= ventana)]
            if otro == "compra":
                viva = viva[f.bid - viva["precio"] < ticks]
            else:
                viva = viva[viva["precio"] - f.ask < ticks]
            c = viva[(viva["fin"] == "cancelada")
                     & (viva["t_fin"] - f.ts <= tras)]
            vol = int(c["qty"].sum())
            if len(c):
                lat = float((c["t_fin"] - f.ts).max())
        filas.append((f.ts, f.quien, f.lado, f.qty, vol, lat))
    ep = pd.DataFrame(filas, columns=["ts", "quien", "lado", "qty",
                                      "vol_cancelado", "ms_cancel"])
    ep["alerta"] = ep["vol_cancelado"] >= ratio * ep["qty"]
    return ep

ep = episodios(log)
res = ep.groupby("quien").agg(ejecuciones=("qty", "size"),
                              alertas=("alerta", "sum"))
res["pct"] = (100 * res["alertas"] / res["ejecuciones"]).round(1)
res["ms_cancel"] = ep[ep["alerta"]].groupby("quien")["ms_cancel"] \
    .median()
res
```
```output
       ejecuciones  alertas    pct  ms_cancel
quien
BOT_A           86       83   96.5        5.0
BOT_B           87        0    0.0        NaN
MM1            354        0    0.0        NaN
MM2            364        0    0.0        NaN
S1              20       20  100.0       87.0
```

*Fig. — Ejecuciones de cada operador marcadas por el detector de spoofing de la lección (porcentaje, umbral de 5 veces el tamaño ejecutado), en la media hora de sesión sintética.* Barras horizontales: S1, el manipulador, 100 % de 20 ejecuciones; BOT_A, el bot con «cancelar todo», 96,5 % de 86 (destacado); los creadores de mercado MM1 y MM2 y el bot BOT_B, 0 %.

El detector marca las 20 ejecuciones de S1, ninguna de los creadores de mercado ni de BOT_B y 83 de las 86 de BOT_A. La
latencia de la cancelación delata además al bot: S1 tarda una mediana de 87 ms en retirar las capas (una persona o un
programa que espera un poco); BOT_A, 5 ms exactos, los de su manejador. Para el sistema de vigilancia, BOT_A es el
caso más claro de los dos.

Los creadores de mercado también cancelan la cotización contraria justo después de cada ejecución: lo que los separa
del layering es el tamaño. Cotizan 300 a cada lado, así que el volumen cancelado nunca llega a cinco veces el
ejecutado. Con un umbral más bajo, la regla los confundiría:

```python
pd.DataFrame({r: episodios(log, ratio=r).groupby("quien")["alerta"]
              .sum() for r in [1, 2, 5, 10]}).rename_axis(
    columns="ratio")
```
```output
ratio   1   2   5   10
quien
BOT_A   83  83  83  83
BOT_B    0   0   0   0
MM1    352   0   0   0
MM2    362   0   0   0
S1      20  20  20  20
```

Con ratio 1, cada renovación de cotización tras una ejecución es una alerta: más de 350 por creador de mercado, ruido
que ningún analista puede revisar. A partir de 2, desaparecen, y entre 2 y 10 el resultado no cambia porque las capas de
S1 son 40 veces su ejecución y la compra de BOT_A, 15 veces su venta. En un mercado real los umbrales se calibran por
instrumento y liquidez (el nuevo punto 5 bis del anexo II lo dice para el plazo de reversión de precios[^4]) y las
alertas pasan a un analista, que mira el contexto: el impacto en el libro, la posición, el historial del operador.

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

Un detector así es el que la FCA británica describe en su caso contra un gestor de un hedge fund multado con 100.000
libras en diciembre de 2020: entre enero y mayo de 2017 introdujo órdenes grandes de CFDs que no pretendía ejecutar
mientras ejecutaba órdenes pequeñas en el lado contrario, y la operativa la identificaron los «surveillance algorithms»
de la propia FCA sobre los datos de libro de los principales centros de renta variable británicos[^5].

</aside>

## El bot que parece un spoofer

BOT_A no pretende engañar a nadie. Pero su log dice lo mismo que el de S1: una orden grande cerca del mejor precio, una
ejecución pequeña en el lado contrario y una cancelación inmediata. El origen está en dos decisiones de diseño
corrientes:

1. **Cancelar todo tras una ejecución.** Es un reflejo habitual («ante cualquier cosa, me quito del mercado») que en la
   lección 4.2.5 tiene sentido como interruptor de emergencia, no como respuesta rutinaria a cada fill.
2. **Perseguir el precio.** Recolocar la compra grande en cuanto el mejor precio se mueve hace que la orden viva poco y
   que se cancele a menudo justo después de una venta, aunque el manejador no cancele nada.

Probar las dos correcciones por separado y juntas:

```python
variantes = {
    "original": {},
    "sin cancelar todo": dict(cancelar_todo=False),
    "sin perseguir": dict(persigue=False),
    "las dos": dict(cancelar_todo=False, persigue=False),
}
filas = {}
for nombre, kw in variantes.items():
    e = episodios(sesion(**kw))
    a = e[e["quien"] == "BOT_A"]
    filas[nombre] = {"ejecuciones": len(a),
                     "alertas": int(a["alerta"].sum())}
pd.DataFrame(filas).T
```
```output
                   ejecuciones  alertas
original                    86       83
sin cancelar todo           89       74
sin perseguir               86        8
las dos                     89        0
```

Quitar solo el «cancelar todo» apenas cambia nada: 74 alertas de 89, porque la persecución sigue cancelando la compra
grande en el segundo siguiente a muchas ventas. Quitar solo la persecución deja 8: las de las ventas que llegan justo
después de que el bot haya repuesto la compra. Con las dos correcciones, ninguna. La compra grande se queda quieta
mientras el precio no se aleje más de cinco ticks y una venta no provoca su cancelación.

Hay una corrección de fondo que el código no resuelve: tener a la vez una compra de 3.000 y una venta de 200 en el mismo
valor y la misma cuenta es tener dos estrategias opuestas que se enseñan al mercado a la vez. Si de verdad lo son,
conviene separarlas (y activar la prevención de autocruce del bróker o del centro, para no caer en las operaciones
ficticias del punto 3); si no, el bot debería decidir un lado cada vez. Es el mismo principio de la lección 6.4.5:
un motor de riesgo previo a la orden puede rechazar, por ejemplo, una venta cuando la cuenta tiene compras vivas cerca
del mejor precio por más de cinco veces su tamaño.

## El bucle de cancelar y reponer

BOT_B no tiene órdenes en el lado contrario y el detector de spoofing no lo ve. Su problema es otro: el volumen de
mensajes. El artículo 48.6 de MiFID II obliga a los mercados regulados a poder limitar la proporción de órdenes no
ejecutadas y operaciones de cada miembro[^6], y el Reglamento Delegado (UE) 2017/566 (RTS 9) fija cómo se calcula al
menos al final de cada sesión, en número y en volumen[^7]:

$$
\text{OTR}_N = \frac{N_O}{N_E} - 1
\qquad
\text{OTR}_V = \frac{V_O}{V_E} - 1
$$

donde $N_O$ y $V_O$ son el número y el volumen de órdenes, y $N_E$ y $V_E$, el número y el volumen de operaciones
ejecutadas. Según su anexo, «cada presentación, modificación o cancelación se contabilizará como una sola orden». El límite
concreto lo fija cada centro:

```python
def otr(log):
    ev = log.groupby(["quien", "evento"]).size().unstack(fill_value=0)
    return ((ev["alta"] + ev["cancelada"]) / ev["ejecutada"] - 1) \
        .round(1)

reg = Registro()
bot_bucle(reg, "BOT_B", np.random.default_rng(5),
          solo_si_cambia=True)
corregido = reg.tabla()["evento"].value_counts()
print(otr(log).to_dict())
print("BOT_B solo si cambia el precio:", corregido.to_dict(),
      otr(reg.tabla()).to_dict())
```
```output
{'BOT_A': 62.2, 'BOT_B': 825.1, 'MM1': 32.0, 'MM2': 30.8, 'S1': 6.0}
BOT_B solo si cambia el precio: {'alta': 2938, 'cancelada': 2851, 'ejecutada': 87} {'BOT_B': 65.5}
```

BOT_B envía 35.980 altas y 35.893 cancelaciones para 87 ejecuciones: una ratio de 825,1, más de veinte veces la de los
creadores de mercado, que cotizan todo el tiempo. Reenviando la orden solo cuando cambia el mejor precio, las
ejecuciones son las mismas y la ratio baja a 65,5. Además del límite del centro, un volumen así se acerca a la
descripción del quote stuffing (punto 4, letra e, del anexo II) y, en un mercado real, perder la prioridad en la cola
con cada reenvío hace que la orden se ejecute peor, no mejor (lección 2.4.8).

## Quién vigila y qué pasa después

```mermaid
flowchart TB
  L[Log de órdenes] --> V[Vigilancia]
  V --> AL[Alerta]
  AL --> AN[Analista]
  AN --> S[STOR a la CNMV]
  AN --> X[Archivo motivado]
```

- **Los centros de negociación** deben prevenir y detectar la manipulación y notificar sin demora a su autoridad las
  órdenes y operaciones, «incluidas las cancelaciones y modificaciones», que pudieran constituirla (art. 16.1 de
  MAR)[^1].
- **Cualquier profesional que prepare o ejecute operaciones**, el bróker de un particular incluido, debe tener sistemas
  para detectar y notificar órdenes y operaciones sospechosas (art. 16.2). El Reglamento Delegado (UE) 2016/957 detalla
  esos sistemas: deben analizar «cada una de las operaciones ejecutadas y las órdenes formuladas, modificadas, canceladas
  o rechazadas» y generar alertas (art. 3), y fija el contenido de la notificación de órdenes y operaciones sospechosas
  (STOR)[^8].
- **Las empresas con negociación algorítmica** deben vigilar toda la actividad de sus sistemas, «incluida la de sus
  clientes», con un sistema automatizado que genere alertas y revisarlo al menos una vez al año (art. 13 del RTS 6), y
  probar sus algoritmos para que no contribuyan a anomalías en las condiciones de negociación (art. 5)[^9].

Para un particular, la consecuencia más probable de un bot como BOT_A no es una multa inmediata, sino una notificación
de su bróker a la autoridad, preguntas sobre su operativa y, a menudo, el cierre de la cuenta. Para evitarlo, lo útil
es tratar el riesgo de abuso de mercado como un requisito más del sistema:

| Control | Para qué |
|---|---|
| Guardar el log completo de órdenes propias (altas, cancelaciones, modificaciones) | Poder ejecutar sobre él el mismo tipo de detector que usa el bróker y explicar cada orden |
| Pasar el detector de esta lección en paper trading antes de ir a real (lección 4.5.3) | Encontrar la huella antes que la vigilancia |
| No cancelar todo por rutina tras cada ejecución; reservar el «cancelar todo» para el interruptor de emergencia | Evitar el patrón de capas retiradas tras una ejecución |
| Tamaños proporcionados entre lados y una sola dirección por valor y cuenta | Que lo que se enseña sea lo que se quiere ejecutar |
| Recolocar solo cuando hace falta, con un ritmo máximo de mensajes | Mantener la ratio del RTS 9 lejos de los límites del centro |
| Prevención de autocruce | No ejecutar contra uno mismo (operaciones ficticias) |
| Documentar la lógica de la estrategia y sus cambios (lección 2.2.4) | Tener las «razones legítimas» por escrito cuando alguien pregunte |

## Ejercicios

1. Un manipulador más prudente espera entre 1,5 y 3 segundos antes de retirar las capas. Simula una sesión con ese
   spoofer y comprueba si el detector lo encuentra con `tras=1000` y con `tras=5000`. ¿Cambian las alertas de los
   creadores de mercado?

**Solución.**

```python
reg = Registro()
creador(reg, "MM1", np.random.default_rng(1))
spoofer(reg, "S2", np.random.default_rng(3), espera=(1500, 3000))
lento = reg.tabla()
for tras in [1000, 5000]:
    e = episodios(lento, tras=tras)
    print(tras, e.groupby("quien")["alerta"].sum().to_dict())
```
```output
1000 {'MM1': 0, 'S2': 0}
5000 {'MM1': 0, 'S2': 20}
```

Con un segundo de margen, el detector no ve a S2: sus capas se cancelan después. Con cinco segundos lo ve entero y los
creadores de mercado siguen sin alertas, porque su problema nunca fue el tiempo sino el tamaño. Por eso los sistemas
reales evalúan varias ventanas a la vez y el punto 10 que añadió el Reglamento Delegado 2026/788 insiste en que los
indicadores pueden calcularse en plazos distintos de una sesión: quien manipula ajusta sus tiempos a los umbrales que
supone.

2. Escribe una función `permitir(reg, quien, lado, precio, qty, t)` que un bot llame antes de cada alta: rechaza la
   orden si la cuenta tiene órdenes vivas del lado contrario a menos de 3 ticks del mejor precio por más de cinco veces
   `qty`. Pruébala con una venta de 200 cuando BOT_A tiene su compra de 3.000 viva.

**Solución.**

```python
def permitir(reg, quien, lado, precio, qty, t, ticks=3, ratio=5):
    b, a = touch(t)
    otro = "compra" if lado == "venta" else "venta"
    vol = 0
    for o in reg.vivas.values():
        if o["quien"] != quien or o["lado"] != otro:
            continue
        cerca = (b - o["precio"] < ticks if otro == "compra"
                 else o["precio"] - a < ticks)
        vol += o["qty"] if cerca else 0
    return vol <= ratio * qty, vol

reg = Registro()
t = 60_000
b, a = touch(t)
reg.alta(t, "BOT_A", "compra", b - 1, 3000)
print(permitir(reg, "BOT_A", "venta", a, 200, t))
print(permitir(reg, "BOT_A", "venta", a, 800, t))
print(permitir(reg, "BOT_A", "compra", b, 200, t))
```
```output
(False, 3000)
(True, 3000)
(True, 0)
```

La venta de 200 se rechaza (3.000 en compras cerca del mejor precio son 15 veces su tamaño); la de 800 pasa, y una
compra no tiene nada en contra. El control no decide si la estrategia es legítima: impide que el bot enseñe a la vez una
intención grande en un lado y ejecute en el otro, que es justo la huella que buscan los detectores. Un rechazo debería
quedar anotado y, si se repite, avisar a una persona, como el motor de riesgo de la lección 6.4.5.

3. Un sistema de momentum compra agresivamente al romper un máximo y vende a los pocos segundos sobre el movimiento.
   Explica qué indicador del anexo II podría disparar y qué datos guardarías para demostrar que la estrategia tiene
   razones legítimas.

**Solución.**

El punto 4, letra f), del anexo II describe el «momentum ignition»: órdenes u operaciones que probablemente inicien o
exacerben una tendencia para cerrar o abrir posición a un precio favorable, ilustrado con una ratio alta de órdenes
canceladas. Si las compras del sistema son una parte importante del volumen de esos segundos y su venta posterior
coincide con el movimiento que han provocado, la huella se parece. Para defender la estrategia hacen falta su
documentación (hipótesis, backtest y resultados fuera de muestra fechados antes de operar), el log completo de órdenes
con la señal que motivó cada una, la participación del sistema en el volumen de cada episodio y un límite de
participación que se respete. Si la ganancia depende de que el propio sistema mueva el precio, no es una señal: es el
problema.

[^1]: Reglamento (UE) n.º 596/2014 del Parlamento Europeo y del Consejo, de 16 de abril de 2014, sobre el abuso de
    mercado, DO L 173 de 12.6.2014, artículos 12, 15, 16 y 30 y anexo I,
    [copia del BOE](https://www.boe.es/doue/2014/173/L00001-00061.pdf). Los artículos 12, 15 y 16 no los ha modificado
    el Reglamento (UE) 2024/2809; el artículo 30 sí, y mantiene los importes citados. Consultados el 23/10/2026.
[^2]: Ley 6/2023, de 17 de marzo, de los Mercados de Valores y de los Servicios de Inversión, artículo 297.1.c,
    [BOE-A-2023-7053](https://www.boe.es/buscar/act.php?id=BOE-A-2023-7053), texto consolidado consultado el 23/10/2026.
[^3]: Reglamento Delegado (UE) 2016/522 de la Comisión, de 17 de diciembre de 2015, anexo II, sección 1, DO L 88 de
    5.4.2016, [copia del BOE](https://www.boe.es/doue/2016/088/L00001-00018.pdf).
[^4]: Reglamento Delegado (UE) 2026/788 de la Comisión, de 8 de abril de 2026, por el que se modifica el Reglamento
    Delegado (UE) 2016/522, DO L de 16.7.2026, anexo I (puntos 1 bis, 2 bis, 4 bis, 5.e, 5 bis y 10),
    [copia del BOE](https://www.boe.es/doue/2026/788/L00001-00006.pdf).
[^5]: FCA, «FCA fines and prohibits hedge fund Chief Investment Officer for market abuse», 15/12/2020,
    [fca.org.uk](https://www.fca.org.uk/news/press-releases/fca-fines-and-prohibits-hedge-fund-chief-investment-officer-market-abuse).
[^6]: Directiva 2014/65/UE (MiFID II), artículo 48, apartado 6,
    [EUR-Lex](https://eur-lex.europa.eu/legal-content/ES/TXT/?uri=CELEX:32014L0065).
[^7]: Reglamento Delegado (UE) 2017/566 de la Comisión (RTS 9), artículos 2 y 3 y anexo,
    [EUR-Lex](https://eur-lex.europa.eu/legal-content/ES/TXT/?uri=CELEX:32017R0566).
[^8]: Reglamento Delegado (UE) 2016/957 de la Comisión, de 9 de marzo de 2016, artículos 3 y 7,
    [EUR-Lex](https://eur-lex.europa.eu/legal-content/ES/TXT/?uri=CELEX:32016R0957).
[^9]: Reglamento Delegado (UE) 2017/589 de la Comisión (RTS 6), artículos 5 y 13,
    [EUR-Lex](https://eur-lex.europa.eu/legal-content/ES/TXT/?uri=CELEX:32017R0589).

## Autoevaluación

1. Un bot vende 200 acciones y, en el mismo milisegundo en que recibe la ejecución, cancela una compra de 3.000 que tenía a un tick del mejor precio. El programador no pretendía engañar a nadie. ¿Qué dice MAR?

   - A. Nada: sin intención no hay manipulación
   - B. Que es manipulación solo si el bot gana dinero
   - C. Que solo se aplica a las personas físicas
   - D. El artículo 12.1.a habla de órdenes que transmitan o puedan transmitir señales falsas o engañosas; la vía de salida que ofrece es demostrar razones legítimas y conformidad con una práctica aceptada. La huella del bot es la de un indicador de manipulación y habrá que explicarla

   *Respuesta D: El artículo 12.1.a habla de órdenes que transmitan o puedan transmitir señales falsas o engañosas; la vía de salida que ofrece es demostrar razones legítimas y conformidad con una práctica aceptada. La huella del bot es la de un indicador de manipulación y habrá que explicarla.* El texto del artículo 12.1.a se fija en el efecto posible de la orden y deja a quien la dio la carga de demostrar razones legítimas. Los indicadores del anexo I no son por sí solos manipulación, pero una alerta de vigilancia obliga a dar explicaciones, y «fue un error de programación» no hace desaparecer la huella.

2. En la lección, ¿por qué los dos creadores de mercado no generan alertas aunque cancelan su cotización contraria justo después de cada ejecución?

   - A. Porque cotizan las dos puntas con el mismo tamaño: el volumen cancelado en el lado contrario (300) no llega a cinco veces el ejecutado (300)
   - B. Porque sus órdenes están lejos del mejor precio
   - C. Porque el detector excluye a los creadores de mercado por nombre
   - D. Porque no cancelan nunca

   *Respuesta A: Porque cotizan las dos puntas con el mismo tamaño: el volumen cancelado en el lado contrario (300) no llega a cinco veces el ejecutado (300).* Con un umbral de ratio 1 los dos creadores de mercado darían más de 350 alertas cada uno; con 2 o más, ninguna. La asimetría de tamaño entre lo que se enseña y lo que se ejecuta es lo que separa el layering de una cotización a dos puntas.

3. ¿Qué cambia el Reglamento Delegado (UE) 2026/788 en los indicadores de manipulación?

   - A. Prohíbe la negociación algorítmica minorista
   - B. Precisa que los indicadores pueden evaluarse en periodos más largos o más cortos que una sesión, en especial con negociación algorítmica, y reescribe la descripción del layering y el spoofing
   - C. Deroga el anexo II del Reglamento Delegado 2016/522
   - D. Fija un umbral numérico de cancelaciones a partir del cual hay manipulación

   *Respuesta B: Precisa que los indicadores pueden evaluarse en periodos más largos o más cortos que una sesión, en especial con negociación algorítmica, y reescribe la descripción del layering y el spoofing.* Publicado en el DO L del 16/07/2026, añade al anexo II el punto 10 (indicadores evaluables en plazos distintos de un día, especialmente en instrumentos poco líquidos y en negociación algorítmica), sustituye el punto 5, letra e), y tiene en cuenta cambios significativos de volumen, no solo de precio. No fija ningún umbral numérico.

4. El bot BOT_B reenvía su orden de compra cada 50 ms aunque el precio no cambie. ¿Qué indicador dispara y cuál no?

   - A. Dispara el detector de spoofing, porque cancela mucho
   - B. No dispara nada, porque solo compra
   - C. No tiene órdenes en el lado contrario, así que no hay patrón de spoofing; pero su proporción de órdenes no ejecutadas y operaciones es de más de 800, cerca de la práctica de «quote stuffing» y de los límites que fijan los centros
   - D. Dispara una alerta de operaciones ficticias

   *Respuesta C: No tiene órdenes en el lado contrario, así que no hay patrón de spoofing; pero su proporción de órdenes no ejecutadas y operaciones es de más de 800, cerca de la práctica de «quote stuffing» y de los límites que fijan los centros.* El RTS 9 cuenta cada presentación, modificación o cancelación como una orden. Con 35.980 altas y 35.893 cancelaciones para 87 ejecuciones, la ratio en número es 825,1; reenviando solo cuando cambia el precio baja a 65,5.

5. ¿Quién tiene la obligación de detectar y notificar órdenes sospechosas de un cliente que opera con un bot a través de un bróker?

   - A. Nadie: solo la CNMV vigila
   - B. Solo el propio cliente
   - C. El bróker, como profesional que ejecuta operaciones (art. 16.2 de MAR), y el centro de negociación (art. 16.1); ambos deben notificar sin demora a la autoridad competente
   - D. El proveedor de datos

   *Respuesta C: El bróker, como profesional que ejecuta operaciones (art. 16.2 de MAR), y el centro de negociación (art. 16.1); ambos deben notificar sin demora a la autoridad competente.* El artículo 16 obliga a los centros y a cualquier profesional que prepare o ejecute operaciones a tener sistemas para detectar órdenes y operaciones sospechosas y a notificarlas (STOR), con el detalle del Reglamento Delegado (UE) 2016/957. Las empresas con negociación algorítmica deben además vigilar automáticamente toda la actividad, la de sus clientes incluida (art. 13 del RTS 6).

## Ideas clave

- MAR prohíbe manipular o intentar manipular el mercado (art. 15). El artículo 12.2.c incluye expresamente las órdenes, cancelaciones y modificaciones enviadas con estrategias algorítmicas y de alta frecuencia que dan señales falsas o desestabilizan el libro.
- El anexo II del Reglamento Delegado 2016/522 describe el layering y el spoofing: órdenes múltiples o grandes en un lado para ejecutar en el otro y retirarlas después. Desde agosto de 2026 (Reglamento Delegado 2026/788), los indicadores pueden evaluarse en plazos menores que una sesión.
- El detector de la lección marca una ejecución cuando el mismo operador cancela, en menos de un segundo, al menos cinco veces su tamaño en órdenes del lado contrario cerca del mejor precio. Marca las 20 ejecuciones del manipulador, ninguna de los creadores de mercado y 83 de las 86 del bot con el manejador «cancelar todo».
- Un bot sin intención de engañar puede dejar la huella de un spoofer: cancelar todo tras cada ejecución y perseguir el precio con órdenes grandes en el otro lado. Quitar el «cancelar todo» no basta (74 alertas de 89); quitar también la persecución deja 0.
- El RTS 9 cuenta cada alta, modificación y cancelación como una orden. Un bucle que reenvía cada 50 ms da una ratio de 825; reenviar solo cuando cambia el precio la baja a 65,5.
- Centros, brókeres y empresas con algoritmos vigilan y notifican (art. 16 de MAR, RTS 6). Las multas máximas que exige MAR por manipulación son de al menos 5 millones de euros para personas físicas. No es asesoramiento jurídico.

## Recursos

### Research

- [Liquidez Order Book - Trading Liquidez Agregada](https://pythonparatrading.com/limit-order-book-y-liquidez-de-mercado/) · Research-0008 · 11 min
- [Máquinas de estado finito para trading en Python](https://pythonparatrading.com/maquinas-estado-finito-trading-python/) · Research-0029 · 49 min

### Documentación

- [Reglamento (UE) n.º 596/2014 sobre el abuso de mercado (MAR), DO L 173 de 12.6.2014 (copia del BOE)](https://www.boe.es/doue/2014/173/L00001-00061.pdf) · boe.es
- [Reglamento Delegado (UE) 2016/522: indicadores de manipulación (anexo II), DO L 88 de 5.4.2016 (copia del BOE)](https://www.boe.es/doue/2016/088/L00001-00018.pdf) · boe.es
- [Reglamento Delegado (UE) 2026/788, que modifica los indicadores de manipulación, DO L de 16.7.2026 (copia del BOE)](https://www.boe.es/doue/2026/788/L00001-00006.pdf) · boe.es
- [Reglamento Delegado (UE) 2017/566 (RTS 9): proporción entre órdenes no ejecutadas y operaciones](https://eur-lex.europa.eu/legal-content/ES/TXT/?uri=CELEX:32017R0566) · eur-lex.europa.eu
- [FCA: multa y prohibición a un gestor por órdenes engañosas en CFDs (15/12/2020)](https://www.fca.org.uk/news/press-releases/fca-fines-and-prohibits-hedge-fund-chief-investment-officer-market-abuse) · fca.org.uk

Lección anterior: [7.4.4 · MiCA después del transitorio: comprobar un proveedor en los registros](https://pythonparatrading.com/curso-python-trading/nivel-7/mica-casp-autorizados-y-registros/)

Lección siguiente: [7.4.6 · Fiscalidad del trading en España](https://pythonparatrading.com/curso-python-trading/nivel-7/fiscalidad-del-trading-en-espana/)
