Lección 7.4.5 · 65 min
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.
- Módulo
- 7.4 · Regulación, ética y fiscalidad
- Duración
- 65 min
Lecciones del nivel 7
7.1 · Ingeniería de software para trading
7.2 · Infraestructura y despliegue
7.3 · MLOps para modelos de trading
- 7.3.1 Registro de experimentos, modelos y datos: MLflow, DVC y una ejecución reproducida de principio a fin
- 7.3.2 Reentrenamiento programado, validación automática y champion/challenger
- 7.3.3 Monitorización del modelo en vivo y desactivación automática
- 7.3.4 Servir modelos: ONNX, inferencia en CPU o GPU y el coste de los LLM en producción
7.4 · Regulación, ética y fiscalidad
- 7.4.1 MiFID II: clientes, idoneidad y trading algorítmico
- 7.4.2 ESMA y CFD: apalancamiento, cierre y saldo negativo
- 7.4.3 PRIIPs, KID y ETFs estadounidenses
- 7.4.4 MiCA después del transitorio: comprobar un proveedor en los registros
- 7.4.5 Abuso de mercado: spoofing, layering y el bot que los imita sin querer
- 7.4.6 Fiscalidad del trading en España
- 7.4.7 Ética: datos personales, inteligencia artificial y la venta de señales
7.5 · Carrera profesional quant
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)1Nota 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. 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.. 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 operaciones1. 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 calcular1. 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ón2Nota 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, texto consolidado consultado el 23/10/2026..
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)3Nota 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.. 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»4Nota 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.. 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 |
Fuente 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:
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:
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:
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:
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)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 20El primer episodio de S1, con las horas en milisegundos desde el inicio de la sesión y los precios en céntimos:
cols = ["ts", "oid", "evento", "lado", "precio", "qty", "bid", "ask"]
log.loc[log["quien"] == "S1", cols].head(8) 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 10015Tres 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 en el instante y las órdenes contrarias que cumplen las condiciones de tiempo y precio:
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 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.0El 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:
pd.DataFrame({r: episodios(log, ratio=r).groupby("quien")["alerta"]
.sum() for r in [1, 2, 5, 10]}).rename_axis(
columns="ratio")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 20Con 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 precios4) y las alertas pasan a un analista, que mira el contexto: el impacto en el libro, la posición, el historial del operador.
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:
- 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.
- 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:
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 ejecuciones alertas
original 86 83
sin cancelar todo 89 74
sin perseguir 86 8
las dos 89 0Quitar 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 miembro6Nota 6 · Directiva 2014/65/UE (MiFID II), artículo 48, apartado 6, EUR-Lex., 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 volumen7Nota 7 · Reglamento Delegado (UE) 2017/566 de la Comisión (RTS 9), artículos 2 y 3 y anexo, EUR-Lex.:
donde y son el número y el volumen de órdenes, y y , 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:
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()){'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
Fuente 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)8Nota 8 · Reglamento Delegado (UE) 2016/957 de la Comisión, de 9 de marzo de 2016, artículos 3 y 7, EUR-Lex..
- 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)9Nota 9 · Reglamento Delegado (UE) 2017/589 de la Comisión (RTS 6), artículos 5 y 13, EUR-Lex..
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
- 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=1000y contras=5000. ¿Cambian las alertas de los creadores de mercado?
Solución
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())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.
- 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 vecesqty. Pruébala con una venta de 200 cuando BOT_A tiene su compra de 3.000 viva.
Solución
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))(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.
- 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.
Footnotes
-
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. 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 ↩3 ↩4
-
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, texto consolidado consultado el 23/10/2026. ↩
-
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. ↩
-
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. ↩ ↩2
-
FCA, «FCA fines and prohibits hedge fund Chief Investment Officer for market abuse», 15/12/2020, fca.org.uk. ↩
-
Directiva 2014/65/UE (MiFID II), artículo 48, apartado 6, EUR-Lex. ↩
-
Reglamento Delegado (UE) 2017/566 de la Comisión (RTS 9), artículos 2 y 3 y anexo, EUR-Lex. ↩
-
Reglamento Delegado (UE) 2016/957 de la Comisión, de 9 de marzo de 2016, artículos 3 y 7, EUR-Lex. ↩
-
Reglamento Delegado (UE) 2017/589 de la Comisión (RTS 6), artículos 5 y 13, EUR-Lex. ↩
Respuesta plegada en cada pregunta
Autoevaluación
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?
- ANada: sin intención no hay manipulación
- BQue es manipulación solo si el bot gana dinero
- CQue solo se aplica a las personas físicas
- DEl 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
Ver respuesta
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.
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?
- APorque 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)
- BPorque sus órdenes están lejos del mejor precio
- CPorque el detector excluye a los creadores de mercado por nombre
- DPorque no cancelan nunca
Ver respuesta
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.
¿Qué cambia el Reglamento Delegado (UE) 2026/788 en los indicadores de manipulación?
- AProhíbe la negociación algorítmica minorista
- BPrecisa 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
- CDeroga el anexo II del Reglamento Delegado 2016/522
- DFija un umbral numérico de cancelaciones a partir del cual hay manipulación
Ver respuesta
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.
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?
- ADispara el detector de spoofing, porque cancela mucho
- BNo dispara nada, porque solo compra
- CNo 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
- DDispara una alerta de operaciones ficticias
Ver respuesta
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.
¿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?
- ANadie: solo la CNMV vigila
- BSolo el propio cliente
- CEl 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
- DEl proveedor de datos
Ver respuesta
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
Documentación
- Reglamento (UE) n.º 596/2014 sobre el abuso de mercado (MAR), DO L 173 de 12.6.2014 (copia del BOE)
- Reglamento Delegado (UE) 2016/522: indicadores de manipulación (anexo II), DO L 88 de 5.4.2016 (copia del BOE)
- Reglamento Delegado (UE) 2026/788, que modifica los indicadores de manipulación, DO L de 16.7.2026 (copia del BOE)
- Reglamento Delegado (UE) 2017/566 (RTS 9): proporción entre órdenes no ejecutadas y operaciones
- FCA: multa y prohibición a un gestor por órdenes engañosas en CFDs (15/12/2020)