Created
January 5, 2026 09:21
-
-
Save jweinst1/c9ce66cdac04a1aef790db9f8383e24d to your computer and use it in GitHub Desktop.
Call Auction implementation with traders
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <vector> | |
| #include <map> | |
| #include <algorithm> | |
| struct Trader { | |
| int id; | |
| double cash; | |
| int inventory; | |
| }; | |
| struct Order { | |
| Trader* trader; | |
| bool is_buy; | |
| double price; // limit price | |
| int size; // quantity | |
| }; | |
| // Run a single call auction for a turn | |
| double run_call_auction(const std::vector<Order>& orders) { | |
| std::map<double, int> demand; // price -> cumulative buy size | |
| std::map<double, int> supply; // price -> cumulative sell size | |
| // Step 1: aggregate orders by price | |
| for (const auto& o : orders) { | |
| if (o.is_buy) | |
| demand[o.price] += o.size; | |
| else | |
| supply[o.price] += o.size; | |
| } | |
| // Step 2: create sorted unique price levels | |
| std::vector<double> prices; | |
| for (auto& [p, _] : demand) prices.push_back(p); | |
| for (auto& [p, _] : supply) prices.push_back(p); | |
| std::sort(prices.begin(), prices.end()); | |
| prices.erase(std::unique(prices.begin(), prices.end()), prices.end()); | |
| // Step 3: find clearing price (max volume) | |
| double clearing_price = prices[0]; | |
| int max_traded = 0; | |
| for (double p : prices) { | |
| int buy_vol = 0, sell_vol = 0; | |
| // cumulative buy at >= p | |
| for (auto& [bp, bs] : demand) | |
| if (bp >= p) buy_vol += bs; | |
| // cumulative sell at <= p | |
| for (auto& [sp, ss] : supply) | |
| if (sp <= p) sell_vol += ss; | |
| int traded = std::min(buy_vol, sell_vol); | |
| if (traded > max_traded) { | |
| max_traded = traded; | |
| clearing_price = p; | |
| } | |
| } | |
| // Step 4: execute trades | |
| if (max_traded > 0) { | |
| // Track remaining quantity per trader | |
| std::map<Trader*, int> buy_remaining; | |
| std::map<Trader*, int> sell_remaining; | |
| for (auto& o : orders) { | |
| if (o.is_buy && o.price >= clearing_price) | |
| buy_remaining[o.trader] += o.size; | |
| if (!o.is_buy && o.price <= clearing_price) | |
| sell_remaining[o.trader] += o.size; | |
| } | |
| int total_buy = max_traded; | |
| int total_sell = max_traded; | |
| // Simple proportional allocation for partial fills | |
| for (auto& [trader, size] : buy_remaining) { | |
| int trade_qty = size * total_buy / std::max(total_buy, 1); | |
| trader->cash -= trade_qty * clearing_price; | |
| trader->inventory += trade_qty; | |
| } | |
| for (auto& [trader, size] : sell_remaining) { | |
| int trade_qty = size * total_sell / std::max(total_sell, 1); | |
| trader->cash += trade_qty * clearing_price; | |
| trader->inventory -= trade_qty; | |
| } | |
| } | |
| std::cout << "Clearing price: " << clearing_price | |
| << " | Volume: " << max_traded << "\n"; | |
| return clearing_price; | |
| } | |
| int main() { | |
| Trader alice{1, 10000.0, 0}; | |
| Trader bob{2, 0.0, 100}; | |
| Trader charlie{3, 5000.0, 50}; | |
| // All orders generated in this turn | |
| std::vector<Order> orders = { | |
| {&alice, true, 101.0, 10}, | |
| {&bob, false, 101.0, 5}, | |
| {&charlie, false, 102.0, 10} | |
| }; | |
| run_call_auction(orders); | |
| std::cout << "\nTrader states after auction:\n"; | |
| std::cout << "Alice: cash=" << alice.cash << " inventory=" << alice.inventory << "\n"; | |
| std::cout << "Bob: cash=" << bob.cash << " inventory=" << bob.inventory << "\n"; | |
| std::cout << "Charlie: cash=" << charlie.cash << " inventory=" << charlie.inventory << "\n"; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment