Portfolio Module
Reference
Every portfolio endpoint, DTO, cache key, and SQL query at a glance
A single-page lookup table for the portfolio module. All paths prefixed with /api/v1/nepse. All require JWT.
All 15 endpoints
Portfolio (tag: Portfolio — 8 endpoints)
| Method | Path | Handler | Status |
|---|---|---|---|
POST | /portfolios | core.Handler.Create | 201 |
GET | /portfolios | core.Handler.List | 200 |
GET | /portfolios/{id} | core.Handler.Get | 200 |
GET | /portfolios/{id}/summary | core.Handler.GetSummary | 200 |
GET | /portfolios/{id}/valuation | core.Handler.GetValuation | 200 |
GET | /portfolios/{id}/broker-analysis | core.Handler.GetBrokerAnalysis | 200 |
PATCH | /portfolios/{id} | core.Handler.Update | 200 |
DELETE | /portfolios/{id} | core.Handler.Delete | 204 |
Portfolio Companies (tag: Portfolio Companies — 3 endpoints)
| Method | Path | Handler | Status |
|---|---|---|---|
GET | /portfolios/{id}/holdings | companies.Handler.ListHoldings | 200 |
GET | /portfolios/{id}/distribution | companies.Handler.GetDistribution | 200 |
GET | /portfolios/{id}/companies/{symbol} | companies.Handler.GetCompanyDetail | 200 |
Portfolio Transactions (tag: Portfolio Transactions — 4 endpoints)
| Method | Path | Handler | Status |
|---|---|---|---|
POST | /portfolios/{id}/transactions | transactions.Handler.Add | 201 |
GET | /portfolios/{id}/transactions | transactions.Handler.List | 200 |
PATCH | /portfolios/{id}/transactions/{txId} | transactions.Handler.Update | 200 |
DELETE | /portfolios/{id}/transactions/{txId} | transactions.Handler.Delete | 204 |
Schema
Three tables back the entire module.
nepse_portfolios
| Column | Type | Notes |
|---|---|---|
| id | uuid PK | UUIDv7 |
| customer_id | uuid | FK |
| name | text | max 100 |
| description | text? | max 500 |
| is_default | bool | exactly one per customer (soft invariant) |
| last_fetched_at | timestamptz? | bumped on every detail read |
| deleted_at | timestamptz? | soft-delete |
| created_at, updated_at | timestamptz |
nepse_portfolio_transactions
| Column | Type | Notes |
|---|---|---|
| id | uuid PK | UUIDv7 |
| portfolio_id | uuid FK | ON DELETE CASCADE |
| company_id | uuid FK | ON DELETE RESTRICT |
| type | text | BUY | SELL | IPO | FPO | RIGHTS | AUCTION | DIVIDEND | BONUS |
| transacted_at | timestamptz | |
| quantity | int? | required for BUY/SELL/IPO/FPO/RIGHTS/AUCTION/BONUS |
| rate | numeric(14,4)? | required for BUY/SELL/IPO/FPO/RIGHTS/AUCTION |
| amount | numeric(16,2)? | required for BUY/SELL/IPO/FPO/RIGHTS/AUCTION/DIVIDEND |
| sebon_commission, broker_commission, dp_fee, net_amount | numeric? | required for BUY and SELL |
| base_price, term, cgt_amount | numeric/text/numeric? | required for SELL |
| notes | text? | max 500 |
| deleted_at | timestamptz? | soft-delete |
| created_at, updated_at | timestamptz |
Per-type required-field shape enforced by CHECK constraints on the table. Indexes:
(portfolio_id, company_id, transacted_at)WHERE deleted_at IS NULL(portfolio_id, transacted_at)WHERE deleted_at IS NULL
nepse_companies
Referenced via FK; not owned by this module.
Cache keys
| Key | TTL | Used by |
|---|---|---|
portfolio:detail:{id} | 30s | GET /portfolios/{id}, GET /portfolios/{id}/summary |
portfolio:valuation:{id}:1D | 60s | GET /portfolios/{id}/valuation?range=1D |
portfolio:valuation:{id}:{1W,1M,3M,6M,1Y,YTD,ALL} | 5min | GET /portfolios/{id}/valuation?range=R |
Defined in internal/platform/cache/keys.go.
sqlc queries
Located in internal/platform/database/queries/:
| Query | File | Used by |
|---|---|---|
CreatePortfolio | portfolios.sql | core |
FindPortfoliosByCustomer | portfolios.sql | core |
FindPortfolioByID | portfolios.sql | shared.FindPortfolio |
UpdatePortfolio | portfolios.sql | core (name/description/is_default) |
UnsetDefaultForOtherPortfolios | portfolios.sql | core (single-default invariant) |
SoftDeletePortfolio | portfolios.sql | core |
SoftDeleteTransactionsByPortfolio | portfolios.sql | core |
CountPortfoliosByCustomer | portfolios.sql | core |
TouchPortfolioFetchedAt | customers.sql | core (every detail read) |
FindActivePortfolioSymbols | portfolios.sql | core (broker-analysis) |
FindLivePricesForCompanies | portfolios.sql | views |
FindBulkPriceHistoryForPortfolio | portfolios.sql | views (daily ranges) |
FindBulkIntradayPricesForPortfolio | portfolios.sql | views (1D range) |
FindBrokerActivityForSymbols | portfolios.sql | core (broker-analysis) |
CreateTransaction | portfolio_transactions.sql | transactions.Add |
FindTransactionsByPortfolio | portfolio_transactions.sql | transactions.Find |
CountTransactionsByPortfolio | portfolio_transactions.sql | transactions.Find |
FindTransactionForUpdate | portfolio_transactions.sql | transactions.Update |
UpdateTransaction | portfolio_transactions.sql | transactions.Update |
SoftDeleteTransaction | portfolio_transactions.sql | transactions.Delete |
FindAllPortfolioTransactionsForViews | portfolio_transactions.sql | views.BuildState |
FindTransactionsForCompanyTimeline | portfolio_transactions.sql | views.BuildCompanyDetail |
GetCurrentUnitsByCompany | portfolio_transactions.sql | transactions (oversell guard) |
Notable file paths
| File | Purpose |
|---|---|
internal/modules/portfolio/portfolio.go | Module aggregator + chi route table |
internal/modules/portfolio/core/service.go | Portfolio CRUD + FindDetail/FindSummary/FindValuation/FindBrokerAnalysis |
internal/modules/portfolio/transactions/service.go | Add/Find/Update/Delete + shape & cross-field validation |
internal/modules/portfolio/views/state.go | BuildState — chronological replay |
internal/modules/portfolio/views/daypnl.go | DayPnL — overnight + today decomposition |
internal/modules/portfolio/views/valuation.go | BuildValuation + BuildIntradayValuation |
internal/modules/portfolio/views/distribution.go | byCompany + bySector pies |
internal/modules/portfolio/views/holdings.go | Per-company holding rows for the table |
internal/modules/portfolio/views/company_detail.go | Single-symbol drilldown + timeline |
internal/modules/portfolio/companies/service.go | Read-side handler logic, sort/paginate |
internal/modules/portfolio/companies/sort.go | Allowed sort columns + comparator |
internal/modules/portfolio/shared/cache.go | TxRunner, Invalidator, RunReadSnapshot |
internal/modules/portfolio/shared/conversions.go | ParseTradeTime (date or RFC3339), date helpers |
internal/modules/portfolio/holdings/view.go | HoldingResponse DTO (vestigial) |
migrations/000023_portfolio_transactions.up.sql | Schema for unified transactions table |
Module size
~3,000 LOC across 7 packages (core, companies, transactions, views, shared, holdings, root)