View as Markdown

Holdings

Holdings are daily positions — what a lens held, per date, valued in one reporting currency. By default the endpoint returns one fully aggregated row per date in the lens client's primary currency; currency reads another of the client's currencies, and the expand parameter breaks the aggregation down by asset, custody account, cash account or tag. This is the heaviest read endpoint on the platform, so scope your date ranges deliberately.

Data model

Each row is one cell of the (date × expansion) grid.

Every holding is kept once per reporting currency of its client: the same position valued in DKK, in EUR and in USD is three legs, as in the legacy platform. A read returns one leg, the currency you ask for. The client's currencies are its primary currency, its entities' primary currencies and their alternative currencies; any other currency is a 400 that lists them.

A holding sits on one custody account or one cash account, or on neither (a private-market or loan position). A portfolio that sits in more than one of the lens's portfolio groups counts once per group, as in the legacy API: a row across accounts includes it once for each group. Split by both accounts (expand=custody_accounts,cash_accounts), each account is one row with its own value.

A lens built in v1 is read through its rules. Each row counts with the lens's weight of it: in full when the lens takes its portfolio, custody or cash account, custodian, asset or tag whole, and with the share when it takes a party, an entity or an ownership path that owns part of the portfolio on the row's date. A lens rule never adds to what you can see: a row counts with the smaller of the lens's weight and your own share. Such a lens counts each row once, also when it sits under several sub-lenses. Before the lens's start_date it shows nothing: a read that ends before it is an empty list, and a window that starts before it starts on it. A lens synced from the legacy platform keeps its legacy portfolio groups.

Numbers follow your ownership. If you see a portfolio only as its owner (an access grant on a party), units and market_value are your share of it on each date: an owner of 40 % sees 40 %. The share changes on the dates the ownership changes. A portfolio you also see through any other access point (the tenant, a client, an entity, a cash or custody account) counts in full, and so does everything an admin reads. Which portfolios you see follows your access today, and an owner grant reaches the entities the party owns directly; the share is the one on each row's date, so a date before the ownership began has no row. The v2 and v3 holdings routes are not weighted.

idstring or null

{account_id}:{asset_id}:{date}:{currency_code}, the id GET /v1/holdings/{id} takes, where account_id is the custody or cash account the holding sits on. Set only when the row is exactly one holding: expand includes assets, custody_accounts and cash_accounts, and the holding sits on an account. null otherwise.

dateISO 8601 date

The day of the position. The envelope's as_of is the last day the read covers, so on a window it is the date of the last rows.

asset_idstring or null

Set when expand includes assets; null when the row aggregates across assets.

custody_account_idstring or null

The custody account id, set when expand includes custody_accounts; null when the row aggregates across custody accounts, and on positions held outside a custody account.

cash_account_idstring or null

The cash account id, set when expand includes cash_accounts; null when the row aggregates across cash accounts, and on positions held outside a cash account. A cash-account position sits on its cash account; every other position on its custody account.

tag_idstring or null

The asset's classification as a tag, set when expand includes tags; null when the row aggregates across tags, and on a classification without a tag.

currency_codestring

ISO 4217 currency the row is valued in: the currency of the read.

unitsdecimal string or null

Quantity held. Set only when expand includes assets; null on rows across assets, where a sum of units means nothing.

market_valuedecimal string

Market value of the position. Serialized as a string — parse with a decimal type, never a float.


GET/v1/holdings

List holdings

Returns positions for a lens on one day or over a date window, in the standard v1 list envelope. An unknown query parameter is a 400. as_of reads one day and from_date with to_date reads a window; with no date the read is today. as_of together with a window, only one of from_date and to_date, or a to_date before from_date, is a 400. The envelope's as_of is the day the read ends on. Positions on a deleted cash or custody account are left out, as those accounts are, and so are the portfolios the legacy platform deleted or that moved away, as the legacy platform hides them. Scope positions:read.

Query parameters

lens_idstring

The lens to read positions for. Required.

as_ofISO 8601 date

The day to read. Today when left out; not together with from_date and to_date.

from_dateISO 8601 date

Inclusive start of the window. Only together with to_date.

to_dateISO 8601 date

Inclusive end of the window. Only together with from_date.

currencystring

The reporting currency to read, one of the lens client's currencies. The client's primary currency when left out; any other is a 400 listing the valid ones. A read that is empty before the check, because the window ends before the lens's start_date or the lens has no portfolio you can see, answers the empty list without checking currency.

expandstring

Comma-separated dimensions to break the aggregation down by: assets, custody_accounts, cash_accounts, tags. Any other token is a 400. Omit for one aggregated row per date.

limitinteger

Page size. Defaults to 1 000, capped at 10 000.

cursorstring

Opaque cursor from the previous page's next_cursor. Omit for the first page.

Request
GET/v1/holdings
curl -G https://api.aleta.ai/v1/holdings \
  -H "Authorization: Bearer {access_token}" \
  -d lens_id=019f91be-1d83-7f0a-bbfc-e64861142e87 \
  -d from_date=2025-01-30 \
  -d to_date=2025-02-28
Response
{
  "data": [
    {
      "id": null,
      "date": "2025-01-30",
      "asset_id": null,
      "custody_account_id": null,
      "cash_account_id": null,
      "tag_id": null,
      "currency_code": "DKK",
      "units": null,
      "market_value": "41.67"
    },
    {
      "id": null,
      "date": "2025-01-31",
      "asset_id": null,
      "custody_account_id": null,
      "cash_account_id": null,
      "tag_id": null,
      "currency_code": "DKK",
      "units": null,
      "market_value": "83.33"
    }
  ],
  "next_cursor": null,
  "limit": 1000,
  "as_of": "2025-02-28"
}
GET/v1/holdings/{id}

Retrieve a holding

Returns one holding by the id of a list row (expand=assets,custody_accounts,cash_accounts): id, as_of (the holding's day, from the id), custody_account_id or cash_account_id (the account the holding sits on; the other is null), asset_id, currency_code (the id's currency leg), units, market_value, ownership_share, the share (0 to 1) units and market_value are weighted by, as on the list, reporting_currency and market_value_reporting. reporting_currency is the currency query parameter, the client's primary currency when left out; market_value_reporting is the same holding's leg in that currency, weighted the same way, and null when the holding has no leg in it. The holding is weighted by your ownership only: it reads no lens, so a lens's rules and start_date do not apply, and its numbers can differ from the same row read through a v1 lens. A holding on the legacy "No Depository" custody account sits on its cash account, as the list names it. An id with no holding behind it, on an account you cannot see, or on a day you own none of it, is a 404. An as_of query parameter must be the id's day; it, a currency outside the client's currencies and an unknown query parameter answer 400. Scope positions:read.

GET/v1/holdings/{id}/look-throughs

List a holding's look-throughs

Returns what a fund holding is made of, in the standard v1 list envelope, whose as_of is the holding's day. Client-fund rows come first, then the composition, each in a stable order.

Rows with source: "xray" are the fund's published composition (Morningstar), shared by every tenant: the composition the legacy holding points to, and the latest on or before the holding's day when it points to none (the row's as_of is the composition's date). Each row has isin, name, asset_id (an asset with that ISIN that you can see: the one the portfolio holds that day first, else the lowest id; null when there is none), tag_id (the component's detailed asset class as a tag, null when it has none), net_weight, gross_weight, weight (the net weight), currency_code, country_code, morningstar_security_id and market_value: the holding's market value times net_weight, in the holding's currency leg, as the legacy X-Ray report shows it (the report reads it in the report currency; here it follows the id's leg).

Rows with source: "client_fund" are the holdings a client fund owns on the holding's day, with asset_id, currency_code (the leg the underlying is valued in), units, market_value, weight and holding_kind. Every row carries every field; the ones its source does not fill are null. weight is the underlying's market value over the holding's, and null when the underlying is valued in another currency than the holding's leg. These rows belong to the fund, not to one currency leg: every leg id of the same fund lists them, so do not add them up across legs.

units and market_value are weighted by your ownership share as the holding is; the weights are fractions and stay as they are. A holding that is not a fund has no rows; an id with no holding behind it is a 404, and an as_of other than the id's day or an unknown query parameter a 400. limit (100 by default, 1 000 at most) and cursor page the rows. Scope positions:read.

GET/v1/historical-holdings

List historical holdings

Returns the historical holdings of an account, as the legacy platform keeps them: the history from before Aleta (origin legacy) and the historical holdings written through the API (origin v1). Pass at least one of cash_account_id, custody_account_id and asset_id; from_date and to_date narrow on as_of_date. The standard v1 list envelope, ordered by as_of_date, then origin, then id; limit (100 by default, 1 000 at most) and cursor; an optional as_of (today when left out) names the date the answer is read at, and the history is the same whatever the date. An unknown parameter, an id that is not a UUID, no account or asset, or a to_date before from_date answers 400. Scope positions:read.

idstring

The legacy holding id on a legacy row, the row's UUID on a v1 row.

originstring

legacy or v1.

kindstring

account, asset or fee.

as_of_dateISO 8601 date

The day the holding is for.

holdingsdecimal string or null

Units, or the balance of an account holding.

market_valuedecimal string or null

The market value, in currency_code.

market_pricedecimal string or null

The price the market value is at, and market_price_date, its date.

currency_exchange_ratedecimal string or null

The exchange rate the holding was valued with.

return_amountdecimal string or null

The return on the day.

external_movementdecimal string or null

Money moved in or out on the day.

currency_codestring

The holding's currency.

holding_typestring or null

asset or redemption on an asset holding, null otherwise.

tag_idstring or null

The holding's classification as a tag, null when it has none.

cash_account_idstring or null

The cash account of an account or fee holding.

custody_account_idstring or null

The custody account of an asset holding.

asset_idstring or null

The asset of an asset or fee holding.

Request
GET/v1/historical-holdings
curl -G https://api.aleta.ai/v1/historical-holdings \
  -H "Authorization: Bearer {access_token}" \
  -d cash_account_id=019dff7d-5ab5-7ed4-a78d-93f03070e34b \
  -d from_date=2020-01-01 \
  -d to_date=2020-12-31
Response
{
  "data": [
    {
      "id": "5e8c1f2a9b3d4c0012ab34cd",
      "origin": "legacy",
      "kind": "account",
      "as_of_date": "2020-03-31",
      "holdings": "1000.5",
      "market_value": "1000.5",
      "market_price": null,
      "market_price_date": null,
      "currency_exchange_rate": "1",
      "return_amount": "12.5",
      "external_movement": "-3",
      "currency_code": "DKK",
      "holding_type": null,
      "tag_id": "019dff7d-6a10-7c22-8e3b-1f2a3b4c5d6e",
      "cash_account_id": "019dff7d-5ab5-7ed4-a78d-93f03070e34b",
      "custody_account_id": null,
      "asset_id": null
    }
  ],
  "next_cursor": null,
  "limit": 100,
  "as_of": "2026-10-18"
}

GET/v1/tax-lots

List tax lots

Returns the tax lots you can see, each as its latest snapshot on or before as_of (today when left out), ordered by lot id, in the standard v1 list envelope with as_of. Filters: cash_account_id, asset_id and status (open, partial or closed); limit (100 by default, 1 000 at most) and cursor. A filter id that is not a UUID, another status, an invalid cursor or an unknown query parameter answers 400. Scope positions:read.

A lot has id (the same across snapshots), as_of_date (the snapshot's day), cash_account_id, custody_account_id, asset_id, currency_code (the trade currency), source (custodian, computed, manual or unknown), external_id and external_source (the bank's own lot id and its source, when the bank delivered it), acquisition_date, original_units, remaining_units, cost_basis, basis_source (purchase, transfer_in, corporate_action, opening_balance or computed) and status. Where a custody account's lots come from is set with switch lot source.


GET/v1/tax-lots/{id}

Retrieve a tax lot

Returns one lot as its latest snapshot on or before as_of (today when left out), with the fields of the list and as_of, the date read. 404 when the id is not a UUID, the lot does not exist or you cannot see it, or it has no snapshot by as_of; an unknown query parameter is a 400. Scope positions:read.


GET/v1/dispositions

List dispositions

Returns the dispositions you can see, the closings of units out of a lot, ordered by disposition_date, then id, in the standard v1 list envelope. Filters: cash_account_id, asset_id, lot_id, and from_date and to_date on disposition_date (both days included); limit (100 by default, 1 000 at most) and cursor. A filter id that is not a UUID, a to_date before from_date, an invalid cursor or an unknown query parameter answers 400. Scope positions:read.

A disposition has id, lot_id, transaction_id (the transaction that closed the units), cash_account_id, asset_id, currency_code, disposition_date, settle_date, units_closed, proceeds, cost_basis_used, realized_gain_loss, holding_period_days, external_id and external_source.

  • Performance — returns computed over the same scope.
  • Lenses — the views holdings are read against.
  • Transactions — the events that positions are built from.