Shop It Docs
Portfolio Module

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.

MethodPathService method
GET/portfolios/{id}/holdingscompanies.Service.FindHoldings
GET/portfolios/{id}/distributioncompanies.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

ParamDefaultNotes
page11-indexed
size20max 100
qCase-insensitive substring on symbol or name
sortsymOne of: sym, name, sec, ltp, pch, cUnits, totalInv, wacc, curVal, curInv, unrealPnl, totalPnl, totalPnlPct, dayPnl, holdingPct
orderascasc or desc
includeSuspendedfalseWhen 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).