Skip to main content
These limits cap how large any single position or account can grow, distributing risk across many participants. Without them, a single large trader could dominate the pool’s exposure, creating concentration risk for all LPs. The caps ensure that the pool remains diversified — at default settings, at least 20 separate positions would be needed to reach the pool’s exposure limit. Beyond the pool-level net exposure cap, the protocol enforces per-position and per-account notional limits. These caps prevent concentration risk — no single position or trader account can dominate the pool’s exposure. All limits are derived from the pool’s net exposure cap and scale dynamically with pool equity.
Position and account caps are enforced by RiskManager.validateOpenPosition() on every call to openPosition and increasePosition. They are checked in real time against current pool equity, so the effective caps change as the pool grows or shrinks.

Cap Calculations

Both caps are expressed as a fraction of maxNetExposure, which itself is derived from pool equity (see Pool Exposure Caps):
maxNetExposure      = (poolEquity * netExposureCapFactorBps) / stressMoveBps
maxPositionNotional = maxNetExposure * perPositionCapFactorBps / BPS_DENOMINATOR
maxAccountNotional  = maxNetExposure * perAccountCapFactorBps / BPS_DENOMINATOR

Default Parameters

ParameterDefault ValuePercentageRole
perPositionCapFactorBps5005% of maxNetExposureRISK_ADMIN_ROLE
perAccountCapFactorBps5005% of maxNetExposureRISK_ADMIN_ROLE
Both factors default to 500 bps (5%), meaning a single position or account can hold at most 5% of the pool’s maximum net exposure. This ensures that at least 20 positions (or 20 accounts) would be needed to reach the pool’s net exposure limit, providing meaningful diversification.

Worked Example

1

Calculate Pool Caps

Given a pool with 10,000,000 USDC (10M) equity and default parameters:
maxNetExposure      = (10,000,000 * 10,000) / 200 = 500,000,000 (500M USDC)
maxPositionNotional = 500,000,000 * 500 / 10,000  = 25,000,000  (25M USDC)
maxAccountNotional  = 500,000,000 * 500 / 10,000  = 25,000,000  (25M USDC)
2

Trader Opens Position

A trader opens a 20M USDC position. This is within the 25M per-position cap and within the 25M per-account cap. The position is accepted.
3

Trader Tries to Open Another

The same trader tries to open a second 10M USDC position. Their account gross notional would become 30M USDC, exceeding the 25M per-account cap. The transaction reverts with ExceedsAccountCap.

Validation Flow

On every openPosition and increasePosition call, RiskManager.validateOpenPosition() performs five checks in strict order. If any check fails, the transaction reverts with the corresponding error:
#CheckError CodeDescription
1notional <= maxPositionNotionalExceedsPositionCapSingle position size limit
2accountGrossNotional + notional <= maxAccountNotionalExceedsAccountCapTotal account exposure limit
3|proposedNetExposure| <= maxNetExposureExceedsPoolExposureCapPool net directional limit
4windowGrossAdded + notional <= maxGrossNotionalDeltaPerWindowRateOfChangeExceededGross addition rate limit
5|windowNetChange + delta| <= maxNetExposureDeltaPerWindowRateOfChangeExceededNet change rate limit
Checks 4 and 5 (rate-of-change limits) are only enforced when their respective thresholds are greater than zero. In the current M2 deployment, both are set to 0 (disabled). See Pool Exposure Caps for details on rate-of-change limits.

Net Exposure Direction

The direction of net exposure change depends on the position side:
When a trader opens a LONG position, the pool takes the opposite (short) side:
proposedNetExposure = currentNetExposure - notional
LONG positions decrease net exposure (push the pool short).

Minimum Position Notional

In addition to upper caps, the protocol enforces a minimum position size to prevent dust positions that would be uneconomical to settle or liquidate:
notional >= minPositionNotional
ParameterDefault ValueUSDC Equivalent
minPositionNotional100,000,000 (raw)100 USDC
Dust positions create several problems:
  • Gas inefficiency: Settlement and liquidation gas costs may exceed the position’s notional value
  • Keeper overhead: The keeper service must scan and process every open position; tiny positions add overhead without meaningful economic value
  • Storage bloat: Each position consumes onchain storage; dust positions waste storage with negligible protocol utility
The 100 USDC minimum is deliberately low to keep the protocol accessible while filtering out positions that are too small to be meaningful.
Yes. When a trader calls reducePosition(positionId, reductionNotional), the remaining notional after reduction must be at least minPositionNotional. If the reduction would leave less than the minimum, the trader must close the position entirely instead. This prevents positions from being reduced to dust over multiple partial reductions.

Dynamic Cap Behavior

Because all caps are derived from pool equity, they exhibit important dynamic properties:

Growing Pool

As LPs deposit USDC, pool equity rises. All caps increase proportionally. More traders can open larger positions, and the protocol can support greater overall trading volume.

Shrinking Pool

When the pool pays out trader profits or absorbs bad debt, equity falls. Caps tighten automatically. Traders attempting to open positions near the old cap limits will find their transactions rejected until the pool recovers.

LP Withdrawals

LP withdrawals reduce totalAssets, which reduces poolEquity. This tightens all caps. The maxUtilizationBps check prevents withdrawals that would make caps too restrictive relative to existing open positions.

Bad Debt Events

Bad debt reduces pool equity directly. After a bad debt event, caps tighten immediately. In extreme cases where pool equity approaches zero, maxNetExposure approaches zero, effectively blocking all new positions.

Interaction Between Caps

The three cap levels form a hierarchy of protection:
Pool Level:     |netExposure| <= maxNetExposure        (500M with 10M pool)
Account Level:  accountNotional <= maxAccountNotional   (25M = 5% of 500M)
Position Level: positionNotional <= maxPositionNotional  (25M = 5% of 500M)
A position can pass the position-level cap and the account-level cap but still be rejected by the pool-level net exposure cap. For example, if the pool is already near its net exposure limit, even a small position that pushes exposure over the cap will be rejected. All three checks must pass for a position to be opened.

Parameter Tuning

All cap parameters are controlled by the RISK_ADMIN_ROLE and take effect immediately:
ParameterRangeEffect of IncreaseEffect of Decrease
perPositionCapFactorBps1 - 10,000Larger individual positions allowedSmaller positions, more diversification
perAccountCapFactorBps1 - 10,000Accounts can hold more total notionalAccounts limited to smaller totals
netExposureCapFactorBps1 - 10,000Pool accepts more net exposurePool becomes more conservative
stressMoveBps1 - 10,000Caps tighten (more stress buffer)Caps loosen (less stress buffer)
Increasing stressMoveBps is the most conservative tuning lever. It increases the assumed worst-case price move, which tightens all derived caps without changing the individual factor ratios. Doubling stressMoveBps from 200 to 400 halves all exposure caps.