Lección 7.4.1 · 55 min

MiFID II: clientes, idoneidad y trading algorítmico

MiFID II no regula al particular que opera su dinero, pero decide lo que su bróker puede venderle y cómo. Esta lección convierte en código las reglas que lo clasifican y lo evalúan, y repasa como cultura lo que el RTS 6 exige a una empresa que opera con algoritmos.

Fig. — Criterios del anexo II de MiFID II que cumple cada perfil sintético de la lección: hacen falta dos de tres
Lecciones del nivel 7
  1. 7.1 · Ingeniería de software para trading

    1. 7.1.1 Estructura de proyecto, configuración y calidad: del notebook al repositorio
    2. 7.1.2 Rendimiento: medir, perfilar y compilar solo lo que hace falta
    3. 7.1.3 Patrones: eventos, colas de mensajes y arquitectura hexagonal
  2. 7.2 · Infraestructura y despliegue

    1. 7.2.1 Docker y docker-compose: imágenes reproducibles con uv
    2. 7.2.2 VPS, nube y latencia; CI/CD con GitHub Actions
    3. 7.2.3 Orquestación y monitorización: grafos de tareas, logs estructurados, métricas, alertas y latidos
    4. 7.2.4 Seguridad y continuidad: 2FA, IP, copias y procedimientos ante fallos
  3. 7.3 · MLOps para modelos de trading

    1. 7.3.1 Registro de experimentos, modelos y datos: MLflow, DVC y una ejecución reproducida de principio a fin
    2. 7.3.2 Reentrenamiento programado, validación automática y champion/challenger
    3. 7.3.3 Monitorización del modelo en vivo y desactivación automática
    4. 7.3.4 Servir modelos: ONNX, inferencia en CPU o GPU y el coste de los LLM en producción
  4. 7.4 · Regulación, ética y fiscalidad

    1. 7.4.1 MiFID II: clientes, idoneidad y trading algorítmico
    2. 7.4.2 ESMA y CFD: apalancamiento, cierre y saldo negativo
    3. 7.4.3 PRIIPs, KID y ETFs estadounidenses
    4. 7.4.4 MiCA después del transitorio: comprobar un proveedor en los registros
    5. 7.4.5 Abuso de mercado: spoofing, layering y el bot que los imita sin querer
    6. 7.4.6 Fiscalidad del trading en España
    7. 7.4.7 Ética: datos personales, inteligencia artificial y la venta de señales
  5. 7.5 · Carrera profesional quant

    1. 7.5.1 Roles del quant: seis oficios alrededor de un mismo sistema
    2. 7.5.2 Dónde trabajar: qué hace cada empresa y qué dicen los registros
    3. 7.5.3 Portfolio público y entrevistas: enseñar criterio, no rentabilidades
    4. 7.5.4 Certificaciones, formación y prop firms: qué certifica cada cosa y qué mide un examen

Una norma para empresas que te afecta como cliente

La Directiva 2014/65/UE (MiFID II) regula a las empresas de servicios de inversión y a los mercados. Quien opera solo con su dinero queda fuera: el artículo 2.1.d) excluye a quien negocia por cuenta propia sin prestar servicios de inversión, salvo que sea creador de mercado, miembro de un mercado regulado o de un SMN, tenga acceso electrónico directo, aplique una técnica de alta frecuencia o ejecute órdenes de clientes contra su cuenta. Un particular con un bot que manda órdenes por la API de su bróker no está en ninguno de esos casos.

Pero ese particular es cliente de una empresa que sí está sujeta, y lo que la norma exige a la empresa decide lo que puede comprar: si le pide un test antes de dejarle operar con derivados, si le aplica los límites de apalancamiento de los CFD (lección 7.4.2), si puede venderle un ETF sin documento de datos fundamentales (lección 7.4.3). Todo depende de dos decisiones que toma la empresa con reglas escritas: en qué categoría está el cliente y qué evaluación le toca.

La segunda mitad de la lección mira al otro lado: lo que exige el Reglamento Delegado (UE) 2017/589, el RTS 6, a una empresa que hace negociación algorítmica. No obliga a un particular, pero es la mejor lista de controles que existe para un sistema que manda órdenes solo, y es lo que encontrará quien trabaje en una mesa (lección 7.5.1).

Tres categorías de cliente

MiFID II define al cliente profesional como el que cumple los criterios del anexo II, y al minorista como «todo cliente que no sea cliente profesional» (artículo 4.1, puntos 10 y 11). La contraparte elegible está en el artículo 30. En España, la Ley 6/2023 de los Mercados de Valores y de los Servicios de Inversión obliga a clasificar a toda la clientela en esas tres clases (artículo 192), y el Real Decreto 813/2023 concreta quién es profesional (artículos 112 a 114).

CategoríaQuiénProtección
MinoristaTodo el que no es profesional: casi todos los particularesMáxima: conveniencia, información, KID, límites de producto
Profesional per seEntidades financieras reguladas; grandes empresas con dos de: balance de 20 M EUR, cifra de negocios de 40 M EUR, fondos propios de 2 M EUR; Estados, bancos centrales, organismos supranacionales; inversores institucionalesMenor; puede pedir trato de minorista
Profesional a peticiónUn minorista que lo solicita, cumple dos de tres criterios y sigue el procedimientoLa que pierde se la advierten por escrito
Contraparte elegibleESI, entidades de crédito, aseguradoras, OICVM, fondos de pensiones, otras financieras reguladas, Estados, bancos centralesMínima; solo en recepción y transmisión de órdenes, ejecución y negociación por cuenta propia

Dos detalles que suelen confundirse. La contraparte elegible no es una cuarta clase de cliente para todo: el artículo 192 de la Ley 6/2023 la limita a la recepción y transmisión de órdenes, la ejecución y la negociación por cuenta propia, y aclara que no cabe en la gestión de carteras ni en el asesoramiento. Y la clasificación se puede cambiar en los dos sentidos: el profesional puede pedir más protección (anexo II, sección I) y la empresa puede ofrecérsela por propia iniciativa (artículo 45.3 del Reglamento Delegado (UE) 2017/565).

síórdenesgestión o asesoríanosínosí, y cumpleCliente nuevo¿Entidad regulada?¿Servicio?Contrap. elegibleProfesional¿2 de 3 tamaños?Minorista¿Pide trato prof.?Prof. a petición
Fuente Mermaid
flowchart TB
  C[Cliente nuevo] --> F{¿Entidad regulada?}
  F -- sí --> S{¿Servicio?}
  S -- órdenes --> CE[Contrap. elegible]
  S -- gestión o asesoría --> P[Profesional]
  F -- no --> G{¿2 de 3 tamaños?}
  G -- sí --> P
  G -- no --> M[Minorista]
  M --> O{¿Pide trato prof.?}
  O -- sí, y cumple --> PP[Prof. a petición]

Pasar a profesional: dos de tres criterios

Un minorista puede renunciar a parte de su protección. La sección II.1 del anexo II, que el artículo 113 del Real Decreto 813/2023 reproduce, pide a la empresa que compruebe al menos dos de estos criterios:

  • Frecuencia. Operaciones de volumen significativo en el mercado del instrumento, o de instrumentos similares, con una frecuencia media de 10 por trimestre durante los cuatro trimestres anteriores.
  • Cartera. Instrumentos financieros y depósitos de efectivo por más de 500.000 EUR.
  • Experiencia profesional. Un cargo de al menos un año en el sector financiero que requiera conocer las operaciones o los servicios previstos.

Y un procedimiento (sección II.2 del anexo; artículo 114 del Real Decreto): el cliente pide por escrito el cambio, general o para un servicio o tipo de producto; la empresa le advierte por escrito de las protecciones y los derechos de indemnización que puede perder; y el cliente declara por escrito, en un documento separado del contrato, que conoce las consecuencias. Si después deja de cumplir los requisitos, el artículo 114.3 del Real Decreto obliga a tratarlo de inmediato como minorista.

La norma no define «volumen significativo». Lo fija la política de cada entidad, y por eso en el código es un parámetro. Seis perfiles sintéticos con sus operaciones de los cuatro últimos trimestres:

IN [01]python
import numpy as np
import pandas as pd

rng = np.random.default_rng(741)
perfiles = pd.DataFrame([
    # ops/trimestre, importe mediano (EUR), cartera, años en el sector
    ("A", 4, 3_000, 120_000, 0),
    ("B", 30, 1_500, 650_000, 0),
    ("C", 14, 9_000, 300_000, 2),
    ("D", 45, 12_000, 2_400_000, 0),
    ("E", 12, 6_000, 480_000, 0),
    ("F", 2, 50_000, 900_000, 6),
], columns=["cliente", "ops_trim", "importe_med", "cartera_eur",
            "anios_sector"]).set_index("cliente")

filas = []
for c in perfiles.itertuples():
    for trim in range(1, 5):
        n = rng.poisson(c.ops_trim)
        importes = rng.lognormal(np.log(c.importe_med), 0.6, n)
        filas += [(c.Index, trim, round(x, 2)) for x in importes]
ops = pd.DataFrame(filas, columns=["cliente", "trimestre", "importe"])
print(len(ops), "operaciones")
ops.groupby("cliente").size().to_dict()
OUT [01]Perfiles y operaciones sintéticos
413 operaciones
{'A': 12, 'B': 113, 'C': 67, 'D': 162, 'E': 54, 'F': 5}

El evaluador separa los criterios, que son hechos medibles, del procedimiento, que son documentos. Una solicitud que cumple los criterios pero no tiene la declaración separada no se acepta:

IN [02]python
UMBRAL_SIGNIFICATIVO = 5_000  # EUR por operación: política (hipótesis)

def criterios(cliente, ops, perfiles, umbral=UMBRAL_SIGNIFICATIVO):
    o = ops[(ops["cliente"] == cliente) & (ops["importe"] >= umbral)]
    por_trim = o.groupby("trimestre").size().reindex(
        range(1, 5), fill_value=0)
    p = perfiles.loc[cliente]
    return {
        "media_trim": por_trim.mean(),
        "frecuencia": bool(por_trim.mean() >= 10),
        "cartera": bool(p["cartera_eur"] > 500_000),
        "sector": bool(p["anios_sector"] >= 1),
    }

def evaluar(cliente, documentos):
    c = criterios(cliente, ops, perfiles)
    n = c["frecuencia"] + c["cartera"] + c["sector"]
    falta = [d for d in ("solicitud_escrita", "advertencia_entregada",
                         "declaracion_separada")
             if not documentos.get(d)]
    if n < 2:
        decision = "rechazada: criterios"
    elif falta:
        decision = "pendiente: " + ", ".join(falta)
    else:
        decision = "aceptada"
    return {**c, "cumple": int(n), "decision": decision}

completos = dict(solicitud_escrita=True, advertencia_entregada=True,
                 declaracion_separada=True)
docs = {k: completos for k in perfiles.index}
docs["D"] = {**completos, "declaracion_separada": False}
tabla = pd.DataFrame({k: evaluar(k, v) for k, v in docs.items()}).T
tabla["media_trim"] = tabla["media_trim"].astype(float).round(2)
tabla
OUT [02]Evaluar una solicitud de paso a profesional
   media_trim frecuencia cartera sector cumple                         decision
A        0.25      False   False  False      0             rechazada: criterios
B        1.50      False    True  False      1             rechazada: criterios
C       14.25       True   False   True      2                         aceptada
D       37.50       True    True  False      2  pendiente: declaracion_separada
E        7.00      False   False  False      0             rechazada: criterios
F        1.25      False    True   True      2                         aceptada

La figura de la cabecera resume la columna cumple. El caso que más enseña es B: unas 30 operaciones por trimestre, que a primera vista superan de sobra la decena, pero de unos 1.500 EUR; por encima del umbral de la entidad solo quedan 1,5 por trimestre. La cartera de 650.000 EUR es su único criterio. E se queda corto en dos (siete operaciones significativas por trimestre y 480.000 EUR de cartera). C, D y F cumplen dos criterios por caminos distintos, y D queda pendiente porque falta la declaración separada: sin ella la renuncia no es válida, por mucho patrimonio que tenga.

Idoneidad, conveniencia y solo ejecución

Clasificado el cliente, el artículo 25 de MiFID II decide qué hay que evaluarle, y depende del servicio, no del producto:

EvaluaciónCuándoQué se preguntaNorma
IdoneidadAsesoramiento y gestión de carterasConocimientos y experiencia, situación financiera y capacidad de soportar pérdidas, objetivos y tolerancia al riesgoArt. 25.2; arts. 54-55 del R. D. 2017/565; art. 204 de la Ley 6/2023
ConvenienciaResto de servicios (recepción, transmisión y ejecución de órdenes)Solo conocimientos y experiencia con ese tipo de producto o servicioArt. 25.3; arts. 55-56 del R. D. 2017/565; art. 205 de la Ley 6/2023
Solo ejecuciónInstrumentos no complejos, a iniciativa del cliente, con advertenciaNadaArt. 25.4; art. 207 de la Ley 6/2023

La idoneidad acaba en una recomendación y en una declaración escrita de por qué se ajusta al cliente (artículo 25.6). ESMA desarrolla cómo hacerla en sus directrices ESMA35-43-3172 (versión española de 2023), que se aplican también cuando el asesoramiento lo da un algoritmo: el artículo 54.1 del Reglamento Delegado dice que la responsabilidad de la evaluación es de la empresa y que no se reduce por usar un sistema automatizado.

Para un trader que usa un bróker de ejecución, lo relevante es la conveniencia. Si el producto no resulta conveniente, la empresa debe advertírselo; si el cliente no da información, debe advertirle de que no puede evaluar. En los dos casos el cliente puede seguir adelante, y la empresa tiene que registrar el resultado, las advertencias y si el cliente siguió (artículo 56.2). La vía de solo ejecución, sin evaluar, se limita a los instrumentos de la lista del artículo 25.4: acciones admitidas en un mercado regulado o equivalente (sin derivados incorporados), bonos sin derivados ni estructuras difíciles de entender, instrumentos del mercado monetario, participaciones de OICVM no estructurados, depósitos estructurados sencillos y otros instrumentos no complejos.

Un evaluador de conveniencia que deja su registro. El criterio de experiencia (cinco operaciones en tres años o una profesión relacionada) es un ejemplo de política, no una cifra de la norma:

IN [03]python
NO_COMPLEJOS = {"accion_cotizada", "etf_ucits", "bono_simple"}
PRODUCTOS = NO_COMPLEJOS | {"cfd", "opcion_cotizada", "futuro"}

def conveniencia(cliente, producto, test, profesional=False,
                 iniciativa_cliente=True):
    if producto not in PRODUCTOS:
        raise ValueError(f"producto desconocido: {producto}")
    if profesional:
        return "conveniente (presunción art. 56.1)"
    if producto in NO_COMPLEJOS and iniciativa_cliente:
        return "solo ejecución (advertencia art. 25.4.c)"
    if test is None:
        return "sin información: advertir (art. 25.3)"
    conoce = producto in test["conoce"]
    experiencia = (test["ops_3a"].get(producto, 0) >= 5
                   or test["profesion_relacionada"])
    if conoce and experiencia:
        return "conveniente"
    return "no conveniente: advertir (art. 25.3)"

tests = {
    "A": dict(conoce={"accion_cotizada"}, ops_3a={},
              profesion_relacionada=False),
    "C": dict(conoce={"accion_cotizada", "futuro", "cfd"},
              ops_3a={"futuro": 12, "cfd": 2},
              profesion_relacionada=True),
    "E": dict(conoce={"cfd"}, ops_3a={"cfd": 3},
              profesion_relacionada=False),
}
registro = []
for cli, prod in [("A", "etf_ucits"), ("A", "cfd"), ("C", "cfd"),
                  ("E", "cfd"), ("E", "opcion_cotizada"),
                  ("B", "futuro"), ("F", "futuro")]:
    res = conveniencia(cli, prod, tests.get(cli),
                       profesional=(cli == "F"))
    registro.append({"cliente": cli, "producto": prod,
                     "resultado": res})
pd.DataFrame(registro)
OUT [03]Evaluador de conveniencia con registro
  cliente         producto                                 resultado
0       A        etf_ucits  solo ejecución (advertencia art. 25.4.c)
1       A              cfd      no conveniente: advertir (art. 25.3)
2       C              cfd                               conveniente
3       E              cfd      no conveniente: advertir (art. 25.3)
4       E  opcion_cotizada      no conveniente: advertir (art. 25.3)
5       B           futuro     sin información: advertir (art. 25.3)
6       F           futuro        conveniente (presunción art. 56.1)

El registro cuenta la historia que pide el artículo 56.2. A no tiene experiencia con CFD y recibe una advertencia; E conoce el producto pero solo ha hecho tres operaciones, menos de las que pide la política; B no ha respondido al test; C cumple por experiencia y por profesión. F aparece como conveniente sin test porque en el ejemplo es profesional para futuros: la presunción del artículo 56.1 es exactamente lo que deja de proteger al cliente que pidió ese trato.

RTS 6: lo que se exige a una empresa con algoritmos

MiFID II define la negociación algorítmica como aquella en la que un algoritmo determina automáticamente los parámetros de las órdenes (si se envían, cuándo, a qué precio, en qué cantidad, cómo se gestionan) con limitada o nula intervención humana; enrutar órdenes o confirmar operaciones no basta (artículo 4.1.39). La empresa que la practica debe tener sistemas resistentes, con umbrales y límites, que impidan órdenes erróneas, pruebas completas y continuidad (artículo 17.1), y notificarlo a su autoridad, que puede pedirle la descripción de sus estrategias, límites y pruebas (artículo 17.2).

El RTS 6 detalla esas obligaciones. Agrupadas:

BloqueArtículosQué exige
Gobernanza1-4Líneas de responsabilidad, separación entre mesa y control, cumplimiento que entienda los algoritmos, personal cualificado; la externalización no traslada la responsabilidad
Pruebas e implantación5-8Métodos de desarrollo y prueba; cada implantación o cambio sustancial, autorizado por una persona designada; registro de cambios (quién, cuándo, quién aprueba, qué); pruebas de conformidad con el centro; entorno de pruebas separado; límites previos a la implantación
Después de implantar9-11Autoevaluación y validación anuales con los criterios del anexo I; pruebas de resistencia; análisis de cada cambio importante
Resiliencia12-18Función cortacircuito; vigilancia automatizada del abuso de mercado; continuidad; controles prenegociación; supervisión en tiempo real con alertas en cinco segundos o menos; controles postnegociación y conciliación; seguridad y pruebas de penetración anuales

La función cortacircuito (artículo 12) y la desactivación tras ejecuciones repetidas (artículo 15.3) ya se programaron en la lección 4.2.5, y la 6.4.5 aplicó límites de ritmo a las propuestas de un agente. Aquí se completa el artículo 15 entero, con dos detalles que suelen olvidarse: los límites cuentan todas las órdenes enviadas, también modificaciones y cancelaciones, en el momento en que salen (artículo 15.2), y debe existir un procedimiento para dejar pasar, de forma excepcional y autorizada, una orden bloqueada (artículo 15.6).

nosíexcesobienfueradentrosíMensaje del algo¿Algoritmo activo?Rechazo y registro¿Mensajes/seg?¿Banda y tamaño?Al centro¿Ejec. repetidas?Desactivar
Fuente Mermaid
flowchart TB
  O[Mensaje del algo] --> D{¿Algoritmo activo?}
  D -- no --> R[Rechazo y registro]
  D -- sí --> M{¿Mensajes/seg?}
  M -- exceso --> R
  M -- bien --> B{¿Banda y tamaño?}
  B -- fuera --> R
  B -- dentro --> E[Al centro]
  E --> X{¿Ejec. repetidas?}
  X -- sí --> P[Desactivar]

El registro de órdenes es sintético: una hora de mensajes normales de un algoritmo alfa sobre dos valores ficticios, más tres incidentes plantados. Un precio un 10 % fuera de mercado, una orden de 15.000 acciones de NOVA (797.250 EUR de valor) y otra de 250.000 acciones de ORBE. Un algoritmo beta entra en un bucle de modificaciones (250 mensajes en un segundo) y otro, gamma, compra 100 acciones cada dos segundos sin parar:

IN [04]python
rng = np.random.default_rng(15)
REF = {"NOVA": 53.15, "ORBE": 12.10}  # precio de referencia
n = 1200
base = pd.DataFrame({
    "ts": np.sort(rng.integers(0, 3_600_000, n)),  # ms en una hora
    "algo": "alfa",
    "instr": rng.choice(list(REF), n),
    "lado": rng.choice(["C", "V"], n),
    "cant": rng.integers(1, 20, n) * 100,
    "tipo": rng.choice(["nueva", "modifica", "cancela"], n,
                       p=[0.6, 0.25, 0.15]),
})
base["precio"] = (base["instr"].map(REF)
                  * (1 + rng.normal(0, 0.002, n))).round(2)

def msg(ts, algo, instr, lado, cant, tipo, precio):
    return dict(ts=ts, algo=algo, instr=instr, lado=lado, cant=cant,
                tipo=tipo, precio=precio)

incidentes = [
    msg(600_000, "alfa", "NOVA", "C", 300, "nueva", 58.47),
    msg(900_000, "alfa", "NOVA", "V", 15_000, "nueva", 53.15),
    msg(1_200_000, "alfa", "ORBE", "V", 250_000, "nueva", 12.08),
]
incidentes += [msg(2_000_000 + 4 * k, "beta", "NOVA", "C", 100,
                   "modifica", 53.10) for k in range(250)]
incidentes += [msg(3_000_000 + 2_000 * k, "gamma", "ORBE", "C", 100,
                   "nueva", 12.11) for k in range(40)]
ordenes = (pd.concat([base, pd.DataFrame(incidentes)])
           .sort_values("ts", kind="stable").reset_index(drop=True))
ordenes.groupby("algo").size().to_dict()
OUT [04]Un registro de órdenes con incidentes
{'alfa': 1203, 'beta': 250, 'gamma': 40}

Los controles, con los límites como datos y no como constantes escondidas en el código (el artículo 8 pide fijarlos antes de implantar el algoritmo). La simplificación del simulador es que toda orden nueva aceptada se ejecuta:

IN [05]python
from collections import Counter, deque
from dataclasses import dataclass

@dataclass(frozen=True)
class Limites:
    banda: float = 0.05          # desviación máxima sobre la referencia
    valor_max: float = 250_000   # EUR por orden
    cant_max: int = 20_000       # acciones por orden
    msg_seg: int = 50            # mensajes por segundo y algoritmo
    repeticiones: int = 20       # ejecuciones iguales en 60 s

class Controles:
    def __init__(self, lim, ref):
        self.lim, self.ref = lim, ref
        self.ventanas = {}                # algo -> ts de sus mensajes
        self.ejecuciones = {}             # (algo, instr, lado) -> ts
        self.desactivados = set()
        self.registro = []

    def evaluar(self, m, excepcion=None):
        w = self.ventanas.setdefault(m.algo, deque())
        w.append(m.ts)                    # art. 15.2: todo cuenta ya
        while w[0] <= m.ts - 1_000:
            w.popleft()
        motivo = self._motivo(m, len(w))
        if motivo and excepcion and motivo != "desactivado":
            motivo = None                 # art. 15.6, autorizado
        if motivo is None and m.tipo == "nueva":
            self._ejecutada(m)
        self.registro.append(dict(ts=m.ts, algo=m.algo,
                                  resultado=motivo or "aceptada",
                                  excepcion=excepcion))
        return motivo

    def _motivo(self, m, en_ventana):
        if m.algo in self.desactivados:
            return "desactivado"
        if en_ventana > self.lim.msg_seg:
            return "mensajes"
        if m.tipo == "cancela":
            return None                   # cancelar siempre se puede
        if abs(m.precio / self.ref[m.instr] - 1) > self.lim.banda:
            return "banda"
        if m.cant > self.lim.cant_max:
            return "volumen"
        if m.precio * m.cant > self.lim.valor_max:
            return "valor"
        return None

    def _ejecutada(self, m):
        e = self.ejecuciones.setdefault((m.algo, m.instr, m.lado),
                                        deque())
        e.append(m.ts)
        while e[0] <= m.ts - 60_000:
            e.popleft()
        if len(e) >= self.lim.repeticiones:
            self.desactivados.add(m.algo)  # art. 15.3

ctl = Controles(Limites(), REF)
for m in ordenes.itertuples():
    ctl.evaluar(m)
reg = pd.DataFrame(ctl.registro)
print(reg.groupby(["algo", "resultado"]).size().to_string())
print("desactivados:", sorted(ctl.desactivados))
OUT [05]Controles prenegociación del artículo 15
algo   resultado
alfa   aceptada       1200
       banda             1
       valor             1
       volumen           1
beta   aceptada         50
       mensajes        200
gamma  aceptada         20
       desactivado      20
desactivados: ['gamma']

Cada control salta donde debe. La banda para el precio un 10 % alto; el volumen máximo, la orden de 250.000 ORBE, y el valor máximo, la de 15.000 NOVA, que no es grande en acciones pero sí en euros. El límite de mensajes corta el bucle de beta en el mensaje 51 de ese segundo, aunque sean modificaciones que nunca ejecutarían nada: si el límite solo mirase las ejecuciones, el bucle habría llegado entero al centro. gamma no supera nunca el ritmo de mensajes, una compra cada dos segundos es tráfico normal, pero a la vigésima compra igual en un minuto queda desactivado y sus 20 órdenes siguientes se rechazan. Ningún mensaje normal de alfa se ha bloqueado: unos límites que rechazan tráfico legítimo acaban desactivados por la propia mesa.

La desactivación no se deshace sola. Un rearme, como una excepción, es un acto de una persona designada que queda registrado:

IN [06]python
bloqueada = ordenes.iloc[reg.index[reg["resultado"] == "valor"][0]]
motivo = ctl.evaluar(bloqueada, excepcion="riesgos: R. Gil, bloque "
                     "acordado con el cliente")
print("reintento con excepción:", motivo or "aceptada")

def rearmar(ctl, algo, persona, causa):
    ctl.desactivados.discard(algo)
    ctl.registro.append(dict(ts=None, algo=algo, resultado="rearme",
                             excepcion=f"{persona}: {causa}"))

rearmar(ctl, "gamma", "L. Mar", "bucle corregido (v1.4.2)")
pd.DataFrame(ctl.registro).tail(2)[["algo", "resultado", "excepcion"]]
OUT [06]Excepción autorizada y rearme manual
reintento con excepción: aceptada
       algo resultado                                        excepcion
1493   alfa  aceptada  riesgos: R. Gil, bloque acordado con el cliente
1494  gamma    rearme                 L. Mar: bucle corregido (v1.4.2)

Pruebas de resistencia y autoevaluación anual

El artículo 10 fija la carga mínima de las pruebas de resistencia: el mayor número de mensajes enviados y recibidos en los seis meses anteriores, multiplicado por dos, y el mayor volumen de negociación del mismo periodo, también por dos, sin tocar el entorno de producción. Con los picos diarios de mensajes por segundo de seis meses (sintéticos):

IN [07]python
rng = np.random.default_rng(10)
sesiones = pd.bdate_range("2026-04-01", periods=126)
pico = pd.Series(rng.lognormal(np.log(22), 0.25, 126).round(),
                 index=sesiones, name="msg_seg")
pico.iloc[87] = 61              # día de resultados y volatilidad
prueba = 2 * pico.max()
print(f"pico de seis meses: {pico.max():.0f} msg/s el "
      f"{pico.idxmax().date()}; prueba: {prueba:.0f} msg/s")
print(f"días por encima del límite de 50 msg/s: "
      f"{(pico > Limites().msg_seg).sum()}")
OUT [07]La carga de la prueba del artículo 10
pico de seis meses: 61 msg/s el 2026-07-31; prueba: 122 msg/s
días por encima del límite de 50 msg/s: 1
Fig. — Pico diario de mensajes por segundo de un algoritmo durante seis meses (sintético): la prueba de resistencia del RTS 6 se hace al doble del máximo

La prueba enseña dos cosas a la vez. El sistema tiene que soportar 122 mensajes por segundo sin degradarse, y el límite prenegociación de 50 por segundo, que en la sección anterior parecía holgado, habría rechazado tráfico legítimo el día del pico. Los límites se calibran con los datos de los seis meses, no una vez al implantar.

La autoevaluación anual (artículo 9) revisa los algoritmos, la gobernanza, la continuidad y el cumplimiento del artículo 17, con los criterios del anexo I: naturaleza del negocio (funciones en el mercado, automatización, estrategias, sensibilidad a la latencia), escala (algoritmos en paralelo, instrumentos, mensajes, posiciones) y complejidad. La elabora la función de gestión de riesgos, la audita auditoría interna y la aprueba la alta dirección. Como lista de comprobación ejecutable:

IN [08]python
REQUISITOS = {
    "art. 5.2 implantación autorizada por persona designada": "ok",
    "art. 5.7 registro de cambios (quién, cuándo, aprueba, qué)": "ok",
    "art. 7 entorno de pruebas separado de producción": "ok",
    "art. 10 prueba al doble del máximo de seis meses": "falla",
    "art. 12 función cortacircuito probada": "ok",
    "art. 14.4 continuidad revisada y probada este año": None,
    "art. 15.1 bandas, valor, volumen y mensajes": "ok",
    "art. 16.5 alertas en 5 s o menos": "ok",
    "art. 18.4 pruebas de penetración anuales": None,
}
for req, estado in REQUISITOS.items():
    if estado != "ok":
        print(f"{'SIN EVIDENCIA' if estado is None else 'FALLA':13}"
              f" {req}")
OUT [08]Lista de comprobación de la autoevaluación
FALLA         art. 10 prueba al doble del máximo de seis meses
SIN EVIDENCIA art. 14.4 continuidad revisada y probada este año
SIN EVIDENCIA art. 18.4 pruebas de penetración anuales

Un particular no tiene que entregar este informe a nadie, pero las preguntas son las mismas para un sistema que opera solo con su dinero: quién aprobó la última versión, dónde están las pruebas, qué pasa si el bot entra en bucle a las tres de la madrugada y quién lo desactiva. Las lecciones 7.2.4 (continuidad) y 7.3.3 (desactivación automática de modelos) desarrollan esas respuestas para la infraestructura propia.

Lo que esto cambia en tu sistema

  • Tu categoría decide tu producto. Antes de diseñar una estrategia con CFD, ETF de EE. UU. u opciones, comprueba qué te dejará hacer tu bróker como minorista; el paso a profesional tiene requisitos y un precio en protección.
  • El test de conveniencia no es un trámite. Responder lo que «desbloquea» un producto convierte en tuya una advertencia que la empresa ya te ha dado y ha registrado.
  • Copia el artículo 15 aunque nadie te obligue. Banda, valor, volumen, mensajes y ejecuciones repetidas cuestan pocas líneas y paran los fallos que más dinero cuestan.
  • Los límites se calibran con datos. El doble del máximo de seis meses es una buena regla para tu propia infraestructura.

Ejercicios

  1. Cambia el umbral de volumen significativo a 1.000 EUR y vuelve a evaluar las solicitudes. ¿Qué clientes cambian de decisión? ¿Qué te dice eso sobre la importancia de la política escrita de la entidad?
Solución
IN [09]python
def evaluar_con(umbral):
    out = {}
    for k in perfiles.index:
        c = criterios(k, ops, perfiles, umbral=umbral)
        out[k] = int(c["frecuencia"] + c["cartera"] + c["sector"])
    return out

print("5.000 EUR:", evaluar_con(5_000))
print("1.000 EUR:", evaluar_con(1_000))
OUT [09]Otro umbral de volumen significativo
5.000 EUR: {'A': 0, 'B': 1, 'C': 2, 'D': 2, 'E': 0, 'F': 2}
1.000 EUR: {'A': 0, 'B': 2, 'C': 2, 'D': 2, 'E': 1, 'F': 2}

Con un umbral de 1.000 EUR, la mayoría de las operaciones de B cuentan y pasa a cumplir dos criterios; E gana el de frecuencia, pero se queda en uno. Con la misma actividad, una entidad aceptaría a B como profesional y otra no. Por eso el anexo II obliga a tener políticas escritas de clasificación y por eso conviene leer la de tu bróker antes de pedir el cambio.

  1. Añade al motor de controles un límite de posición por algoritmo e instrumento (por ejemplo, 5.000 acciones netas) que use las órdenes nuevas aceptadas. ¿Qué artículo del RTS 6 lo pide y para qué instrumentos lo exige expresamente?
Solución
IN [10]python
class ControlesConPosicion(Controles):
    def __init__(self, lim, ref, pos_max=5_000):
        super().__init__(lim, ref)
        self.pos_max, self.pos = pos_max, Counter()

    def _motivo(self, m, en_ventana):
        motivo = super()._motivo(m, en_ventana)
        if motivo or m.tipo != "nueva":
            return motivo
        signo = 1 if m.lado == "C" else -1
        if abs(self.pos[(m.algo, m.instr)] + signo * m.cant) \
                > self.pos_max:
            return "posición"
        return None

    def _ejecutada(self, m):
        signo = 1 if m.lado == "C" else -1
        self.pos[(m.algo, m.instr)] += signo * m.cant
        super()._ejecutada(m)

ctl2 = ControlesConPosicion(Limites(), REF)
for m in ordenes.itertuples():
    ctl2.evaluar(m)
r2 = pd.DataFrame(ctl2.registro)
r2.groupby(["algo", "resultado"]).size().to_dict()
OUT [10]Límite de posición neta
{('alfa', 'aceptada'): 1132, ('alfa', 'banda'): 1, ('alfa', 'posición'): 68, ('alfa', 'valor'): 1, ('alfa', 'volumen'): 1, ('beta', 'aceptada'): 50, ('beta', 'mensajes'): 200, ('gamma', 'aceptada'): 20, ('gamma', 'desactivado'): 20}

El límite de posición recorta las órdenes de alfa que acumularían más de 5.000 acciones netas: con un algoritmo que compra y vende al azar, la posición deriva y el límite acaba frenando parte del tráfico normal, así que en la práctica se fija con el historial de posiciones. El artículo 15.4 pide límites de riesgo de crédito y de mercado según el capital y la estrategia, y el 17.4 exige, para los derivados, controles sobre las posiciones largas, cortas y globales máximas.

  1. Un amigo te enseña un anuncio: «Hazte cliente profesional y opera con apalancamiento 1:500». Explica, con los artículos de esta lección, qué tendría que acreditar, qué documentos firmaría y qué protecciones perdería.
Solución

Tendría que cumplir dos de los tres criterios del anexo II (artículo 113 del Real Decreto 813/2023): diez operaciones de volumen significativo por trimestre de media en el último año, una cartera de más de 500.000 EUR o un año en un cargo del sector financiero relacionado. Firmaría una solicitud por escrito, recibiría una advertencia escrita de las protecciones que pierde y firmaría una declaración en un documento separado del contrato (artículo 114). Perdería, entre otras cosas, la evaluación de conveniencia efectiva (la entidad puede presumir sus conocimientos), los límites de apalancamiento, el cierre al 50 % del margen y la protección de saldo negativo de los CFD, que solo se aplican a minoristas (lección 7.4.2). La entidad, además, debe comprobar con medidas razonables que cumple los requisitos antes de aceptar: un anuncio que lo ofrece sin más es una señal de alarma.

Respuesta plegada en cada pregunta

Autoevaluación

  1. Un cliente hace unas 30 operaciones por trimestre de 1.500 EUR de media y tiene una cartera de 650.000 EUR. Nunca ha trabajado en el sector financiero. ¿Puede tratarle su bróker como profesional si lo pide?

    • ASí: opera más de 10 veces por trimestre
    • BSí: la cartera supera los 500.000 EUR y con eso basta
    • CNo: solo cumple un criterio (la cartera); las operaciones cuentan si son de volumen significativo, y las suyas no lo son según la política de la entidad
    • DNo: los particulares nunca pueden ser profesionales
    Ver respuesta

    Respuesta C. No: solo cumple un criterio (la cartera); las operaciones cuentan si son de volumen significativo, y las suyas no lo son según la política de la entidad

    El anexo II exige al menos dos de tres criterios. La frecuencia se mide en operaciones «de volumen significativo», no en operaciones a secas: en la lección, el cliente B solo tiene 1,5 por trimestre por encima del umbral de la entidad.

  2. ¿Qué evaluación hace un bróker que solo recibe y ejecuta órdenes cuando un minorista quiere comprar un CFD?

    • ALa de conveniencia: si tiene conocimientos y experiencia para entender el producto (art. 25.3)
    • BLa de idoneidad: si el producto se ajusta a sus objetivos y a su capacidad de soportar pérdidas
    • CNinguna: es una orden a iniciativa del cliente
    • DSolo le pide el KID
    Ver respuesta

    Respuesta A. La de conveniencia: si tiene conocimientos y experiencia para entender el producto (art. 25.3)

    La idoneidad (art. 25.2) es para el asesoramiento y la gestión de carteras. En los demás servicios se evalúa la conveniencia (art. 25.3). La vía de solo ejecución (art. 25.4) solo cabe con instrumentos no complejos, y un CFD no lo es.

  3. Según el artículo 15 del RTS 6, ¿qué cuenta para los límites prenegociación?

    • ASolo las órdenes ejecutadas
    • BTodas las órdenes enviadas al centro de negociación, incluidas inmediatamente en el cálculo
    • CSolo las órdenes nuevas, no las modificaciones
    • DLas órdenes del día anterior
    Ver respuesta

    Respuesta B. Todas las órdenes enviadas al centro de negociación, incluidas inmediatamente en el cálculo

    El artículo 15.2 obliga a incluir inmediatamente todas las órdenes enviadas. Un control que solo mirase las ejecuciones no frenaría un bucle de modificaciones, que es justo lo que corta el límite de mensajes en la lección.

  4. ¿Con qué carga deben hacerse las pruebas de resistencia del artículo 10 del RTS 6?

    • ACon la media de los últimos seis meses
    • BCon el volumen de un día normal
    • CCon el doble del mayor número de mensajes y del mayor volumen de negociación de los seis meses anteriores
    • DCon la que fije libremente cada empresa
    Ver respuesta

    Respuesta C. Con el doble del mayor número de mensajes y del mayor volumen de negociación de los seis meses anteriores

    El artículo 10 fija las dos pruebas mínimas: mensajes y volumen de negociación, cada uno al doble del máximo de los seis meses anteriores, y sin afectar al entorno de producción.

  5. ¿Cuándo es una entidad «contraparte elegible» según la Ley 6/2023?

    • ASiempre que sea una entidad financiera, para cualquier servicio
    • BCuando lo solicita un particular con más de 500.000 EUR
    • CCuando opera con algoritmos
    • DSolo en recepción y transmisión de órdenes, ejecución y negociación por cuenta propia; no en gestión de carteras ni asesoramiento
    Ver respuesta

    Respuesta D. Solo en recepción y transmisión de órdenes, ejecución y negociación por cuenta propia; no en gestión de carteras ni asesoramiento

    El artículo 192 de la Ley 6/2023 limita la categoría a esos servicios y sus auxiliares. Para la gestión de carteras o el asesoramiento, la misma entidad es cliente profesional.

Ideas clave

  • Toda empresa de servicios de inversión clasifica a sus clientes en minoristas, profesionales o contrapartes elegibles; un particular es minorista salvo que pida y obtenga el trato de profesional.
  • El paso a profesional exige dos de tres criterios (10 operaciones de volumen significativo por trimestre de media en los cuatro anteriores, cartera de más de 500.000 EUR, un año en un cargo del sector financiero) y un procedimiento por escrito con declaración separada.
  • Idoneidad para asesoramiento y gestión de carteras; conveniencia para el resto; solo ejecución únicamente con instrumentos no complejos, a iniciativa del cliente y con advertencia.
  • Ser profesional quita protecciones: la entidad puede presumir conocimientos y experiencia, y dejan de aplicarse las medidas pensadas para minoristas (límites de los CFD, KID).
  • El RTS 6 obliga a las empresas con negociación algorítmica a gobernanza, pruebas, autoevaluación anual, función cortacircuito, controles prenegociación sobre todas las órdenes y pruebas de resistencia al doble de su máximo de seis meses.

Recursos