MarketMakingStrategyClient
The MarketMakingStrategyClient provides automated market making capabilities with intelligent spread management, inventory controls, and risk limits.
Overview
Unlike the basic MarketMakingClient for manual quote submission, this client manages complete market making strategies that automatically:
- Calculate and adjust spreads based on market conditions
- Manage inventory within defined limits
- Apply risk controls and circuit breakers
- Auto-hedge positions when thresholds are reached
Quick Start
from zenotc import ZenOTCClient
client = ZenOTCClient(
api_key="your_api_key",
api_secret="your_api_secret"
)
# Create a market making strategy
strategy = await client.mm_strategy.create(
name="BTC Market Maker",
asset="BTC",
base_spread_bps=10, # 10 basis points spread
quote_size=1.0, # 1 BTC per side
max_position=10.0, # Max 10 BTC position
enabled=True,
)
print(f"Strategy created: {strategy.id}")
Strategy Configuration
Create Strategy
strategy = await client.mm_strategy.create(
# Basic settings
name="BTC Market Maker",
asset="BTC",
enabled=True,
# Spread settings
base_spread_bps=10, # Base spread in basis points
min_spread_bps=5, # Minimum spread
max_spread_bps=50, # Maximum spread (volatility adjustment)
# Quote settings
quote_size=1.0, # Size per quote
min_quote_size=0.1, # Minimum quote size
max_quote_size=5.0, # Maximum quote size
quote_refresh_seconds=5, # Quote refresh interval
# Inventory settings
max_position=10.0, # Maximum long/short position
target_position=0.0, # Target inventory level
skew_factor=0.5, # Price skew per unit of inventory
# Risk settings
max_daily_loss=5000.0, # Daily loss limit (USD)
max_drawdown_percent=5.0, # Maximum drawdown percentage
circuit_breaker_enabled=True,
# Hedging settings
auto_hedge_enabled=True,
hedge_threshold=5.0, # Hedge when position exceeds 5 BTC
hedge_target=2.0, # Hedge back to 2 BTC
)
Configuration Parameters
Spread Settings
| Parameter | Type | Description |
|---|---|---|
base_spread_bps | int | Base bid-ask spread in basis points |
min_spread_bps | int | Minimum spread (floor) |
max_spread_bps | int | Maximum spread (ceiling) |
spread_model | str | "fixed", "volatility", "inventory" |
volatility_multiplier | float | Spread adjustment per volatility unit |
Quote Settings
| Parameter | Type | Description |
|---|---|---|
quote_size | float | Default quote size per side |
min_quote_size | float | Minimum quote size |
max_quote_size | float | Maximum quote size |
quote_refresh_seconds | int | How often to refresh quotes |
quote_layers | int | Number of price layers (1-5) |
layer_size_multiplier | float | Size multiplier per layer |
Inventory Settings
| Parameter | Type | Description |
|---|---|---|
max_position | float | Maximum absolute position |
target_position | float | Target inventory level |
skew_factor | float | Price skew per inventory unit |
skew_model | str | "linear", "exponential", "step" |
Risk Settings
| Parameter | Type | Description |
|---|---|---|
max_daily_loss | float | Daily loss limit (USD) |
max_drawdown_percent | float | Maximum drawdown % |
circuit_breaker_enabled | bool | Enable circuit breaker |
pause_on_error | bool | Pause strategy on errors |
Strategy Management
Get Strategy
strategy = await client.mm_strategy.get(strategy_id)
print(f"Name: {strategy.name}")
print(f"Status: {strategy.status}")
print(f"Position: {strategy.current_position}")
print(f"PnL Today: {strategy.daily_pnl}")
List Strategies
strategies = await client.mm_strategy.list(
asset="BTC", # Filter by asset
status="active", # "active", "paused", "stopped"
)
for s in strategies:
print(f"{s.name}: {s.status} - Position: {s.current_position}")
Update Strategy
strategy = await client.mm_strategy.update(
strategy_id,
base_spread_bps=15, # Widen spread
quote_size=2.0, # Increase size
max_position=20.0, # Increase position limit
)
Start/Stop/Pause
# Start strategy
await client.mm_strategy.start(strategy_id)
# Pause (cancel quotes, keep position)
await client.mm_strategy.pause(strategy_id)
# Resume from pause
await client.mm_strategy.resume(strategy_id)
# Stop (cancel quotes, option to flatten position)
await client.mm_strategy.stop(strategy_id, flatten_position=True)
Spread Models
Fixed Spread
Constant spread regardless of market conditions:
strategy = await client.mm_strategy.create(
spread_model="fixed",
base_spread_bps=10,
...
)
Volatility-Adjusted Spread
Spread widens with market volatility:
strategy = await client.mm_strategy.create(
spread_model="volatility",
base_spread_bps=10,
min_spread_bps=5,
max_spread_bps=100,
volatility_multiplier=2.0, # 2x spread per volatility unit
volatility_window_minutes=30, # 30-min rolling volatility
...
)
Inventory-Adjusted Spread
Spread skews based on current inventory:
strategy = await client.mm_strategy.create(
spread_model="inventory",
base_spread_bps=10,
skew_factor=0.5, # 0.5 bps skew per unit
skew_model="linear", # "linear", "exponential", "step"
...
)
Inventory Management
Skew Models
Linear Skew: Bid/ask adjusted linearly with position
# With position +5 BTC and skew_factor=0.5:
# Bid price -= 2.5 bps (encourage selling)
# Ask price -= 2.5 bps (discourage buying)
Exponential Skew: Aggressive skew at position extremes
strategy = await client.mm_strategy.create(
skew_model="exponential",
skew_factor=0.3,
skew_exponent=1.5,
...
)
Position Limits
strategy = await client.mm_strategy.create(
max_position=10.0, # Hard limit
soft_position_limit=7.0, # Start reducing quote size
position_reduction_factor=0.5, # 50% size reduction at soft limit
...
)
Auto-Hedging
Automatically hedge positions when thresholds are reached:
strategy = await client.mm_strategy.create(
auto_hedge_enabled=True,
hedge_threshold=5.0, # Hedge when |position| > 5
hedge_target=2.0, # Hedge back to |position| = 2
hedge_algo="twap", # "market", "twap", "vwap"
hedge_duration_minutes=30, # TWAP duration
hedge_venue="external", # "internal", "external", "both"
...
)
Hedge Settings
| Parameter | Type | Description |
|---|---|---|
auto_hedge_enabled | bool | Enable auto-hedging |
hedge_threshold | float | Position threshold to trigger hedge |
hedge_target | float | Target position after hedge |
hedge_algo | str | "market", "twap", "vwap" |
hedge_duration_minutes | int | Duration for algo hedges |
hedge_venue | str | Where to hedge |
Risk Controls
Circuit Breakers
strategy = await client.mm_strategy.create(
circuit_breaker_enabled=True,
# Loss-based circuit breaker
max_daily_loss=5000.0, # Pause if daily loss > $5000
max_hourly_loss=1000.0, # Pause if hourly loss > $1000
# Drawdown circuit breaker
max_drawdown_percent=5.0, # Pause if drawdown > 5%
# Volatility circuit breaker
max_volatility_pause=True,
volatility_threshold=5.0, # Pause if volatility > 5%
# Recovery settings
auto_resume_enabled=True,
resume_delay_minutes=30, # Wait 30 min before auto-resume
...
)
Manual Override
# Emergency stop all strategies
await client.mm_strategy.emergency_stop_all()
# Cancel all quotes for an asset
await client.mm_strategy.cancel_all_quotes(asset="BTC")
Performance Monitoring
Get Performance Metrics
metrics = await client.mm_strategy.get_metrics(strategy_id)
print(f"Total PnL: ${metrics.total_pnl}")
print(f"Daily PnL: ${metrics.daily_pnl}")
print(f"Realized PnL: ${metrics.realized_pnl}")
print(f"Unrealized PnL: ${metrics.unrealized_pnl}")
print(f"Total Volume: {metrics.total_volume}")
print(f"Trade Count: {metrics.trade_count}")
print(f"Win Rate: {metrics.win_rate}%")
print(f"Sharpe Ratio: {metrics.sharpe_ratio}")
print(f"Max Drawdown: {metrics.max_drawdown}%")
Get Quote Statistics
stats = await client.mm_strategy.get_quote_stats(strategy_id)
print(f"Quotes Sent: {stats.quotes_sent}")
print(f"Quotes Filled: {stats.quotes_filled}")
print(f"Fill Rate: {stats.fill_rate}%")
print(f"Avg Time to Fill: {stats.avg_fill_time_ms}ms")
print(f"Avg Spread Captured: {stats.avg_spread_captured_bps} bps")
Real-Time Events
Subscribe to strategy events:
async def on_strategy_event(event):
if event.type == "quote_filled":
print(f"Fill: {event.side} {event.quantity} @ {event.price}")
elif event.type == "position_changed":
print(f"Position: {event.old_position} -> {event.new_position}")
elif event.type == "hedge_triggered":
print(f"Hedging {event.hedge_quantity} via {event.hedge_algo}")
elif event.type == "circuit_breaker":
print(f"Circuit breaker triggered: {event.reason}")
elif event.type == "pnl_update":
print(f"PnL: ${event.daily_pnl}")
await client.mm_strategy.subscribe_events(strategy_id, on_strategy_event)
Event Types
| Event | Description |
|---|---|
started | Strategy started |
stopped | Strategy stopped |
paused | Strategy paused |
resumed | Strategy resumed |
quote_sent | Quote submitted |
quote_filled | Quote filled |
quote_cancelled | Quote cancelled |
position_changed | Position updated |
hedge_triggered | Auto-hedge started |
hedge_completed | Auto-hedge completed |
circuit_breaker | Circuit breaker triggered |
pnl_update | PnL changed |
error | Error occurred |
Error Handling
from zenotc.exceptions import (
StrategyError,
PositionLimitError,
CircuitBreakerError,
InsufficientMarginError,
)
try:
await client.mm_strategy.start(strategy_id)
except PositionLimitError as e:
print(f"Position limit reached: {e.current_position} / {e.max_position}")
except CircuitBreakerError as e:
print(f"Circuit breaker active: {e.reason}")
except InsufficientMarginError as e:
print(f"Need more margin: {e.required} / {e.available}")
except StrategyError as e:
print(f"Strategy error: {e}")
Best Practices
- Start conservative: Begin with wide spreads and small sizes
- Monitor closely: Watch performance metrics in real-time initially
- Set appropriate limits: Configure position limits based on your risk tolerance
- Enable circuit breakers: Always have loss limits configured
- Test in sandbox: Thoroughly test strategies before production
- Gradual scaling: Increase sizes gradually as you gain confidence