Status: DRAFT. The data model is exercised against real data before any v1.0. Nothing here is frozen until v1.0.
Asset Notation is an open, portable data format for a person’s or household’s assets and liabilities over time: the accounts, positions, properties, debts and their values, across institutions and asset classes, in a single self-describing document that any application can read and write. The consolidated net worth is one thing it makes derivable, but the format itself is the broader notation: it records what you own and owe, not a dashboard.
It exists to fill a concrete gap: today wealth data is locked inside each aggregator and institution, with no neutral interchange format. Asset Notation is the neutral payload. It does not replace bank-messaging standards (ISO 20022) or brokerage feeds; it consolidates their outputs into one portable picture the owner controls.
In scope (v0.1): subjects/owners, institutions, holdings (the single generic asset-or-liability entity), optional instruments for market-priced positions, valuations over time, optional transactions, ownership shares.
Out of scope (v0.1): market-data/FX rate distribution, tax computation, budgeting categories, performance analytics, real-time messaging. These belong in tools or in namespaced extensions, not in the core.
kind
says what flavour it is; a nature (asset or liability) says which side of the
balance sheet it sits on. There is no separate account / holding / liability
hierarchy to learn or to migrate.kind / source
values are preserved, not rejected. App-specific data lives in namespaced
extensions, keeping the core small. New kinds and new fields never force a migration.YYYY-MM-DD). Timestamps are RFC 3339
(2026-06-24T20:00:00Z), UTC unless an offset is given. A valuation’s asOf MAY be
either; consumers SHOULD compare them as instants.EUR, USD).FR, US).{ "amount": string, "currency": string }, where amount is
a base-10 decimal string (e.g. "1234.56", "-50.00"). Producers MUST NOT emit
floating-point numbers for monetary values. Quantities (share counts, units) are
also decimal strings to avoid precision loss.id, a string unique within its collection. An
id is opaque and stable across exports of the same logical thing (a UUID or a
stable slug is RECOMMENDED). External identifiers (ISIN, IBAN, BIC, LEI, …) live in
an ids object and are never used as the id.A document is a single JSON object:
{
"assetnotation": "0.1.0",
"generatedAt": "2026-06-24T20:00:00Z",
"generator": { "name": "example", "version": "1.0.0" },
"baseCurrency": "EUR",
"subjects": [],
"institutions": [],
"holdings": [],
"instruments": [],
"valuations": [],
"transactions": [],
"ownership": [],
"extensions": {}
}
| Field | Req | Type | Notes |
|---|---|---|---|
assetnotation |
MUST | string | Spec version major.minor.patch of this document. |
generatedAt |
MUST | timestamp | When the document was produced. |
generator |
MAY | object | { name, version } of the producing app. |
baseCurrency |
SHOULD | ISO 4217 | Reporting currency for derived totals. |
subjects |
SHOULD | Subject[] | Owners. If omitted, a single implicit owner is assumed. |
institutions |
MAY | Institution[] | Banks, brokers, lenders. |
holdings |
MUST | Holding[] | The assets and liabilities. The heart of the document. |
instruments |
MAY | Instrument[] | Market-priced securities referenced by holdings. |
valuations |
SHOULD | Valuation[] | Time-stamped values of holdings. |
transactions |
MAY | Transaction[] | Optional movement history. |
ownership |
MAY | Ownership[] | Shares. Defaults to 100% to the primary subject. |
extensions |
MAY | object | Namespaced app-specific data (section 10). |
Collections are unordered sets keyed by id. This maps directly onto a
schema-agnostic store of named collections (section 12).
A person or legal entity that can own assets and owe liabilities.
| Field | Req | Type | Notes |
|---|---|---|---|
id |
MUST | string | |
type |
MUST | enum | person, entity. |
name |
SHOULD | string | Display name. |
kind |
MAY | enum | e.g. self, spouse, child, company, trust, sci, joint. Open. |
country |
MAY | ISO 3166 | Tax residence or registration. |
ids |
MAY | object | e.g. { "lei": "..." } for legal entities. |
A bank, broker, insurer, exchange, custodian or lender.
| Field | Req | Type | Notes |
|---|---|---|---|
id |
MUST | string | |
name |
SHOULD | string | |
kind |
MAY | enum | bank, broker, insurer, exchange, custodian, other. |
country |
MAY | ISO 3166 | |
ids |
MAY | object | { "bic"?: ISO 9362, "lei"?: ISO 17442 }. |
Anything of value, owned or owed. A bank account, a savings book, a brokerage position, a directly-held house or car, a mortgage or a consumer loan are all Holdings. Its values over time are Valuations (5.5); its movements, if any, are Transactions (5.6).
| Field | Req | Type | Notes |
|---|---|---|---|
id |
MUST | string | |
kind |
MUST | string | Open: checking, savings, brokerage, retirement, life_insurance, crypto_wallet, cash, real_estate, vehicle, valuable, mortgage, loan, credit_line… A consumer MUST accept an unknown kind and render it generically. |
nature |
MUST | enum | asset or liability. Drives the sign in net worth. |
label |
SHOULD | string | Display name. |
currency |
SHOULD | ISO 4217 | The holding’s native currency. |
parentId |
MAY | ref | The containing holding (a stock in a fund, a fund in a securities account). A holding with no parent is a root. |
institutionId |
MAY | ref | -> Institution. |
ids |
MAY | object | { "iban"?: ISO 13616, "bic"?, "number"? }. number SHOULD be masked. |
instrumentId |
MAY | ref | -> Instrument, for a market-priced position. |
quantity |
MAY | decimal string | Units held, when instrumentId is set. |
links |
MAY | Link[] | Relations to other holdings (below). |
attributes |
MAY | object | Type-specific scalars the core does not model (a loan’s rate, a property’s address…). Forward-compatible: producers put extra kind-specific data here, not in core fields. |
openedAt / closedAt |
MAY | date |
A Link is { "rel": string, "holdingId": ref }. rel is an open vocabulary:
funded_by (a card -> its account), financed_by (a property -> its mortgage),
held_in (a position -> its account). Links express the relationships a 360 view
needs without nesting.
Sign convention: a holding’s values are its native signed amounts. An
account-style holding carries a signed balance (a negative balance is an overdraft or
a card debt, and lowers net worth on its own). A loan-style holding carries a
positive outstanding and is marked nature: "liability", so net worth subtracts
it. This keeps both representations consistent (section 6).
A market-priced thing referenced by a holding, so the shared price lives once. Only needed for securities and similar; a house is just a holding with a value.
| Field | Req | Type | Notes |
|---|---|---|---|
id |
MUST | string | |
name |
SHOULD | string | |
kind |
MUST | enum | security, cash, crypto, commodity, other. |
ids |
MAY | object | { "isin"?: ISO 6166, "figi"?, "ticker"?, "mic"?: ISO 10383 }. |
currency |
MAY | ISO 4217 | Pricing currency. |
The value of a holding, as of a date, from a stated source. Valuations form a time series; multiple per holding are expected (a statement each month, an appraisal each year, the amortized balance of a loan over its life).
| Field | Req | Type | Notes |
|---|---|---|---|
id |
MUST | string | |
holdingId |
MUST | ref | -> Holding. |
asOf |
MUST | date/timestamp | When the value held true. |
value |
MUST | Money | The total value of the holding (signed per 5.3). |
unitPrice |
MAY | Money | For an instrument-priced holding: price per unit. |
source |
SHOULD | string | Open: purchase, statement, market, estimate, appraisal, repayment, manual, derived… |
confidence |
MAY | number | 0..1. An estimate is softer than a statement. |
A movement on a holding. Optional: a document MAY carry valuations only. Semantics align loosely with ISO 20022 / OFX.
| Field | Req | Type | Notes |
|---|---|---|---|
id |
MUST | string | |
holdingId |
MUST | ref | -> Holding. |
date |
MUST | date | |
amount |
MUST | Money | Signed: negative = outflow, positive = inflow. |
type |
MAY | enum | transfer, trade, fee, dividend, interest, income, expense, other. |
description |
MAY | string | |
category |
MAY | string | Open budgeting category. |
counterpartHoldingId |
MAY | ref | The other side of the flow: a transfer target, the loan repaid, the asset funded. |
instrumentId / quantity |
MAY | ref / decimal string | For trades. |
Who owns how much of what. Enables joint accounts, family consolidation and holding
structures. If ownership is omitted, every holding is owned 100% by the primary
subject.
| Field | Req | Type | Notes |
|---|---|---|---|
subjectId |
MUST | ref | -> Subject. |
holdingId |
MUST | ref | -> Holding. |
share |
MUST | number | 0..1. Shares of one holding SHOULD sum to <= 1. |
A standard models facts; tools compute. Net worth for a set of subjects S as of date T:
netWorth(S, T) =
sum over ROOT holdings h (no parentId) of
sign(h.nature) * treeValue(h, T) * ownedShare(h, S)
where treeValue(h, T) = the sum of treeValue over h's children, or h's own
latestValue(h, T) when it has none; sign(asset) = +1, sign(liability) = -1; all
converted to baseCurrency using an FX rate as of T.
A container (a fund, a securities account) is therefore counted through its children, never twice.
latestValue(h, T) is the value of the valuation of h with the greatest asOf <=
T. A richer tool MAY refine an account’s value between statements using its
transactions; this reference derivation uses valuations only. FX rates are not
distributed by Asset Notation (out of scope); a tool supplies them. This is
non-normative: implementations MAY derive differently, but SHOULD document how.
| Concept | Standard |
|---|---|
| Currency | ISO 4217 |
| Date / time | ISO 8601 / RFC 3339 |
| Country | ISO 3166-1 alpha-2 |
| Language (in extensions) | BCP 47 |
| Security | ISIN (ISO 6166); FIGI, ticker as alternates |
| Exchange | MIC (ISO 10383) |
| Institution | BIC (ISO 9362), LEI (ISO 17442) |
| Bank account | IBAN (ISO 13616) |
Producers SHOULD populate ids with whichever external identifiers they have.
Consumers MUST NOT assume any ids entry is present.
Money is { "amount": "<decimal string>", "currency": "<ISO 4217>" }. The decimal
string is base-10, optional leading -, . as the decimal separator, no thousands
separators, no currency symbol. Rationale: binary floats cannot represent most decimal
cash values exactly; ISO 20022 likewise uses decimal.
assetnotation field carries the spec version as major.minor.patch.
Compatibility is determined by major.minor; the patch is editorial (clarifications
that do not change the data model).major it does not support. A consumer
SHOULD accept a higher minor and MUST ignore fields it does not recognize.extensions namespaces.kind and source values, and new enum values, MAY appear in a minor version; a
consumer MUST treat an unknown one generically rather than failing.App-specific or domain-specific data that is not part of the core MUST NOT be placed in core fields. Three mechanisms, from most to least scoped:
attributes object for kind-specific scalars (a loan’s rate, a
property’s surface). Already part of the core, so no namespace needed.extensions object, keyed by a namespace (reverse-DNS or app id):
"extensions": { "com.example": { ... } }.x- prefixed field on any entity for small additions: "x-color": "...".This keeps the core small and stable while letting tools carry their own state (for example an app’s own categorization rules) without polluting the standard.
Asset Notation documents are plaintext and inherently sensitive. Confidentiality, integrity and tamper-evidence are the responsibility of the container that carries the document (for example an encrypted, authenticated container), not of this schema.
Asset Notation is a payload schema, independent of how a document is stored or moved. A document MAY live in a plain file, inside a versioned and optionally encrypted container, in a ZIP, or be served over an API; none of that is the schema’s concern.
Because a document is a set of collections keyed by id (subjects, institutions,
holdings, valuations, transactions, ownership), it maps one-to-one onto any
document or key-value store. A producer whose internal model is Asset-Notation-shaped
can therefore emit a conformant document directly from its own state, reconciling
entities by id.
assetnotation and generatedAt; MUST give every
entity a unique id; MUST give every holding a kind and a nature; MUST represent
money as decimal strings with ISO 4217 currencies and dates as ISO 8601; SHOULD
populate valuations so totals can be derived.kind/source/enum value generically, and MUST
reject an unsupported major version.{ "assetnotation": "0.1.0", "generatedAt": "<ts>", "holdings": [] }.instruments stay optional, or is the position model (instrument + quantity)
worth promoting once securities are first-class in implementations?A household: one person, a checking account with a cash balance, a brokerage position in an ETF, a directly-owned apartment financed by a mortgage.
{
"assetnotation": "0.1.0",
"generatedAt": "2026-06-24T20:00:00Z",
"generator": { "name": "example", "version": "1.0.0" },
"baseCurrency": "EUR",
"subjects": [
{ "id": "s1", "type": "person", "name": "A. Doe", "country": "FR" }
],
"institutions": [
{ "id": "i1", "name": "Example Bank", "kind": "bank", "ids": { "bic": "EXAMPLEXXX" } },
{ "id": "i2", "name": "Example Broker", "kind": "broker" }
],
"holdings": [
{ "id": "h1", "label": "Current account", "kind": "checking", "nature": "asset", "currency": "EUR", "institutionId": "i1", "ids": { "iban": "FR76 XXXX XXXX XX" } },
{ "id": "h2", "label": "World ETF", "kind": "brokerage", "nature": "asset", "currency": "EUR", "institutionId": "i2", "instrumentId": "n1", "quantity": "40" },
{ "id": "h3", "label": "12 Main St", "kind": "real_estate", "nature": "asset", "currency": "EUR", "links": [{ "rel": "financed_by", "holdingId": "h4" }] },
{ "id": "h4", "label": "Mortgage", "kind": "mortgage", "nature": "liability", "currency": "EUR", "institutionId": "i1", "attributes": { "rate": 1.8, "startDate": "2022-01-01", "endDate": "2042-01-01", "payment": "990.00" } }
],
"instruments": [
{ "id": "n1", "name": "World ETF", "kind": "security", "ids": { "isin": "IE00B4L5Y983" }, "currency": "EUR" }
],
"valuations": [
{ "id": "v1", "holdingId": "h1", "asOf": "2026-06-24", "value": { "amount": "4210.55", "currency": "EUR" }, "source": "statement" },
{ "id": "v2", "holdingId": "h2", "asOf": "2026-06-24", "value": { "amount": "4920.00", "currency": "EUR" }, "unitPrice": { "amount": "123.00", "currency": "EUR" }, "source": "market" },
{ "id": "v3", "holdingId": "h3", "asOf": "2026-06-01", "value": { "amount": "300000.00", "currency": "EUR" }, "source": "estimate", "confidence": 0.5 },
{ "id": "v4", "holdingId": "h4", "asOf": "2026-06-24", "value": { "amount": "176400.00", "currency": "EUR" }, "source": "statement" }
],
"ownership": [
{ "subjectId": "s1", "holdingId": "h1", "share": 1 },
{ "subjectId": "s1", "holdingId": "h3", "share": 1 },
{ "subjectId": "s1", "holdingId": "h4", "share": 1 }
]
}
Net worth as of 2026-06-24: 4210.55 + 4920.00 + 300000.00 - 176400.00 = 132730.55 EUR.