Portfolio Companies
ListHoldings, GetDistribution, and GetCompanyDetail — the read-side per-company surface
Three endpoints under the Portfolio Companies Swagger tag drive the holdings table, the distribution pies, and the per-company drill-down. The companies/ package is fully read-only — no TxRunner.
| Method | Path | Service method |
|---|---|---|
GET | /portfolios/{id}/holdings | companies.Service.FindHoldings |
GET | /portfolios/{id}/distribution | companies.Service.FindDistribution |
GET | /portfolios/{id}/companies/{symbol} | companies.Service.FindCompanyDetail |
GET /portfolios/{id}/holdings
Paginated, filterable, sortable list of holdings — what the dashboard's stock table consumes.
Query params
| Param | Default | Notes |
|---|---|---|
page | 1 | 1-indexed |
size | 20 | max 100 |
q | — | Case-insensitive substring on symbol or name |
sort | sym | One of: sym, name, sec, ltp, pch, cUnits, totalInv, wacc, curVal, curInv, unrealPnl, totalPnl, totalPnlPct, dayPnl, holdingPct |
order | asc | asc or desc |
includeSuspended | false | When false, suspended/delisted companies are dropped from the list and counted only in suspendedSummary on /portfolios/{id} |
totalPnlPct orders nil values last (NULLS LAST) regardless of direction.
Response shape
type HoldingResponse = {
sym: string; name: string; icon?: string; sec?: string;
cUnits: number; sUnits: number;
wacc: number; // CostCurrent / CurUnits
ltp: number; pch: number;
totalInv: number; // all-time invested
curInv: number; // current cost basis
curVal: number; // LTP × CurUnits
unrealPnl: number; // curVal − curInv
realPnl: number;
totalPnl: number;
totalPnlPct?: number;
dayPnl: number;
holdingPct: number; // curVal / Σ curVal × 100
suspended: boolean;
};GET /portfolios/{id}/distribution
Two pies for the portfolio: byCompany and bySector, both valued at LTP × current units. Sorted by current value desc. Suspended and zero-unit holdings are excluded. Percentages rounded to 2 decimal places.
{
"message": "Portfolio distribution fetched successfully",
"data": {
"byCompany": [
{ "label": "NABIL", "v": 60000, "pct": 60.00 },
{ "label": "NICA", "v": 40000, "pct": 40.00 }
],
"bySector": [
{ "label": "Commercial Banks", "v": 100000, "pct": 100.00 }
]
}
}The previous byInvested sector view (WACC × units) is no longer surfaced. If the frontend needs invested-cost shares, reconstruct from positions[] in /portfolios/{id}.
GET /portfolios/{id}/companies/{symbol}
Single-company drill-down. Returns live market data, the holding summary, and the per-symbol transaction timeline.
{
"message": "Portfolio company fetched successfully",
"data": {
"company": {
"sym": "NABIL", "name": "Nabil Bank Limited",
"sec": "Commercial Banks", "icon": "...",
"ltp": 600, "ch": 5, "pch": 0.84, "prevClose": 595,
"52h": 750, "52l": 480, "52wPos": 0.48,
"suspended": false
},
"holding": {
"cUnits": 100, "sUnits": 0,
"wacc": 500, "waccBreakdown": { /* per-source qty/cost */ },
"totalInv": 50000, "curInv": 50000, "curVal": 60000,
"soldVal": 0, "div": 250,
"realPnl": 0, "unrealPnl": 10000, "totalPnl": 10250,
"totalPnlPct": 20.50, "dayPnl": 500, "recv": 0
},
"timeline": [
{
"kind": "transaction", "id": "...",
"type": "BUY", "qty": 100, "price": 500,
"amount": 50000, "totalAmount": 50250,
"at": "2025-11-10T00:00:00.000Z"
}
]
}
}The lots query parameter is accepted for backwards compatibility but no longer returns lot data — FIFO lot tracking was removed when the unified transactions model was adopted. The fifoLots field, when present, will always be empty.
waccBreakdown decomposes the WACC pool by source (boughtCost/Qty, bonusQty, rightSubscribedCost/Qty, ipoCost/Qty, fpoCost/Qty, auctionCost/Qty, totalCost, totalQty, wacc).