Skip to main content

Market Maker Protection (MMP)

Fill-window limits and auto-cancel behavior for market makers.

Overview

MMP protects market makers from rapid adverse fills by:

  1. Tracking cumulative fill metrics within a rolling time window
  2. Canceling orders when limits are breached
  3. Auto-canceling all other MMP-enabled orders for the same wallet+underlying

Configuration

Set MMP Config

Endpoint: POST /mmp-config

Request: SetMmpConfigRequest

{
"wallet": "0x...",
"currency": "BTC",
"interval_ms": 60000,
"frozen_time_ms": 300000,
"qty_limit": 1000000,
"delta_limit": 10.0,
"vega_limit": 5.0,
"enabled": true,
"nonce": 1,
"signature": "0x..."
}

Fields:

  • wallet: Wallet address
  • currency: Underlying currency (e.g., "BTC", "ETH")
  • interval_ms: Rolling window length in milliseconds
  • frozen_time_ms: Freeze duration after trigger (milliseconds)
  • qty_limit: Optional quantity limit (contract units)
  • delta_limit: Optional delta limit
  • vega_limit: Optional vega limit
  • enabled: Whether MMP is enabled for this wallet+currency

Response: ApiResponse<MmpConfigData>

Get MMP Config

Endpoint: GET /mmp-config?wallet=...&currency=...

Query parameters:

  • wallet (required)
  • currency (optional filter)

Response: MmpConfigResponse

{
"success": true,
"data": [
{
"wallet_address": "0x...",
"currency": "BTC",
"interval_ms": 60000,
"frozen_time_ms": 300000,
"qty_limit": 1000000,
"delta_limit": 10.0,
"vega_limit": 5.0,
"enabled": true
}
]
}

Delete MMP Config

Endpoint: DELETE /mmp-config

Request: DeleteMmpConfigRequest

{
"wallet": "0x...",
"currency": "BTC",
"nonce": 1,
"signature": "0x..."
}

Response: ApiResponse<String>

Reset MMP State

Endpoint: POST /mmp-config/reset

Request: ResetMmpRequest

{
"wallet": "0x...",
"currency": "BTC",
"nonce": 1,
"signature": "0x..."
}

Response: ApiResponse<String>

Notes: Resets MMP fill-window state (clears cumulative metrics, unfreezes currency).

MMP Behavior (Critical to Understand)

Fill Processing Order

MMP evaluation is POST-FILL:

  1. Engine matches and accepts the fill first
  2. Then MMP checks cumulative metrics
  3. If limits are breached:
    • MMP triggers
    • Remaining quantity on active order stops processing
    • Order becomes CANCELED with reason: "MMP triggered during fill processing"
    • Engine emits MmpTriggered event
    • Engine auto-cancels all other MMP-enabled open orders for the same wallet+underlying

Implication: You can receive fills that push you beyond your configured thresholds before MMP triggers.

Trigger Conditions

MMP triggers if any configured limit is breached:

  • Quantity limit: cumulative_qty > qty_limit
  • Delta limit: |cumulative_delta| > delta_limit
  • Vega limit: |cumulative_vega| > vega_limit

Cumulative metrics are computed over the rolling window (interval_ms).

Cancel Behavior

When MMP triggers:

  1. Active order: Remaining quantity stops, order becomes CANCELED with reason "MMP triggered during fill processing"
  2. Other orders: All other MMP-enabled open orders for the same wallet+underlying are auto-canceled with reason "Order canceled by MMP trigger"

Note: Only MMP-enabled orders are canceled. Orders with mmp_enabled=false are not affected.

Freeze Behavior

After MMP triggers:

  • Currency is frozen for frozen_time_ms milliseconds
  • While frozen, new fills may be rejected (implementation-dependent)

Fill Metrics Calculation

Quantity

  • Source: Fill size (contract units)
  • Cumulative: Sum of all fills in the rolling window

Delta

  • Source: Calculated from fill symbol and GreeksCache
  • Cumulative: Sum of all fill deltas in the rolling window

Vega

  • Source: Calculated from fill symbol and GreeksCache
  • Cumulative: Sum of all fill vegas in the rolling window

Rolling Window

Window Eviction

Fills older than interval_ms are automatically evicted from the window:

  • Eviction runs periodically (background task)
  • Cumulative metrics are decremented as old fills are removed

Window Reset

Reset MMP state via POST /mmp-config/reset:

  • Clears all fills in the window
  • Resets cumulative metrics to zero
  • Unfreezes currency

MMP Events

MmpTriggered Event

Internal event (not currently forwarded to WS clients):

MmpTriggeredMessage {
wallet: WalletAddress,
currency: String,
reason: String, // "qty_limit" | "delta_limit" | "vega_limit"
}

Best Practices

Configuration

  1. Set appropriate limits: Based on your risk tolerance and typical fill sizes
  2. Use currency-specific configs: Configure per underlying (BTC, ETH, etc.)
  3. Monitor MMP triggers: High trigger rate may indicate misconfigured limits or adverse market conditions

Order Management

  1. Enable MMP selectively: Set mmp_enabled=true only for orders you want protected
  2. Use consistent currency: Ensure all orders for the same underlying use the same currency in MMP config
  3. Handle MMP cancels: Implement logic to detect MMP-triggered cancels and adjust quoting

Monitoring

  1. Track MMP triggers: Monitor for "MMP triggered during fill processing" and "Order canceled by MMP trigger" reasons
  2. Review fill windows: Check cumulative metrics via MMP config (not exposed via API currently)
  3. Adjust limits: If MMP triggers too frequently, increase limits; if too infrequently, decrease limits

Common Issues

MMP Triggers Too Frequently

Causes:

  • Limits too low for typical fill sizes
  • Adverse market conditions
  • Rapid quoting causing many fills

Solutions:

  • Increase qty_limit, delta_limit, or vega_limit
  • Increase interval_ms to allow more fills in window
  • Reduce quoting frequency

MMP Doesn't Trigger

Causes:

  • Limits too high
  • MMP not enabled on orders (mmp_enabled=false)
  • Currency mismatch (MMP config currency != order underlying)

Solutions:

  • Decrease limits
  • Verify mmp_enabled=true on orders
  • Verify MMP config currency matches order underlying

Unexpected Cancels

Symptom: Orders canceled with reason "Order canceled by MMP trigger" but you didn't expect it.

Cause: Another order for the same wallet+underlying triggered MMP.

Solution: Review all MMP-enabled orders for the same underlying when one triggers.