mirror of
https://github.com/luxfi/dex.git
synced 2026-09-24 00:26:31 +00:00
476 lines
15 KiB
Go
476 lines
15 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/luxfi/dex/pkg/lx"
|
|
)
|
|
|
|
// Global counters
|
|
var (
|
|
totalOrders int64
|
|
totalTrades int64
|
|
peakMemory uint64
|
|
currentMemory uint64
|
|
)
|
|
|
|
func main() {
|
|
// Configure runtime
|
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
debug.SetGCPercent(100)
|
|
|
|
fmt.Println("╔══════════════════════════════════════════════════════════════════╗")
|
|
fmt.Println("║ LX DEX Production Memory Test - 21,000 Global Markets ║")
|
|
fmt.Println("╚══════════════════════════════════════════════════════════════════╝")
|
|
fmt.Println()
|
|
fmt.Println("Configuration:")
|
|
fmt.Println(" • 11,000 Securities (US Stocks, ETFs, International)")
|
|
fmt.Println(" • 10,000 Crypto Markets (Spot, Futures, Perpetuals)")
|
|
fmt.Println(" • Target Hardware: Mac Studio M2 Ultra (512GB)")
|
|
fmt.Println(" • Expected Load: 1M+ orders, 100K+ trades/sec")
|
|
fmt.Println()
|
|
|
|
// Initial state
|
|
runtime.GC()
|
|
debug.FreeOSMemory()
|
|
var m runtime.MemStats
|
|
runtime.ReadMemStats(&m)
|
|
startMem := m.Alloc
|
|
|
|
// Phase 1: Market Creation
|
|
fmt.Println("【Phase 1】Creating Global Markets")
|
|
fmt.Println("─────────────────────────────────")
|
|
markets := createGlobalMarkets()
|
|
|
|
runtime.GC()
|
|
runtime.ReadMemStats(&m)
|
|
phase1Mem := m.Alloc - startMem
|
|
|
|
fmt.Printf("✓ Securities Markets: 11,000\n")
|
|
fmt.Printf("✓ Crypto Markets: 10,000\n")
|
|
fmt.Printf("✓ Memory Used: %.2f MB\n", float64(phase1Mem)/(1024*1024))
|
|
fmt.Println()
|
|
|
|
// Phase 2: Order Population (Realistic Distribution)
|
|
fmt.Println("【Phase 2】Populating Order Books")
|
|
fmt.Println("─────────────────────────────────")
|
|
populateRealisticOrders(markets)
|
|
|
|
runtime.GC()
|
|
runtime.ReadMemStats(&m)
|
|
phase2Mem := m.Alloc - startMem
|
|
orderMem := phase2Mem - phase1Mem
|
|
|
|
fmt.Printf("✓ Total Orders: %d\n", atomic.LoadInt64(&totalOrders))
|
|
fmt.Printf("✓ Average Orders/Market: %.1f\n",
|
|
float64(atomic.LoadInt64(&totalOrders))/float64(len(markets)))
|
|
fmt.Printf("✓ Order Memory: %.2f GB\n", float64(orderMem)/(1024*1024*1024))
|
|
fmt.Println()
|
|
|
|
// Phase 3: Trading Simulation
|
|
fmt.Println("【Phase 3】Trading Simulation")
|
|
fmt.Println("─────────────────────────────────")
|
|
simulateProduction(markets)
|
|
|
|
runtime.GC()
|
|
runtime.ReadMemStats(&m)
|
|
finalMem := m.Alloc - startMem
|
|
|
|
// Final Analysis
|
|
fmt.Println()
|
|
fmt.Println("╔══════════════════════════════════════════════════════════════════╗")
|
|
fmt.Println("║ FINAL RESULTS ║")
|
|
fmt.Println("╚══════════════════════════════════════════════════════════════════╝")
|
|
fmt.Println()
|
|
|
|
fmt.Println("Market Statistics:")
|
|
fmt.Printf(" • Total Markets: %d\n", len(markets))
|
|
fmt.Printf(" • Total Orders: %d\n", atomic.LoadInt64(&totalOrders))
|
|
fmt.Printf(" • Total Trades: %d\n", atomic.LoadInt64(&totalTrades))
|
|
fmt.Println()
|
|
|
|
fmt.Println("Memory Analysis:")
|
|
fmt.Printf(" • Total Memory Used: %.2f GB\n", float64(finalMem)/(1024*1024*1024))
|
|
fmt.Printf(" • Per Market Average: %.2f MB\n", float64(finalMem)/(float64(len(markets))*1024*1024))
|
|
fmt.Printf(" • System Memory: %.2f GB\n", float64(m.Sys)/(1024*1024*1024))
|
|
fmt.Printf(" • Heap Objects: %d\n", m.HeapObjects)
|
|
fmt.Printf(" • GC Runs: %d\n", m.NumGC)
|
|
fmt.Println()
|
|
|
|
// Mac Studio Analysis
|
|
analyzeMacStudio(finalMem, len(markets))
|
|
|
|
// Performance Benchmarks
|
|
runPerformanceBenchmarks(markets)
|
|
}
|
|
|
|
func createGlobalMarkets() map[string]*lx.OrderBook {
|
|
markets := make(map[string]*lx.OrderBook, 21000)
|
|
|
|
// Top US Securities
|
|
topSecurities := []string{
|
|
"AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "META", "TSLA", "BRK.B",
|
|
"V", "JPM", "JNJ", "WMT", "PG", "UNH", "HD", "MA", "DIS", "BAC",
|
|
"CVX", "ABBV", "PFE", "AVGO", "CSCO", "MRK", "TMO", "LLY", "PEP",
|
|
"COST", "ORCL", "ACN", "ADBE", "NKE", "NFLX", "ABT", "TXN", "CRM",
|
|
"DHR", "VZ", "NEE", "CMCSA", "XOM", "INTC", "WFC", "QCOM", "T",
|
|
"AMD", "UPS", "PM", "RTX", "LOW", "INTU", "BMY", "HON", "SPGI",
|
|
"ELV", "UNP", "CVS", "SCHW", "GS", "MS", "BA", "MDT", "AXP", "BLK",
|
|
"AMGN", "CAT", "SBUX", "AMT", "IBM", "GE", "GILD", "DE", "MU", "LMT",
|
|
}
|
|
|
|
// Create securities
|
|
for i := 0; i < 11000; i++ {
|
|
var symbol string
|
|
if i < len(topSecurities) {
|
|
symbol = topSecurities[i]
|
|
} else {
|
|
// Generate remaining symbols
|
|
symbol = fmt.Sprintf("SEC%04d", i)
|
|
}
|
|
markets[symbol] = lx.NewOrderBook(symbol)
|
|
}
|
|
|
|
// Top Crypto Markets
|
|
topCrypto := []string{
|
|
"BTC/USD", "ETH/USD", "BNB/USD", "XRP/USD", "SOL/USD",
|
|
"ADA/USD", "AVAX/USD", "DOGE/USD", "TRX/USD", "DOT/USD",
|
|
"MATIC/USD", "SHIB/USD", "LTC/USD", "UNI/USD", "LINK/USD",
|
|
"BCH/USD", "NEAR/USD", "XLM/USD", "ATOM/USD", "XMR/USD",
|
|
"ETC/USD", "APT/USD", "FIL/USD", "LDO/USD", "ARB/USD",
|
|
"ICP/USD", "HBAR/USD", "VET/USD", "MKR/USD", "QNT/USD",
|
|
}
|
|
|
|
// Create crypto markets
|
|
for i := 0; i < 10000; i++ {
|
|
var symbol string
|
|
if i < len(topCrypto) {
|
|
symbol = topCrypto[i]
|
|
} else {
|
|
// Generate synthetic pairs
|
|
base := fmt.Sprintf("TOK%d", i)
|
|
quote := []string{"USD", "USDT", "BTC", "ETH"}[i%4]
|
|
symbol = fmt.Sprintf("%s/%s", base, quote)
|
|
}
|
|
markets[symbol] = lx.NewOrderBook(symbol)
|
|
}
|
|
|
|
return markets
|
|
}
|
|
|
|
func populateRealisticOrders(markets map[string]*lx.OrderBook) {
|
|
// Market distribution (based on real-world data):
|
|
// Top 20 markets: 1000-5000 orders each (very liquid)
|
|
// Top 100 markets: 500-1000 orders each (liquid)
|
|
// Top 1000 markets: 100-500 orders each (moderate)
|
|
// Top 5000 markets: 20-100 orders each (thin)
|
|
// Rest: 5-20 orders each (very thin)
|
|
|
|
marketList := make([]*lx.OrderBook, 0, len(markets))
|
|
for _, book := range markets {
|
|
marketList = append(marketList, book)
|
|
}
|
|
|
|
// Use goroutines for parallel population
|
|
var wg sync.WaitGroup
|
|
numWorkers := runtime.NumCPU()
|
|
marketsPerWorker := len(marketList) / numWorkers
|
|
|
|
for w := 0; w < numWorkers; w++ {
|
|
start := w * marketsPerWorker
|
|
end := start + marketsPerWorker
|
|
if w == numWorkers-1 {
|
|
end = len(marketList)
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func(books []*lx.OrderBook, startIdx int) {
|
|
defer wg.Done()
|
|
|
|
for i, book := range books {
|
|
globalIdx := startIdx + i
|
|
|
|
// Determine order count based on market liquidity tier
|
|
var orderCount int
|
|
switch {
|
|
case globalIdx < 20:
|
|
orderCount = 1000 + rand.Intn(4000) // 1000-5000 (ultra liquid)
|
|
case globalIdx < 100:
|
|
orderCount = 500 + rand.Intn(500) // 500-1000 (very liquid)
|
|
case globalIdx < 1000:
|
|
orderCount = 100 + rand.Intn(400) // 100-500 (liquid)
|
|
case globalIdx < 5000:
|
|
orderCount = 20 + rand.Intn(80) // 20-100 (moderate)
|
|
default:
|
|
orderCount = 5 + rand.Intn(15) // 5-20 (thin)
|
|
}
|
|
|
|
// Generate realistic price/size distribution
|
|
midPrice := 100.0 + rand.Float64()*1000
|
|
spread := midPrice * 0.0005 // 0.05% typical spread
|
|
|
|
// Add bid orders (buy side)
|
|
for j := 0; j < orderCount/2; j++ {
|
|
price := midPrice - spread*(1+rand.Float64()*20)
|
|
size := generateRealisticSize()
|
|
|
|
order := &lx.Order{
|
|
ID: uint64(globalIdx*10000 + j),
|
|
Type: lx.Limit,
|
|
Side: lx.Buy,
|
|
Price: price,
|
|
Size: size,
|
|
User: fmt.Sprintf("mm%d", j%20), // Market makers
|
|
Timestamp: time.Now(),
|
|
}
|
|
book.AddOrder(order)
|
|
atomic.AddInt64(&totalOrders, 1)
|
|
}
|
|
|
|
// Add ask orders (sell side)
|
|
for j := 0; j < orderCount/2; j++ {
|
|
price := midPrice + spread*(1+rand.Float64()*20)
|
|
size := generateRealisticSize()
|
|
|
|
order := &lx.Order{
|
|
ID: uint64(globalIdx*10000 + orderCount/2 + j),
|
|
Type: lx.Limit,
|
|
Side: lx.Sell,
|
|
Price: price,
|
|
Size: size,
|
|
User: fmt.Sprintf("mm%d", j%20),
|
|
Timestamp: time.Now(),
|
|
}
|
|
book.AddOrder(order)
|
|
atomic.AddInt64(&totalOrders, 1)
|
|
}
|
|
}
|
|
}(marketList[start:end], start)
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func generateRealisticSize() float64 {
|
|
// Realistic size distribution (power law)
|
|
r := rand.Float64()
|
|
switch {
|
|
case r < 0.5:
|
|
return float64(1 + rand.Intn(10)) // Small: 1-10
|
|
case r < 0.8:
|
|
return float64(10 + rand.Intn(90)) // Medium: 10-100
|
|
case r < 0.95:
|
|
return float64(100 + rand.Intn(900)) // Large: 100-1000
|
|
default:
|
|
return float64(1000 + rand.Intn(9000)) // Very large: 1000-10000
|
|
}
|
|
}
|
|
|
|
func simulateProduction(markets map[string]*lx.OrderBook) {
|
|
// Simulate realistic trading patterns
|
|
marketList := make([]*lx.OrderBook, 0, len(markets))
|
|
for _, book := range markets {
|
|
marketList = append(marketList, book)
|
|
}
|
|
|
|
// Run for 5 seconds
|
|
duration := 5 * time.Second
|
|
endTime := time.Now().Add(duration)
|
|
|
|
// Progress tracker
|
|
startTime := time.Now()
|
|
go func() {
|
|
ticker := time.NewTicker(time.Second)
|
|
defer ticker.Stop()
|
|
|
|
for time.Now().Before(endTime) {
|
|
<-ticker.C
|
|
trades := atomic.LoadInt64(&totalTrades)
|
|
elapsed := time.Since(startTime).Seconds()
|
|
if elapsed > 0 {
|
|
fmt.Printf(" Trades/sec: %.0f\n", float64(trades)/elapsed)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Trading workers
|
|
var wg sync.WaitGroup
|
|
numTraders := runtime.NumCPU() * 2
|
|
|
|
for t := 0; t < numTraders; t++ {
|
|
wg.Add(1)
|
|
go func(traderID int) {
|
|
defer wg.Done()
|
|
|
|
for time.Now().Before(endTime) {
|
|
// Pick market with realistic distribution
|
|
// 80% of volume in top 20% of markets
|
|
var marketIdx int
|
|
if rand.Float64() < 0.8 {
|
|
marketIdx = rand.Intn(len(marketList) / 5)
|
|
} else {
|
|
marketIdx = rand.Intn(len(marketList))
|
|
}
|
|
|
|
book := marketList[marketIdx]
|
|
|
|
// Create market order
|
|
order := &lx.Order{
|
|
ID: uint64(time.Now().UnixNano()),
|
|
Type: lx.Market,
|
|
Side: lx.Side(rand.Intn(2)),
|
|
Size: float64(1 + rand.Intn(100)),
|
|
User: fmt.Sprintf("trader%d", traderID),
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
trades := book.AddOrder(order)
|
|
if trades > 0 {
|
|
atomic.AddInt64(&totalTrades, int64(trades))
|
|
}
|
|
|
|
// Realistic trading frequency
|
|
time.Sleep(time.Microsecond * time.Duration(10+rand.Intn(90)))
|
|
}
|
|
}(t)
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func analyzeMacStudio(memoryUsed uint64, numMarkets int) {
|
|
fmt.Println("╔══════════════════════════════════════════════════════════════════╗")
|
|
fmt.Println("║ Mac Studio M2 Ultra (512GB) Capability ║")
|
|
fmt.Println("╚══════════════════════════════════════════════════════════════════╝")
|
|
fmt.Println()
|
|
|
|
memPerMarketMB := float64(memoryUsed) / (float64(numMarkets) * 1024 * 1024)
|
|
memUsedGB := float64(memoryUsed) / (1024 * 1024 * 1024)
|
|
|
|
// Mac Studio configs
|
|
configs := []struct {
|
|
name string
|
|
memoryGB int
|
|
available int // After OS overhead
|
|
}{
|
|
{"Mac Studio M2 Max (64GB)", 64, 56},
|
|
{"Mac Studio M2 Max (96GB)", 96, 86},
|
|
{"Mac Studio M2 Ultra (128GB)", 128, 115},
|
|
{"Mac Studio M2 Ultra (192GB)", 192, 175},
|
|
{"Mac Studio M2 Ultra (512GB)", 512, 462},
|
|
}
|
|
|
|
fmt.Println("Current 21,000 Market Performance:")
|
|
fmt.Printf(" • Memory per market: %.3f MB\n", memPerMarketMB)
|
|
fmt.Printf(" • Total memory used: %.2f GB\n", memUsedGB)
|
|
fmt.Printf(" • Percentage of 512GB: %.2f%%\n", memUsedGB*100/512)
|
|
fmt.Println()
|
|
|
|
fmt.Println("Capacity Analysis by Configuration:")
|
|
for _, cfg := range configs {
|
|
maxMarkets := int(float64(cfg.available*1024) / memPerMarketMB)
|
|
fmt.Printf("\n%s:\n", cfg.name)
|
|
fmt.Printf(" • Available memory: %d GB\n", cfg.available)
|
|
fmt.Printf(" • Max markets: %d\n", maxMarkets)
|
|
|
|
if maxMarkets >= 1000000 {
|
|
fmt.Printf(" • Status: ✅ Can handle 1M+ markets\n")
|
|
} else if maxMarkets >= 100000 {
|
|
fmt.Printf(" • Status: ✅ Can handle 100K+ markets\n")
|
|
} else if maxMarkets >= 21000 {
|
|
fmt.Printf(" • Status: ✅ Can handle current load\n")
|
|
} else {
|
|
fmt.Printf(" • Status: ⚠️ Below current requirements\n")
|
|
}
|
|
}
|
|
|
|
fmt.Println()
|
|
fmt.Println("Scaling Projections:")
|
|
scales := []int{21000, 50000, 100000, 250000, 500000, 1000000}
|
|
for _, scale := range scales {
|
|
memRequired := memPerMarketMB * float64(scale) / 1024
|
|
fmt.Printf(" %7d markets: %7.1f GB", scale, memRequired)
|
|
|
|
// Find minimum config needed
|
|
minConfig := "Distributed"
|
|
for _, cfg := range configs {
|
|
if float64(cfg.available) >= memRequired {
|
|
minConfig = cfg.name
|
|
break
|
|
}
|
|
}
|
|
fmt.Printf(" → %s\n", minConfig)
|
|
}
|
|
}
|
|
|
|
func runPerformanceBenchmarks(markets map[string]*lx.OrderBook) {
|
|
fmt.Println()
|
|
fmt.Println("╔══════════════════════════════════════════════════════════════════╗")
|
|
fmt.Println("║ Performance Benchmarks ║")
|
|
fmt.Println("╚══════════════════════════════════════════════════════════════════╝")
|
|
fmt.Println()
|
|
|
|
marketList := make([]*lx.OrderBook, 0, len(markets))
|
|
for _, book := range markets {
|
|
marketList = append(marketList, book)
|
|
}
|
|
|
|
// Benchmark 1: Order placement
|
|
start := time.Now()
|
|
numOps := 100000
|
|
for i := 0; i < numOps; i++ {
|
|
book := marketList[i%len(marketList)]
|
|
order := &lx.Order{
|
|
ID: uint64(i),
|
|
Type: lx.Limit,
|
|
Side: lx.Buy,
|
|
Price: 100.0,
|
|
Size: 10,
|
|
User: "bench",
|
|
Timestamp: time.Now(),
|
|
}
|
|
book.AddOrder(order)
|
|
}
|
|
orderTime := time.Since(start)
|
|
|
|
// Benchmark 2: Best price queries
|
|
start = time.Now()
|
|
for i := 0; i < numOps; i++ {
|
|
book := marketList[i%len(marketList)]
|
|
_ = book.GetBestBid()
|
|
_ = book.GetBestAsk()
|
|
}
|
|
queryTime := time.Since(start)
|
|
|
|
// Benchmark 3: Snapshot generation
|
|
start = time.Now()
|
|
for i := 0; i < 1000; i++ {
|
|
book := marketList[i%len(marketList)]
|
|
_ = book.GetSnapshot()
|
|
}
|
|
snapshotTime := time.Since(start)
|
|
|
|
fmt.Println("Operation Latencies:")
|
|
fmt.Printf(" • Order Placement: %.2f µs/op (%d ops/sec)\n",
|
|
float64(orderTime.Nanoseconds())/float64(numOps)/1000,
|
|
int(float64(numOps)/orderTime.Seconds()))
|
|
fmt.Printf(" • Best Price Query: %.2f µs/op (%d ops/sec)\n",
|
|
float64(queryTime.Nanoseconds())/float64(numOps*2)/1000,
|
|
int(float64(numOps*2)/queryTime.Seconds()))
|
|
fmt.Printf(" • Snapshot Generation: %.2f µs/op\n",
|
|
float64(snapshotTime.Nanoseconds())/1000000)
|
|
|
|
fmt.Println()
|
|
fmt.Println("Projected System Throughput:")
|
|
fmt.Printf(" • Orders/second: %d\n", int(float64(numOps)/orderTime.Seconds()))
|
|
fmt.Printf(" • Queries/second: %d\n", int(float64(numOps*2)/queryTime.Seconds()))
|
|
fmt.Printf(" • With MLX GPU acceleration (100x): %d orders/sec\n",
|
|
int(float64(numOps)/orderTime.Seconds())*100)
|
|
}
|