Shop It Docs
Stock Analysis (Fundamentals)

Ownership

Two-tier (always) / four-tier (Phase A) ownership donut, board of directors, management team, optional auditor and top shareholders

The ownership endpoint backs the "Who owns this stock" panel: a donut chart, two people-list cards (BOD, Management), and two optional cards (Auditor, Top Shareholders) that light up when Phase A CSV ingest has uploaded data for the symbol.

MethodPathCache TTL
GET/api/nepse/companies/{symbol}/fundamentals/ownership1 hr

Request

No query parameters.

curl "$BASE/api/nepse/companies/NABIL/fundamentals/ownership"

Response

{
  "message": "ok",
  "data": {
    "symbol": "NABIL",
    "name": "Nabil Bank Limited",
    "sector": "Commercial Banks",
    "tier2": {
      "promoter": { "value": 51, "source": "mdp", "status": "valid" },
      "public":   { "value": 49, "source": "mdp", "status": "valid" }
    },
    "tier4": {
      "fiscalYearBs":  "2080/2081",
      "promoter":      { "value": 51,  "source": "manual_swp", "status": "valid" },
      "public":        { "value": 31,  "source": "manual_swp", "status": "valid" },
      "institutional": { "value": 14,  "source": "manual_swp", "status": "valid" },
      "government":    { "value":  4,  "source": "manual_swp", "status": "valid" },
      "uploadedAt": "2026-04-12T10:00:00+05:45"
    },
    "bod": [
      { "name": "Mr. X", "designation": "Chairperson" },
      { "name": "Mr. Y", "designation": "Director" }
    ],
    "managementTeam": [
      { "name": "Mr. Z", "designation": "Chief Executive Officer" }
    ],
    "auditor": {
      "fiscalYearBs": "2080/2081",
      "auditorName": "CA Foo",
      "auditorFirm": "Foo & Associates",
      "source": "manual_swp",
      "uploadedAt": "2026-04-12T10:00:00+05:45"
    },
    "topShareholders": [
      {
        "fiscalYearBs": "2080/2081",
        "rank": 1,
        "holderName": "Foo Holdings",
        "shares": 1500000,
        "percent": 5.4,
        "source": "manual_swp",
        "uploadedAt": "2026-04-12T10:00:00+05:45"
      }
    ],
    "dataAsOf": "2026-04-12T10:00:00+05:45",
    "servedAt": "2026-05-23T14:56:30+05:45"
  }
}

Tier resolution

The donut has two possible shapes — FE picks the richer one when both are present.

Tier2 (always present)

Backed by the MDP-denormalized promoter_percent / public_percent columns on nepse_companies. When the company-sync worker hasn't filled the row yet, both fields surface status: upstream_missing so the donut renders a tooltip rather than empty bars.

Tier4 (Phase A override)

Returned when an operator-uploaded row exists in nepse_ownership_extended for the company. The four categories sum to 100 in a clean upload, but institutional and government may carry status: upstream_missing on a partial CSV.

The tier4 object is omitted entirely (not present in the JSON) when no Phase A row exists. Don't render the four-tier donut when tier4 is absent; render the tier2 donut.

People panels

bod and managementTeam decode from the nepse_company_profiles JSONB (board_members / management_team). Each entry is {name, designation}; rows with both fields blank are dropped.

Malformed JSON falls through with a slog.Warn and returns an empty slice — the endpoint never 500s because of upstream JSON quirks.

Auditor + Top Shareholders

Both are present only when Phase A CSV ingest has populated the override tables. Until then:

  • auditor is omitted from the response (FE renders the "Awaiting data" state).
  • topShareholders is always present (possibly empty) — easier for FE to iterate over an empty slice than to null-check.
type Auditor = {
  fiscalYearBs: string;
  auditorName: string;
  auditorFirm?: string;
  source: 'manual_swp';
  uploadedAt: string;  // RFC3339
};

type TopShareholder = {
  fiscalYearBs: string;
  rank: number;        // 1-indexed within FY
  holderName: string;
  shares?: number;     // nullable — some CSVs only carry percent
  percent?: number;    // 0-100 scale
  source: 'manual_swp';
  uploadedAt: string;
};

Field rules

FieldTypeNotes
tier2.promoter / tier2.publicMetricFloat64source: mdp, percentages on 0–100
tier4Tier4?Object omitted when no Phase A override
bod / managementTeamPersonRow[]Always present (possibly empty)
auditorAuditor?Object omitted when no Phase A override
topShareholdersTopShareholder[]Always present (possibly empty)
dataAsOfRFC3339 NPTOldest signal across profile + overrides + top shareholders

Caching

KeyTTL
nepse:fundamentals:ownership:{SYMBOL}1 hr

The long TTL reflects that promoter/public changes land via the weekly company-sync worker and Phase A CSV uploads are operator-initiated — neither cadence justifies a shorter TTL.

Errors

CodeReason
200OK (even when every override is empty)
404Symbol not in nepse_companies
500DB error