Saltar al contenido
ALX Quant
← M0.3 · Cómo funciona un mercado

Lección 224 min60 XPGratis

Los tipos de orden y qué hace cada uno en el libro

Al terminar sabrás

  • Distinguir orden de mercado, límite, stop y stop-límite por cómo interactúan con el libro
  • Saber qué orden da certeza de ejecución y cuál da certeza de precio, y por qué no puedes tener ambas
  • Elegir el tipo de orden y la validez (TIF) adecuados para entrar, salir y proteger

Antes de esta conviene haber hecho Quién está al otro lado de tu orden.


Hay dos cosas que puedes pedirle a una orden: que se ejecute seguro, o que se ejecute a un precio concreto. No puedes pedir las dos. Toda la zoología de tipos de orden (mercado, límite, stop, stop-límite, OCO, IOC...) son formas distintas de repartir ese compromiso, y elegir mal es una de las fugas de dinero más silenciosas que hay.

El problema

La mayoría opera con dos botones, "comprar" y "vender", que suelen ser órdenes de mercado. Es la forma más cara de operar. Y luego pone un stop "de protección" sin saber que un stop es una orden de mercado en diferido, que se ejecuta exactamente cuando la liquidez es peor. Esta lección abre la caja y te enseña qué hace cada orden dentro del libro, con un simulador que puedes ejecutar.

Tres capas

Intuición

Vuelve al mercadillo de la bici de M0.1.

  • Orden de mercado: "Quiero una bici ya, al precio que sea." Te la llevas seguro, al precio del vendedor más barato que haya en ese instante, y si quieres varias, cada una un poco más cara. Certeza de ejecución, cero control del precio.
  • Orden límite: "Compro una bici si alguien me la vende por 99,50 o menos." Escribes tu papel y lo dejas en la pila. Si algún vendedor baja a 99,50, te la llevas a ese precio. Si no baja, te vas sin bici. Certeza de precio, cero certeza de ejecución. Y hay más papeles antes que el tuyo en la pila: los que llegaron antes cobran antes.
  • Orden stop: "Si la bici llega a bajar a 99,60, entonces vende la mía al precio que sea." Es un disparador que, al activarse, se convierte en orden de mercado. Sirve para limitar pérdidas, pero se ejecuta justo cuando el precio corre y hay poca gente, así que casi siempre sales peor que el nivel que pusiste.
  • Stop-límite: "Si baja a 99,60, vende, pero no por menos de 99,40." Te protege de salir a cualquier precio; a cambio, si el mercado salta por debajo de 99,40, te quedas dentro con la pérdida corriendo.

Ejemplo numérico

El libro de M0.1 y cuatro órdenes, una detrás de otra. Los números salen del simulador de abajo.

Libro inicial: bids 4 @ 99,75, 10 @ 99,50, 25 @ 99,25; asks 3 @ 100,00, 8 @ 100,25, 20 @ 100,50.

  1. Pones una límite de compra de 5 @ 99,50. Se coloca en el libro, en el nivel 99,50, detrás de los 10 que ya estaban. El nivel muestra 15. No se ejecuta nada.
  2. Llega una orden de mercado de compra de 10. Cruza el spread: 3 @ 100,00 y 7 @ 100,25. Precio medio 100,175. Último precio: 100,25.
  3. Alguien vende 6 a mercado. Se lleva los 4 de 99,75 y 2 de 99,50, pero los 2 de 99,50 son de quien estaba delante de ti, no tuyos. Último precio: 99,50.
  4. Tenías un stop de venta en 99,60 para proteger un largo. El último precio (99,50) está por debajo de 99,60, así que se dispara y se convierte en orden de mercado: vende 2 @ 99,50. Querías salir a 99,60; sales a 99,50. Medio punto de slippage por diseño, no por mala suerte.

Y tu límite de compra de 99,50 sigue sin ejecutarse: el nivel ha bajado de 15 a 11 contratos, pero los 4 que se fueron eran de otros. Mismo precio, misma orden, posición en la cola distinta.

Formal

En un libro con prioridad precio-tiempo (el mecanismo de CME para ES y la mayoría de sus contratos; algunos productos de tipos usan pro-rata), una orden pasiva se ordena primero por precio (mejor precio primero) y, a igual precio, por instante de llegada (FIFO). Una orden agresora se ejecuta contra la cola del mejor precio del lado contrario, agotándola nivel a nivel.

TipoQué hace al llegarCerteza de ejecuciónCerteza de precioRiesgo principal
MercadoCruza y consume niveles hasta completarseTotal (si hay liquidez)NingunaSlippage por tamaño y por libro vacío
LímiteSi cruza el spread, se ejecuta como agresora hasta su precio; el resto se coloca pasivo al final de la cola de su nivelNingunaTotal (precio o mejor)No ejecutarse; selección adversa (te ejecutan justo cuando el precio va a seguir en tu contra)
StopInactiva hasta que el último precio (o el bid/ask, según configuración) toca el disparador; entonces se envía como mercadoAlta una vez disparadaNingunaSe dispara en el peor momento de liquidez; gaps
Stop-límiteAl dispararse se envía como límite al precio indicadoParcialParcialQuedarte dentro si el precio salta el límite
Market-if-touched (MIT)Como un stop pero en sentido contrario (compra si baja a un nivel)AltaNingunaIgual que el stop

Validez (Time in Force): Day (muere al cierre de sesión), GTC (hasta cancelar; CME purga GTC en cada vencimiento y algunos brokers no las soportan), IOC (ejecuta lo que pueda inmediatamente y cancela el resto), FOK (todo o nada inmediato), GTD (hasta fecha). Una límite IOC es la forma de "tomar liquidez pero no más allá de este precio" sin dejar nada en el libro.

Órdenes compuestas: OCO (una cancela la otra): típicamente un take-profit límite y un stop de protección, de modo que al ejecutarse uno se cancela el otro. Bracket: entrada + OCO de salida en una sola instrucción. Estas las gestiona la plataforma o el broker, no la bolsa; si tu plataforma se cae, tu bracket puede desaparecer. Los stops nativos de CME existen (stop y stop-limit de Globex) pero la mayoría de plataformas minoristas los simulan en sus servidores.

Dónde vive cada orden. Las límites pasivas están en el libro de CME y son visibles en la profundidad (agregadas por nivel, sin identificar). Los stops de plataforma no están en el libro: están en el servidor de tu broker. Nadie en el mercado ve tu stop; sí ve el volumen que se ejecuta cuando muchos stops saltan a la vez.

Ejemplo trabajado con código

# pandas 3.0 — Un libro de órdenes con prioridad precio-tiempo y los cuatro tipos de orden básicos
from dataclasses import dataclass, field
from collections import deque
 
@dataclass
class Orden:
    id: str; lado: str; cantidad: int; precio: float | None = None; tipo: str = "limite"; stop: float | None = None
 
@dataclass
class Libro:
    tick: float = 0.25
    bids: dict = field(default_factory=dict)   # precio -> deque de (id, cantidad)   (FIFO: el primero en llegar, primero en ejecutar)
    asks: dict = field(default_factory=dict)
    ultimo: float | None = None
    fills: list = field(default_factory=list)
 
    def mejor_bid(self): return max(self.bids) if self.bids else None
    def mejor_ask(self): return min(self.asks) if self.asks else None
 
    def _cruzar(self, o: Orden, limite: float | None):
        """Ejecuta una orden agresora contra el lado contrario, nivel a nivel, respetando el límite si lo hay."""
        contra = self.asks if o.lado == "compra" else self.bids
        while o.cantidad > 0 and contra:
            p = min(contra) if o.lado == "compra" else max(contra)
            if limite is not None and ((o.lado == "compra" and p > limite) or (o.lado == "venta" and p < limite)):
                break
            cola = contra[p]
            while o.cantidad > 0 and cola:
                oid, q = cola[0]
                tomo = min(q, o.cantidad)
                self.fills.append((o.id, oid, p, tomo)); self.ultimo = p
                o.cantidad -= tomo
                if tomo == q: cola.popleft()
                else: cola[0] = (oid, q - tomo)
            if not cola: del contra[p]
 
    def enviar(self, o: Orden):
        if o.tipo == "mercado":
            self._cruzar(o, None)
        elif o.tipo == "limite":
            self._cruzar(o, o.precio)                                 # si cruza el spread, se ejecuta como agresora
            if o.cantidad > 0:                                        # el resto se queda en el libro, al final de la cola
                (self.bids if o.lado == "compra" else self.asks).setdefault(o.precio, deque()).append((o.id, o.cantidad))
        elif o.tipo == "stop":                                        # se activa cuando el último precio toca el stop
            pass                                                      # (gestionado por el broker/plataforma; ver comprobar_stops)
        return o
 
    def comprobar_stops(self, stops: list[Orden]):
        """Un stop de venta se dispara si el último precio <= stop y se convierte en orden de MERCADO."""
        disparados = []
        for s in stops:
            if self.ultimo is not None and ((s.lado == "venta" and self.ultimo <= s.stop) or (s.lado == "compra" and self.ultimo >= s.stop)):
                disparados.append(s); self.enviar(Orden(s.id, s.lado, s.cantidad, tipo="mercado"))
        return disparados
 
    def mostrar(self, niveles=3):
        b = sorted(self.bids, reverse=True)[:niveles]; a = sorted(self.asks)[:niveles]
        print(f"{'BID':>18s} | {'ASK':<18s}")
        for i in range(niveles):
            lb = f"{sum(q for _, q in self.bids[b[i]]):3d} @ {b[i]:8.2f}" if i < len(b) else ""
            la = f"{a[i]:8.2f} @ {sum(q for _, q in self.asks[a[i]]):3d}" if i < len(a) else ""
            print(f"{lb:>18s} | {la:<18s}")
 
if __name__ == "__main__":
    L = Libro()
    # Libro inicial (el de M0.1.1): órdenes límite que aportan liquidez, con su orden de llegada
    for oid, lado, q, p in [("b1","compra",4,99.75),("b2","compra",10,99.50),("b3","compra",25,99.25),
                            ("a1","venta",3,100.00),("a2","venta",8,100.25),("a3","venta",20,100.50)]:
        L.enviar(Orden(oid, lado, q, p))
    print("Libro inicial:"); L.mostrar()
 
    print("\n1) Tú pones una LÍMITE de compra de 5 @ 99.50 -> se coloca detrás de b2 (10 contratos por delante):")
    L.enviar(Orden("tu_limite", "compra", 5, 99.50)); L.mostrar()
 
    print("\n2) Llega una MERCADO de compra de 10 -> cruza el spread y consume niveles:")
    L.enviar(Orden("m1", "compra", 10, tipo="mercado")); L.mostrar()
    print("   fills:", [(f[2], f[3]) for f in L.fills if f[0] == "m1"], "| último:", L.ultimo)
 
    print("\n3) Alguien vende 6 a MERCADO -> se lleva los 4 de 99.75 y 2 de 99.50 (los de b2, que estaba antes que tú):")
    L.enviar(Orden("m2", "venta", 6, tipo="mercado")); L.mostrar()
    print("   fills:", [(f[2], f[3]) for f in L.fills if f[0] == "m2"], "| último:", L.ultimo)
 
    print("\n4) Tenías un STOP de venta en 99.60 (protección de un largo). Último = 99.50 -> se dispara como MERCADO:")
    disparados = L.comprobar_stops([Orden("tu_stop", "venta", 2, tipo="stop", stop=99.60)])
    print("   fills del stop:", [(f[2], f[3]) for f in L.fills if f[0] == "tu_stop"], "-> querías salir a 99.60, sales a", L.ultimo)
    L.mostrar()
    print("\n¿Se ejecutó tu límite de compra @ 99.50? ->", "sí" if any(f[1]=="tu_limite" for f in L.fills) else "no, sigue en cola detrás de b2")
Libro inicial:
               BID | ASK               
      4 @    99.75 |   100.00 @   3    
     10 @    99.50 |   100.25 @   8    
     25 @    99.25 |   100.50 @  20    
 
1) Tú pones una LÍMITE de compra de 5 @ 99.50 -> se coloca detrás de b2 (10 contratos por delante):
               BID | ASK               
      4 @    99.75 |   100.00 @   3    
     15 @    99.50 |   100.25 @   8    
     25 @    99.25 |   100.50 @  20    
 
2) Llega una MERCADO de compra de 10 -> cruza el spread y consume niveles:
               BID | ASK               
      4 @    99.75 |   100.25 @   1    
     15 @    99.50 |   100.50 @  20    
     25 @    99.25 |                   
   fills: [(100.0, 3), (100.25, 7)] | último: 100.25
 
3) Alguien vende 6 a MERCADO -> se lleva los 4 de 99.75 y 2 de 99.50 (los de b2, que estaba antes que tú):
               BID | ASK               
     13 @    99.50 |   100.25 @   1    
     25 @    99.25 |   100.50 @  20    
                   |                   
   fills: [(99.75, 4), (99.5, 2)] | último: 99.5
 
4) Tenías un STOP de venta en 99.60 (protección de un largo). Último = 99.50 -> se dispara como MERCADO:
   fills del stop: [(99.5, 2)] -> querías salir a 99.60, sales a 99.5
               BID | ASK               
     11 @    99.50 |   100.25 @   1    
     25 @    99.25 |   100.50 @  20    
                   |                   
 
¿Se ejecutó tu límite de compra @ 99.50? -> no, sigue en cola detrás de b2

Fíjate en el paso 4: el stop no falló. Hizo exactamente lo que es: una orden de mercado que se envía tarde. Y en tu límite de compra: sigue ahí, ejecutándose los de delante, sin que tú puedas hacer nada más que esperar o mover el precio (y perder la posición en la cola).

Figura

Cuatro paneles pequeños con el mismo libro de órdenes (bids a la izquierda, asks a la derecha). Panel 1 'Mercado': una flecha atraviesa el spread y colorea dos niveles del ask en coral. Panel 2 'Límite': un rectángulo nuevo en teal aparece al final de la cola del nivel 99,50, detrás de un bloque gris etiquetado 'los que llegaron antes'. Panel 3 'Stop': un disparador marcado en 99,60 con una línea punteada; cuando el último precio lo cruza, se convierte en la flecha del panel 1. Panel 4 'Stop-límite': igual que el 3 pero la flecha se detiene en una línea 'límite 99,40' y deja un resto sin ejecutar.
Solo las límites pasivas están dentro del libro. Los stops viven en tu broker y, cuando se activan, se comportan como el panel 1 en el peor momento posible.

(Figura estática en SVG.)

El error típico

Usar órdenes de mercado para entrar y stops para salir, en una estrategia de muchas operaciones. Cada entrada paga el spread; cada salida por stop paga el spread más el slippage del momento en que el precio corre. Con un edge de medio tick por operación, esa combinación garantiza perder. La alternativa no es "usar siempre límites", que tampoco funciona porque te ejecutan solo cuando el precio va contra ti y te dejan fuera cuando va a favor. La alternativa es saberlo, medirlo (siguiente lecciones) y diseñar la estrategia con esos costes dentro desde el primer día, eligiendo el tipo de orden según lo que la estrategia necesite en cada momento: entrar seguro cuando la señal es rara y valiosa; ahorrar spread con límites cuando la señal es frecuente y el precio no corre.

Quiz

5 preguntas · se aprueba con 60 %

  1. 1.¿Qué orden te da certeza de ejecución sin certeza de precio?

  2. 2.Un stop de venta en 99,60 se dispara y ejecuta a 99,50. ¿Qué ha pasado?

  3. 3.Pones una límite de compra a 99,50 cuando hay 10 contratos en ese nivel. Se ejecutan 6 contratos en 99,50. ¿Cuántos tuyos?

  4. 4.Una límite de compra a 100,30 cuando el mejor ask es 100,00:

  5. 5.Tu plataforma se cae con un bracket abierto (entrada ejecutada, stop y take-profit pendientes). ¿Qué pasa con el stop?

0 de 5 respondidas

Ejercicio

Con libro.py, añade la orden stop-límite: al dispararse, se envía como límite al precio limite indicado, en vez de a mercado. Repite el escenario de la lección con un stop-límite de venta 99,60 / límite 99,45 y otro con límite 99,55, y anota qué se ejecuta y qué se queda en el libro en cada caso. Después implementa IOC: una límite que ejecuta lo que puede y cancela el resto en vez de dejarlo en el libro.

Resultado verificable: con límite 99,45, el stop-límite vende 2 @ 99,50 (el mejor bid está dentro del límite); con límite 99,55, no se ejecuta nada porque el mejor bid (99,50) es peor que el límite, y la orden queda como ask a 99,55, con la posición larga aún abierta. Con IOC, la límite de compra de 5 @ 99,50 del paso 1 se cancela entera porque no cruza nada.

Qué te llevas

  1. Certeza de ejecución o certeza de precio: elige, porque no hay orden que dé las dos.
  2. Un stop es una orden de mercado en diferido que se envía cuando la liquidez es peor. El slippage no es un fallo, es su diseño.
  3. Las límites pasivas hacen cola por precio y tiempo. Los que llegaron antes cobran antes, y tu stop no está en el libro: está en tu broker.

Siguiente lección: El libro y la profundidad. Cuántos contratos hay de verdad detrás del precio que ves, qué es una orden iceberg y por qué la cola FIFO decide si tu límite se ejecuta o no.