From 24e98f3850862307fb0fc94367f4da92d8de0c25 Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 14 May 2026 11:11:32 +0800 Subject: [PATCH] Fix market simulator hanging issue --- .gitignore | 1 + include/velox/book/order_book.hpp | 1 + include/velox/core/symbol_engine.hpp | 5 + include/velox/sim/env/market_sim.hpp | 118 +++++++++++++++ include/velox/sim/strategy/bots.hpp | 21 ++- src/CMakeLists.txt | 7 +- src/book/order_book.cpp | 6 + src/main.cpp | 189 +++++++++++++------------ src/sim/env/market_sim.cpp | 182 ++++++++++++++++++++++++ src/{ => sim}/strategy/bot.cpp | 0 src/{ => sim}/strategy/bot_manager.cpp | 11 +- 11 files changed, 430 insertions(+), 111 deletions(-) create mode 100644 include/velox/sim/env/market_sim.hpp create mode 100644 src/sim/env/market_sim.cpp rename src/{ => sim}/strategy/bot.cpp (100%) rename src/{ => sim}/strategy/bot_manager.cpp (74%) diff --git a/.gitignore b/.gitignore index 28dbfca..0763214 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build/ build_*/ market_data/ cmake-build-*/ +src/old_main.cpp .vscode/ .idea/ *.swp diff --git a/include/velox/book/order_book.hpp b/include/velox/book/order_book.hpp index 4d72393..e8d2f25 100644 --- a/include/velox/book/order_book.hpp +++ b/include/velox/book/order_book.hpp @@ -20,6 +20,7 @@ class OrderBook { Order* match(Order* incoming_order, std::vector& fills); const std::vector& get_bid_levels() const { return m_bid_levels; } const std::vector& get_ask_levels() const { return m_ask_levels; } + void set_market_price(int64_t bid, int64_t ask); // Market data int64_t best_bid() const { return m_best_bid.load(std::memory_order_acquire); } diff --git a/include/velox/core/symbol_engine.hpp b/include/velox/core/symbol_engine.hpp index 881efc2..2421399 100644 --- a/include/velox/core/symbol_engine.hpp +++ b/include/velox/core/symbol_engine.hpp @@ -45,6 +45,11 @@ class SymbolEngine { return m_book; } + // Set best bid/ask to Order Book w/o going through Matching Engine + void set_market_price(int64_t bid, int64_t ask) { + m_book.set_market_price(bid, ask); + } + private: OrderBook m_book; MatchingEngine m_engine; diff --git a/include/velox/sim/env/market_sim.hpp b/include/velox/sim/env/market_sim.hpp new file mode 100644 index 0000000..11b3e80 --- /dev/null +++ b/include/velox/sim/env/market_sim.hpp @@ -0,0 +1,118 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "velox/core/symbol_engine.hpp" +#include "lockfree/pool.hpp" + +namespace velox { +namespace env { + + // Store PooledPtrs separately for active orders + struct ActiveOrders { + lockfree::PooledPtr bid; + lockfree::PooledPtr ask; + + // Default constructor + ActiveOrders() = default; + + // Move constructor + ActiveOrders(ActiveOrders&& other) noexcept + : bid(std::move(other.bid)) + , ask(std::move(other.ask)) {} + + // Move assignment + ActiveOrders& operator=(ActiveOrders&& other) noexcept { + if (this != &other) { + bid = std::move(other.bid); + ask = std::move(other.ask); + } + return *this; + } + + // Constructor from moved PooledPtrs + ActiveOrders(lockfree::PooledPtr&& b, + lockfree::PooledPtr&& a) noexcept + : bid(std::move(b)) + , ask(std::move(a)) {} + + // No copy + ActiveOrders(const ActiveOrders&) = delete; + ActiveOrders& operator=(const ActiveOrders&) = delete; + }; + +class MarketSimulator { +public: + // Polymorphic function wrapper for callback + using PriceCallback = std::function; + + MarketSimulator(double tick_intervals_ms = 100.0, + int64_t initial_price = 10000, + int64_t volatility = 50, + int64_t min_spread = 5, + int64_t max_spread = 50, + uint32_t resting_quantity = 500) : + m_tick_interval(std::chrono::milliseconds(static_cast(tick_intervals_ms))), + m_initial_price(initial_price), + m_volatility(volatility), + m_min_spread(min_spread), + m_max_spread(max_spread), + m_resting_quantity(resting_quantity), + m_gen(std::random_device{}()), + m_dist(0, volatility), + m_spread_dist(min_spread, max_spread) {} + + ~MarketSimulator() { stop(); } + + // Core operations + void start (std::vector>& engines, PriceCallback callback); + void stop(); + + // Resting orders operations + void initialize_resting_orders(SymbolEngine& engine, int64_t initial_price); + void update_resting_orders(SymbolEngine& engine, int64_t bid_price, int64_t ask_price); + void clear_resting_orders(); + +private: + std::chrono::milliseconds m_tick_interval; + int64_t m_initial_price; + int64_t m_volatility; + int64_t m_min_spread; + int64_t m_max_spread; + std::uniform_int_distribution m_spread_dist; + int32_t m_resting_quantity; + std::mt19937 m_gen; + std::normal_distribution m_dist; + + // Flags + std::atomic m_running{false}; + + // Env thread + std::thread m_thread; + + // Resting orders for each symbol + struct SymbolOrders { + uint64_t bid_order_id = 0; + uint64_t ask_order_id = 0; + int64_t current_bid_price = 0; + int64_t current_ask_price = 0; + }; + + // Per-symbol resting orders + std::unordered_map m_symbol_orders; + + + + + std::unordered_map> m_active_orders; + + // Shared pool for simulator orders + lockfree::ObjectPool m_order_pool; + uint64_t m_next_order_id = 1000000; +}; + +} +} \ No newline at end of file diff --git a/include/velox/sim/strategy/bots.hpp b/include/velox/sim/strategy/bots.hpp index 663f7f4..a66fab7 100644 --- a/include/velox/sim/strategy/bots.hpp +++ b/include/velox/sim/strategy/bots.hpp @@ -46,12 +46,14 @@ class MarketMakerBot : public TradingBot { , m_quantity(quantity) {} void on_snapshot(const BookSnapshot& snapshot) override { + /* static int call_count = 0; if (call_count++ < 3) { std::cout << "[MarketMakerBot] on_snapshot called for " << snapshot.symbol << " best_bid=" << snapshot.best_bid << " best_ask=" << snapshot.best_ask << std::endl; } + */ static int update_count = 0; @@ -74,7 +76,7 @@ class MarketMakerBot : public TradingBot { std::strncpy(bid.symbol, m_symbol.c_str(), 7); submit_order(bid); - std::cout << "[MarketMaker " << name() << "] Placed BUY at " << bid.price << std::endl; + // std::cout << "[MarketMaker " << name() << "] Placed BUY at " << bid.price << std::endl; } // If no asks, place an ask @@ -89,7 +91,7 @@ class MarketMakerBot : public TradingBot { std::strncpy(ask.symbol, m_symbol.c_str(), 7); submit_order(ask); - std::cout << "[MarketMaker " << name() << "] Placed SELL at " << ask.price << std::endl; + // std::cout << "[MarketMaker " << name() << "] Placed SELL at " << ask.price << std::endl; } // Gradually adjust base price based on recent trades @@ -99,8 +101,10 @@ class MarketMakerBot : public TradingBot { } void on_fill(const Order& order, uint32_t fill_qty, int64_t fill_price) override { + /* std::cout << "[MarketMaker " << name() << "] Filled " << fill_qty << " @ " << fill_price << std::endl; + */ } private: @@ -114,7 +118,7 @@ class MarketMakerBot : public TradingBot { class SpreadBot : public TradingBot { public: SpreadBot(const std::string& name, const std::string& symbol, - int64_t threshold = 50, uint32_t quantity = 100) + int64_t threshold = 5, uint32_t quantity = 100) : TradingBot(name, symbol) , m_threshold(threshold) , m_quantity(quantity) {} @@ -175,12 +179,6 @@ class RandomWalkBot : public TradingBot { , m_dist(0.0, 1.0) {} void on_snapshot(const BookSnapshot& snapshot) override { - static int call_count = 0; - if (call_count++ < 5) { - std::cout << "[RandomWalkBot " << name() << "] Received snapshot, price=" - << snapshot.mid_price << std::endl; - } - double r = m_dist(m_gen); if (r < m_buy_prob) { @@ -192,8 +190,9 @@ class RandomWalkBot : public TradingBot { order.remaining_quantity = m_quantity; order.type = OrderType::LIMIT; std::strncpy(order.symbol, snapshot.symbol, 7); + /* std::cout << "[RandomWalkBot " << name() << "] SUBMITTING BUY order at " - << order.price << std::endl; + << order.price << std::endl; */ submit_order(order); } else if (r < m_buy_prob + m_sell_prob) { @@ -298,7 +297,7 @@ class MeanReversionBot : public TradingBot { class MomentumBot : public TradingBot { public: MomentumBot(const std::string& name, const std::string& symbol, - int64_t momentum_threshold = 50, uint32_t quantity = 100) + int64_t momentum_threshold = 15, uint32_t quantity = 100) : TradingBot(name, symbol) , m_momentum_threshold(momentum_threshold) , m_quantity(quantity) {} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1c8b3f1..72dc7fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,8 +19,11 @@ add_library(velox_core STATIC gateway/fix_encoder.cpp # Strategy - strategy/bot_manager.cpp - strategy/bot.cpp + sim/strategy/bot_manager.cpp + sim/strategy/bot.cpp + + # Environment + sim/env/market_sim.cpp ) target_link_libraries(velox_core diff --git a/src/book/order_book.cpp b/src/book/order_book.cpp index ffb6b97..da8b44a 100644 --- a/src/book/order_book.cpp +++ b/src/book/order_book.cpp @@ -245,4 +245,10 @@ void OrderBook::update_depth() { m_ask_depth.store(ask_depth, std::memory_order_release); } +// Update Order Book w/o going through Matching Engine +void OrderBook::set_market_price(int64_t bid, int64_t ask) { + m_best_bid.store(bid, std::memory_order_release); + m_best_ask.store(ask, std::memory_order_release); +} + } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 38cd600..11ad8e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "velox/matching/matching_engine.hpp" #include "velox/sim/strategy/bot_manager.hpp" #include "velox/sim/strategy/bots.hpp" +#include "velox/sim/env/market_sim.hpp" #include #include @@ -20,7 +21,11 @@ using namespace velox; static std::atomic g_running{true}; void signal_handler(int) { g_running = false; } -int main() { +int main(int argc, char* argv[]) { + + // Parse second argument for desired trading window in minutes (default is 10 minutes) + int duration_minutes = (argc > 1) ? std::stoi(argv[1]) : 10; + signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); @@ -31,6 +36,9 @@ int main() { auto bot_manager = std::make_unique(); + // Initialize simulated market environment + env::MarketSimulator simulator(100.0, 10000, 50, 5, 50, 500); + // Gateway workers const int num_workers = std::thread::hardware_concurrency(); for (int i = 0; i < num_workers; ++i) { @@ -46,90 +54,42 @@ int main() { s.c_str(), &risk_manager, &gateway, &pos_manager)); } - // Bots + // Bots for each symbol + + // AAPL bot_manager->register_bot(std::make_unique("MM_AAPL", "AAPL", 10000, 100, 500)); bot_manager->register_bot(std::make_unique("Spread_AAPL", "AAPL")); bot_manager->register_bot(std::make_unique("RW_AAPL", "AAPL")); bot_manager->register_bot(std::make_unique("MR_AAPL", "AAPL")); bot_manager->register_bot(std::make_unique("Mom_AAPL", "AAPL")); - // Shared pools - static lockfree::ObjectPool global_pool; - static std::vector> order_storage; - - // Feed handler routing - feed_handler.on_add_order([&](const Order& order) { - for (auto& e : engines) { - if (strcmp(e->order_book().symbol(), order.symbol) == 0) { - auto ptr = global_pool.acquire(); - *ptr = order; - - // CRITICAL: normalize state - ptr->remaining_quantity = ptr->quantity; - ptr->filled_quantity = 0; - ptr->status = OrderStatus::NEW; - - e->on_market_order(ptr.get()); - order_storage.push_back(std::move(ptr)); - break; - } - } - }); - - std::cout << "[MAIN] Seeding market with extreme prices (bid=1, ask=999999)...\n"; - - static lockfree::ObjectPool seed_pool; - static std::vector> seed_storage; - - for (auto& e : engines) { - // BID at $0.01 (price=1) – extremely low, never matched by any sell order - auto bid = seed_pool.acquire(); - bid->order_id = 1000; - bid->side = OrderSide::BUY; - bid->price = 1; - bid->quantity = 1000; - bid->remaining_quantity = 1000; - bid->filled_quantity = 0; - bid->status = OrderStatus::NEW; - std::strncpy(bid->symbol, e->symbol(), 7); - e->get_order_book().add_order(bid.get()); // NOTE: populate order book WITHOUT going through matching engine (otherwise rejection) - seed_storage.push_back(std::move(bid)); - - // ASK at $9999.99 (price=999999) – extremely high, never matched by any buy order - auto ask = seed_pool.acquire(); - ask->order_id = 1001; - ask->side = OrderSide::SELL; - ask->price = 999999; - ask->quantity = 1000; - ask->remaining_quantity = 1000; - ask->filled_quantity = 0; - ask->status = OrderStatus::NEW; - std::strncpy(ask->symbol, e->symbol(), 7); - e->get_order_book().add_order(ask.get()); - seed_storage.push_back(std::move(ask)); - } - - // Verify seed orders are in the book - for (auto& e : engines) { - std::cout << "[POST-SEED] " << e->symbol() - << " bid=" << e->order_book().best_bid() - << " ask=" << e->order_book().best_ask() << "\n"; - } - - // Process seed orders - for (auto& e : engines) { - for (int i = 0; i < 10; ++i) { - e->run_match_cycle(); - } - } - - // Verification - for (auto& e : engines) { - std::cout << "[POST-SEED] " << e->symbol() - << " seq=" << e->order_book().sequence() - << " bid=" << e->order_book().best_bid() - << " ask=" << e->order_book().best_ask() << "\n"; - } + // MSFT + bot_manager->register_bot(std::make_unique("MM_MSFT", "MSFT", 10000, 100, 500)); + bot_manager->register_bot(std::make_unique("Spread_MSFT", "MSFT")); + bot_manager->register_bot(std::make_unique("RW_MSFT", "MSFT")); + bot_manager->register_bot(std::make_unique("MR_MSFT", "MSFT")); + bot_manager->register_bot(std::make_unique("Mom_MSFT", "MSFT")); + + // GOOGL + bot_manager->register_bot(std::make_unique("MM_GOOGL", "GOOGL", 10000, 100, 500)); + bot_manager->register_bot(std::make_unique("Spread_GOOGL", "GOOGL")); + bot_manager->register_bot(std::make_unique("RW_GOOGL", "GOOGL")); + bot_manager->register_bot(std::make_unique("MR_GOOGL", "GOOGL")); + bot_manager->register_bot(std::make_unique("Mom_GOOGL", "GOOGL")); + + // AMZN + bot_manager->register_bot(std::make_unique("MM_AMZN", "AMZN", 10000, 100, 500)); + bot_manager->register_bot(std::make_unique("Spread_AMZN", "AMZN")); + bot_manager->register_bot(std::make_unique("RW_AMZN", "AMZN")); + bot_manager->register_bot(std::make_unique("MR_AMZN", "AMZN")); + bot_manager->register_bot(std::make_unique("Mom_AMZN", "AMZN")); + + // META + bot_manager->register_bot(std::make_unique("MM_META", "META", 10000, 100, 500)); + bot_manager->register_bot(std::make_unique("Spread_META", "META")); + bot_manager->register_bot(std::make_unique("RW_META", "META")); + bot_manager->register_bot(std::make_unique("MR_META", "META")); + bot_manager->register_bot(std::make_unique("Mom_META", "META")); // Worker threads std::vector workers; @@ -137,6 +97,7 @@ int main() { workers.emplace_back([&e]() { while (g_running) { e->run_match_cycle(); + std::this_thread::yield(); } }); } @@ -178,24 +139,62 @@ int main() { } } } + + /* Periodically drain bot_storage of orders already processed (O(n)) + // Sweep bot storage array + auto it = bot_storage.begin(); + + while (it != bot_storage.end()) { + if ((*it)->status != OrderStatus::NEW) { + it = bot_storage.erase(it); + // std::cout << "[CLEAN] bot_storage slot returned to pool" << std::endl; + } else { + ++it; + } + } + */ } - std::this_thread::sleep_for(std::chrono::milliseconds(5)); + + // Swap processed orders to separate list & clear (faster than O(n)) + std::vector> next_storage; + next_storage.reserve(bot_storage.size()); + + for (auto& ptr : bot_storage) { + if (ptr->status == OrderStatus::NEW || ptr->status == OrderStatus::PARTIAL) { + next_storage.push_back(std::move(ptr)); + } + // Finished orders: ptr destructs, slot returned to pool + } + + // Swap back + bot_storage = std::move(next_storage); + + if (!g_running) break; + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - }); - // Feed thread (process ITCH file) - std::thread feed_thread([&]() { - feed_handler.process_file("test_data/NASDAQ_ITCH50_sample.bin"); + std::cout << "Exiting snapshot thread" << std::endl; }); - feed_thread.join(); + // Start simulator + simulator.start(engines, [](SymbolEngine& engine, int64_t bid, int64_t ask) { + static std::unordered_map counters; + int& count = counters[engine.symbol()]; - std::this_thread::sleep_for(std::chrono::seconds(2)); - g_running = false; + // Log every 100th tick + if (++count % 100 == 0) { + std::cout << "============= COUNT " << count << " ==============" << std::endl; + std::cout << engine.symbol() << " bid=" << bid << " ask=" << ask << std::endl; + } + }); - snapshot_thread.join(); + std::cout << "=== MARKET OPEN ===" << std::endl; + std::cout << "Trading for " << duration_minutes << " minutes..." << std::endl; + std::this_thread::sleep_for(std::chrono::minutes(duration_minutes)); + std::cout << "=== MARKET CLOSE ===" << std::endl; - for (auto& t : workers) t.join(); + g_running = false; // Final drain for (auto& e : engines) { @@ -204,6 +203,14 @@ int main() { } } + simulator.stop(); + + if (snapshot_thread.joinable()) { + snapshot_thread.join(); + } + + for (auto& t : workers) t.join(); + std::cout << "\n=== FINAL STATS ===\n"; for (auto& e : engines) { auto s = e->get_stats(); @@ -211,6 +218,10 @@ int main() { << " matched=" << s.orders_matched << " rejected=" << s.orders_rejected << " partial=" << s.orders_partially_filled + << " bid=" << e->order_book().best_bid() + << " ask=" << e->order_book().best_ask() + << " bid_depth="<< e->order_book().bid_depth() + << " ask_depth="<< e->order_book().ask_depth() << "\n"; } diff --git a/src/sim/env/market_sim.cpp b/src/sim/env/market_sim.cpp new file mode 100644 index 0000000..0669bbd --- /dev/null +++ b/src/sim/env/market_sim.cpp @@ -0,0 +1,182 @@ +#include "velox/sim/env/market_sim.hpp" +#include + +namespace velox { +namespace env { + +void MarketSimulator::start(std::vector>& engines, PriceCallback callback) { + m_running = true; + + m_thread = std::thread([this, &engines, callback]() { + // Price per symbol (random walk) + std::unordered_map current_prices; + + // Initialize resting orders for each symbol before starting simulation + for (auto& e : engines) { + current_prices[e->symbol()] = m_initial_price; + initialize_resting_orders(*e, m_initial_price); + } + + std::normal_distribution delta_dist(0.0, m_volatility / 3.0); + + while (m_running) { + auto start = std::chrono::steady_clock::now(); + + for (auto& e : engines) { + // Random walk + double delta = delta_dist(m_gen); + int64_t new_price = current_prices[e->symbol()] + static_cast(delta); + if (new_price < 100) new_price = 100; + if (new_price > 1000000) new_price = 1000000; + current_prices[e->symbol()] = new_price; + + int64_t spread = m_spread_dist(m_gen); + int64_t bid = new_price - spread / 2; + int64_t ask = new_price + spread / 2; + if (bid < 1) bid = 1; + if (ask > 999999) ask = 999999; + + e->set_market_price(bid, ask); + + // Only update resting orders when price changes significantly + static std::unordered_map last_update_price; + if (std::abs(new_price - last_update_price[e->symbol()]) > 10) { + update_resting_orders(*e, bid, ask); + last_update_price[e->symbol()] = new_price; + } + + if (callback) callback(*e, bid, ask); + } + + auto elapsed = std::chrono::steady_clock::now() - start; + if (elapsed < m_tick_interval) { + std::this_thread::sleep_for(m_tick_interval - elapsed); + } + } + }); +} + +void MarketSimulator::stop() { + m_running = false; + + if (m_thread.joinable()) m_thread.join(); + + clear_resting_orders(); +} + +void MarketSimulator::initialize_resting_orders(SymbolEngine& engine, int64_t initial_price) { + SymbolOrders orders; + int64_t bid_price = initial_price - 10; + int64_t ask_price = initial_price + 10; + + // Create resting bid order + auto bid_order = m_order_pool.acquire(); + if (!bid_order) throw std::runtime_error("Pool exhausted during init"); + bid_order->order_id = ++m_next_order_id; + bid_order->side = OrderSide::BUY; + bid_order->price = bid_price; + bid_order->quantity = m_resting_quantity; + bid_order->remaining_quantity = m_resting_quantity; + bid_order->filled_quantity = 0; + bid_order->status = OrderStatus::NEW; + std::strncpy(bid_order->symbol, engine.symbol(), 7); + engine.get_order_book().add_order(bid_order.get()); + + // Create resting ask order + auto ask_order = m_order_pool.acquire(); + if (!bid_order) throw std::runtime_error("Pool exhausted during init"); + ask_order->order_id = ++m_next_order_id; + ask_order->side = OrderSide::SELL; + ask_order->price = ask_price; + ask_order->quantity = m_resting_quantity; + ask_order->remaining_quantity = m_resting_quantity; + ask_order->filled_quantity = 0; + ask_order->status = OrderStatus::NEW; + std::strncpy(ask_order->symbol, engine.symbol(), 7); + engine.get_order_book().add_order(ask_order.get()); + + // Store active PooledPtrs (overwrites any previous) + auto active = std::make_unique(); + active->bid = std::move(bid_order); + active->ask = std::move(ask_order); + m_active_orders[engine.symbol()] = std::move(active); + + // Store for later updates + orders.bid_order_id = m_next_order_id - 1; + orders.ask_order_id = m_next_order_id; + orders.current_bid_price = bid_price; + orders.current_ask_price = ask_price; + + m_symbol_orders[engine.symbol()] = std::move(orders); +} + +void MarketSimulator::update_resting_orders(SymbolEngine& engine, int64_t bid_price, int64_t ask_price) { + auto it = m_symbol_orders.find(engine.symbol()); + if (it == m_symbol_orders.end()) return; + + auto& orders = it->second; + + // If prices haven't changed, nothing to do + if (orders.current_bid_price == bid_price && orders.current_ask_price == ask_price) { + return; + } + + // Cancel old orders + engine.get_order_book().cancel_order(orders.bid_order_id); + engine.get_order_book().cancel_order(orders.ask_order_id); + + // Create new resting bid order + auto bid_order = m_order_pool.acquire(); + if (!bid_order) { + std::cerr << "ERROR: Pool exhausted. Aborting update.\n"; + return; + } + + bid_order->order_id = ++m_next_order_id; + bid_order->side = OrderSide::BUY; + bid_order->price = bid_price; + bid_order->quantity = m_resting_quantity; + bid_order->remaining_quantity = m_resting_quantity; + bid_order->filled_quantity = 0; + bid_order->status = OrderStatus::NEW; + std::strncpy(bid_order->symbol, engine.symbol(), 7); + engine.get_order_book().add_order(bid_order.get()); + + // Create new resting ask order + auto ask_order = m_order_pool.acquire(); + if (!bid_order) { + std::cerr << "ERROR: Pool exhausted. Aborting update.\n"; + return; + } + + ask_order->order_id = ++m_next_order_id; + ask_order->side = OrderSide::SELL; + ask_order->price = ask_price; + ask_order->quantity = m_resting_quantity; + ask_order->remaining_quantity = m_resting_quantity; + ask_order->filled_quantity = 0; + ask_order->status = OrderStatus::NEW; + std::strncpy(ask_order->symbol, engine.symbol(), 7); + engine.get_order_book().add_order(ask_order.get()); + + // Replace active orders (pooled pointers go out of scope here) + auto active = std::make_unique(); + active->bid = std::move(bid_order); + active->ask = std::move(ask_order); + m_active_orders[engine.symbol()] = std::move(active); + + // Update metadata + orders.bid_order_id = m_next_order_id - 1; + orders.ask_order_id = m_next_order_id; + orders.current_bid_price = bid_price; + orders.current_ask_price = ask_price; + m_symbol_orders[engine.symbol()] = orders; +} + +void MarketSimulator::clear_resting_orders() { + m_active_orders.clear(); + m_symbol_orders.clear(); +} + +} +} \ No newline at end of file diff --git a/src/strategy/bot.cpp b/src/sim/strategy/bot.cpp similarity index 100% rename from src/strategy/bot.cpp rename to src/sim/strategy/bot.cpp diff --git a/src/strategy/bot_manager.cpp b/src/sim/strategy/bot_manager.cpp similarity index 74% rename from src/strategy/bot_manager.cpp rename to src/sim/strategy/bot_manager.cpp index cd2abaf..a33b9a5 100644 --- a/src/strategy/bot_manager.cpp +++ b/src/sim/strategy/bot_manager.cpp @@ -25,20 +25,13 @@ void BotManager::on_snapshot(const BookSnapshot& snapshot) { } */ void BotManager::on_snapshot(const BookSnapshot& snapshot) { - static int snap_count = 0; - if (snap_count++ < 5) { - std::cout << "[BotManager] on_snapshot for " << snapshot.symbol - << " best_bid=" << snapshot.best_bid << std::endl; - } - auto it = m_bots_by_symbol.find(snapshot.symbol); if (it == m_bots_by_symbol.end()) { - std::cout << "[BotManager] No bots for symbol " << snapshot.symbol << std::endl; + // std::cout << "[BotManager] No bots for symbol " << snapshot.symbol << std::endl; return; } - std::cout << "[BotManager] Found " << it->second.size() << " bots for " - << snapshot.symbol << std::endl; + //std::cout << "[BotManager] Found " << it->second.size() << " bots for " << snapshot.symbol << std::endl; for (auto* bot : it->second) { bot->on_snapshot(snapshot);