---
title: "7.4.1 MiFID II: clientes, idoneidad y trading algorítmico"
description: "Clasificación de clientes, paso a profesional, idoneidad y conveniencia, y los controles del RTS 6 para la negociación algorítmica, con código."
url: "https://pythonparatrading.com/curso-python-trading/nivel-7/mifid-ii-clientes-idoneidad-y-trading-algoritmico/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-7/mifid-ii-clientes-idoneidad-y-trading-algoritmico.png"
---

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

- 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: 55 min
- Requisitos: [4.2.5 · Control del drawdown y kill switch](https://pythonparatrading.com/curso-python-trading/nivel-4/control-del-drawdown-y-kill-switch/), [4.4.1 · Elegir bróker desde España o la UE: regulación, activos, costes y API](https://pythonparatrading.com/curso-python-trading/nivel-4/elegir-broker-desde-espana-y-la-ue/), [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

- Distinguir cliente minorista, profesional y contraparte elegible según la Directiva 2014/65/UE, la Ley 6/2023 y el Real Decreto 813/2023.
- Programar la evaluación de una solicitud de paso a profesional: dos de tres criterios del anexo II y el procedimiento por escrito.
- Separar idoneidad (asesoramiento y gestión de carteras), conveniencia (resto de servicios) y solo ejecución, y escribir un evaluador de conveniencia con su registro.
- Enumerar lo que exige el Reglamento Delegado (UE) 2017/589 (RTS 6) a una empresa con negociación algorítmica y aplicar sus controles prenegociación a un registro de órdenes.

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

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

**No es asesoramiento.** Esta lección resume normas de la UE y de España a 23 de octubre de 2026, con enlaces a los
textos del BOE. Los perfiles, umbrales y cuestionarios del código son sintéticos y sirven para entender la lógica, no
para clasificar a nadie: cada entidad aplica su política escrita, y la vinculante es la del contrato.

</aside>

## 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ía | Quién | Protección |
|---|---|---|
| Minorista | Todo el que no es profesional: casi todos los particulares | Máxima: conveniencia, información, KID, límites de producto |
| Profesional per se | Entidades 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 institucionales | Menor; puede pedir trato de minorista |
| Profesional a petición | Un minorista que lo solicita, cumple dos de tres criterios y sigue el procedimiento | La que pierde se la advierten por escrito |
| Contraparte elegible | ESI, entidades de crédito, aseguradoras, OICVM, fondos de pensiones, otras financieras reguladas, Estados, bancos centrales | Mí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).

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

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

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

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

Lo que se pierde al ser profesional no es simbólico. La empresa puede presumir que el cliente tiene los conocimientos y
la experiencia necesarios para los productos por los que es profesional (artículos 54.3 y 56.1 del Reglamento
Delegado 2017/565); dejan de aplicarse las medidas de intervención para minoristas, como los límites de apalancamiento
y la protección de saldo negativo de los CFD (lección 7.4.2), y la obligación de entregar el KID (lección 7.4.3). Las
publicidades de «hazte profesional para tener más apalancamiento» venden exactamente esa renuncia.

</aside>

## 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ón | Cuándo | Qué se pregunta | Norma |
|---|---|---|---|
| Idoneidad | Asesoramiento y gestión de carteras | Conocimientos y experiencia, situación financiera y capacidad de soportar pérdidas, objetivos y tolerancia al riesgo | Art. 25.2; arts. 54-55 del R. D. 2017/565; art. 204 de la Ley 6/2023 |
| Conveniencia | Resto de servicios (recepción, transmisión y ejecución de órdenes) | Solo conocimientos y experiencia con ese tipo de producto o servicio | Art. 25.3; arts. 55-56 del R. D. 2017/565; art. 205 de la Ley 6/2023 |
| Solo ejecución | Instrumentos no complejos, a iniciativa del cliente, con advertencia | Nada | Art. 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:

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

| Bloque | Artículos | Qué exige |
|---|---|---|
| Gobernanza | 1-4 | Lí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ón | 5-8 | Mé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 implantar | 9-11 | Autoevaluación y validación anuales con los criterios del anexo I; pruebas de resistencia; análisis de cada cambio importante |
| Resiliencia | 12-18 | Funció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).

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

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

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

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

```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()}")
```
```output
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.* Barras de 126 sesiones, de abril a septiembre de 2026, con el pico diario de mensajes por segundo, entre 13 y 40 salvo un día. El máximo, destacado, es de 61 el 31 de julio. Una línea discontinua marca el límite prenegociación de 50 mensajes por segundo, que ese día se supera, y otra la carga de la prueba, 122.

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:

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

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

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

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

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

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

   - A. Sí: opera más de 10 veces por trimestre
   - B. Sí: la cartera supera los 500.000 EUR y con eso basta
   - 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
   - D. No: los particulares nunca pueden ser profesionales

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

   - A. La de conveniencia: si tiene conocimientos y experiencia para entender el producto (art. 25.3)
   - B. La de idoneidad: si el producto se ajusta a sus objetivos y a su capacidad de soportar pérdidas
   - C. Ninguna: es una orden a iniciativa del cliente
   - D. Solo le pide el KID

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

   - A. Solo las órdenes ejecutadas
   - B. Todas las órdenes enviadas al centro de negociación, incluidas inmediatamente en el cálculo
   - C. Solo las órdenes nuevas, no las modificaciones
   - D. Las órdenes del día anterior

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

   - A. Con la media de los últimos seis meses
   - B. Con el volumen de un día normal
   - C. Con el doble del mayor número de mensajes y del mayor volumen de negociación de los seis meses anteriores
   - D. Con la que fije libremente cada empresa

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

   - A. Siempre que sea una entidad financiera, para cualquier servicio
   - B. Cuando lo solicita un particular con más de 500.000 EUR
   - C. Cuando opera con algoritmos
   - 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

   *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

### Research

- [Máquinas de estado finito para trading en Python](https://pythonparatrading.com/maquinas-estado-finito-trading-python/) · Research-0029 · 49 min

### Documentación

- [Directiva 2014/65/UE (MiFID II), en el BOE: artículos 4, 17, 25 y 30 y anexo II](https://www.boe.es/buscar/doc.php?id=DOUE-L-2014-81285) · boe.es
- [Reglamento Delegado (UE) 2017/589 (RTS 6), en el BOE](https://www.boe.es/buscar/doc.php?id=DOUE-L-2017-80584) · boe.es
- [Reglamento Delegado (UE) 2017/565: categorización (art. 45), idoneidad y conveniencia (arts. 54-56), en el BOE](https://www.boe.es/buscar/doc.php?id=DOUE-L-2017-80560) · boe.es
- [Ley 6/2023 de los Mercados de Valores y de los Servicios de Inversión (arts. 192-208), texto consolidado](https://www.boe.es/buscar/act.php?id=BOE-A-2023-7053) · boe.es
- [Real Decreto 813/2023 (arts. 112-114: clientes profesionales), texto consolidado](https://www.boe.es/buscar/act.php?id=BOE-A-2023-22763) · boe.es
- [ESMA (2023): Directrices relativas a determinados aspectos de los requisitos de idoneidad de MiFID II (ESMA35-43-3172)](https://www.esma.europa.eu/sites/default/files/2023-04/ESMA35-43-3172_Guidelines_on_certain_aspects_of_the_MiFID_II_suitability_requirements_ES.pdf) · esma.europa.eu

Lección anterior: [7.3.4 · Servir modelos: ONNX, inferencia en CPU o GPU y el coste de los LLM en producción](https://pythonparatrading.com/curso-python-trading/nivel-7/servir-modelos-onnx-y-coste-de-llms/)

Lección siguiente: [7.4.2 · ESMA y CFD: apalancamiento, cierre y saldo negativo](https://pythonparatrading.com/curso-python-trading/nivel-7/esma-y-cfds-apalancamiento-y-proteccion/)
