C++ in hoogfrequente handel: van marktgegevens tot deterministische latentie

C++ in hoogfrequente handel: van marktgegevens tot deterministische latentie

C++ in hoogfrequente handel: van marktgegevens tot deterministische latentie

Invoering

Hoogfrequente handel heeft een manier om technische argumenten te vereenvoudigen. Op veel softwaregebieden kan een systeem respectabel blijven terwijl het de inefficiëntie verbergt achter schaalgrootte, hardwarebudgetten of genereuze verwachtingen op het gebied van de responstijd. Bij HFT is traagheid niet louter onelegant. Het is kostbaar. Instabiliteit is niet alleen maar vervelend. Het schaadt de kwaliteit van de strategie, vertroebelt de diagnose en verzwakt het vertrouwen in de hele stapel. Het domein elimineert de theorie niet, maar dwingt de theorie om op de tijd in te spelen.

Dat is de reden waarom C++ zo belangrijk blijft in handelssystemen. De taal overleeft daar niet omdat de industrie niet in staat is tot verandering en niet omdat ingenieurs onnodige ontberingen ervaren. Het overleeft omdat HFT herhaaldelijk vraagt ​​om een ​​combinatie van eigenschappen die C++ nog steeds buitengewoon goed biedt: controle over de geheugenindeling, nauwkeurig prestatiewerk, volwassen native tooling, diepgaande OS- en netwerkintegratie, en een enorme hoeveelheid praktische kennis die is verzameld door tientallen jaren van echte systemen die onder druk zijn gebouwd.

Het is verleidelijk om dit terug te brengen tot één slogan, zoals C++ is snel. Maar die slogan is te klein. HFT beloont ruwe snelheid niet in abstracto. Het beloont deterministisch gedrag over het hele traject, van marktgegevens tot besluit en ordertransmissie. Gemiddelde latentie helpt, maar voorspelbare latentie helpt nog meer. Een systeem dat af en toe briljant en regelmatig zenuwachtig is, is vaak slechter dan een systeem dat iets langzamer en consistent begrijpelijk is. Het diepere verhaal is dus niet dat C++ alleen maar snel is. Het is dat C++ een van de sterkste talen blijft voor het bouwen van systemen met lage latentie waarvan het gedrag tot in de kleinste details kan worden gevormd, gemeten en gecorrigeerd.

Waarom HFT steeds terugkeert naar C++

Een handelsstapel die op tijd concurreert, geeft om details die de meeste andere domeinen zich kunnen veroorloven te vervagen. Hoeveel toewijzingen vinden plaats op het warme pad? Welke gegevens leven samen in de cache? Welk draadje loopt waar? Hoeveel wachtrijhops scheiden de aankomst van pakketten van de strategielogica? Raakt de parser meer geheugen aan dan nodig is? Migreert de gateway tussen kernen? Verbreedt een zogenaamd ongevaarlijke log- of normalisatiestap de staart van de latentieverdeling? Dit zijn geen decoratieve vragen. Zij zijn het werk.

C++ blijft een natuurlijk thuis voor dit werk, omdat het ingenieurs in staat stelt deze details rechtstreeks onder ogen te zien. De taal dwingt niet één toewijzingsmodel, één wachtrijverhaal, één eigendomsverhaal of één runtime-planner op het hele systeem af. Die vrijheid is gevaarlijk in de handen van onzorgvuldige teams, maar HFT is een van de plaatsen waar gedisciplineerd gebruik van die vrijheid een echte voorsprong creëert. Volwassen handelsorganisaties willen het de machine niet vriendelijk vragen. Ze willen precies weten wat de machine moet doen en waar de kosten precies zitten.

Er is ook een ecosysteemargument dat belangrijker is dan mensen toegeven. HFT is niet alleen een taalprobleem. Het is een tooling- en ervaringsprobleem. C++ wordt geleverd met volwassen compilers, profilers, vlamgrafieken, hardware-counter-workflows, ondersteuning voor ontsmettingsmiddelen, integratiepatronen op besturingssysteemniveau en een lange erfenis van aangrenzende prestatiekritische industrieën. AI-assistenten profiteren steeds meer van diezelfde publieke erfenis. Wanneer een ingenieur om hulp vraagt ​​bij het verbeteren van een parser, het aanscherpen van een wachtrij of het interpreteren van profileringsuitvoer in een native hot path, blijft de historische dichtheid rond C++ een serieus voordeel.

Wat een Market-Data Event werkelijk ervaart

Het helpt om een ​​marktdatagebeurtenis niet voor te stellen als abstracte informatie, maar als een fysieke last die door een machine beweegt. Het pakket arriveert. Het moet worden ontvangen van de netwerkstack of feedhandler, worden geparseerd, in kaart gebracht in een interne representatie, toegepast op een of meer boekstructuren, geobserveerd door strategielogica, gefilterd door risicocontroles en misschien omgezet in een uitgaande order of een annulering. Als alles goed is, voelt deze ketting onmiddellijk aan. Als de architectuur onzorgvuldig is, krijgt het pakket bij elke stap meer gewicht.

Eén extra toewijzing hier, één gedeelde wachtrij daar, een normalisatiepas die meer kopieert dan zou moeten, een boekstructuur die elegant is in de zin van het leerboek, maar koud in het geheugen, een logpad dat alleen bedoeld was voor de veiligheid, een draad die op het verkeerde moment migreert: geen van deze kosten klinkt op zichzelf mythisch. Hun gevaar schuilt in accumulatie en herhaling. HFT-ingenieurs leren op deze accumulatieve manier te denken omdat het systeem optimisme bestraft. Een inefficiëntie die per gebeurtenis klein is, wordt groot wanneer deze wordt vermenigvuldigd met marktactiviteit, strategiefrequentie en het zakelijke belang van voorspelbare reactietijd.

Dit is ook de reden waarom het hete pad in de handel zelden slechts één functie heeft. Het is een ecologie. Marktgegevens, statusbeheer, planning, serialisatie, risico's en transmissie zijn allemaal met elkaar verbonden. Ingenieurs die alleen de meest glamoureuze lus optimaliseren en daarbij de coördinatie en lay-out slordig laten, produceren vaak systemen die in fragmenten goed presteren en teleurstellen op de enige plek die ertoe doet: het volledige pad.

Deterministische latentie is een architecturale discipline

De uitdrukking lage latentie wordt vaak gebruikt alsof het een eigenschap van een functie beschrijft. Bij serieuze HFT is lage latentie een eigenschap van architectuur. Het komt voort uit de manier waarop het hele systeem wordt vormgegeven. Hot data moeten hot blijven. Het eigendom van het geheugen moet duidelijk zijn. Draden moeten opzettelijk worden geplaatst in plaats van te laten afdrijven. Een gedeelde veranderlijke staat moet met argwaan worden behandeld. Wachtrijen moeten bestaan ​​omdat ze noodzakelijk zijn, niet omdat ze ervoor zorgen dat diagrammen modulair aanvoelen. Waarneembaarheid moet zo goedkoop zijn dat het systeem inspecteerbaar kan blijven zonder te verdrinken in zijn eigen diagnostiek.

De lay-out van gegevens is van belang omdat de machine nog steeds door het geheugen beweegt, en niet door intenties. Aaneengesloten lay-outs, compacte boekrepresentaties en structuren die toegangspatronen weerspiegelen in plaats van het sentiment van de programmeur zijn meer waard dan slimme abstracties die er herbruikbaar uitzien maar overal een hot state verspreiden. Toewijzingsdiscipline is van belang omdat dynamisch geheugen op het hete pad niet alleen maar traag is in een gemiddelde zin; het kan ook zorgen voor onrust, twist en verrassende interacties met de rest van de looptijd. Bij HFT is jitter vaak het meest vernederende probleem.

Threading verdient dezelfde ernst. Meer threads betekenen niet automatisch meer prestaties. Soms betekenen ze meer coördinatie, meer cachebewegingen, meer affiniteitsfouten en meer plaatsen waar het besturingssysteem een ​​onvrijwillige co-auteur kan worden. Volwassen handelssystemen leggen doelbewust de draad vast, respecteren waar relevant de NUMA-grenzen en houden het aantal gedeelde beslissingen zo laag als de architectuur toelaat. Hierdoor voelt de code niet modieus aan. Het maakt het gedrag stabieler, wat meestal veel waardevoller is.

Netwerken, parseren en boekonderhoud

Het netwerkpad in de handel verdient zijn eigen soort respect, omdat abstractie daar het meest geneigd is te liegen. Een binaire feed is niet alleen invoer. Het is een stroom van staatsveranderingen die getrouw en snel geïnterpreteerd moet worden. Hoe sneller de parser, hoe minder ruimte er is voor verwarring stroomafwaarts. Hoe minder toewijzing en vertakkingen er worden uitgevoerd, hoe gemakkelijker het wordt om te begrijpen waar de machine voor betaalt. Juist om deze reden ziet de code voor de verwerking van diervoeders er vaak sober uit. Het heeft door pijn geleerd welke vormen van elegantie de markt niet beloont.

Het onderhoud van het orderboek heeft een soortgelijk karakter. Een boek is niet waardevol omdat het theoretisch mooi is. Het is waardevol omdat het onder belasting kan worden bijgewerkt, opgevraagd, opnieuw afgespeeld en erover kan worden beredeneerd. Herspeelbaarheid is hier belangrijker dan buitenstaanders soms verwachten. HFT-teams leren enorm veel door echt verkeer opnieuw af te spelen, strategiegedrag over revisies heen te vergelijken en te diagnosticeren waar een systeem langzamer of minder stabiel is geworden. Een boekrepresentatie die moeilijk opnieuw af te spelen of te inspecteren is, kan in een beperkte test nog steeds snel lijken en toch operationeel zwak zijn. In de handel is snel en diagnosticeerbaar snel en mysterieus.

Dit is waar C++ bijzonder goed past. Het zorgt ervoor dat dezelfde codebase vloeiend kan spreken om parsers, geheugenbewuste datastructuren, profileringstools en low-level besturingssysteemgedrag te voeden. Andere talen kunnen deelnemen aan handelssystemen, en veel talen doen dat ook, maar wanneer het subsysteem in kwestie zelf het hot path is, biedt C++ nog steeds een van de beste combinaties van controle en ecosysteemondersteuning.

Risico, herhaling en operationele volwassenheid

Het is een vergissing om HFT voor te stellen als pure snelheid, ontdaan van bestuur. Het snelste pad ter wereld is nutteloos als het de verkeerde order kan sturen, er niet in slaagt de toestand te herstellen of onverklaarbaar wordt na een volatiele marktgebeurtenis. Goede handelssystemen houden risicocontroles daarom expliciet, het afhandelen van fouten wordt geoefend en de herhalingsinfrastructuur blijft dicht bij het dagelijkse technische leven. Dit zijn geen bureaucratische accessoires. Ze maken deel uit van het concurrentievermogen.

Een gezonde HFT-codebase weerspiegelt doorgaans deze volwassenheid. Het bevat goedkope waarneembaarheid in plaats van geen waarneembaarheid. Het bevat replay-tools omdat teams weten dat wat niet opnieuw kan worden gespeeld, niet met vertrouwen kan worden verbeterd. Het bevat benchmarks en profilers die naar het hele pad kijken, niet alleen naar zorgvuldig geselecteerde microkernels. Het behandelt implementatieconsistentie, compilerinstellingen, affiniteitsstrategie en machineconfiguratie als eersteklas technische problemen. Met andere woorden: de beste handelssystemen zijn niet alleen maar snelle stukjes code. Het zijn gedisciplineerde technische omgevingen.

Dit is een van de redenen waarom stabiliteit zo vaak het wint van pure slimheid. Een kleine verbetering in een laboratoriumbenchmark is minder waard dan een herhaalbaar systeem waarvan de staarten worden begrepen, waarvan de voerverwerking verklaarbaar is en waarvan het strategiegedrag achteraf kan worden gereconstrueerd. Ingenieurs die HFT betreden, verwachten soms heldendaden. Wat volwassen teams in plaats daarvan vaak oefenen, is een soort kalme strengheid. Ze nemen verrassingen weg. De markt biedt daar al genoeg van.

Gemeenschappelijke mythen verdienen het om met pensioen te gaan

Verschillende mythen blijven bestaan ​​omdat ze ingenieurs vleien. Men zegt dat HFT-prestaties vooral te maken hebben met handgeschreven assemblage of esoterische micro-optimalisaties. In werkelijkheid komen de meest betekenisvolle overwinningen voort uit architectuur, metingen en het herhaaldelijk verwijderen van gewoon afval. Een ander zegt dat slotvrije structuren automatisch superieur zijn. Soms hebben ze precies gelijk. Soms importeren ze de complexiteit en de kosten voor het bestellen van geheugen op plaatsen waar een eenvoudiger ontwerp beter zou hebben gewerkt. Een derde zegt dat meer threads altijd helpen. In systemen met lage latentie kan extra gelijktijdigheid de voorspelbaarheid sneller verminderen dan de doorvoer verbetert.

Er bestaat ook een moderne mythe dat het voortdurende gebruik van C++ in HFT grotendeels een historische inertie moet zijn. De geschiedenis doet er zeker toe, maar traagheid alleen overleeft niet in een veld waar systemen voortdurend worden afgemeten aan geld en tijd. C++ blijft bestaan ​​omdat teams blijven ontdekken dat de taal, de tools en de omringende technische cultuur nog steeds goed aansluiten bij de realiteit van deterministisch ontwerp met lage latentie. Als een andere taal consequent betere resultaten zou opleveren op de populairste handelspaden, zouden HFT-bedrijven dit merken. Ze hebben prikkels die sterk genoeg zijn om op te letten.

Waarom dit domein nog steeds de moeite waard is om te bestuderen

Zelfs voor ingenieurs die nooit in een handelsfirma werken, blijft HFT een waardevolle leermeester omdat het de systeemwaarheid moeilijk te vermijden maakt. Het dwingt een nauwe relatie af tussen code en consequenties. Het leert dat de lay-out van data geen versiering is, dat wachtrijen niet gratis zijn, dat de gemiddelde latentie kan liggen, dat herhalen een vorm van begrip is, en dat architectuur vaak de belangrijkste optimalisatie is. Deze lessen gaan veel verder dan alleen handelen.

C++ blijft centraal staan ​​in deze les, omdat het de ingenieur in staat stelt een moeilijk evenwicht te bewaren. Het is expressief genoeg om substantiële systemen te bouwen, laag genoeg om de kosten eerlijk bloot te leggen, en oud genoeg om te beschikken over een enorme erfenis aan hulpmiddelen en praktijkervaring. Die combinatie is nog steeds van belang in een van de meest veeleisende prestatiedomeinen die we hebben.

Als er iets motiverends is aan HFT, dan is het niet de mythologie van snelheid op zichzelf. Het herinnert ons eraan dat software onder druk nauwkeurig, meetbaar en waardig kan worden gemaakt. C++ blijft één van de talen waarin dat vakgebied nog steeds het meest vloeiend gesproken wordt.

Praktijklab: bouw een kleine feed-to-book-herhaling

Laten we eindigen met het bouwen van een miniatuur speelgoed in HFT-stijl. Het zal geen geld opleveren. Dat is uitstekend. De meeste codevoorbeelden die beloven geld te verdienen zijn op de slechtst mogelijke manier leerzaam.

Wat het zal doen is nuttiger: een reeks marktupdates opnieuw afspelen in een kleine boekweergave in het geheugen en het beste bod en de beste vraag rapporteren.

main.cpp

#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";
}

Bouwen

Op Linux of macOS:

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

Op Windows:

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

Wat dit je leert

Zelfs dit kleine herhalingsprogramma roept al snel echte HFT-vragen op:

  • Moeten prijsniveaus in vectoren, kaarten, arrays of aangepaste ladders leven?
  • wat gebeurt er als de herhaling groeit van 7 updates naar 7 miljoen?
  • Hoeveel tijd gaat er zitten in statusupdates versus rapportage?
  • waar verschijnen toewijzingen als de structuur dynamisch uitbreidt?

Het voorbeeld is klein, maar de vragen zijn helemaal niet klein.

Testtaken voor liefhebbers

  1. Vervang de lineaire zoekopdracht in apply door een structuur die beter schaalt en de herspeeltijden vergelijkt.
  2. Genereer een miljoen synthetische updates en meet hoe de naïeve structuur verslechtert.
  3. Voeg één producententhread en één consumententhread toe met een SPSC-wachtrij tussen het opnieuw afspelen van de feed en het bijwerken van het boek, en vergelijk vervolgens de stabiliteit en complexiteit.
  4. Pin de replay-thread op een kern op Linux en vergelijk de run-to-run-variantie.
  5. Voeg opzettelijk een logpad met veel ruis toe en kijk hoe snel een "onschadelijke" foutopsporingsbeslissing de latentiemetingen vervuilt.

Deze oefeningen zijn bescheiden, en dat is precies waarom ze goed zijn. Echte techniek met lage latentie is opgebouwd uit vele eenvoudige structuren die zorgvuldig zijn gekozen of waar later spijt van wordt.

Samenvatting

C++ blijft centraal staan ​​bij hoogfrequente handel, omdat HFT niet alleen gaat over het schrijven van snelle functies. Het gaat erom deterministische systemen met lage latentie te bouwen over het hele traject van marktgegevens tot ordertransmissie, en deze systemen vervolgens begrijpelijk genoeg te houden om onder druk diagnoses te kunnen stellen. Dat werk hangt af van een gedisciplineerde data-indeling, ingetogen toewijzing, zorgvuldige threading, eerlijke profilering, herspeelbare validatie en een cultuur die zowel stabiliteit als snelheid waardeert.

Dit is de reden waarom C++ stand blijft houden. Het geeft ingenieurs het niveau van controle, diepgang van de tools en historische praktijk die dit domein nog steeds beloont. Andere talen kunnen een bijdrage leveren aan het verhandelen van stapels, en dat doen ze ook, maar als het probleem het hete pad zelf is, blijft C++ een van de sterkste manieren die we kennen om prestaties van een slogan om te zetten in een herhaalbare technische eigenschap.

Referenties

  1. NASDAQ TotalView-ITCH-specificatie: https://nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/NQTVITCHSpecification.pdf
  2. DPDK-documentatie: https://doc.dpdk.org/guides/
  3. Linux socket API-manpagina: https://man7.org/linux/man-pages/man7/socket.7.html
  4. Documentatie voor Linux-tijdstempels: https://docs.kernel.org/networking/timestamping.html
  5. Linux PTP hardwareklokinfrastructuur: https://docs.kernel.org/driver-api/ptp.html
  6. Linux perf manpagina: https://man7.org/linux/man-pages/man1/perf.1.html
  7. Vlamgrafieken door Brendan Gregg: https://www.brendangregg.com/flamegraphs.html
  8. Intel VTune Profiler-documentatie: https://www.intel.com/content/www/us/en/docs/vtune-profiler/overview.html
Philip P.

Philip P. – Technisch directeur

Terug naar blog

Contact

Begin het gesprek

Een paar duidelijke lijnen zijn voldoende. Beschrijf het systeem, de druk en de beslissing die wordt geblokkeerd. Of schrijf rechtstreeks naar 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