🛡️ sandbox-labs GitHub ↗

CM-04 · Enrutamiento inteligente de órdenes#

En una frase, para cualquiera: si el mismo instrumento se negocia en varios sitios, alguien decide a cuál mandar tu orden. Esa decisión te cuesta o te ahorra dinero, y casi nunca te la explican.

Estado real: 🟠 prototype — hay código y escenarios que se ejecutan, sin verificación en un entorno real · Módulo: crates/sandbox-markets/src/cases/routing.rs

Mercados, precios y latencias simulados. No es una autorización regulatoria ni una recomendación de inversión.

Por qué se realiza este caso#

«Mejor ejecución» no significa «mejor precio». Significa el mejor resultado considerando todo, y ese todo tiene partes que se compensan entre sí:

FactorPor qué puede cambiar la decisión
PrecioEl más obvio, y no siempre el que decide
ComisiónUn precio mejor con comisión mayor puede salir peor
LiquidezUn precio excelente para 10 acciones no sirve para 10 000
LatenciaEl precio que ves puede no existir cuando llegue tu orden
Probabilidad de ejecuciónUn mercado barato donde no se ejecuta nada no es barato
Tamaño y fragmentaciónPartir la orden mejora el precio y multiplica comisiones
DeslizamientoTu propia orden mueve el precio en tu contra

El conflicto de interés está siempre cerca: si un destino paga a quien le envía órdenes, el enrutador tiene un motivo para elegirlo que no es el del cliente.

La idea que enseña, y que ningún otro caso enseña#

Una decisión que no se puede explicar no es una decisión defendible. Cada enrutamiento produce el razonamiento completo: qué destinos se consideraron, con qué números, cuánto pesó cada factor y por qué ganó el elegido. El producto del caso no es la ejecución, es la explicación.

Casos de uso reales#

supervisor.

Cómo funcionará#

flowchart LR
  O["📥 Orden del cliente"] --> S["🧮 Evaluador"]
  M1["🏛️ Destino A<br/>precio 100 · com. 5 · liq. baja"] --> S
  M2["🏛️ Destino B<br/>precio 101 · com. 0 · liq. alta"] --> S
  M3["🏛️ Destino C<br/>precio 100 · com. 2 · latencia alta"] --> S
  S --> D["🎯 Decisión<br/>+ posible fragmentación"]
  D --> E["📄 Explicación:<br/>cada factor, cada número,<br/>cada peso"]
flowchart TB
  A["Para cada destino"] --> B["Costo total = precio × cantidad + comisión + deslizamiento estimado"]
  B --> C["Ajustar por probabilidad de ejecución"]
  C --> D{"¿Alguno cubre<br/>la orden entera?"}
  D -- sí --> E["Enviar completa al mejor"]
  D -- no --> F["Fragmentar y anotar<br/>el sobrecoste de fragmentar"]

Esquemas#

{
  "order": { "instrument": "ACME-SIM", "side": "buy", "quantity": 10000 },
  "venues": [
    { "id": "A", "price": 10000, "fee": 500, "displayedSize": 2000, "latencyMs": 3, "fillProbability": 0.9 },
    { "id": "B", "price": 10100, "fee": 0,   "displayedSize": 15000, "latencyMs": 12, "fillProbability": 0.99 }
  ]
}
{
  "decision": [{ "venue": "B", "quantity": 10000 }],
  "explanation": {
    "consideredVenues": ["A", "B"],
    "totalCost": { "A": 101500000, "B": 101000000 },
    "why": "A ofrece mejor precio pero solo cubre 2000 unidades; fragmentar añade comisión y deslizamiento estimado superiores a la diferencia de precio",
    "conflictsDisclosed": []
  }
}

flujo de órdenes, tiene que aparecer ahí.

Software necesario#

ComponentePara qué
Rust 1.75+El evaluador y la explicación
Node.js 20+ / pnpm 9+Visualizar la comparación en el panel (opcional)

No necesita jaula ni Linux: lógica determinista sobre datos simulados.

Instalación#

cargo build --release

Procesos que se crearán#

sandboxctl markets route --order orden.json --venues destinos.json
  │
  └─ un proceso determinista, sin red
      └─ mercados simulados con latencia y liquidez sintéticas

Los destinos son simulados: sin conectividad de producción, que es una regla innegociable de esta familia.

Tiempo de carga estimado#

OperaciónCoste esperado
Evaluar 10 destinos< 1 ms
Generar la explicación< 5 ms
Simular una sesión de 10 000 órdenessegundos

Qué hace falta para construirlo#

  1. Modelo de destino con precio, comisión, liquidez, latencia y probabilidad.
  2. Función de costo total, con pesos configurables y versionados.
  3. Explicación estructurada, obligatoria en cada decisión.
  4. Declaración de conflictos de interés por destino.
  5. Escenarios: destino que paga por el flujo, precio obsoleto, liquidez fantasma.

Si algo falla#

El caso ya tiene código y escenarios que se ejecutan. Lo que sigue son sus fallos con la causa y la salida:

SituaciónCausaCómo se resuelve
La decisión no se puede explicarFalta el bloque explanationUna decisión sin explicación no es defendible ante un cliente ni ante un supervisor. El esquema la hace obligatoria: sin ella no se emite la decisión
Siempre gana el mismo destinoPuede ser correcto, o puede ser un sesgo del cálculoRevisar los pesos de la función de costo, que van versionados. Y comprobar conflictsDisclosed: si ese destino remunera el flujo, hay que declararlo
El precio del destino ya no existe al llegar la ordenLatenciaSe pondera por fillProbability. Un destino barato donde no se ejecuta nada no es barato, y el cálculo tiene que reflejarlo
Fragmentar sale más caro que no fragmentarCada trozo paga comisión y mueve el precioEl cálculo incluye el sobrecoste de fragmentar. Si aun así fragmenta, la explicación dice con qué números lo decidió

Los fallos que afectan a cualquier caso —la compilación, el catálogo, la evidencia— están resueltos uno a uno en Cuando algo falla.

Esta familia no necesita aislamiento del sistema: no ejecuta código ajeno, sino reglas de negocio deterministas. Por eso casi ningún fallo suyo viene del entorno, y casi todos vienen de los datos.

Cómo se comprueba#

cargo run -p sandboxctl -- markets check --case CM-04

Ejecuta los escenarios de este caso y compara cada uno con lo que declara de antemano que debe salir. Corre en cada commit: si el caso deja de detectar lo que dice detectar, la integración continua se pone roja.

cargo test -p sandbox-markets routing

Los invariantes del módulo, incluidos los que ningún escenario de arriba cubre.

Sigue en prototype, no en functional. Los escenarios se ejecutan y pasan, pero el caso no emite evidencia firmada por ejecución ni se ha usado contra datos que no sean los suyos. La regla completa está en el ROADMAP.

Ver también: Catálogo completo · CM-05 · intermediación · CM-02 · libro de órdenes