---
title: "Get Attribution"
method: POST
path: "/api/portfolio/attribution"
tags: ["Portfolio Analytics"]
---

# Get Attribution

`POST /api/portfolio/attribution`

## Request

- Base URL: `https://api.harmoney.in`
- URL: `https://api.harmoney.in/api/portfolio/attribution`
- Auth: API key in header `x-api-key`

Brinson-Fachler active-return attribution of one portfolio against another. Both legs are funds or model portfolios, not a market index.

## Request body

- AttributionRequest: Request body for POST /api/portfolio/attribution. Two mutually exclusive evaluation-window modes, identical to ``/api/portfolio/holding-analytics``: * **Mode A** — ``as_of`` + ``horizon``: the window covers ``as_of − horizon`` to ``as_of``. * **Mode B** — ``start_date`` + ``end_date``: explicit window. The window is snapped to snapshot dates on *both* sides and then intersected, so the two portfolios are always attributed over the same period grid.
  - `portfolio_id` string, required: ``unique_id`` of the fund / portfolio being attributed.
  - `benchmark_id` string, required: ``unique_id`` of the fund / portfolio to attribute against. Symmetric with ``portfolio_id`` — a peer fund or model portfolio, not a market index.
  - `frequency` 'monthly' | 'annual' | 'MAX', required: The grid the window is MEASURED on. The response is one column whatever this is, so it does not pick a display cadence. ``MAX`` measures a single period, which weights every holding at the window's open — a name bought mid-window earns nothing however it performed, and a traded fund's turnover lands in the residual. Ask for ``monthly`` unless that is what you want.
  - `group_by` 'asset_class' | 'macro_sector' | 'sector' | 'industry' | 'rating' | 'tenor' | 'market_cap_category', required: The single pivot the active return is decomposed across. Brinson effects are defined against one partition of the portfolio; nested pivots would need a nested (multi-level) Brinson, which this endpoint does not implement.
  - `as_of` string, date: Mode A: last period-end for the window
  - `horizon` '1M' | '3M' | '6M' | 'YTD' | '1Y' | '3Y' | '5Y' | '10Y' | 'MAX': Mode A: outer-window size — ``as_of − horizon`` is the start
  - `start_date` string, date: Mode B: first period-end for the window
  - `end_date` string, date: Mode B: last period-end for the window

## Response `200`

Successful Response

- AttributionResponse: Response for POST /api/portfolio/attribution. ``requested_start`` / ``requested_end`` echo the resolved outer window; ``effective_start`` / ``effective_end`` are the intersection of the two sides' snapped windows — the span actually attributed. ``periods`` lists the period grid both sides were evaluated on; periods where either side had no boundary snapshot are excluded from it (and from the compounding). ``null_reason`` is populated — with ``groups`` empty and ``totals`` null — when the run could not produce an attribution: ``portfolio_not_found``, ``benchmark_not_found``, ``no_holdings_data``, ``no_overlapping_window``, or ``no_common_periods``.
  - `portfolio_id` string, required
  - `benchmark_id` string, required
  - `portfolio_name` string
  - `benchmark_name` string
  - `requested_start` string, date, required
  - `requested_end` string, date, required
  - `effective_start` string, date
  - `effective_end` string, date
  - `as_of` string, date
  - `horizon` '1M' | '3M' | '6M' | 'YTD' | '1Y' | '3Y' | '5Y' | '10Y' | 'MAX'
  - `frequency` 'monthly' | 'annual' | 'MAX', required
  - `group_by` string, required
  - `periods` Period[]
    - `period_start` string, date, required
    - `period_end` string, date, required
  - `totals` AttributionTotals: Window-level reconciliation figures, in percent. Side returns are compounded across periods and computed on a **holdings** basis (``Σ weight × return`` over disclosed positions) — not the funds' NAV returns, which carry expenses and unexplained residual that have no benchmark counterpart and no group to sit in. ``linking_residual`` is ``active_return − (allocation + selection + interaction)``. Cariño linking drives the *linking* part of it to zero, but the side returns above are the funds' NAV returns while the effects explain only the disclosed holdings — so what remains is the fee and unexplained gap between the two funds, split into ``expense`` and ``residual``. ``*_expense`` is each fund's own pro-rated TER (negative) and ``*_residual`` what neither its holdings nor its fee explain. They are per-side because the benchmark is a fund too, and their differences sum to ``linking_residual`` — so ``allocation + selection + interaction + (portfolio − benchmark) expense + (portfolio − benchmark) residual`` reconciles to ``active_return``. ``*_expense`` is ``None`` without a TER series, ``*_residual`` without NAV.
    - `portfolio_return` number, required
    - `benchmark_return` number, required
    - `active_return` number, required
    - `portfolio_contribution` number, required
    - `benchmark_contribution` number, required
    - `allocation` number, required
    - `selection` number, required
    - `interaction` number, required
    - `portfolio_expense` number, required
    - `benchmark_expense` number, required
    - `portfolio_residual` number, required
    - `benchmark_residual` number, required
    - `linking_residual` number, required
  - `groups` GroupAttribution[]
    - `group` string, required
    - `allocation` number, required
    - `selection` number, required
    - `interaction` number, required
    - `total` number, required
    - `avg_portfolio_weight` number, required
    - `avg_benchmark_weight` number, required
    - `avg_active_weight` number, required
    - `portfolio_return` number, required
    - `benchmark_return` number, required
    - `portfolio_contribution` number, required
    - `benchmark_contribution` number, required
    - `start_portfolio_weight` number, required
    - `start_benchmark_weight` number, required
    - `end_portfolio_weight` number, required
    - `end_benchmark_weight` number, required
    - `constituents` ConstituentAttribution[]
      - `name` string, required
      - `identifier` string, required
      - `selection` number, required
      - `start_portfolio_weight` number, required
      - `start_benchmark_weight` number, required
      - `end_portfolio_weight` number, required
      - `end_benchmark_weight` number, required
      - `portfolio_contribution` number, required
      - `benchmark_contribution` number, required
      - `avg_portfolio_weight` number, required
      - `avg_benchmark_weight` number, required
      - `avg_active_weight` number, required
      - `portfolio_return` number
      - `benchmark_return` number
  - `null_reason` string

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/inferedge/apis/india-mutual-funds-data-feed.md) - [All operations](https://skmtc.dev/inferedge/apis/india-mutual-funds-data-feed/llms.txt) - [OpenAPI document](https://skmtc.dev/inferedge/apis/india-mutual-funds-data-feed/revisions/fe0d3be721c0?raw)
