BAEngine - bid/ask market engine
This repo is reachable at https://github.com/mmirko/baengine.git
A concurrent financial market simulation engine written in Go, designed for modeling and simulating trading activities with agents, securities, and markets.
Overview
BAEngine is a high-performance trading simulation engine that implements a full order matching system with concurrent order processing, portfolio management, and real-time market depth visualization. It provides a framework for simulating multi-agent trading scenarios with limit orders, order books, and automated matching.
Features
- Concurrent Order Processing: Built with Go’s goroutines and channels for high-performance concurrent operations
- Multi-Agent Support: Multiple trading agents with individual portfolios
- Order Book Management: Separate bid and ask books with price-time priority
- Order Types: Limit orders with buy/sell directions (market orders in development)
- Portfolio Management: Automatic fund reservation and tracking for each agent
- Real-time Data Export:
- JSON depth charts via HTTP API
- Protocol Buffers export for efficient data serialization
- Debug Mode: Comprehensive logging for debugging trading scenarios
- Market Liquidity Tracking: Monitors spread profits and market liquidity
Project Structure
baengine/
├── baengine.go # Core engine implementation
├── agent.go # Agent structure and methods
├── agents.go # Agent registration and management
├── market.go # Market structure and trading logic
├── markets.go # Market registration and management
├── order.go # Order types and order list management
├── portfolio.go # Portfolio management with fund reservation
├── portfolios.go # Portfolio operations
├── security.go # Security definition
├── securities.go # Security registration
├── manager_default.go # Default order matching algorithm
├── depth_charts.go # Market depth chart exporters
├── logging.go # Logging utilities
├── *_test.go # Test files
├── go.mod # Go module definition
└── Makefile # Build configuration
frontend/
└── frontend_test.html # Example visualization with Plotly.js
Installation
Prerequisites
- Go 1.12 or higher
- godebug (for conditional debugging information)
Dependencies
go get github.com/golang/protobuf
go get github.com/mmirko/dpt
Usage
Basic Example
package main
import (
"baengine"
)
func main() {
// Initialize the engine
e := new(baengine.BAEngine)
e.Init()
// Optional: Enable debug mode for detailed logging
e.SetDebug()
// Register agents
agent1_id, _ := e.RegisterAgent("Trader1")
agent2_id, _ := e.RegisterAgent("Trader2")
// Register securities
sec1_symbol, _ := e.RegisterSecurity("Security 1", "SEC1")
sec2_symbol, _ := e.RegisterSecurity("Security 2", "SEC2")
// Register market (format: "SECURITY-CURRENCY")
market_symbol, _ := e.RegisterMarket("Main Market", "SEC1-SEC2")
// Deposit funds to agents
e.Deposit(agent1_id, sec1_symbol, 1000.0)
e.Deposit(agent2_id, sec2_symbol, 1000.0)
// Place orders
// Sell order: agent1 sells 5 units of SEC1 at price 3.0
oid1, _ := e.Order(agent1_id, baengine.SELL, market_symbol,
baengine.OLIMIT, 5.0, 3.0)
// Buy order: agent2 buys 5 units of SEC1 at price 3.04
oid2, _ := e.Order(agent2_id, baengine.BUY, market_symbol,
baengine.OLIMIT, 5.0, 3.04)
// View portfolios
e.DumpPortfolios()
}
Order Types
OLIMIT: Limit order with specified priceOMARKET: Market order (currently unimplemented)
Order Directions
BUY: Buy orderSELL: Sell order
Order States
SUBMITTED: Order submitted to the marketPARTIALLY: Order partially filledCOMPLETED: Order fully executed
API Reference
Engine Initialization
e := new(BAEngine)
e.Init() // Initialize the engine
e.SetDebug() // Enable debug logging
e.SetVerbose() // Enable verbose logging
Agent Management
// Register a new trading agent
agentID, err := e.RegisterAgent(name string) (uint16, error)
// Unregister an agent
agentID, err := e.UnRegisterAgent(agent_id uint16) (uint16, error)
// Get agent by ID
agent, err := e.AgentIndex(agent_id uint16) (*agent, error)
Security Management
// Register a new security
symbol, err := e.RegisterSecurity(name, symbol string) (string, error)
// Unregister a security
symbol, err := e.UnRegisterSecurity(symbol string) (string, error)
// Get security by symbol
security, err := e.SecurityIndex(symbol string) (*security, error)
Market Management
// Register a new market (symbol format: "SEC1-SEC2")
symbol, err := e.RegisterMarket(name, symbol string) (string, error)
// Unregister a market
symbol, err := e.UnRegisterMarket(symbol string) (string, error)
// Get market by symbol
market, err := e.MarketIndex(symbol string) (*market, error)
// Start/stop trading on a market
err := market.StartTrading()
err := market.StopTrading()
Portfolio Operations
// Deposit funds to an agent's portfolio
err := e.Deposit(agent_id uint16, security_symbol string, qty float32)
// Withdraw funds from an agent's portfolio
err := e.Withdrawn(agent_id uint16, security_symbol string, qty float32)
// Display all portfolios
e.DumpPortfolios()
Order Placement
// Place an order
orderID, err := e.Order(
agent_id uint16, // Agent placing the order
direction uint8, // BUY or SELL
market_symbol string, // Market symbol (e.g., "SEC1-SEC2")
order_type uint8, // OLIMIT or OMARKET
quantity float32, // Order quantity
price float32 // Order price
) (uint64, error)
Market Depth Visualization
HTTP API
The engine provides an HTTP endpoint for real-time market depth charts:
http.HandleFunc("/market/depthchart/", e.marketJsonDepthChartExporter)
http.ListenAndServe(":3000", nil)
Access depth chart: http://localhost:3000/market/depthchart/SEC1-SEC2
The response is a JSON object with x (prices) and y (cumulative quantities) arrays.
Frontend Example
See frontend/frontend_test.html for a complete example using Plotly.js to visualize market depth in real-time.
<script>
fetch('http://localhost:3000/market/depthchart/SEC1-SEC2')
.then(response => response.json())
.then(data => {
Plotly.restyle('graph', data, [0]);
});
</script>
Protocol Buffers Export
For high-performance data export:
e.marketProtobufDepthChartExporter(market_symbol, interval_ms, io.Writer)
Order Matching Algorithm
The engine uses a price-time priority matching algorithm:
-
BUY orders are matched against the ask book:
- If buy price ≥ ask price, orders are matched
- Market captures the spread if buy price > ask price
-
SELL orders are matched against the bid book:
- If sell price ≤ bid price, orders are matched
- Market captures the spread if sell price < bid price
-
Unmatched portions remain in the order book sorted by price priority
Concurrency Model
BAEngine leverages Go’s concurrency primitives for thread-safe operations:
- Goroutines: Each market, agent portfolio, and registration service runs in its own goroutine
- Channels: Used for communication between components (order submission, registration, fund reservation)
- RWMutex: Protects shared data structures (order books, agent lists, portfolios)
Testing
Run tests with the provided Makefile:
make test
Or run specific tests:
go test --run TestAgentRegistration
go test --run TestSecurityRegistration
go test --run TestMarketRegistration
go test --run TestTrading
go test --run TestExport
go test --run TestProtobuf
Clean up generated debug files:
make clean
Debug Mode
Enable debug mode for detailed execution logging:
e.SetDebug()
Debug logs include:
- Order submissions and matching details
- Portfolio reserve requests
- Order state changes
- Market liquidity updates
- Spread profits
The project uses conditional compilation with the GODEBUG build tag to include/exclude debug code.
Architecture
Core Components
- BAEngine: Central coordinator managing all components
- Agents: Trading entities with unique IDs and portfolios
- Securities: Tradable assets identified by symbols
- Markets: Order matching venues with bid/ask books
- Orders: Trade instructions with quantity, price, and state
- Portfolios: Asset tracking with automatic fund reservation
Data Flow
Agent → Order Request → Market → Order Matching → Portfolio Update
↓
Order Book (Bid/Ask)
↓
Depth Chart Export
Performance Considerations
- Order matching is performed sequentially within each market but markets operate concurrently
- Portfolio operations use channel-based reservation to prevent race conditions
- Order books use linked lists for efficient insertion/removal
- Read-write mutexes allow concurrent reads of order books for depth chart export
Limitations
- Market orders are not yet implemented
- No order cancellation functionality
- No stop-loss or conditional order types
- Markets must be in “accepting” state to receive orders
- Trading must be started explicitly on each market
Future Enhancements
Potential areas for expansion:
- Market order implementation
- Order cancellation and modification
- Stop-loss and take-profit orders
- Historical trade data export
- Advanced order matching algorithms
- WebSocket support for real-time updates
- More sophisticated market makers
- Multi-market order routing
License
This project is hosted at https://github.com/mmirko/baengine.
Contributing
When contributing, please:
- Run tests before submitting changes
- Follow Go best practices and formatting (
gofmt) - Add tests for new functionality
- Update documentation as needed
- Use the debug build tag for debugging code
Note: This is a simulation engine designed for research and educational purposes. It is not intended for production trading systems.