Market Maker
Automated market making and liquidity provision
What is a Market Maker?
A market maker is an automated agent that continuously provides liquidity to the market by placing both buy and sell orders. This ensures there's always someone to trade with and helps maintain healthy bid-ask spreads.
Market makers are essential for liquid, efficient markets. They profit from the bid-ask spread while providing a valuable service to traders.
How It Works
Basic Strategy
The market maker follows a simple but effective strategy:
Track Current Price
Monitor recent trades to determine the current market price.
Place Bid Orders
Place buy orders below the current price:
Bid Price = Current Price - Spread/2Place Ask Orders
Place sell orders above the current price:
Ask Price = Current Price + Spread/2Adjust Continuously
As the price moves or orders fill, continuously adjust the quotes to maintain presence on both sides.
Configuration
The market maker can be configured with several parameters:
type MarketMaker struct {
UserID int64
TargetSpread float64 // Desired bid-ask spread
OrderSize int // Size of each order
NumLevels int // Number of price levels
RefreshInterval time.Duration
}Example Configuration
mm := MarketMaker{
UserID: 8,
TargetSpread: 20.0, // $20 spread
OrderSize: 50, // 50 shares per order
NumLevels: 3, // 3 levels on each side
RefreshInterval: 5 * time.Second,
}This creates an orderbook like:
Asks:
1030 50 shares (Current + 30)
1020 50 shares (Current + 20)
1010 50 shares (Current + 10)
Current Price: 1000
Bids:
990 50 shares (Current - 10)
980 50 shares (Current - 20)
970 50 shares (Current - 30)Strategies
Simple Market Making
The basic strategy places orders at fixed intervals around the current price:
func (mm *MarketMaker) Quote(currentPrice float64) {
halfSpread := mm.TargetSpread / 2
// Place bid
bidPrice := currentPrice - halfSpread
mm.PlaceBidOrder(bidPrice, mm.OrderSize)
// Place ask
askPrice := currentPrice + halfSpread
mm.PlaceAskOrder(askPrice, mm.OrderSize)
}Multi-Level Market Making
Advanced market makers place orders at multiple price levels to provide depth:
for level := 1; level <= mm.NumLevels; level++ {
offset := float64(level) * mm.PriceIncrement
// Bid side
bidPrice := currentPrice - offset
mm.PlaceBidOrder(bidPrice, mm.OrderSize)
// Ask side
askPrice := currentPrice + offset
mm.PlaceAskOrder(askPrice, mm.OrderSize)
}Inventory Management
Market makers must manage inventory to avoid excessive risk:
Position Limits
Set maximum long and short positions:
const (
MaxLongPosition = 1000 // Maximum shares to own
MaxShortPosition = 1000 // Maximum shares to owe
)Skewing Quotes
When inventory gets too high, adjust quotes to encourage trading in the desired direction:
if mm.Position > TargetPosition {
// Too long - make selling more attractive
bidPrice -= SkewAmount
askPrice -= SkewAmount
} else if mm.Position < TargetPosition {
// Too short - make buying more attractive
bidPrice += SkewAmount
askPrice += SkewAmount
}Risk Management
Market makers face several risks:
Adverse Selection
Getting picked off when the market moves against you.
Mitigation:
- Fast price updates
- Wider spreads during volatility
- Position limits
Inventory Risk
Holding too much or too little inventory.
Mitigation:
- Dynamic position management
- Skewing quotes based on inventory
- Hedging with correlated instruments
Operational Risk
System failures, network issues, or bugs.
Mitigation:
- Robust error handling
- Position monitoring
- Kill switches
Profitability
Market makers profit from:
- Bid-Ask Spread: Earn the spread on every round trip
- Rebates: Many exchanges offer maker rebates
- Price Improvement: Occasionally buy low and sell high
Example P&L
Buy: 100 shares @ 995 = -99,500
Sell: 100 shares @ 1005 = +100,500
─────────
Profit: = 1,000 (1% on capital)Implementation
Here's a simplified market maker implementation:
func (mm *MarketMaker) Run(ctx context.Context) {
ticker := time.NewTicker(mm.RefreshInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
mm.refresh()
}
}
}
func (mm *MarketMaker) refresh() {
// Get current price
currentPrice := mm.getCurrentPrice()
// Cancel old orders
mm.cancelAllOrders()
// Place new orders
for level := 1; level <= mm.NumLevels; level++ {
offset := float64(level) * 10.0
mm.PlaceBidOrder(currentPrice - offset, mm.OrderSize)
mm.PlaceAskOrder(currentPrice + offset, mm.OrderSize)
}
}Monitoring
Key metrics to monitor:
- Fill Rate: Percentage of orders that get filled
- Spread Capture: Average profit per trade
- Position: Current inventory position
- PnL: Cumulative profit and loss
- Quote Time: Time spent with active quotes
Advanced Topics
Adaptive Spreads
Adjust spreads based on:
- Market volatility
- Order flow toxicity
- Competition from other market makers
Cross-Asset Market Making
Make markets in multiple correlated instruments and hedge across them.
High-Frequency Market Making
Optimize for ultra-low latency with:
- Co-location
- FPGA acceleration
- Custom network protocols