When to Use
Use this skill to enforce pre-trade concentration risk limits across equity, futures, or crypto portfolios. Concentrating too much capital in a single security or issuer creates extreme idiosyncratic risk (e.g. unexpected earnings crash, regulatory action) and market impact risk during liquidation. This module validates proposed orders against maximum % NAV limits and % ADV (Average Daily Volume) constraints, automatically downsizing or rejecting non-compliant orders.
The NAV cap is applied to the absolute resulting exposure, so shorts are capped symmetrically with longs.
When NOT to Use
- As the only pre-trade control. A single-name cap does not bound leverage, aggregate portfolio exposure, correlated-cluster exposure, or drawdown. Compose it with the risk skills listed under Related Skills.
- For issuer-level or group-level limits. This module keys on a single tradable symbol. Aggregating multiple share classes, ADRs, or a parent and its subsidiaries into one issuer limit requires an issuer-mapping layer this skill does not provide.
- As a compliance attestation. The default 5%/10% thresholds are illustrative risk-policy defaults, not a certified implementation of any fund-diversification rule. See
references/standards.mdfor what the real rules do and do not say. - For a deliberately concentrated mandate (activist, single-name, or pair strategies), unless the limits are raised to the values the mandate actually authorises.
Prerequisites
- Portfolio Net Asset Value (NAV) and current signed position market values (negative for shorts).
- Average Daily Volume over the firm's chosen lookback window, and market price for each security.
- The signed notional of any orders already sent to a venue and not yet filled or cancelled.
Workflow
- Pre-Trade Evaluation: Submit the proposed order (
symbol,side,quantity,price) toSingleNameConcentrationLimiter.evaluate_order.sidemust be exactlyBUYorSELL— an unrecognised side raises rather than defaulting, because a side-parsing typo must never fail open into an unlimited branch. - Establish effective exposure: $E = \text{Current Signed Value} + \text{Pending Order Notional}$. Pending (unfilled) orders must be included, or several concurrent orders will each pass individually and breach collectively.
- NAV Concentration Check against the absolute cap $L = \text{Max NAV Pct} \times \text{NAV}$:
- If the order increases absolute exposure (same direction as $E$, or $E = 0$): headroom $= L - |E|$. If the position is already at or beyond $L$, headroom is zero and no further increase is approved.
- If the order reduces absolute exposure: headroom $= |E| + L$ — the full unwind is always permitted, plus a compliant position on the far side. A de-risking trade is never blocked, even when the position is already non-compliant.
- $N_{nav} = \lfloor \text{headroom} / \text{Price} \rfloor$.
- ADV Liquidity Check: $N_{adv} = \lfloor \text{Max ADV Pct} \times \text{ADV} \rfloor$, applied to both sides — market impact is side-agnostic. A missing or non-positive ADV rejects the order; it is never read as "no liquidity constraint".
- Order Downsizing: If the order exceeds $\min(N_{nav}, N_{adv})$, downsize to that quantity (or hard-reject when
allow_downsizing=False). Share counts are floored, never rounded up past a limit. - Portfolio HHI Calculation: Compute the Herfindahl-Hirschman Index ($HHI = \sum w_i^2$) and Effective Assets ($N_{eff} = 1 / HHI$) over gross exposure. Both return
NaNwhen gross exposure is zero, since concentration is undefined for an empty portfolio.
Full procedure: see
references/workflows.md. Standards reference: seereferences/standards.md. Printable pre-flight checklist: seeassets/checklist.md.
Common Pitfalls
- Applying the NAV cap only to buys. Treating "SELL reduces exposure" as universally true leaves short selling unbounded: a sell from a flat position opens a brand-new single-name short with no cap. Bound the absolute resulting exposure, and separate "this trade reduces |exposure|" from "this side is a sell".
- Ignoring unfilled orders. Checking only filled positions lets ten concurrent 4%-of-NAV orders each pass a 5% limit and settle into a 40% position. MiFID II RTS 6 Art. 15(2) makes including all orders sent to a venue an explicit requirement for firms in scope.
- Blocking de-risking trades. A limiter that rejects any order on an already-over-limit position traps the portfolio in the breach. Reducing trades must always pass.
- Ignoring Offsetting Futures/Derivatives: Calculating single-name equity concentration without factoring in single-stock futures or options delta. Fold delta notional into
current_position_valueif it shares the limit. - Static Share Limits in Volatile Markets: Hardcoding maximum share counts instead of dynamic % ADV limits. As market volume fluctuates, static share limits can cause severe market impact.
- Evaluating Post-Trade Only: Checking concentration after order execution when the position is already over-allocated. Concentration limits MUST be enforced pre-trade.
- Percent/fraction unit errors. Passing
5to mean "5%" would install a 500%-of-NAV cap and silently disable the control. The constructor rejects any limit outside(0, 1]. - Treating a downsized quantity as tradable as-is. The limiter returns a raw share count with no lot-size or minimum-fill rounding; see
minimum-fill-size-and-lot-rounding-logicbefore routing.
Verification
- Instantiate
SingleNameConcentrationLimiterwith a 5% NAV limit and 10% ADV limit. Submit an order for AAPL that would push NAV weight to 8% and consume 15% ADV. Verify that the limiter downsizes the order to satisfy both the 5% NAV and 10% ADV bounds. - Submit a
SELLof 5,000 shares from a flat position with ample ADV and verify it is downsized to the same 5%-of-NAV share count as the equivalentBUY— the short side must not be unbounded. - Submit a
SELLagainst an already-over-limit long and verify it passes untouched. - Calculate portfolio HHI across 10 equal-weighted positions and verify $HHI = 0.10$ ($N_{eff} = 10.0$).
- Run
python -m unittest discover -s skills/concentration-risk-single-name-limits/scripts.