# Risk parity (risk_parity)

Desk: Macro. Page: https://hedgefund.md/models/risk_parity

**What it asks.** What weights give each asset the same share of risk, how much leverage reaches the vol target, and what does a jump in correlation do to it?

Solves equal risk contribution weights for the vols and correlations sent, shows the leverage that reaches a vol target and what financing it costs, then moves every correlation up 0.3 with weights held and shows the new vol and the leverage cut. The vols and correlations come from the caller, and the 0.3 move is fixed. It is arithmetic on those estimates, not an allocation to follow and not a forecast.

Results are hypothetical: arithmetic on the numbers sent, a made-up template or a stated default. Nobody achieved them, and they aren't a forecast, a recommendation, an offer or a record of real trading. Don't show any output to investors or clients, or use it to market an investment. Terms: https://hedgefund.md/terms

## Inputs

| Input | Type | Range | Required | What it is |
| --- | --- | --- | --- | --- |
| `assets` | array | 1 to 40 items | yes | The assets, up to 40. |
| `assets[].name` | string |  | yes | A label for the asset, such as Bond sleeve. |
| `assets[].vol_pct` | number | 0.01 to 500 | yes | Annual volatility, in percent. Must be above zero. |
| `assets[].expected_return_pct` | number | -100 to 500 | no | Optional hypothetical annual return, in percent. Send it for all assets or none. |
| `corr` | array | 1 to 40 items | yes | Correlation matrix, in the order of assets. |
| `target_vol_pct` | number | 0.01 to 200 | yes | Annual vol target for the levered book, in percent. |
| `financing_rate_pct` | number | -10 to 100 | yes | Annual rate on leverage above 1, in percent. |
| `reference_weights_pct` | array | 0 to 40 items | no | Optional weights to compare against, in percent, in the order of assets. |

## Template

Made-up numbers that run as they stand, so you can see the shape of a result. They aren't market levels, typical values or estimates, and the names in them don't exist. Replace every number before you read anything into a run.

```json
{
  "assets": [
    { "name": "Stock sleeve", "vol_pct": 16, "expected_return_pct": 7 },
    { "name": "Bond sleeve", "vol_pct": 6, "expected_return_pct": 4.5 },
    {
      "name": "Commodity sleeve",
      "vol_pct": 18,
      "expected_return_pct": 5
    },
    {
      "name": "Inflation bond sleeve",
      "vol_pct": 7,
      "expected_return_pct": 4.5
    }
  ],
  "corr": [
    [1, 0.1, 0.3, 0.1],
    [0.1, 1, -0.1, 0.6],
    [0.3, -0.1, 1, 0.2],
    [0.1, 0.6, 0.2, 1]
  ],
  "target_vol_pct": 10,
  "financing_rate_pct": 4,
  "reference_weights_pct": [60, 40, 0, 0]
}
```

## What the template gives

On these numbers, equal risk weights put 40.66% in Bond sleeve, leverage of 1.67x reaches the 10% target, and every correlation up 0.3 takes vol to 12.53% with weights and leverage held.

- Vol before leverage: 5.9894%
- Leverage to reach target: 1.6696x
- Financing cost: 2.6784% of NAV a year
- Vol with correlations up 0.3: 12.53% (watch)
- Leverage cut to get back to target: 0.3371x
- Solver residual: 5.50e-11 (ok)
- Hypothetical return: 5.5919% a year
- Reference top risk share: Stock sleeve: 92.13% (watch)

Where it flips, with the other inputs held:

- `target_vol_pct` at 5.989421: At this target the book needs no leverage. Above it, leverage is above 1 and financing starts.
- `financing_rate_pct` at 4.9535: At this financing rate leverage adds nothing to the hypothetical return. Above it, leverage takes away.

## Formula

Sigma_ij = corr_ij vol_i vol_j. Cyclical coordinate descent: x_i = (-b + sqrt(b^2 + 4 a c)) / (2 a), a = Sigma_ii, b = sum over j != i of x_j Sigma_ij, c = 1 / n. w = x / sum x. Risk share_i = w_i (Sigma w)_i / w' Sigma w. Leverage k = target / sqrt(w' Sigma w). Return = k sum w_i mu_i - max(0, k - 1) financing. Stress: corr_ij + 0.3, capped at 0.99, with w and k held.

## Assumptions

- Weights are long only and add to 100% before leverage. Equal risk contribution has one solution for a positive definite matrix.
- Descent stops when every risk share is within 1e-10 of 1 / n. The residual shows how close it got.
- If 2000 sweeps haven't got there, which happens when the matrix is close to singular, Newton steps on the same objective finish it. The objective has one minimum, so the route doesn't change the answer.
- Above a leverage of 1, financing is charged on the amount above 1. Below 1, the rest sits idle and earns nothing.
- Stress adds 0.3 to every off-diagonal correlation, capped at 0.99, and never lowers one already above 0.99.
- The leverage cut is the fall in leverage that brings stressed vol back to the target.
- Reference weights are used as sent, without scaling to 100%.

## Not modeled

- Error in the vols and correlations themselves. Small changes in them can move these weights.
- Vol and correlation that change over time, except for the one stress here.
- Interest on idle cash, financing spreads that widen under stress, margin calls and forced selling.
- Rebalancing costs, slippage, fees and taxes.
- Fat tails: this works on variance alone.

## Call it

Every result has the same fields: summary, readings, tables, charts, flags, flips, formula, assumptions, not_modeled, hypothetical and inputs_hash. Read flags, flips and not_modeled with the summary, and pass them along together.

### HTTP

No key. The tools keep nothing you send. Send positions by POST, because a URL can land in a server log.

```bash
curl -s https://hedgefund.md/api/v1/risk_parity \
  -H 'content-type: application/json' \
  -d '{"assets":[{"name":"Stock sleeve","vol_pct":16,"expected_return_pct":7},{"name":"Bond sleeve","vol_pct":6,"expected_return_pct":4.5},{"name":"Commodity sleeve","vol_pct":18,"expected_return_pct":5},{"name":"Inflation bond sleeve","vol_pct":7,"expected_return_pct":4.5}],"corr":[[1,0.1,0.3,0.1],[0.1,1,-0.1,0.6],[0.3,-0.1,1,0.2],[0.1,0.6,0.2,1]],"target_vol_pct":10,"financing_rate_pct":4,"reference_weights_pct":[60,40,0,0]}' \
  | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

### MCP

The tool is `risk_parity` on the server at https://hedgefund.md/mcp.

```bash
curl -s https://hedgefund.md/mcp \
  -H 'content-type: application/json' -H 'accept: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"risk_parity","arguments":{"assets":[{"name":"Stock sleeve","vol_pct":16,"expected_return_pct":7},{"name":"Bond sleeve","vol_pct":6,"expected_return_pct":4.5},{"name":"Commodity sleeve","vol_pct":18,"expected_return_pct":5},{"name":"Inflation bond sleeve","vol_pct":7,"expected_return_pct":4.5}],"corr":[[1,0.1,0.3,0.1],[0.1,1,-0.1,0.6],[0.3,-0.1,1,0.2],[0.1,0.6,0.2,1]],"target_vol_pct":10,"financing_rate_pct":4,"reference_weights_pct":[60,40,0,0]}}}' \
  | jq '.result.structuredContent | {summary, flags, flips, not_modeled, inputs_hash}'
```

### The library

The same code on your own machine, with no dependencies, and nothing is sent anywhere.

```bash
curl -fsSL https://hedgefund.md/hedgefund.mjs -o hedgefund.mjs   # read it before you run it
node hedgefund.mjs template risk_parity > mine.json           # replace every number in it
node hedgefund.mjs run risk_parity mine.json | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

In a script:

```js
import { run, template } from "./hedgefund.mjs";

const inputs = template("risk_parity"); // replace every number in it
const { summary, flags, flips, not_modeled } = run("risk_parity", inputs);
```
