top of page

Get auto trading tips and tricks from our experts. Join our newsletter now

Thanks for submitting!

Ultra Low Latency Market Making System for HFT (C++)


Below is a conceptual framework for an ultra low latency market making system combining futures and options. This is a simplified version that would need significant customization for a billion-dollar HFT firm.


ultra low latency market making
 
#include <iostream>
#include <vector>
#include <unordered_map>
#include <chrono>
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <cmath>
#include <algorithm>
#include <random>
#include <boost/lockfree/spsc_queue.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
 
// Market Data Structures
struct MarketData {
    double bid;
    double ask;
    int bid_size;
    int ask_size;
    std::chrono::nanoseconds timestamp;
};
 
struct Order {
    std::string symbol;
    double price;
    int quantity;
    bool is_buy;
    std::chrono::nanoseconds timestamp;
    std::string order_id;
};
 
// Configuration
struct Config {
    double futures_spread_target = 0.0005; // 0.05% spread target
    double options_spread_target = 0.001;  // 0.1% spread target
    int max_order_size = 100;
    int inventory_limit = 10000;
    double gamma_threshold = 0.0001;
    double vega_threshold = 0.0001;
    int order_expiry_ns = 100000000; // 100ms
};
 
// Market Making Engine
class MarketMaker {
private:
    // Data structures
    std::unordered_map<std::string, MarketData> futures_market_data;
    std::unordered_map<std::string, MarketData> options_market_data;
    std::unordered_map<std::string, Order> active_orders;
 
    // Queues for ultra-low latency
    boost::lockfree::spsc_queue<MarketData, boost::lockfree::capacity<1024>> futures_data_queue;
    boost::lockfree::spsc_queue<MarketData, boost::lockfree::capacity<1024>> options_data_queue;
 
    // State
    std::atomic<int> futures_inventory{0};
    std::atomic<int> options_inventory{0};
    std::atomic<double> current_gamma{0.0};
    std::atomic<double> current_vega{0.0};
 
    // Configuration
    Config config;
 
    // Networking
    boost::asio::io_context io_context;
    boost::asio::ip::udp::socket market_data_socket;
    boost::asio::ip::udp::socket order_socket;
 
    // Random number generator for order IDs
    std::random_device rd;
    std::mt19937 gen;
    std::uniform_int_distribution<> dis;
 
    // Performance tracking
    std::atomic<uint64_t> orders_sent{0};
    std::atomic<uint64_t> orders_canceled{0};
    std::chrono::steady_clock::time_point start_time;
 
public:
    MarketMaker(const Config& cfg)
        : config(cfg),
          market_data_socket(io_context, boost::asio::ip::udp::v4()),
          order_socket(io_context, boost::asio::ip::udp::v4()),
          gen(rd()), dis(0, 999999) {
 
        start_time = std::chrono::steady_clock::now();
    }
 
    // Initialize the system
    void initialize() {
        // Bind sockets to appropriate ports
        market_data_socket.bind(boost::asio::ip::udp::endpoint(
            boost::asio::ip::udp::v4(), 12345));
 
        order_socket.bind(boost::asio::ip::udp::endpoint(
            boost::asio::ip::udp::v4(), 12346));
 
        // Start async receive for market data
        start_receive_futures_data();
        start_receive_options_data();
 
        // Start processing threads
        std::thread futures_processor(&MarketMaker::process_futures_data, this);
        std::thread options_processor(&MarketMaker::process_options_data, this);
        std::thread strategy_thread(&MarketMaker::run_strategy, this);
 
        futures_processor.detach();
        options_processor.detach();
        strategy_thread.detach();
    }
 
    // Market data handlers
    void start_receive_futures_data() {
        market_data_socket.async_receive_from(
            boost::asio::buffer(futures_data_buffer), remote_endpoint,
            boost::bind(&MarketMaker::handle_receive_futures_data, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
    }
 
    void handle_receive_futures_data(const boost::system::error_code& error,
                                   std::size_t bytes_transferred) {
        if (!error) {
            // Parse market data and push to queue
            MarketData data = parse_futures_data(futures_data_buffer.data());
            while (!futures_data_queue.push(data)) {
                // Handle queue full (in real system, would have better backpressure)
                std::this_thread::yield();
            }
            start_receive_futures_data();
        }
    }
 
    // Similar for options data...
 
    // Processing threads
    void process_futures_data() {
        MarketData data;
        while (futures_data_queue.pop(data)) {
            // Update market data
            futures_market_data[data.symbol] = data;
 
            // Check for order expirations
            check_order_expirations();
        }
    }
 
    // Similar for options data...
 
    // Core strategy
    void run_strategy() {
        while (true) {
            auto now = std::chrono::steady_clock::now();
 
            // Calculate Greeks (simplified - real system would use proper models)
            update_greeks();
 
            // Futures market making
            for (auto& [symbol, data] : futures_market_data) {
                make_futures_market(symbol, data);
            }
 
            // Options market making with futures hedge
            for (auto& [symbol, data] : options_market_data) {
                make_options_market(symbol, data);
            }
 
            // Sleep for minimal time to prevent CPU overload
            std::this_thread::sleep_for(std::chrono::nanoseconds(1000));
        }
    }
 
    void make_futures_market(const std::string& symbol, const MarketData& data) {
        // Calculate fair value (simplified)
        double mid = (data.bid + data.ask) / 2.0;
        double spread = data.ask - data.bid;
 
        // Adjust for inventory
        double inventory_adjustment = futures_inventory.load() * 0.00001;
 
        // Calculate our quotes
        double our_bid = mid - (config.futures_spread_target/2) + inventory_adjustment;
        double our_ask = mid + (config.futures_spread_target/2) + inventory_adjustment;
 
        // Ensure we don't cross the market
        our_bid = std::min(our_bid, data.bid);
        our_ask = std::max(our_ask, data.ask);
 
        // Size based on liquidity and inventory
        int bid_size = std::min(config.max_order_size,
                              std::max(1, data.bid_size / 2 - futures_inventory.load()/10));
        int ask_size = std::min(config.max_order_size,
                              std::max(1, data.ask_size / 2 + futures_inventory.load()/10));
 
        // Cancel existing orders if needed
        cancel_orders(symbol);
 
        // Send new orders
        send_order(symbol, our_bid, bid_size, true);
        send_order(symbol, our_ask, ask_size, false);
    }
 
    void make_options_market(const std::string& symbol, const MarketData& data) {
        // Calculate fair value using Black-Scholes or other model (simplified)
        double mid = (data.bid + data.ask) / 2.0;
        double spread = data.ask - data.bid;
 
        // Adjust for Greeks
        double gamma_adjustment = current_gamma.load() * 1000;
        double vega_adjustment = current_vega.load() * 100;
 
        // Calculate our quotes
        double our_bid = mid - (config.options_spread_target/2) + gamma_adjustment;
        double our_ask = mid + (config.options_spread_target/2) + vega_adjustment;
 
        // Ensure we don't cross the market
        our_bid = std::min(our_bid, data.bid);
        our_ask = std::max(our_ask, data.ask);
 
        // Size based on liquidity and inventory
        int bid_size = std::min(config.max_order_size,
                              std::max(1, data.bid_size / 2 - options_inventory.load()/10));
        int ask_size = std::min(config.max_order_size,
                              std::max(1, data.ask_size / 2 + options_inventory.load()/10));
 
        // Cancel existing orders if needed
        cancel_orders(symbol);
 
        // Send new orders
        send_order(symbol, our_bid, bid_size, true);
        send_order(symbol, our_ask, ask_size, false);
 
        // Hedge with futures if needed
        hedge_with_futures();
    }
 
    void hedge_with_futures() {
        // Simplified hedge logic - real system would use proper delta hedging
        double delta = calculate_delta(); // Would come from options pricing model
 
        if (delta > 0.1) {
            // Need to sell futures to hedge
            auto it = futures_market_data.find("ESM23"); // Example futures symbol
            if (it != futures_market_data.end()) {
                send_order(it->first, it->second.bid, std::abs(delta) * 100, false);
            }
        } else if (delta < -0.1) {
            // Need to buy futures to hedge
            auto it = futures_market_data.find("ESM23");
            if (it != futures_market_data.end()) {
                send_order(it->first, it->second.ask, std::abs(delta) * 100, true);
            }
        }
    }
 
    // Order management
    void send_order(const std::string& symbol, double price, int quantity, bool is_buy) {
        Order order;
        order.symbol = symbol;
        order.price = price;
        order.quantity = quantity;
        order.is_buy = is_buy;
        order.timestamp = std::chrono::steady_clock::now().time_since_epoch();
        order.order_id = "ORD" + std::to_string(dis(gen));
 
        // Store active order
        active_orders[order.order_id] = order;
 
        // In real system, would serialize and send via FPGA/low-latency network
        // Here we just increment counter
        orders_sent++;
 
        // Simulate network send
        std::string message = serialize_order(order);
        order_socket.send_to(boost::asio::buffer(message),
                           boost::asio::ip::udp::endpoint(
                               boost::asio::ip::address::from_string("192.168.1.1"), 12347));
    }
 
    void cancel_orders(const std::string& symbol) {
        std::vector<std::string> to_cancel;
 
        for (auto& [id, order] : active_orders) {
            if (order.symbol == symbol) {
                to_cancel.push_back(id);
            }
        }
 
        for (const auto& id : to_cancel) {
            // In real system, would send cancel message
            active_orders.erase(id);
            orders_canceled++;
        }
    }
 
    void check_order_expirations() {
        auto now = std::chrono::steady_clock::now();
        std::vector<std::string> to_cancel;
 
        for (auto& [id, order] : active_orders) {
            auto age = now - std::chrono::steady_clock::time_point(
                std::chrono::nanoseconds(order.timestamp.count()));
 
            if (age > std::chrono::nanoseconds(config.order_expiry_ns)) {
                to_cancel.push_back(id);
            }
        }
 
        for (const auto& id : to_cancel) {
            active_orders.erase(id);
            orders_canceled++;
        }
    }
 
    // Utility functions
    void update_greeks() {
        // In real system, would calculate proper Greeks from portfolio
        // Here we just simulate some values
        current_gamma.store(0.00005 * (options_inventory.load() / 100.0));
        current_vega.store(0.0001 * (options_inventory.load() / 100.0));
    }
 
    double calculate_delta() {
        // Simplified delta calculation
        return 0.001 * options_inventory.load();
    }
 
    // Performance monitoring
    void print_stats() {
        auto now = std::chrono::steady_clock::now();
        auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
            now - start_time).count();
 
        std::cout << "Uptime: " << uptime << "s\n";
        std::cout << "Orders sent: " << orders_sent.load() << "\n";
        std::cout << "Orders canceled: " << orders_canceled.load() << "\n";
        std::cout << "Futures inventory: " << futures_inventory.load() << "\n";
        std::cout << "Options inventory: " << options_inventory.load() << "\n";
        std::cout << "Current gamma: " << current_gamma.load() << "\n";
        std::cout << "Current vega: " << current_vega.load() << "\n";
    }
 
    // Parse and serialize functions would be implemented...
};
 
// Main function
int main() {
    Config config;
    // Customize config based on your needs
    config.futures_spread_target = 0.0002; // 2 bps
    config.options_spread_target = 0.0005; // 5 bps
    config.max_order_size = 50;
    config.inventory_limit = 5000;
 
    MarketMaker mm(config);
    mm.initialize();
 
    // Run stats printer in separate thread
    std::thread stats_thread([&mm]() {
        while (true) {
            mm.print_stats();
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    });
 
    stats_thread.join();
 
    return 0;
 
ree

Key Components of a Production-Grade System

 

For a billion-dollar HFT firm, you would need to enhance this framework with:

 

  1. Ultra-Low Latency Infrastructure:

    • FPGA acceleration for critical path operations

    • Kernel bypass networking (DPDK, Solarflare OpenOnload)

    • Colocation at exchange data centers

    • Specialized hardware (low-latency switches, NICs)

  2. Advanced Market Making Models:

    • Sophisticated options pricing models (Black-Scholes with volatility surface adjustments)

    • Machine learning for spread prediction

    • Microprice models for better quote placement

    • Inventory management algorithms

  3. Risk Management:

    • Real-time PnL calculation

    • Stress testing and scenario analysis

    • Kill switches and circuit breakers

    • Position limits and exposure monitoring

  4. Order Routing:

    • Smart order routing across multiple venues

    • Exchange-specific order types optimization

    • Latency arbitrage prevention

  5. Monitoring and Analytics:

    • Nanosecond-level latency monitoring

    • Performance attribution

    • Market impact analysis

    • Competitor behavior tracking

  6. Resilience:

    • Redundant systems with automatic failover

    • Message recovery and replay capabilities

    • Disaster recovery planning

  7. Compliance:

    • Trade surveillance

    • Audit trails

    • Regulatory reporting

 

Implementation Considerations

  1. Language Choice: While C++ is shown here, some firms use:

    • Java with Azul Zing JVM for garbage collection tuning

    • Rust for memory safety

    • Custom languages like KDB+/Q for time-series analysis

  2. Data Structures:

    • Lock-free queues for inter-thread communication

    • Custom hash maps optimized for cache locality

    • SIMD-optimized math operations

  3. Testing:

    • Extensive unit and integration testing

    • Market replay testing with historical data

    • Chaos engineering to test failure modes

  4. Deployment:

    • Blue-green deployments for zero-downtime updates

    • Canary releases for new strategies

    • A/B testing of different parameters

 

This framework provides a starting point, but a production system would require significant additional development and optimization by a team of quantitative developers, network engineers, and trading strategists.

 

 

Comments


bottom of page