Skip to main content

Bond Portfolio Service API

Handles bond registrations, transactions, pricing, and accrued interest calculations. The service still throws standard Error objects; it is scheduled to migrate to Result signatures in Phase 2 of the neverthrow rollout.

Location: worker/services/portfolio/bond/service.ts

Dependencies

  • BondPortfolioRepository – persistence for bonds, holdings, transactions, and stored prices
  • FundService – verifies fund ownership and organization context
  • dseApi – shared client for Dhaka Stock Exchange bond endpoints
  • Shared financial constants – coupon rate scaling, time conversions, and day-count helpers

Core Operations

registerBond(input: BondRegistration): Promise<{ id: string; dayCountConvention: string }> : Normalizes identifiers, enforces uniqueness (scrip code + ISIN), and persists the bond. Throws descriptive errors when validation fails.

recordTransaction(input: BondTransaction): Promise<void> : Validates fund ownership via FundService.getFundById, ensures bonds exist, verifies saleable quantity for sells, and records the transaction. Throws on violations.

Business Rule: All bond 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 throws a BusinessRuleViolation error if the transaction date is not allowed.

generateDailySnapshot(input: BondSnapshot): Promise<void> : Aggregates transactions, calculates accrued interest, fetches prices from DSE (falling back to stored prices), and persists holdings with accrued interest/market value. Throws when upstream calls fail.

getHoldingsForDate(input: BondSnapshot): Promise<BondHolding[]> : Returns holdings for a specific fund/date pair.

upsertHoldingsForMigration(organizationId: string, snapshots: MigrationBondHoldingSnapshot[]): Promise<void> : Validates organization/fund ownership, numeric invariants, and persists migration holdings. Throws descriptive errors on violations.

getBondsByIds(bondIds: string[]): Promise<BondEntity[]> getTickersForBonds(bondIds: string[]): Promise<Array<{ id: string; ticker: string }>> : Repository helpers used by EOD workflows.

fetchBondPrices(request: FetchBondPricesRequest): Promise<FetchBondPricesResult> (internal) : Called by generateDailySnapshot to retrieve daily prices. Errors bubble up as thrown exceptions today.

Day Count & Accrued Interest Helpers

  • calculateDayCountFactor(convention, valuationDate, settlementDate): number
  • calculateAccruedInterest(bondId, valuationDate): Promise<number>
  • calculateCleanAndDirtyPrice(...) (see source for full signature)

These helpers support multiple conventions (ACT_365, ACT_360, THIRTY_360), convert coupon rates from basis points, and are used by EOD orchestration.

Migration Notes

  • All public methods still throw exceptions. Wrap calls with try/catch at the tRPC layer and convert errors to DomainError until the migration replaces these with Result signatures.
  • The migration plan tracks this service under Portfolio Services (Phase 2). Once migrated, expect parity with the Equity Portfolio service’s error handling.

Last Validated: 2025-11-10 against commit 0342a62e