Skip to content
/api/v1/portfolio/analyze

Full portfolio analytics for a set of holdings

Computes a complete portfolio report for the CURRENT composition from dividend-adjusted daily prices: dollar-weighted allocation by sector (vs the benchmark) and market-cap bucket, a base-100 portfolio-vs-benchmark performance curve with period returns and excess, 52-week and 3-year risk (beta, annualized volatility, Sharpe, tracking error, information ratio), 1-day historical VaR and expected shortfall at 95% and 99%, maximum drawdown, real-weight risk contribution, correlation clusters, per-stock beta/volatility/drawdown, traffic-light concentration flags, and a 0-100 portfolio score with diversification/risk/quality subscores. Equal-weight requests may set one performance start date; risk and scoring retain the trailing 3-year window. Aggregates only — no per-ticker price series is ever returned.

Why use this

One call turns a list of tickers (with optional share counts) into an institutional-grade portfolio X-ray. Post `{ holdings: [{ symbol, shares? }], benchmark }`; when every `shares` is omitted the analysis falls back to equal weight and sets `equal_weighted: true`. That equal-weight request may add `start_date` to begin both displayed performance curves there and receive each holding's stored exact-date unadjusted close. Symbols with no price coverage are returned in `excluded[]` (never silently dropped). The response is a single aggregate object — the base-100 `performance.index` carries the portfolio-level curve and the benchmark curve only, so raw price series never leave the server. Risk-free rate is the 30-day average SOFR. Ideal for a holdings-review dashboard, a pre-trade concentration check, or a daily portfolio briefing.

Common use case

A wealth dashboard sends the user's 25 holdings and renders the scorecard, sector-vs-SPY allocation, cumulative performance chart, and a risk panel (beta, VaR, drawdown, concentration flags) from one response.

Portfolio analytics for the current composition over a trailing 3-year window. Post a set of holdings (with optional share counts) and a benchmark; receive dollar-weighted allocation vs the benchmark, a base-100 performance curve with period returns and excess, 52-week and 3-year risk statistics (beta, volatility, Sharpe, tracking error, information ratio, VaR/shortfall at 95/99, max drawdown), real-weight risk contribution, correlation clusters, per-stock risk, traffic-light concentration flags, and a deterministic 0-100 portfolio score. Returns aggregates only — the base-100 curves are portfolio-level and benchmark-level; no per-ticker price series is exposed.

Parameters

NameInRequiredDefaultAllowedDescriptionExample
include_researchbodyoptionalOptional boolean, default false. Include stored public research for all resolved holdings in one serial batch: completed and early-quarter institutional ownership, discretionary insider activity and clusters, 13D/G threshold filings, convergence, source dates and coverage omissions. No account identity, transactions or writes. Missing fundamentals and technical records remain null; website consumers may join the existing financial and technical batch readers.true
start_datebodyoptionalOptional ISO date for an equal-weight request, no later than today. Both displayed performance curves begin on this date and are rebased to 100. The response also includes one exact-date unadjusted closing-price reference for every submitted holding through a single batched query. Missing closes remain null; no nearby date is substituted. The trailing 3-year risk and score calculations remain unchanged. Cannot be combined with share counts or price_dates.2024-01-02
price_datesbodyoptionalOptional list of up to 100 exact symbol/date pairs for submitted holdings. ISO dates must be no later than today. Returns unadjusted closing-price references for missing purchase costs. Never substitutes another date, changes the current-composition analysis or posts a transaction.[{"symbol":"AAPL","date":"2026-09-11"}]
holdingsbodyrequiredArray of 1-100 holdings. Each item: `symbol` (required, ticker string) and `shares` (optional, positive number). If EVERY item omits `shares`, the portfolio is analyzed equal-weighted and the response sets `equal_weighted: true`. Duplicate symbols are collapsed (last `shares` wins). More than 100 holdings returns 400.[{"symbol":"AAPL","shares":50},{"symbol":"MSFT","shares":30}]
benchmarkbodyoptionalSPYBenchmark symbol for beta, tracking error, excess return, and the sector-weight comparison. Defaults to `SPY`. Must have price coverage or the call returns 502 BENCHMARK_UNAVAILABLE.SPY

Response schema

FieldTypeNullableDescription
researchobjectnoPresent only when include_research=true. Contains as_of, records (one per resolved symbol), sections, omissions and version. Each record includes company/sector/industry, completed/early institutional data, insider summary, buy/sell clusters, ownership filings and convergence. Source windows, comparison rules and missing-data semantics are unchanged; no News input.
entry_pricesarraynoPresent with nonempty price_dates or an equal-weight start_date. One row per requested pair; start_date produces one row for every submitted holding. Each row contains symbol, date, price (positive decimal string or null), price_type=unadjusted_close, and reason (null or no_price_for_date). These are exact-date market references, not actual trade prices or a price series. Missing references do not alter weights.
data_datestringnoISO `YYYY-MM-DD` of the latest trading date the analysis is computed through.
benchmarkstringnoThe benchmark symbol used (echoes the request; default `SPY`).
risk_free_ratenumbernoAnnualized risk-free rate (decimal, e.g. `0.043`) used in every Sharpe/information-ratio calculation — the 30-day average SOFR.
equal_weightedbooleanno`true` when the portfolio was analyzed equal-weighted because no share counts were supplied.
holdingsarraynoPer-holding rows: `{ symbol, company, sector, industry, mkt_cap, shares, adj_close, value, weight, pct_change_1d, beta, vol_ann, max_drawdown }`, sorted by weight descending. `beta`/`vol_ann`/`max_drawdown` are null for names with insufficient trading history (never a substituted value).
totalsobjectno`{ value, day_change_pct, day_change_value, positions }`. `value`/`day_change_value` are null under equal-weight (no share counts to value).
allocationobjectno`{ by_sector: [{ name, weight, spy_weight }], by_cap: [{ bucket, weight }], spy_weight_source }`. Dollar-weighted. `spy_weight` is the benchmark's static sector weight (source tagged by `spy_weight_source`).
performanceobjectno`{ index: [{ date, portfolio, benchmark }], periods: [{ period, portfolio, benchmark, excess }] }`. `index` is base-100; with start_date both curves begin there and are rebased to 100, otherwise the existing trailing window applies. Periods: `1w`, `mtd`, `ytd`, `1m`, `3m`, `6m`, `12m` (periods beyond the available performance history are omitted).
riskobjectno`{ windows: { "52w": {...}, "3y": {...} }, max_drawdown, risk_contribution: [{ symbol, pct }], correlation_clusters: [[symbol,...]], flags: [{ key, level, label, detail }] }`. Each window carries `{ beta, vol, sharpe, tracking_error, information_ratio, var95, var99, shortfall95, shortfall99 }`. VaR/shortfall are positive 1-day loss magnitudes. Flags are red/amber/green.
scoreobjectno`{ total (0-100), band, subscores: { diversification, risk, quality }, quality_basis }`. Deterministic — the same holdings always score the same. `band` is one of Strong / Solid / Mixed / Fragile / High Risk.
excludedarrayno`[{ symbol, reason }]` for submitted symbols with no price coverage (e.g. `reason: "no_price_data"`). Empty array when every holding resolved.

Sample response

·
  • "data_date": "2026-06-30"
  • "benchmark": "SPY"
  • "risk_free_rate": 0.043
  • "equal_weighted": false
  • "holdings":
    ]
  • "totals":
    • "value": 17361
    • "day_change_pct": 0.34
    • "day_change_value": 59.03
    • "positions": 2
    }
  • "allocation":
    • "by_sector":
    • "by_cap":
    • "spy_weight_source": "static_2026Q2"
    }
  • "performance":
    • "index":
    • "periods":
    }
  • "risk":
    • "windows":
    • "max_drawdown": -0.29
    • "risk_contribution":
    • "correlation_clusters":
    • "flags":
    }
  • "score":
    • "total": 41
    • "band": "Fragile"
    • "subscores":
    • "quality_basis": "risk_adjusted_return_3y"
    }
  • "excluded": []
}

Errors

StatusLabelDescription
200OKRequest succeeded.
400Bad RequestInvalid query, body, or path parameter.
401UnauthorizedMissing or invalid Authorization header / api_Token.
402Payment RequiredInsufficient token balance for this call. Top up
429Too Many RequestsRate limit exceeded for your tier (see /pricing for tier limits). Tier limits
500Server ErrorUnexpected server-side failure. Retry with backoff; report if persistent.

Code samples

curl -X POST "https://api.finradar.ai/api/v1/portfolio/analyze?api_Token=YOUR_API_KEY" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "include_research": "true",
  "start_date": "2024-01-02",
  "price_dates": "[{\"symbol\":\"AAPL\",\"date\":\"2026-09-11\"}]",
  "holdings": "[{\"symbol\":\"AAPL\",\"shares\":50},{\"symbol\":\"MSFT\",\"shares\":30}]",
  "benchmark": "SPY"
}'

Generate an API key in /account/credentials to run live queries (literal YOUR_API_KEY placeholder shown until then).