Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ build/
build_*/
market_data/
cmake-build-*/
src/old_main.cpp
.vscode/
.idea/
*.swp
Expand Down
1 change: 1 addition & 0 deletions include/velox/book/order_book.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class OrderBook {
Order* match(Order* incoming_order, std::vector<Fill>& fills);
const std::vector<PriceLevel*>& get_bid_levels() const { return m_bid_levels; }
const std::vector<PriceLevel*>& 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); }
Expand Down
5 changes: 5 additions & 0 deletions include/velox/core/symbol_engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
118 changes: 118 additions & 0 deletions include/velox/sim/env/market_sim.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#pragma once
#include <atomic>
#include <thread>
#include <chrono>
#include <random>
#include <functional>
#include <unordered_map>
#include "velox/core/symbol_engine.hpp"
#include "lockfree/pool.hpp"

namespace velox {
namespace env {

// Store PooledPtrs separately for active orders
struct ActiveOrders {
lockfree::PooledPtr<Order, 100000> bid;
lockfree::PooledPtr<Order, 100000> 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<Order, 100000>&& b,
lockfree::PooledPtr<Order, 100000>&& 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<void(SymbolEngine&, int64_t bid, int64_t ask)>;

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<int>(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<std::unique_ptr<SymbolEngine>>& 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<int64_t> m_spread_dist;
int32_t m_resting_quantity;
std::mt19937 m_gen;
std::normal_distribution<double> m_dist;

// Flags
std::atomic<bool> 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<std::string, SymbolOrders> m_symbol_orders;




std::unordered_map<std::string, std::unique_ptr<ActiveOrders>> m_active_orders;

// Shared pool for simulator orders
lockfree::ObjectPool<Order, 100000> m_order_pool;
uint64_t m_next_order_id = 1000000;
};

}
}
21 changes: 10 additions & 11 deletions include/velox/sim/strategy/bots.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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) {}
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {}
Expand Down
7 changes: 5 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/book/order_book.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}
Loading
Loading