Market Maker Protection (MMP)
Fill-window limits and auto-cancel behavior for market makers.
Every wallet-scoped MMP endpoint requires EIP-712 authentication. Configuration writes accept the wallet owner or an authorized agent for that wallet. Reads are owner-only and reject authorized-agent signatures.
MMP applies to the trading account that owns fills. For a portfolio-margin
manager, reads and writes signed for the manager wallet resolve to its
Account.sol trading account, the same account that order placement uses.
Responses report that account in wallet_address, and the MMP nonce sequence
belongs to that account.
For read-after-write consistency, send X-Hypercall-Fence-Wait: 1 on any MMP
mutation. A successful response then includes X-Hypercall-Fence; echo that
value as X-Hypercall-Fence on the authenticated GET. Mutations without the
opt-in wait header intentionally do not return a fence.
Overview
MMP protects market makers from rapid adverse fills by:
- Tracking cumulative fill metrics within a rolling time window
- Canceling orders when limits are breached
- 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",
"nonce": 1,
"signature": "0x..."
}
Fields:
wallet: Wallet addresscurrency: Underlying currency (e.g.,"BTC","ETH")interval_ms: Rolling window length in millisecondsfrozen_time_ms: Freeze duration after trigger (milliseconds)qty_limit: Optional quantity limit (contract units), serialized as a decimal stringdelta_limit: Optional delta limit, serialized as a decimal stringvega_limit: Optional vega limit, serialized as a decimal stringnonce: Replay-protection nonce, exactly one greater than the latest committed nonce returned by the authenticated readsignature: EIP-712SetMmpConfigsignature over every request field
Response: ApiResponse<MmpConfigData>
Get MMP Config
Endpoint: GET /mmp-config?wallet=...¤cy=...
Query parameters:
wallet(required)currency(optional filter)
Required headers:
X-Hypercall-Expires-At-Ms: Unix expiry in milliseconds, no more than 60 seconds in the futureX-Hypercall-Signature:GetMmpConfigsignature overwallet,currency(empty string when omitted), and the header expiry
The recovered signer must be the wallet owner. Authorized-agent signatures are not accepted for this read.
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,
"nonce": 1
}
]
}
Delete MMP Config
Endpoint: DELETE /mmp-config
Request: DeleteMmpConfigRequest
{
"wallet": "0x...",
"currency": "BTC",
"nonce": 1,
"signature": "0x..."
}
Response: ApiResponse<String>. A successful command disables the numeric
wallet/underlying authority state. With request header
X-Hypercall-Fence-Wait: 1, the response includes X-Hypercall-Fence for the
projection-backed read.
Reset MMP State
Endpoint: POST /mmp-config/reset
Request: ResetMmpRequest
{
"wallet": "0x...",
"currency": "BTC",
"nonce": 1,
"signature": "0x..."
}
Response: ApiResponse<String>. A successful command clears the MMP fill
window and freeze state in replayable authority. With request header
X-Hypercall-Fence-Wait: 1, the response includes X-Hypercall-Fence for the
projection-backed read.
MMP Behavior (Critical to Understand)
Which Fills Count
Every fill counts toward the MMP window of each side whose order has
mmp_enabled=true, whether that order was resting on the book (maker) or
arriving (taker):
- Resting quotes: when an incoming order trades against your resting MMP-enabled order, that fill is added to your window. This is the case MMP exists for: a burst of fills against your quotes trips MMP and cancels your remaining MMP-enabled quotes on that underlying.
- Incoming orders: an MMP-enabled order that takes liquidity counts its own fills the same way.
- Combos and implied orders: each protected participant of a package counts every leg it trades, assessed once per package.
- RFQ and RPI fills do not count toward MMP yet. An MMP freeze does block new RFQ executions for the frozen quote provider.
Each fill is measured from your own side: a fill that sells a call adds negative delta to the seller's window and positive delta to the buyer's.
If an order has mmp_enabled=true but its wallet has no MMP configuration
for that underlying, MMP fails closed: the first fill trips and cancels that
wallet's MMP-enabled orders on the underlying. Configure MMP before flagging
orders.
Fill Processing Order
MMP evaluation is POST-FILL:
- Engine matches and accepts the fill first
- Then MMP checks cumulative metrics for each protected side of the fill
- If a side's limits are breached:
- MMP triggers for that side's wallet and underlying
- Engine emits
MmpTriggeredevent - Engine auto-cancels all MMP-enabled open orders for that wallet+underlying
- If the breaching side was the incoming order, its remaining quantity
stops and it becomes
CANCELEDwith reason"MMP triggered during fill processing" - If the breaching side was a resting maker, the incoming order keeps matching against other liquidity; the maker's other MMP-enabled quotes are already cancelled
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:
- Incoming order that tripped: remaining quantity stops, order becomes
CANCELEDwith reason"MMP triggered during fill processing" - Resting order that tripped: the fill stands; the order's remainder, if any, is cancelled with the other MMP-enabled orders
- Other orders: All 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_msmilliseconds - 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
- Set appropriate limits: Based on your risk tolerance and typical fill sizes
- Use currency-specific configs: Configure per underlying (BTC, ETH, etc.)
- Monitor MMP triggers: High trigger rate may indicate misconfigured limits or adverse market conditions
Order Management
- Enable MMP selectively: Set
mmp_enabled=trueonly for orders you want protected - Use consistent currency: Ensure all orders for the same underlying use the same currency in MMP config
- Handle MMP cancels: Implement logic to detect MMP-triggered cancels and adjust quoting
Monitoring
- Track MMP triggers: Monitor for
"MMP triggered during fill processing"and"Order canceled by MMP trigger"reasons - Review fill windows: Check cumulative metrics via MMP config (not exposed via API currently)
- 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, orvega_limit - Increase
interval_msto 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=trueon 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.