Market Maker Protections (MMP)

Automatically freeze protected quotes when fills exceed configured limits

Market Maker Protections (MMP) is currently available for a set of whitelisted users.

Overview

MMP protects market makers from excessive fills during fast markets by automatically freezing protected orders once configured limits are exceeded.

MMP is available for perps and dated options and is configured per account (L2 address) and underlying (base asset). For example, one BTC configuration applies across all BTC perps and dated options for that account.

Only order book orders explicitly flagged for MMP are protected. Orders without the MMP flag are unaffected. RFQ and block trade fills count toward MMP limits only if the account opts in. See RFQ and block trades.

How MMP works

MMP tracks protected fills over a single sliding time window. Size, delta and vega are measured over the same window and compared independently against their configured limits.

Only MMP flagged fills count toward the window, and on the order book only the fill where your order was the resting maker. A flagged order that crosses the book is not counted. RFQ and block trade legs follow their own rules.

Because flagged orders that cross the book are not counted, they do not offset your maker fills in the window. Delta and vega limits trip on your gross maker exposure, so hedging with flagged aggressive orders does not keep the window’s net delta or vega down.

FeatureBehavior
SizeGross traded size over the window: the sum of absolute fill sizes.
DeltaAbsolute value of the net (signed) delta of protected fills.
VegaAbsolute value of the net (signed) vega of protected fills, measured in USD per 1% move in implied volatility.
TriggerA limit trips only when the corresponding window value exceeds the configured limit. Matching the limit exactly does not trip MMP.
Triggering fillThe fill that first breaches a limit completes in full. Protection activates immediately afterward.
CancellationResting MMP orders for that underlying are canceled after a trigger. Further protected fills on that underlying are blocked while the cancellations complete.
Cross marketPerps and dated options for the same underlying share one MMP state and set of limits.

MMP is enforced by the matching engine, so concurrent fills across markets share the same state. At most one fill can take exposure beyond a configured limit before protection activates.

MMP relies on up to date Greeks. If Greeks become stale or unavailable for any market on an underlying, all MMP orders on that underlying are canceled with reason MMP_GREEKS_UNAVAILABLE, including orders on perps. This check applies to MMP orders that can rest, so an MMP IOC order is not canceled for stale Greeks.

RFQ and block trades

RFQ and block trade fills are protected only for accounts that opt in. Set protect_block_trades to true in the configuration for a base asset to protect your maker legs on that base asset. RFQ executions are included, because an RFQ settles as a block trade. The option is off by default.

RuleBehavior
ScopeSet per account and per base asset, not per quote or per block.
Which legsOn a block you create directly, only your maker legs are protected. On an offer you submit, your own legs are protected whichever side they end up on, because answering a request for quotes is quoting. MARKET legs are never protected. A protected leg counts toward the window whether it is the maker or the taker side.
RequesterOn a block built from offers, such as an RFQ, the account that requested the quotes is never protected.
Shared windowProtected block trade legs count toward the same window and limits as your MMP order book fills on that base asset.
Set at creationProtection is decided when the block is created and is not changed afterward. Turning the option off applies only to blocks created afterward; existing blocks keep the protection their legs were created with. Turning it off can take a short time to take effect.
VisibilityA leg’s flags are returned only to the account that owns the leg. A counterparty reading the same block cannot see whether your leg is protected, and you cannot see whether theirs is.

You cannot set the MMP flag on a block trade leg yourself. Paradex applies it based on your protect_block_trades setting, to the legs described in Which legs above.

Before accepting a block trade request, Paradex checks the MMP settings of the accounts involved. If those settings cannot be read, the request is refused with HTTP 503 rather than proceeding without protection. This can happen even for accounts that have no MMP configuration. Retry the request later. The accounts checked depend on the request:

  • Creating a block: every account with a maker leg, except the account that requested the quotes on a block built from offers.
  • Creating an offer: the offering account.

Portfolio Margin

MMP can reduce the initial margin charged for protected resting orders under Portfolio Margin.

Without MMP, portfolio margin assumes all resting orders may fill. With MMP, Paradex instead considers the worst fills that can occur before protection activates: fills up to the size limit, plus one full triggering fill.

Only the size limit is used for margin relief. Delta and vega limits provide execution protection but do not reduce margin. Orders without the MMP flag are unaffected.

Calculation

For each portfolio margin stress scenario:

  1. Select the most damaging protected fills up to the size limit, allowing the last fill before the trigger to be partial, plus one full triggering fill.
  2. Add their loss to the position loss and the loss from orders without the MMP flag.
  3. The scenario with the largest combined loss sets the requirement.

The selected protected fills may differ across scenarios.

Example

Assume an account has a short BTC straddle and protected resting BTC call quotes with:

  • MMP size limit: 2 BTC
  • Quote size: 0.5 BTC
  • 10 bids, each losing $4,000 in the BTC −14% scenario
  • 6 asks, each losing $1,000 in the BTC +14% scenario

The position itself loses:

  • $6,000 at BTC −14%
  • $10,000 at BTC +14%

MMP can allow four 0.5 BTC fills up to the 2 BTC limit, plus one additional 0.5 BTC fill that triggers protection.

Portfolio margin therefore considers at most 2.5 BTC of protected fills per scenario, selecting whichever five quotes are most damaging.

ScenarioPositionProtected ordersTotal
BTC −14%$6,0005 × $4,000 = $20,000$26,000
BTC +14%$10,0005 × $1,000 = $5,000$15,000

Note that the position alone loses more in the +14% scenario. The protected bids move the binding scenario to −14%.

Without MMP, all 10 bids would count in the −14% scenario:

$6,000 + 10 × $4,000 = $46,000

MMP therefore reduces the scenario loss driving the requirement from $46,000 to $26,000.

Notes

  • If the size limit is at or above the total protected resting size, the requirement is the same as without MMP.
  • Maintenance margin is unchanged because it does not include open orders.

API

All endpoints are private. GET /v1/account/mmp also accepts a read only token, so you can always check which configurations are still enforced. All other endpoints require a regular (trading) JWT.

Sizes are in base units such as BTC or ETH. Times are in milliseconds.

Configuration changes can return HTTP 503 when market data is temporarily unavailable, and resets can return HTTP 503 while the matching engine is unavailable. Retry the request later.

Configuration

GET /v1/account/mmp
POST /v1/account/mmp/{base_asset}
DELETE /v1/account/mmp/{base_asset}
POST /v1/account/mmp/reset
EndpointBehavior
GET /v1/account/mmpList configuration and live status per underlying. Optionally filter by base_asset.
POST /v1/account/mmp/{base_asset}Create or replace the configuration for one underlying.
DELETE /v1/account/mmp/{base_asset}Disable MMP for one underlying. This is the only way to remove a configuration. Returns no body.
POST /v1/account/mmp/resetClear the freeze and trading window for one underlying, or for all underlying assets. See Reset.

POST /v1/account/mmp/{base_asset} is a full create or replace: it overwrites any existing configuration. Omitted limits are set to 0. The exception is protect_block_trades: if you omit it, the stored value is kept. Only sending false turns block trade protection off. Each limit is optional, but at least one of size_limit, delta_limit or vega_limit must be non zero.

FieldTypeMeaning
interval_msintegerLength of the shared sliding window over which protected fills are aggregated, from 100 to 3600000 ms (1 hour).
frozen_time_msintegerDuration of a freeze after a trigger, from 0 to 3600000 ms (1 hour). 0 means frozen until manually reset.
size_limitdecimal stringTrips when gross traded size in the window exceeds this value. 0 disables the check. Used for margin relief. Maximum 4 decimal places.
delta_limitdecimal stringTrips when absolute net delta in the window exceeds this value. 0 disables the check. Maximum 4 decimal places.
vega_limitdecimal string (USD)Trips when absolute net vega in the window exceeds this value. 0 disables the check. Maximum 4 decimal places. Cannot be the only limit for an underlying with no dated option markets, because perps carry no vega.
protect_block_tradesBooleanProtects your maker legs on RFQ and block trades for this base asset. Defaults to false. If omitted, the stored value is kept. See RFQ and block trades.

Invalid configurations, including values outside the allowed ranges and unsupported base assets, are rejected with MMP_CONFIG_INVALID (HTTP 400).

Request:

POST /v1/account/mmp/BTC
{
"interval_ms": 1000,
"frozen_time_ms": 5000,
"size_limit": "2",
"delta_limit": "1.5",
"vega_limit": "0",
"protect_block_trades": true
}

A successful POST or DELETE means the configuration has been persisted. The matching engine applies the change shortly afterward. Until it does, live fields in GET /v1/account/mmp, including is_frozen and the window totals, may be absent.

Example GET /v1/account/mmp response once the configuration is live:

{
"account": "0x1234...abcd",
"enabled": true,
"results": [
{
"base_asset": "BTC",
"interval_ms": 1000,
"frozen_time_ms": 5000,
"size_limit": "2",
"delta_limit": "1.5",
"vega_limit": "0",
"protect_block_trades": true,
"is_frozen": false,
"frozen_until": 0,
"window_size": "0.5",
"window_delta": "-0.21",
"window_vega": "0",
"updated_at": 1758067200000
}
]
}

account is the account the configurations belong to, and enabled indicates whether the account is enabled for MMP. A configuration listed while enabled is false is still enforced until it is deleted.

window_size, window_delta and window_vega are the current values accumulated over the same interval_ms window.

frozen_until is:

  • the freeze expiry timestamp when frozen_time_ms > 0;
  • 0 when frozen until manual reset;
  • 0 when not frozen.

Use is_frozen to distinguish the last two cases.

Changing the configuration clears the trading window for that underlying. Posting an identical configuration again does not.

A freeze survives disabling MMP. Deleting and adding the configuration again does not clear an active freeze.

Disabling MMP cancels all resting MMP orders for that underlying with reason MMP_DISABLED, so no order remains resting without protection.

Reset

A reset keeps the configuration but clears the freeze and trading window. A successful reset takes effect immediately.

Send POST /v1/account/mmp/reset with a base_asset to reset one underlying, or with an empty body to reset all underlying assets:

POST /v1/account/mmp/reset
{
"base_asset": "BTC"
}

A successful reset returns the underlying assets that were reset:

{
"results": [
{
"base_asset": "BTC",
"reset_at": 1758067205000
}
]
}
ConditionResult
Reset within 1 second of a triggerRejected with MMP_MIN_FREEZE_NOT_ELAPSED (HTTP 400).
base_asset has no configurationRejected with MMP_NOT_CONFIGURED (HTTP 400).
Reset sent with a read only tokenRejected with INVALID_TOKEN_SCOPE (HTTP 403).
More than 60 resets per minute per account, or a short burst of resets over the burst limitRejected with HTTP 429.

A reset of all underlying assets is applied underlying by underlying, and underlying assets without a configuration are skipped. If any underlying is still inside its 1 second lockout, the call returns HTTP 400 even though others may already have been reset. The error data lists which underlying assets were reset (data.reset) and which were refused (data.refused).

Canceled quotes are not restored automatically. The maker must place new quotes.

Orders

Add "MMP" to flags on a LIMIT order on a perp or dated option market.

The flag is supported on REST, batch and WebSocket order entry. The MMP flag is fixed when the order is placed and is kept when the order is modified. To remove it, cancel and replace the order. You cannot set the flag on block trade legs. Paradex sets it on eligible legs when protect_block_trades is on. See RFQ and block trades.

POST /v1/orders
{
"market": "BTC-USD-26DEC26-100000-C",
"side": "SELL",
"type": "LIMIT",
"size": "0.5",
"price": "4150",
"instruction": "POST_ONLY",
"flags": ["MMP"]
}
ConditionResult
Account is not enabled for MMPRejected with MMP_NOT_ENABLED (HTTP 403).
MMP flag set but no configuration exists for the underlyingOrder is accepted and then canceled with reason MMP_NOT_CONFIGURED.
MMP flag set while the underlying is frozenOrder is accepted and then canceled with reason MMP_FROZEN.
MMP flag set on an order type other than LIMIT, an unsupported market or a block trade legRejected with INVALID_PARAMETER and an explanatory message.

MMP related cancellation reasons:

ReasonWhen
MMP_TRIGGEREDResting MMP orders are canceled because a limit was breached.
MMP_FROZENAn MMP order is canceled because the underlying is frozen.
MMP_DISABLEDResting MMP orders are canceled because MMP was disabled for the underlying, or because MMP is temporarily unavailable, for example during a restart.
MMP_NOT_CONFIGUREDAn MMP order is canceled because no configuration exists for its underlying.
MMP_GREEKS_UNAVAILABLEMMP orders on an underlying are canceled because Greeks are stale or unavailable for one of its markets.

WebSocket

Private channel mmp pushes state changes through the standard private subscription envelope.

Every event includes:

FieldMeaning
kindState change type: TRIGGERED, UNFROZEN, RESET or CONFIG_UPDATED.
base_assetUnderlying whose MMP state changed.
accountAccount associated with the MMP state change.
frozen_untilOn TRIGGERED, the freeze expiry, or 0 for a freeze that lasts until manual reset. Always 0 on other kinds.
created_atEvent timestamp.

TRIGGERED events also include:

FieldMeaning
trip_reasonTrigger reason: SIZE, DELTA or VEGA when that limit was exceeded, or INVALID_FILL when a fill could not be counted and the underlying was frozen as a precaution.
window_sizeGross protected size in the window at the time of the trigger.
window_deltaNet delta of protected fills in the window at the time of the trigger.
window_vegaNet vega of protected fills in the window at the time of the trigger.
trigger_marketMarket whose fill triggered MMP.

CONFIG_UPDATED events also include:

FieldMeaning
configThe updated configuration for the underlying, including protect_block_trades. Its removed flag is set when the configuration was deleted.

frozen_until on non TRIGGERED events does not indicate whether an underlying is frozen. Track the frozen state from TRIGGERED, UNFROZEN and RESET events, or read is_frozen from GET /v1/account/mmp.

Error codes

CodeHTTPMeaning
MMP_NOT_ENABLED403Account is not enabled for MMP.
INVALID_TOKEN_SCOPE403Reset sent with a read only token.
MMP_CONFIG_INVALID400Configuration or base asset is invalid.
MMP_NOT_CONFIGURED400Reset requested for an underlying with no configuration.
MMP_MIN_FREEZE_NOT_ELAPSED400Reset requested within 1 second of a trigger.
INVALID_PARAMETER400MMP flag used on an unsupported order type, market or block trade leg, or another request parameter is invalid.

Configuration changes return HTTP 503 when market data is temporarily unavailable, and resets return HTTP 503 while the matching engine is unavailable.

Resets are limited to 60 per minute per account, with an additional short burst limit. Exceeding a limit returns HTTP 429.

SDK support

paradex-py 0.7.1 is the first version that supports the MMP order flag. Earlier versions cannot parse MMP flagged orders or protected block trade legs, including your own. Upgrade to 0.7.1 or later before placing MMP orders or opting in to block trade protection.