npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@protocolwealthos/holdings

v0.2.0

Published

Account / security / event-stream / daily-snapshot primitives for advisor portfolio data — immutable HoldingEvent log + materialized HoldingSnapshot, AccountBalance with inflow/outflow decomposition, custodian connection mirrors, scoped advisor access

Readme

@protocolwealthos/holdings

Account / security / event-stream / daily-snapshot primitives for advisor portfolio data. Immutable HoldingEvent log, materialized HoldingSnapshot, AccountBalance with inflow/outflow decomposition, custodian connection mirrors, scoped advisor access.

Apache 2.0 · Patent Pending: USPTO #64/034,215 · part of pwos-core.

Why this exists

Tracking client portfolio state is most often hacked together: current_qty columns that drift from reality, cost-basis fields stomped by every Plaid sync, no clean way to ask "what did this position look like on 2025-12-31?". This package ships the right shape:

  • Immutable event streamHoldingEvent records buy/sell/dividend/split/transfer_in/transfer_out/mark. Append-only; corrections are new reversing events.
  • Daily materialized snapshotsmaterializeSnapshots(events, prices, { asOfDate }) replays the stream to produce one HoldingSnapshot per (account, security, currency). Deterministic; same inputs always produce byte-identical outputs (which makes audit-log hash chaining meaningful).
  • Cost-basis provenance — every snapshot carries costBasisSource: 'manual' | 'computed' | 'custodian' and a costBasisLocked flag, so user-edited basis isn't stomped by the next provider sync.
  • Inflow/outflow decompositionAccountBalance stores cashInflows, cashOutflows, nonCashInflows, nonCashOutflows, marketDrift, adjustments separately, feeding TWR / MWR / IRR calculations directly.

Architectural lineage: clean-room re-derivation inspired by Maybe Finance / its AGPL fork (Sure). No GPL/AGPL code copied. Improvements over prior art:

  • Explicit firmId for RIA tenancy above household
  • ISIN / CUSIP / SEDOL first-class on Security (not ticker overload)
  • Splits / corporate actions as first-class event kinds (not smuggled into Trade rows)
  • Audit-log integration on every mutation (SEC 204-2 retention)

Install

pnpm add @protocolwealthos/holdings

Quick start

import {
  buyEvent, dividendEvent, splitEvent,
  materializeSnapshots,
  parseAmount,
  InMemoryHoldingsStore,
} from "@protocolwealthos/holdings";

const USD = "USD" as Currency;
const store = new InMemoryHoldingsStore();

// Open a brokerage account
await store.upsertAccount({
  id: "acc_1" as AccountId,
  firmId: "firm_1" as FirmId,
  householdId: "hh_1" as HouseholdId,
  kind: "brokerage",
  displayName: "Schwab Brokerage",
  currency: USD,
  status: "active",
  openedOn: "2026-01-01",
  ownerPrincipalId: "p_advisor" as PrincipalId,
  meta: {},
});

// Append events
await store.appendHoldingEvent(buyEvent({
  id: "ev_1" as EventId,
  accountId: "acc_1" as AccountId,
  occurredOn: "2026-05-01",
  securityId: "sec_aapl" as SecurityId,
  qtyDelta: 100n, qtyScale: 0,
  cashDelta:       parseAmount("-15000.00", USD, 2),
  costBasisDelta:  parseAmount("15000.00",  USD, 2),
  sourceId: "plaid",
  externalId: "plaid_tx_999",
}));

await store.appendHoldingEvent(dividendEvent({
  id: "ev_2" as EventId,
  accountId: "acc_1" as AccountId,
  occurredOn: "2026-06-15",
  securityId: "sec_aapl" as SecurityId,
  qtyScale: 0,
  cashDelta: parseAmount("23.50", USD, 2),
}));

// Materialize a snapshot for any date
const events = await store.listHoldingEvents({ accountId: "acc_1" as AccountId });
const prices = await store.listSecurityPrices("sec_aapl" as SecurityId);
const snapshots = materializeSnapshots(events, prices, { asOfDate: "2026-06-30" });

Event shape conventions

| Kind | qtyDelta | cashDelta | costBasisDelta | |---|---|---|---| | buy | + | − | + | | sell | − | + | − | | dividend (cash) | 0 | + | — | | dividend (reinvested) | — | — | — (use cash dividend + buy) | | interest | 0 | + | — | | fee | 0 | − | — | | split | + or − | 0 | 0 (basis preserved) | | transfer_in | + | 0 | + (basis from origin) | | transfer_out | − | 0 | − (basis to destination) | | mark | 0 | 0 | — (declares an asOf price) | | reorg | application-defined |

Idempotency

When a provider pushes events, set sourceId + externalId:

buyEvent({ ..., sourceId: "plaid", externalId: "plaid_tx_999" });

(accountId, sourceId, externalId) is the dedup contract. Re-appending the same external event is a no-op (returns idempotency_conflict with the existing event id).

Audit-log integration

Pair with @protocolwealthos/audit-log — every appendHoldingEvent, upsertAccount, grantAccess call should produce one audit row with the canonical JSON + content hash. The materializeSnapshots() function is deterministic, so a replay from the audit log alone reproduces snapshot state byte-for-byte.

What's NOT in v0.5.0

Deferred to v0.5.1+:

  • AccountBalance materialization — the type ships; the materializer that produces it from events lands next.
  • TransferLink auto-matching — the type ships; the heuristic that pairs debits and credits across accounts comes later.
  • Lot-level basis tracking — current snapshots roll up basis per security; per-lot tracking (FIFO/LIFO/HIFO/AVERAGE selection) lands when the ledger package gains lots.

License

Apache 2.0 with USPTO Application #64/034,215 defensive patent grant.