---
title: "Correlation vs macro factor returns"
method: POST
path: "/correlation"
tags: ["Risk Metrics"]
---

# Correlation vs macro factor returns

`POST /correlation`

## Request

- Base URL: `https://riskmodels.app/api`
- URL: `https://riskmodels.app/api/correlation`
- Auth: HTTP bearer

Computes Pearson or Spearman correlation between a stock return series and daily macro factor returns stored in `macro_factors` (e.g. bitcoin, vix). Stock series may be gross returns or ERM3 residual returns (L1/L2/L3) using the replication identity with SPY and sector/subsector ETFs. Request body accepts a single ticker or an array for batch (`results` array). Cost: $0.01 per request. Factor strings are normalized to lowercase canonical keys matching `macro_factors.factor_key` (aliases such as btc → bitcoin). JSON Schema (MCP / validation): request body https://riskmodels.app/schemas/factor-correlation-request-v1.json ; single-ticker 200 body https://riskmodels.app/schemas/factor-correlation-v1.json .

## Request body

- FactorCorrelationRequest
  - `ticker` union, required
    - string
    - string[]
  - `factors` string[]: Factor keys matching `macro_factors.factor_key`. Two sleeves share the table and endpoint; the `metadata.category` field on each row distinguishes them. **Macro sleeve (10 keys, from `ds_macro_factor.zarr`):** `inflation` (TIP), `term_spread` (VGIT), `short_rates` (BIL), `credit` (HYG), `oil` (USO), `gold` (GLD), `usd` (UUP), `volatility` (VXX short-term vol futures), `bitcoin` (BITO), `vix_spot` (FRED VIXCLS spot index). Note: `volatility` and `vix_spot` are DIFFERENT — VXX captures futures roll dynamics, VIXCLS is pure spot. **Style sleeve (8 keys, mirrored from `ds_etf.zarr`):** `momentum` (MTUM), `quality` (QUAL), `low_vol` (USMV), `value` (VLUE), `growth` (IWF), `size` (IWM), `dividend` (SCHD), `moat` (MOAT). History starts at each ETF's launch date (2011–2013 for MSCI factors; 2000 for IWF/IWM); pre-launch windows return `null` correlations. Style factors are most informative against `return_type=l3_residual`: because the residual is already orthogonal to SPY/sector/subsector, correlation isolates the pure style tilt. Legacy v1 aliases (`dxy`→`usd`, `vix`→`vix_spot`, `ust10y2y`→`term_spread`), common synonyms (btc, wti, xau, gld, cpi, tips), and style aliases (mtum, qual, usmv, vlue, iwf, iwm, schd, moat, minvol, small_cap) are normalized server-side. Defaults to all 18 canonical keys when omitted.
  - `return_type` 'gross' | 'l1' | 'l2' | 'l3_residual'
  - `window_days` integer
  - `method` 'pearson' | 'spearman'

## Response `200`

Correlations per macro factor (null when insufficient overlap or missing data).

- union
  - object
    - `ticker` string, required
    - `return_type` 'gross' | 'l1' | 'l2' | 'l3_residual', required
    - `window_days` integer, required
    - `method` 'pearson' | 'spearman', required
    - `correlations` object, required
    - `overlap_days` integer, required: Largest number of paired observations used among requested factors.
    - `warnings` string[], required
    - `_metadata` RiskMetadata: Lineage and provenance metadata for all data responses. Included in response body as _metadata and as X-Risk-* headers.
      - `model_version` string: ERM3 model version (e.g. ERM3-L3-v30).
      - `data_as_of` string, date: Latest trading date reflected in the response (model outputs / latest snapshot).
      - `factor_set_id` string: Factor set identifier.
      - `universe_size` integer: Number of stocks in the universe.
      - `wiki_uri` string, uri: Methodology documentation URL.
      - `factors` string[]: L3 factor ETF tickers (market + 11 sectors).
      - `data_source` 'zarr': Present on history-style JSON responses. Always `"zarr"` as of the pure-Zarr SSOT cutover: all historical time series (daily metrics, hedge weights, returns decomposition, rankings) are served from consolidated Zarr stores on GCS. The Supabase `security_history` table has been removed; the legacy `"supabase"` value is retained in clients only for backwards compatibility.
      - `range` string[]: Optional inclusive ISO date bounds of the returned history window when `data_source` is set.
    - `_agent` object
      - `latency_ms` integer
      - `request_id` string
  - object
    - `results` union[], required
      - union
        - FactorCorrelationResultBody
          - `ticker` string, required
          - `return_type` 'gross' | 'l1' | 'l2' | 'l3_residual', required
          - `window_days` integer, required
          - `method` 'pearson' | 'spearman', required
          - `correlations` object, required
          - `overlap_days` integer, required: Largest number of paired observations used among requested factors.
          - `warnings` string[], required
        - object
          - `ticker` string, required
          - `error` string, required
          - `status` integer, required
    - `_metadata` RiskMetadata: Lineage and provenance metadata for all data responses. Included in response body as _metadata and as X-Risk-* headers.
      - `model_version` string: ERM3 model version (e.g. ERM3-L3-v30).
      - `data_as_of` string, date: Latest trading date reflected in the response (model outputs / latest snapshot).
      - `factor_set_id` string: Factor set identifier.
      - `universe_size` integer: Number of stocks in the universe.
      - `wiki_uri` string, uri: Methodology documentation URL.
      - `factors` string[]: L3 factor ETF tickers (market + 11 sectors).
      - `data_source` 'zarr': Present on history-style JSON responses. Always `"zarr"` as of the pure-Zarr SSOT cutover: all historical time series (daily metrics, hedge weights, returns decomposition, rankings) are served from consolidated Zarr stores on GCS. The Supabase `security_history` table has been removed; the legacy `"supabase"` value is retained in clients only for backwards compatibility.
      - `range` string[]: Optional inclusive ISO date bounds of the returned history window when `data_source` is set.
    - `_agent` object
      - `latency_ms` integer
      - `request_id` string

## Other responses

- `400`: Invalid body.
- `401`: Missing or invalid Bearer token.
- `402`: Insufficient balance.
- `404`: Ticker not found or insufficient history.
- `429`: Rate limit exceeded.

---

[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)
