Summary & Valuation
Headline summary numbers, valuation time-series, and broker-analysis bucket
Three read endpoints under the Portfolio Swagger tag drive dashboard widgets that don't fit on the bundled /portfolios/{id} detail call.
| Method | Path | Service method |
|---|---|---|
GET | /portfolios/{id}/summary | core.Service.FindSummary |
GET | /portfolios/{id}/valuation | core.Service.FindValuation |
GET | /portfolios/{id}/broker-analysis | core.Service.FindBrokerAnalysis |
GET /portfolios/{id}/summary
Returns just the summary block from the full detail call. Cheap for clients that only need headline numbers. Internally delegates to FindDetail and slices the summary out — same Redis cache key (portfolio:detail:{id}).
{
"message": "Portfolio summary fetched successfully",
"data": {
"cUnits": 100, // current units across all companies
"sUnits": 40, // cumulative sold units across all companies
"totalInv": 50000, // all-time invested (current cost basis + user cost of sold)
"curInv": 30000, // current cost basis (WACC × current units)
"curVal": 36000, // market value (LTP × current units)
"soldVal": 24000, // gross sold (Σ SELL.amount)
"div": 4000, // Σ DIVIDEND.amount
"unrealPnl": 6000, // curVal − curInv
"realPnl": 3800, // Σ (SELL.netAmount − SELL.basePrice × qty)
"totalPnl": 13800, // unrealPnl + realPnl + div
"totalPnlPct": 27.6, // totalPnl / totalInv × 100, omitted if totalInv == 0
"dayPnl": 1850, // sum of per-company DayPnL
"dayPnlPct": 5.42, // dayPnl / (Σ CurUnits × PrevClose) × 100
"recv": 0 // Σ pending dividend receivables
}
}GET /portfolios/{id}/valuation
Time-series of portfolio value (v) and cost basis (cb) for charting.
Query params
| Param | Default | Values |
|---|---|---|
range | 3M | 1D, 1W, 1M, 3M, 6M, 1Y, YTD, ALL |
Algorithm
1D is intraday: bulk-fetch 15-min bucketed prices for today, replay transactions, emit one point per bucket.
All other ranges are daily: bulk-fetch close prices for the requested window, replay transactions, emit one point per trading day:
for each trading day d in range:
apply every event with transactedAt <= d to running State
v(d) = Σ CurUnits × close(company, d)
cb(d) = Σ CostCurrentThe same incremental sweep produces both the value and profit-over-time series.
Success
{
"message": "Portfolio valuation fetched successfully",
"data": {
"range": "3M",
"series": [
{ "d": "2026-01-28", "v": 28500, "cb": 30000 },
{ "d": "2026-01-29", "v": 29100, "cb": 30000 }
]
}
}Caching
| Key | TTL |
|---|---|
portfolio:valuation:{id}:1D | 60s |
portfolio:valuation:{id}:{1W,1M,3M,6M,1Y,YTD,ALL} | 5min |
All ranges are busted as a group on every transaction mutation.
GET /portfolios/{id}/broker-analysis
Today's accumulation/distribution/mixed buckets for the portfolio's currently held companies. Standalone — not bundled with /portfolios/{id} because most users never scroll to it and the dashboard shouldn't pay for it on first paint.
{
"message": "Broker analysis fetched successfully",
"data": {
"tradeDate": "2026-04-28",
"accumulation": {
"count": 1,
"items": [
{
"sym": "NABIL", "name": "Nabil Bank Limited", "icon": "...",
"brokerSignal": { "buyers": 4, "sellers": 12 },
"impact": 12345678.50
}
]
},
"distribution": { "count": 0, "items": [] },
"mixed": { "count": 0, "items": [] }
}
}Threshold ratio for accumulation/distribution classification: 1.5×.