---
title: "Decompose a position into named blocks (market / industry / style / stock-specific)"
method: POST
path: "/v4/decompose"
tags: ["Risk Metrics"]
---

# Decompose a position into named blocks (market / industry / style / stock-specific)

`POST /v4/decompose`

## Request

- Base URL: `https://riskmodels.app/api`
- URL: `https://riskmodels.app/api/v4/decompose`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://riskmodels.app/oauth/authorize`, token URL `https://riskmodels.app/api/oauth/token`, scopes: `mcp:read`)

The v4 product spine: `market + industry(sector, subsector) + style + stock_specific ≈ 1.0`.

The `industry` block is ETF-hedgeable (per-layer `hedge_notional_per_1_long`); the `style` block is a **diagnostic** — exposures plus incremental explained variance, never a tradeable hedge. `stock_specific` is the doubly-cleaned skill residual.

`basis` selects the explained-variance basis for the `style` and `stock_specific` blocks: `L3` (default — the clean variance partition) or `lstar` (skill basis). Industry hedge layers are always L3.

Two leaf fields are `null` until their upstream producer lands and fill in with no schema change: `style.exposures.{size,value}.beta` and `stock_specific.{sharpe_36m,rank_percentile}`.

The legacy flat shape remains at `POST /decompose`, same billing profile.

**Authentication:** Required (API Key or OAuth2)
**Billing:** $0.005 per request
**Rate Limit:** 120 requests/minute (default)

## Request body

- object
  - `ticker` string, required
  - `basis` 'L3' | 'lstar'

## Response `200`

Named-block decomposition

- object
  - `ticker` string
  - `symbol` string
  - `basis` 'L3' | 'lstar'
  - `data_as_of` string, date
  - `teo` string, date
  - `decomposition` object
    - `market` object
      - `factor` string
      - `beta` number, nullable
      - `explained_variance` number, nullable
      - `hedge_notional_per_1_long` number, nullable
      - `hedgeable` boolean
    - `industry` object
      - `explained_variance` number, nullable
      - `hedgeable` boolean
      - `layers` object
        - `sector` object
          - `etf` string, nullable
          - `beta` number, nullable
          - `explained_variance` number, nullable
          - `hedge_notional_per_1_long` number, nullable
        - `subsector` object
          - `etf` string, nullable
          - `beta` number, nullable
          - `explained_variance` number, nullable
          - `hedge_notional_per_1_long` number, nullable
    - `style` object: Diagnostic only — not hedgeable.
      - `explained_variance` number, nullable
      - `hedgeable` boolean
      - `exposures` object: `size` is the IWM-IWB (SMB) spread; `value` is IWD-IWF (HML). `beta` is null until the ERM3 per-stock loadings land.
    - `stock_specific` object
      - `explained_variance` number, nullable
      - `sharpe_36m` number, nullable
      - `rank_percentile` number, nullable
  - `_data_health` object
    - `er_populated` boolean
    - `er_sum` number, nullable

## Other responses

- `400`: Invalid request body (ticker missing or malformed)
- `401`: Authentication required
- `402`: Insufficient balance
- `404`: Ticker not found in the universe

---

[API](https://skmtc.dev/riskmodels/apis/riskmodels-api.md) · [All operations](https://skmtc.dev/riskmodels/apis/riskmodels-api/llms.txt) · [OpenAPI document](https://skmtc.dev/riskmodels/apis/riskmodels-api/revisions/1a034cad408a?raw)
