Documentation
ΒΆ
Overview ΒΆ
Package spot-engine provides a high-performance, deterministic matching engine for spot trading.
Overview ΒΆ
The spot-engine is a production-ready matching engine designed for cryptocurrency and financial exchanges. It implements a complete order book with advanced features including iceberg orders, order amendments, time-in-force policies, and state management.
Key Features ΒΆ
- High Performance: ~23ns best price lookup with zero allocations
- Deterministic Replay: All events use upstream-assigned timestamps
- Time-in-Force (TIF): GTC, IOC, FOK, and PostOnly support
- Iceberg Orders: Hide order quantity with automatic replenishment
- Order Amendments: Modify orders with proper priority rules
- Market Management: Create, suspend, resume markets with state enforcement
- Snapshot & Restore: Point-in-time recovery with CRC32 validation
- Event Logging: Comprehensive audit trail for all operations
Architecture ΒΆ
The engine follows a single-threaded event-driven architecture for determinism:
βββββββββββββββββββββββββββββββββββββββββββββββ β MatchingEngine β β βββββββββββ βββββββββββ βββββββββββ β β β Market β β Market β β Market β β β β BTC-USD β β ETH-USD β β SOL-USD β β β ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ β β β β β β β ββββββΌβββββββββββββΌβββββββββββββΌβββββ β β β OrderBook (B-Tree) β β β β ββββββββ ββββββββ β β β β β Bids β β Asks β β β β β ββββββββ ββββββββ β β β βββββββββββββββββββββββββββββββββββββ β β βββββββββββββββββββββββββββββββββββββ β β β Matcher β β β β Execute β Amend β Cancel β β β ββββββββββββ¬βββββββββββββββββββββββββ β β βΌ β β ββββββββββββββββββββββββββββββββββββ β β β Event Publisher β β β β (Trade, Fill, Cancel, Reject) β β β ββββββββββββββββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββ
Quick Start ΒΆ
Basic usage with a single market:
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/order"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
// Create matching engine
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
// Start engine
eng.Start()
defer eng.Stop()
// Listen to events
go func() {
for log := range publisher.Channel() {
switch log.LogType {
case event.LogTypeTrade:
fmt.Printf("Trade: %s @ %s\n",
log.TradeQuantity, log.TradePrice)
case event.LogTypeFill:
fmt.Printf("Fill: Order %s, %s\n",
log.OrderID, log.FillQuantity)
}
}
}()
// Create market
ctx := context.Background()
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-1",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
future.Wait(ctx)
// Place orders
placeReq := &protocol.PlaceOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-2",
UserID: 1001,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-1",
Side: "buy",
OrderType: "limit",
Price: decimal.NewFromInt(50000),
Size: decimal.NewFromFloat(0.1),
}
eng.SubmitOrder(placeReq)
}
Performance ΒΆ
Benchmark results on Intel Core i5-3330 @ 3.00GHz:
- BestBid/BestAsk: 23ns, 0 allocations (44M ops/sec)
- Market Order: 288ns (3.5M ops/sec)
- Limit Order: 687ns (1.5M ops/sec)
- Full Match: 3.2Β΅s with event emission
The engine achieves sub-microsecond latency for critical operations, making it suitable for high-frequency trading applications.
Packages ΒΆ
The SDK is organized into focused packages:
- book: OrderBook and price level management using B-Tree
- engine: Core engine and multi-market orchestration
- event: Event logging and publishing system
- matcher: Order matching logic and execution
- order: Order types and validation
- protocol: Request/response protocol with validation
- queue: Ring buffer for command queue
- snapshot: Snapshot and restore functionality
- trade: Trade record structures
Design Principles ΒΆ
1. Deterministic Replay: All timestamps come from upstream, no time.Now() 2. Event Sourcing: Every state change emits an immutable event log 3. Type Safety: Strongly-typed requests with validation before processing 4. State Enforcement: Market states (running/suspended/halted) strictly enforced 5. Precision Control: Decimal arithmetic, no float rounding errors
Event Sourcing ΒΆ
All operations emit events for audit trail and replay:
- Trade: A match between two orders
- Fill: Partial or full order execution
- Cancel: Order cancelled by user or system
- Reject: Order rejected due to validation or state
- Admin: Market state changes (suspend, resume, halt)
- Replenish: Iceberg order replenishment
Events include CommandID for idempotency and Timestamp for ordering.
Thread Safety ΒΆ
The engine uses a single-threaded event loop for deterministic execution. Multiple markets run on the same thread, ensuring consistent ordering. External callers submit commands via a ring buffer, and receive results via Future objects or event callbacks.
Snapshot & Recovery ΒΆ
The engine supports point-in-time snapshots for disaster recovery:
// Take snapshot
snapshots, seqID := eng.TakeSnapshot()
writer := snapshot.NewWriter("./snapshots")
writer.WriteSnapshot(snapshots, seqID)
// Restore from snapshot
reader := snapshot.NewReader("./snapshots")
metadata, snapshots, _ := reader.ReadSnapshot()
eng.RestoreFromSnapshot(snapshots)
Snapshots include CRC32 checksums for integrity validation and use atomic file writes (temp + rename) to prevent corruption.
Production Readiness ΒΆ
The engine has been thoroughly tested and benchmarked:
- 142 unit tests with 97.7% coverage on critical paths
- 12+ integration examples demonstrating real-world usage
- Comprehensive benchmarks showing HFT-grade performance
- Production-ready audit score: 9.1/10
See PRODUCTION_READINESS_AUDIT.md for detailed analysis.
License ΒΆ
MIT License - see LICENSE file for details.
Example ΒΆ
Example demonstrates basic usage of the matching engine
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
// Create matching engine
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
// Start engine
eng.Start()
defer eng.Stop()
// Create market
ctx := context.Background()
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-create",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
_, _ = future.Wait(ctx)
fmt.Println("Market created")
}
Output: Market created
Example (AmendOrder) ΒΆ
Example_amendOrder demonstrates order amendment
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
eng.Start()
defer eng.Stop()
ctx := context.Background()
// Create market
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-1",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
future.Wait(ctx)
// Place original order
placeReq := &protocol.PlaceOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-2",
UserID: 1001,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-1",
Side: "buy",
OrderType: "limit",
Price: decimal.NewFromInt(49000),
Size: decimal.NewFromFloat(0.1),
}
eng.SubmitOrder(placeReq)
time.Sleep(10 * time.Millisecond) // Let order process
// Amend order (reduce size, keep priority)
amendReq := &protocol.AmendOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-3",
UserID: 1001,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-1",
NewPrice: decimal.NewFromInt(49000), // Same price
NewSize: decimal.NewFromFloat(0.05), // Reduce size
}
eng.SubmitOrder(amendReq)
fmt.Println("Order amended")
}
Output: Order amended
Example (Events) ΒΆ
Example_events demonstrates listening to engine events
package main
import (
"fmt"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
)
func main() {
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
// Listen to events
go func() {
for log := range publisher.Channel() {
switch log.LogType {
case event.LogTypeTrade:
fmt.Printf("Trade: %s @ %s\n", log.TradeQuantity, log.TradePrice)
case event.LogTypeFill:
fmt.Printf("Fill: Order %s\n", log.OrderID)
case event.LogTypeCancel:
fmt.Printf("Cancel: Order %s\n", log.OrderID)
case event.LogTypeReject:
fmt.Printf("Reject: %s\n", log.RejectReason)
}
}
}()
eng.Start()
defer eng.Stop()
// Place orders...
fmt.Println("Event listener started")
}
Output: Event listener started
Example (IcebergOrder) ΒΆ
Example_icebergOrder demonstrates iceberg order with hidden quantity
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
eng.Start()
defer eng.Stop()
ctx := context.Background()
// Create market
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-1",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
future.Wait(ctx)
// Place iceberg order (total 1.0, show 0.1 at a time)
icebergReq := &protocol.PlaceOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-2",
UserID: 1001,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-1",
Side: "sell",
OrderType: "limit",
Price: decimal.NewFromInt(50000),
Size: decimal.NewFromFloat(1.0),
VisibleSize: decimal.NewFromFloat(0.1), // Show 0.1 at a time
}
eng.SubmitOrder(icebergReq)
fmt.Println("Iceberg order placed")
}
Output: Iceberg order placed
Example (LimitOrder) ΒΆ
Example_limitOrder demonstrates placing limit orders
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
eng.Start()
defer eng.Stop()
ctx := context.Background()
// Create market
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-1",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
future.Wait(ctx)
// Place buy order
buyReq := &protocol.PlaceOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-2",
UserID: 1001,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-1",
Side: "buy",
OrderType: "limit",
Price: decimal.NewFromInt(50000),
Size: decimal.NewFromFloat(0.1),
}
eng.SubmitOrder(buyReq)
fmt.Println("Buy order placed")
}
Output: Buy order placed
Example (MarketOrder) ΒΆ
Example_marketOrder demonstrates market order execution
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
eng.Start()
defer eng.Stop()
ctx := context.Background()
// Create market
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-1",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
future.Wait(ctx)
// Place sell limit order first (liquidity)
sellReq := &protocol.PlaceOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-2",
UserID: 1001,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-1",
Side: "sell",
OrderType: "limit",
Price: decimal.NewFromInt(50000),
Size: decimal.NewFromFloat(0.1),
}
eng.SubmitOrder(sellReq)
time.Sleep(10 * time.Millisecond) // Let order process
// Place market buy order
buyReq := &protocol.PlaceOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-3",
UserID: 1002,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-2",
Side: "buy",
OrderType: "market",
Size: decimal.NewFromFloat(0.05),
}
eng.SubmitOrder(buyReq)
fmt.Println("Market order executed")
}
Output: Market order executed
Example (Snapshot) ΒΆ
Example_snapshot demonstrates taking and restoring snapshots
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
eng.Start()
defer eng.Stop()
ctx := context.Background()
// Create market
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-1",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
future.Wait(ctx)
time.Sleep(10 * time.Millisecond)
// Take snapshot
snapshots, seqID := eng.TakeSnapshot()
fmt.Printf("Snapshot taken: %d markets, seqID=%d\n", len(snapshots), seqID)
// Snapshots can be written to disk using snapshot.Writer
// and restored using snapshot.Reader
}
Output: Snapshot taken: 1 markets, seqID=0
Example (TimeInForce) ΒΆ
Example_timeInForce demonstrates IOC (Immediate-or-Cancel) order
package main
import (
"context"
"fmt"
"time"
"github.com/adimiuprix/spot-engine/engine"
"github.com/adimiuprix/spot-engine/event"
"github.com/adimiuprix/spot-engine/protocol"
"github.com/shopspring/decimal"
)
func main() {
publisher := event.NewChannelPublisher(10000)
eng := engine.NewMatchingEngine(publisher)
eng.Start()
defer eng.Stop()
ctx := context.Background()
// Create market
createReq := &protocol.CreateMarketRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-1",
UserID: 1000,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
MinLotSize: decimal.NewFromFloat(0.001),
}
future, _ := eng.CreateMarket(ctx, createReq)
future.Wait(ctx)
// Place IOC order (matches immediately, cancels rest)
iocReq := &protocol.PlaceOrderRequest{
BaseCommand: protocol.BaseCommand{
CommandID: "cmd-2",
UserID: 1001,
MarketID: "BTC-USDT",
Timestamp: time.Now().UnixNano(),
},
OrderID: "order-1",
Side: "buy",
OrderType: "limit",
Price: decimal.NewFromInt(50000),
Size: decimal.NewFromFloat(0.1),
}
// Note: TIF is set on the Order struct when converting from request
eng.SubmitOrder(iocReq)
fmt.Println("IOC order processed")
}
Output: IOC order processed
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
Package book provides the core order book implementation using efficient B-Tree data structures.
|
Package book provides the core order book implementation using efficient B-Tree data structures. |
|
Package engine provides the core matching engine and multi-market orchestration.
|
Package engine provides the core matching engine and multi-market orchestration. |
|
Package event provides event logging and publishing for the matching engine.
|
Package event provides event logging and publishing for the matching engine. |
|
example
|
|
|
amend
command
|
|
|
async_place_order
command
|
|
|
async_trading
command
|
|
|
auto_snapshot
command
|
|
|
iceberg
command
|
|
|
management
command
|
|
|
market_order
command
|
|
|
simple
command
|
|
|
snapshot_recovery_test
command
|
|
|
state_management
command
|
|
|
tif
command
|
|
|
trading
command
|
|
|
Package matcher implements the order matching logic and execution engine.
|
Package matcher implements the order matching logic and execution engine. |
|
Package order defines order types and related data structures.
|
Package order defines order types and related data structures. |
|
Package protocol defines the command protocol for interacting with the matching engine.
|
Package protocol defines the command protocol for interacting with the matching engine. |
|
Package snapshot provides point-in-time snapshot and restore functionality.
|
Package snapshot provides point-in-time snapshot and restore functionality. |