Equity Portfolio Service API
Manages equity registrations, transactions, and daily snapshots. The service fully embraces neverthrow to return Result values with structured DomainErrors.
Location: worker/services/portfolio/equity/service.ts
Dependencies
EquityPortfolioRepository– persistence for equities, holdings, transactions, and pricing snapshotsFundService– ensures funds exist and belong to the caller's organizationdseApi– shared HTTP client for the Dhaka Stock Exchange API- Shared domain error helpers – validation, conflict, forbidden, business logic errors
Public Methods
registerEquity(input: EquityRegistration): Promise<Result<{ id: string }, DomainError>>
: Validates ticker/scrip code, enforces uniqueness, creates the equity, and returns the new ID. Conflicts surface as BusinessLogicError with a dedicated code.
recordTransaction(input: EquityTransaction): Promise<Result<void, DomainError>>
: Verifies fund and equity existence, checks saleable quantity for sells, and records transactions in the repository.
Business Rule: All equity transactions can only be recorded on the calendar day after the last EOD date. If EOD was done for 2025-07-01, transactions can only be recorded for 2025-07-02. This validation is enforced automatically and returns a BusinessRuleViolation error with code TRANSACTION_DATE_NOT_ALLOWED if the transaction date is not allowed.
generateDailySnapshot(input: EquitySnapshot): Promise<Result<void, DomainError>>
: Aggregates transactions, fetches closing prices, persists holdings, and logs when fallback pricing is used.
getHoldingsForDate(input: EquitySnapshot): Promise<Result<EquityHolding[], DomainError>>
: Returns holdings for a fund on a given date.
upsertHoldingsForMigration(organizationId: string, snapshots: MigrationEquityHoldingSnapshot[]): Promise<Result<void, DomainError>>
: Validates organization/fund ownership, numeric invariants, and persists migration holdings.
ensureEquities(options?: EnsureEquitiesOptions): Promise<Result<EnsureEquitiesResult, DomainError>>
: Synchronizes securities from DSE, supports dry-run previews, and records per-ticker sync errors without failing the entire operation.
Error Semantics
createValidationError– invalid ticker/scrip code, negative quantities, improper numeric inputscreateNotFoundError– missing equities or fundscreateForbiddenError– organization mismatch during migration upsertscreateBusinessLogicError– DSE API failures, saleable quantity issues, unexpected repository errors
Usage Example
const snapshotResult = await services.portfolio.equity.generateDailySnapshot({
fundId: fund.id,
asOf: "2025-11-08",
});
await snapshotResult.match(
async () => undefined,
async (error) => {
throw mapDomainErrorToTRPC(error);
},
);
Migration Notes
- All public methods return
Resultobjects. Avoid_unsafeUnwrap()in callers; prefer.match()orexecuteResult()inside tRPC routers. fetchClosingPricesremains a private helper; consumers should rely onensureEquitiesandgenerateDailySnapshotfor pricing synchronization.
Last Validated: 2025-11-10 against commit
0342a62e