Ultra Low Latency Market Making System for HFT (C++)
- Bryan Downing
- 3 days ago
- 7 min read
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.

#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 Structuresstruct 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;}; // Configurationstruct 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 Engineclass 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 functionint 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; 
Key Components of a Production-Grade System
For a billion-dollar HFT firm, you would need to enhance this framework with:
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)
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
Risk Management:
Real-time PnL calculation
Stress testing and scenario analysis
Kill switches and circuit breakers
Position limits and exposure monitoring
Order Routing:
Smart order routing across multiple venues
Exchange-specific order types optimization
Latency arbitrage prevention
Monitoring and Analytics:
Nanosecond-level latency monitoring
Performance attribution
Market impact analysis
Competitor behavior tracking
Resilience:
Redundant systems with automatic failover
Message recovery and replay capabilities
Disaster recovery planning
Compliance:
Trade surveillance
Audit trails
Regulatory reporting
Implementation Considerations
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
Data Structures:
Lock-free queues for inter-thread communication
Custom hash maps optimized for cache locality
SIMD-optimized math operations
Testing:
Extensive unit and integration testing
Market replay testing with historical data
Chaos engineering to test failure modes
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