Skip to main content

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

ParameterTypeDescription
base_spread_bpsintBase bid-ask spread in basis points
min_spread_bpsintMinimum spread (floor)
max_spread_bpsintMaximum spread (ceiling)
spread_modelstr"fixed", "volatility", "inventory"
volatility_multiplierfloatSpread adjustment per volatility unit

Quote Settings

ParameterTypeDescription
quote_sizefloatDefault quote size per side
min_quote_sizefloatMinimum quote size
max_quote_sizefloatMaximum quote size
quote_refresh_secondsintHow often to refresh quotes
quote_layersintNumber of price layers (1-5)
layer_size_multiplierfloatSize multiplier per layer

Inventory Settings

ParameterTypeDescription
max_positionfloatMaximum absolute position
target_positionfloatTarget inventory level
skew_factorfloatPrice skew per inventory unit
skew_modelstr"linear", "exponential", "step"

Risk Settings

ParameterTypeDescription
max_daily_lossfloatDaily loss limit (USD)
max_drawdown_percentfloatMaximum drawdown %
circuit_breaker_enabledboolEnable circuit breaker
pause_on_errorboolPause 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

ParameterTypeDescription
auto_hedge_enabledboolEnable auto-hedging
hedge_thresholdfloatPosition threshold to trigger hedge
hedge_targetfloatTarget position after hedge
hedge_algostr"market", "twap", "vwap"
hedge_duration_minutesintDuration for algo hedges
hedge_venuestrWhere 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

EventDescription
startedStrategy started
stoppedStrategy stopped
pausedStrategy paused
resumedStrategy resumed
quote_sentQuote submitted
quote_filledQuote filled
quote_cancelledQuote cancelled
position_changedPosition updated
hedge_triggeredAuto-hedge started
hedge_completedAuto-hedge completed
circuit_breakerCircuit breaker triggered
pnl_updatePnL changed
errorError 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

  1. Start conservative: Begin with wide spreads and small sizes
  2. Monitor closely: Watch performance metrics in real-time initially
  3. Set appropriate limits: Configure position limits based on your risk tolerance
  4. Enable circuit breakers: Always have loss limits configured
  5. Test in sandbox: Thoroughly test strategies before production
  6. Gradual scaling: Increase sizes gradually as you gain confidence