C++ en el comercio de alta frecuencia: de los datos de mercado a la latencia determinista

C++ en el comercio de alta frecuencia: de los datos de mercado a la latencia determinista

C++ en el comercio de alta frecuencia: de los datos de mercado a la latencia determinista

Introducción

El comercio de alta frecuencia tiene una forma de simplificar los argumentos técnicos. En muchas áreas del software, un sistema puede seguir siendo respetable y al mismo tiempo esconder ineficiencia detrás de la escala, los presupuestos de hardware o las generosas expectativas de tiempo de respuesta. En HFT, la lentitud no es simplemente poco elegante. Es costoso. La inestabilidad no es sólo molesta. Daña la calidad de la estrategia, oscurece el diagnóstico y debilita la confianza en todo el conjunto. El dominio no elimina la teoría, pero la obliga a responder al tiempo.

Por eso C++ sigue siendo tan importante en los sistemas comerciales. El lenguaje sobrevive allí no porque la industria sea incapaz de cambiar ni porque los ingenieros disfruten de dificultades innecesarias. Sobrevive porque HFT solicita repetidamente una combinación de propiedades que C++ todavía proporciona inusualmente bien: control sobre el diseño de la memoria, trabajo de rendimiento preciso, herramientas nativas maduras, integración profunda del sistema operativo y de la red, y un enorme conjunto de conocimientos prácticos acumulados a lo largo de décadas de sistemas reales construidos bajo presión.

Es tentador reducir esto a un eslogan, como por ejemplo C++ es rápido. Pero ese eslogan es demasiado pequeño. HFT no recompensa la velocidad bruta en abstracto. Recompensa el comportamiento determinista a lo largo de todo un camino, desde los datos del mercado hasta la decisión y la transmisión de órdenes. La latencia promedio ayuda, pero la latencia predecible ayuda más. Un sistema que en ocasiones es brillante y regularmente nervioso es a menudo peor que uno que es un poco más lento y consistentemente comprensible. La historia más profunda, entonces, no es que C++ sea simplemente rápido. Es que C++ sigue siendo uno de los lenguajes más sólidos para construir sistemas de baja latencia cuyo comportamiento se puede moldear, medir y corregir con gran detalle.

Por qué HFT sigue regresando a C++

Una pila comercial que compite a tiempo se preocupa por los detalles que la mayoría de los otros dominios pueden permitirse el lujo de desdibujar. ¿Cuántas asignaciones ocurren en la ruta activa? ¿Qué datos viven juntos en el caché? ¿Qué hilo corre dónde? ¿Cuántos saltos de cola separan la llegada de paquetes de la lógica de la estrategia? ¿El analizador toca más memoria de la necesaria? ¿La puerta de enlace migra entre núcleos? ¿Un paso de normalización o registro supuestamente inofensivo amplía la cola de la distribución de latencia? Éstas no son cuestiones decorativas. Ellos son el trabajo.

C++ sigue siendo un hogar natural para este trabajo porque permite a los ingenieros afrontar esos detalles directamente. El lenguaje no impone un modelo de asignación, una historia de cola, una historia de propiedad o un programador de tiempo de ejecución en todo el sistema. Esa libertad es peligrosa en manos de equipos descuidados, pero HFT es uno de los lugares donde el uso disciplinado de esa libertad crea una ventaja real. Las organizaciones comerciales maduras no quieren preguntarle amablemente a la máquina. Quieren saber exactamente qué se le pide a la máquina que haga y exactamente dónde se esconden los costes.

También existe un argumento sobre el ecosistema que importa más de lo que la gente admite. HFT no es sólo un problema de idioma. Es un problema de herramientas y experiencia. C++ viene con compiladores maduros, perfiladores, gráficos de llama, flujos de trabajo de contador de hardware, compatibilidad con desinfectantes, patrones de integración a nivel de sistema operativo y una larga herencia de industrias adyacentes críticas para el rendimiento. Los asistentes de IA se benefician cada vez más de esa misma herencia pública. Cuando un ingeniero pide ayuda para mejorar un analizador, ajustar una cola o interpretar la salida de perfiles en una ruta activa nativa, la densidad histórica en torno a C++ sigue siendo una gran ventaja.

Lo que realmente experimenta un evento de datos de mercado

Es útil imaginar un evento de datos de mercado no como información abstracta sino como una carga física que se mueve a través de una máquina. Llega el paquete. Debe recibirse de la pila de red o del controlador de alimentación, analizarse, asignarse a alguna representación interna, aplicarse a una o más estructuras de libros, observarse mediante lógica estratégica, filtrarse mediante comprobaciones de riesgo y tal vez convertirse en una orden de salida o una cancelación. Si todo va bien, esta cadena se siente instantánea. Si la arquitectura es descuidada, el paquete gana peso a cada paso.

Una asignación adicional aquí, una cola compartida allá, un pase de normalización que copia más de lo que debería, una estructura de libro que es elegante en el sentido de un libro de texto pero fría en la memoria, una ruta de registro que fue pensada solo por seguridad, un hilo que migra en el momento equivocado: ninguno de estos costos suena mítico de forma aislada. Su peligro reside en la acumulación y la repetición. Los ingenieros de HFT aprenden a pensar de esta forma acumulativa porque el sistema castiga el optimismo. Una ineficiencia que es pequeña por evento se vuelve grande cuando se multiplica por la actividad del mercado, la frecuencia de las estrategias y la importancia empresarial de un tiempo de reacción predecible.

Esta es también la razón por la que el camino caliente en el comercio rara vez es solo una función. Es una ecología. Los datos de mercado, la gestión estatal, la programación, la serialización, el riesgo y la transmisión interactúan. Los ingenieros que optimizan sólo el bucle más glamoroso y dejan la coordinación y el diseño descuidados a menudo producen sistemas que se comparan bien en fragmentos y decepcionan en el único lugar que importa: el camino completo.

La latencia determinista es una disciplina arquitectónica

La frase baja latencia se utiliza a menudo como si describiera una propiedad de una función. En HFT serio, la baja latencia es una propiedad de la arquitectura. Surge de cómo se configura todo el sistema. Los datos candentes deberían permanecer calientes. La propiedad de la memoria debería ser obvia. Los hilos deben colocarse deliberadamente en lugar de dejarlos a la deriva. El estado mutable compartido debe tratarse con sospecha. Las colas deberían existir porque son necesarias, no porque hagan que los diagramas parezcan modulares. La observabilidad debería ser lo suficientemente barata como para que el sistema pueda seguir siendo inspeccionable sin ahogarse en sus propios diagnósticos.

El diseño de los datos es importante porque la máquina todavía se mueve a través de la memoria, no a través de intenciones. Los diseños contiguos, las representaciones de libros compactos y las estructuras que reflejan patrones de acceso en lugar del sentimiento del programador valen más que abstracciones inteligentes que parecen reutilizables pero que dispersan el estado activo por todas partes. La disciplina de asignación es importante porque la memoria dinámica en el camino caliente no es simplemente lenta en algún sentido promedio; también puede crear inquietud, contención e interacciones sorprendentes con el resto del tiempo de ejecución. En HFT, la fluctuación suele ser el problema más humillante.

El enhebrado merece la misma seriedad. Más subprocesos no significan automáticamente más rendimiento. A veces significan más coordinación, más movimiento de caché, más errores de afinidad y más lugares para que el sistema operativo se convierta en coautor involuntario. Los sistemas comerciales maduros fijan hilos deliberadamente, respetan los límites de la NUMA cuando sea relevante y mantienen el número de decisiones compartidas tan bajo como lo permite la arquitectura. Esto no hace que el código parezca moderno. Hace que el comportamiento sea más estable, lo que suele ser mucho más valioso.

Redes, análisis y mantenimiento de libros

El camino de la creación de redes en el comercio merece su propio tipo de respeto porque es donde la abstracción tiende más a residir. Una fuente binaria no es solo una entrada. Es una corriente de cambio de estado que debe interpretarse fiel y rápidamente. Cuanto más rápido sea el analizador, menos espacio habrá para la confusión posterior. Cuanta menos asignación y ramificación realice, más fácil será comprender por qué está pagando la máquina. El código de manejo de feeds a menudo parece austero exactamente por esta razón. Ha aprendido, a través del dolor, qué formas de elegancia no recompensa el mercado.

El mantenimiento del libro de pedidos tiene un carácter similar. Un libro no es valioso porque sea teóricamente hermoso. Es valioso porque se puede actualizar, consultar, reproducir y razonar bajo carga. La rejugabilidad importa aquí más de lo que a veces esperan los forasteros. Los equipos de HFT aprenden muchísimo al reproducir tráfico real, comparar el comportamiento de la estrategia entre revisiones y diagnosticar dónde un sistema se volvió más lento o menos estable. Una representación de un libro que es difícil de reproducir o inspeccionar aún puede parecer rápida en una prueba estrecha y, sin embargo, ser operativamente débil. En el trading, lo rápido y diagnosticable vence a lo rápido y misterioso.

Aquí es donde C++ encaja especialmente bien. Permite que la misma base de código hable con fluidez para alimentar analizadores, estructuras de datos conscientes de la memoria, herramientas de creación de perfiles y comportamiento del sistema operativo de bajo nivel. Otros lenguajes pueden participar en los sistemas comerciales, y muchos lo hacen, pero cuando el subsistema en cuestión es el camino activo en sí, C++ sigue proporcionando una de las mejores combinaciones de control y soporte del ecosistema.

Riesgo, repetición y madurez operativa

Es un error imaginar el HFT como pura velocidad despojada de gobernanza. El camino más rápido del mundo es inútil si puede enviar una orden incorrecta, no recuperar el estado o volverse inexplicable después de un evento volátil del mercado. Por lo tanto, los buenos sistemas comerciales mantienen explícitas las comprobaciones de riesgos, ensayan el manejo de fallas y reproducen la infraestructura cerca de la vida diaria de ingeniería. Estos no son accesorios burocráticos. Son parte de la competitividad.

Una base de código HFT saludable suele reflejar esta madurez. Contiene observabilidad barata en lugar de ninguna observabilidad. Contiene herramientas de repetición porque los equipos saben que lo que no se puede reproducir no se puede mejorar con confianza. Contiene puntos de referencia y perfiladores que analizan todo el camino, no solo micronúcleos cuidadosamente seleccionados. Trata la coherencia de la implementación, la configuración del compilador, la estrategia de afinidad y la configuración de la máquina como preocupaciones de ingeniería de primera clase. En otras palabras, los mejores sistemas comerciales no son simplemente fragmentos de código rápidos. Son entornos técnicos disciplinados.

Ésta es una de las razones por las que la estabilidad a menudo supera a la inteligencia pura. Una pequeña mejora en un punto de referencia de laboratorio vale menos que un sistema repetible cuyas colas se entiendan, cuyo manejo de la alimentación sea explicable y cuyo comportamiento estratégico pueda reconstruirse después del hecho. Los ingenieros que ingresan a HFT a veces esperan actos heroicos. Lo que los equipos maduros suelen practicar es una especie de rigor tranquilo. Quitan sorpresas. El mercado ya ofrece suficientes.

Los mitos comunes merecen ser retirados

Varios mitos sobreviven porque halagan a los ingenieros. Se dice que el rendimiento HFT se trata principalmente de ensamblaje escrito a mano o microoptimizaciones esotéricas. En realidad, los logros más significativos provienen de la arquitectura, la medición y la eliminación repetida de desechos ordinarios. Otro dice que las estructuras sin cerraduras son automáticamente superiores. A veces tienen toda la razón. A veces importan complejidad y costos de ordenación de la memoria a lugares donde un diseño más simple se habría comportado mejor. Un tercero dice que más hilos siempre ayudan. En sistemas de baja latencia, la concurrencia adicional puede degradar la previsibilidad más rápido de lo que mejora el rendimiento.

También existe el mito moderno de que el uso continuo de C++ en HFT debe deberse principalmente a una inercia histórica. La historia ciertamente importa, pero la inercia por sí sola no sobrevive en un campo donde los sistemas se miden continuamente en función del dinero y el tiempo. C++ permanece porque los equipos siguen descubriendo que el lenguaje, sus herramientas y la cultura de ingeniería que lo rodea aún se alinean bien con las realidades del diseño determinista de baja latencia. Si otro idioma creara consistentemente mejores resultados en las rutas comerciales más populares, las empresas de HFT lo notarían. Tienen incentivos lo suficientemente fuertes como para prestar atención.

Por qué todavía vale la pena estudiar este dominio

Incluso para los ingenieros que nunca trabajan en una empresa comercial, HFT sigue siendo un maestro valioso porque hace que la verdad del sistema sea difícil de evitar. Obliga a una estrecha relación entre el código y las consecuencias. Enseña que el diseño de los datos no es decoración, que las colas no son libres, que la latencia promedio puede mentir, que la reproducción es una forma de comprensión y que la arquitectura es a menudo la optimización más importante. Esas lecciones van mucho más allá del comercio.

C++ continúa siendo el centro de esa lección porque permite al ingeniero mantener un equilibrio difícil. Es lo suficientemente expresivo como para construir sistemas sustanciales, lo suficientemente bajo como para exponer los costos honestamente y lo suficientemente antiguo como para venir con una vasta herencia de herramientas y prácticas vividas. Esa combinación sigue siendo importante en uno de los ámbitos de rendimiento más exigentes que tenemos.

Si hay algo motivador en HFT no es la mitología de la velocidad por sí misma. Es un recordatorio de que el software puede hacerse preciso, mensurable y digno bajo presión. C++ sigue siendo uno de los lenguajes en los que todavía se habla esa disciplina con mayor fluidez.

Laboratorio práctico: cree una pequeña repetición del feed al libro

Terminemos construyendo un juguete en miniatura estilo HFT. No generará dinero. Eso es excelente. La mayoría de los ejemplos de código que prometen generar ingresos son educativos de la peor manera posible.

Lo que hará es más útil: reproducir una secuencia de actualizaciones del mercado en una pequeña representación de libro en memoria e informar la mejor oferta y demanda.

__CÓDIGO_0__

#include <algorithm>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <limits>
#include <string>
#include <vector>

enum class Side { Bid, Ask };

struct Update {
    Side side;
    int price;
    int qty;
};

struct Book {
    std::vector<Update> bids;
    std::vector<Update> asks;

    void apply(const Update& u) {
        auto& side = (u.side == Side::Bid) ? bids : asks;
        auto it = std::find_if(side.begin(), side.end(), [&](const Update& x) {
            return x.price == u.price;
        });

        if (u.qty == 0) {
            if (it != side.end()) side.erase(it);
            return;
        }

        if (it == side.end()) {
            side.push_back(u);
        } else {
            it->qty = u.qty;
        }
    }

    int best_bid() const {
        int best = 0;
        for (const auto& b : bids) best = std::max(best, b.price);
        return best;
    }

    int best_ask() const {
        int best = std::numeric_limits<int>::max();
        for (const auto& a : asks) best = std::min(best, a.price);
        return best;
    }
};

int main() {
    std::vector<Update> replay{
        {Side::Bid, 10010, 5},
        {Side::Bid, 10020, 3},
        {Side::Ask, 10040, 4},
        {Side::Ask, 10035, 8},
        {Side::Bid, 10020, 0},
        {Side::Ask, 10035, 6},
        {Side::Bid, 10025, 7}
    };

    Book book;
    const auto t0 = std::chrono::steady_clock::now();

    for (const auto& u : replay) {
        book.apply(u);
    }

    const auto t1 = std::chrono::steady_clock::now();
    const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count();

    std::cout << "best_bid=" << book.best_bid() << "\n";
    std::cout << "best_ask=" << book.best_ask() << "\n";
    std::cout << "replay_ns=" << ns << "\n";
}

Construir

En Linux o macOS:

g++ -O2 -std=c++20 -o tiny_book main.cpp
./tiny_book

En Windows:

cl /O2 /std:c++20 main.cpp
.\main.exe

Lo que esto te enseña

Incluso este pequeño programa de repetición plantea rápidamente preguntas reales sobre HFT:

  • ¿Deberían los niveles de precios vivir en vectores, mapas, matrices o escaleras personalizadas?
  • ¿Qué sucede cuando la repetición crece de 7 actualizaciones a 7 millones?
  • ¿Cuánto tiempo se dedica a las actualizaciones estatales en comparación con los informes?
  • ¿Dónde aparecen las asignaciones si la estructura se expande dinámicamente?

El ejemplo es pequeño, pero las preguntas no lo son en absoluto.

Tareas de prueba para entusiastas

  1. Reemplace la búsqueda lineal en apply con una estructura que escale mejor y compare los tiempos de reproducción.
  2. Genere un millón de actualizaciones sintéticas y mida cómo se degrada la estructura ingenua.
  3. Agregue un hilo de productor y un hilo de consumidor con una cola SPSC entre la reproducción del feed y la actualización del libro, luego compare la estabilidad y la complejidad.
  4. Fije el hilo de reproducción a un núcleo en Linux y compare la variación entre ejecuciones.
  5. Agregue una ruta de registro deliberadamente ruidosa y observe con qué rapidez una decisión de depuración "inofensiva" contamina las mediciones de latencia.

Estos ejercicios son humildes y precisamente por eso son buenos. La verdadera ingeniería de baja latencia se construye a partir de muchas estructuras humildes que se eligen con cuidado o de las que luego se lamenta.

Resumen

C++ sigue siendo fundamental para el comercio de alta frecuencia porque HFT no se trata simplemente de escribir funciones rápidas. Se trata de construir sistemas deterministas de baja latencia a lo largo de todo el camino, desde los datos de mercado hasta la transmisión de órdenes, y luego mantener esos sistemas lo suficientemente comprensibles como para diagnosticar bajo presión. Ese trabajo depende de un diseño disciplinado de los datos, una asignación restringida, un procesamiento cuidadoso, una elaboración de perfiles honesta, una validación reproducible y una cultura que valore tanto la estabilidad como la velocidad.

Esta es la razón por la que C++ continúa manteniéndose firme. Brinda a los ingenieros el nivel de control, profundidad de herramientas y práctica histórica que este dominio aún recompensa. Otros lenguajes pueden contribuir y contribuyen al intercambio de pilas, pero cuando el problema es el camino activo en sí, C++ sigue siendo una de las formas más sólidas que conocemos para convertir el rendimiento de un eslogan a una propiedad de ingeniería repetible.

Referencias

  1. Especificación NASDAQ TotalView-ITCH: https://nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/NQTVITCHSpecification.pdf
  2. Documentación DPDK: https://doc.dpdk.org/guides/
  3. Página de manual de API de socket de Linux: https://man7.org/linux/man-pages/man7/socket.7.html
  4. Documentación de marca de tiempo de Linux: https://docs.kernel.org/networking/timestamping.html
  5. Infraestructura de reloj de hardware PTP de Linux: https://docs.kernel.org/driver-api/ptp.html
  6. Página de manual de Linux perf: https://man7.org/linux/man-pages/man1/perf.1.html
  7. Gráficos de llamas de Brendan Gregg: https://www.brendangregg.com/flamegraphs.html
  8. Documentación de Intel VTune Profiler: https://www.intel.com/content/www/us/en/docs/vtune-profiler/overview.html
Philip P.

Philip P. – Director de Tecnología

Volver al blog

Contacto

Iniciar la conversación

Unas pocas líneas claras son suficientes. Describe el sistema, la presión y la decisión que está bloqueada. O escribe directamente a midgard@stofu.io.

01 What the system does
02 What hurts now
03 What decision is blocked
04 Optional: logs, specs, traces, diffs
0 / 10000