Asset Categories
AssetCategory is the reusable grouping layer for assets.
If an Asset is the identity of one instrument, an AssetCategory is the named universe that collects many assets into one object you can reference elsewhere.
This is useful when you want to say:
- "these are the assets my portfolio trades"
- "these are the assets in this benchmark universe"
- "these are the assets I want to interpolate prices for"
without repeating the full asset list every time.
What an asset category is
An asset category is a named collection of assets with:
unique_identifierdisplay_nameassets- optional
description
In practice, it is the platform object you use when the asset universe itself should be reusable.
Why asset categories matter
There are two common ways to define a universe:
- pass a concrete list of assets directly into code
- point to a reusable category by identifier
Direct asset lists are fine for:
- small one-off runs
- very local scripts
- experiments where reuse does not matter yet
Asset categories are better when:
- the same universe is reused across several workflows
- a portfolio or signal should reference a stable named universe
- you want the universe to be easy to update without rewriting every consumer
- the asset list is conceptually part of the business definition
The mental model
Think of asset categories as reusable containers for universes.
Examples:
sp500_constituentsus_ig_credit_universecrypto_core_assetsmock_category_assets_tutorial
The point is not only grouping. The point is having a stable platform object that other workflows can reference by name.
How the tutorial uses them
The portfolio tutorial introduces an asset category here:
In that chapter, the category is used to collect the mock assets so later price and portfolio configuration can refer to the universe by unique_identifier instead of by repeating the list manually.
That is the right intuition:
- assets define what each instrument is
- the asset category defines which instruments belong to the working universe
Core client operations
The normal client entrypoint is:
import mainsequence.client as msc
Create or fetch a category
asset_category = msc.AssetCategory.get_or_create(
display_name="Mock Category Assets Tutorial",
unique_identifier="mock_category_assets_tutorial",
description="Tutorial asset universe for mock fixed-income examples.",
)
Append assets
asset_category.append_assets(assets=assets)
You can also append by id if that is what you already have:
asset_category.append_assets(asset_ids=[101, 102, 103])
Read the assets back
category_assets = asset_category.get_assets()
Remove assets
asset_category.remove_assets(asset_ids=[101, 102])
Replace the universe
asset_category.update_assets(asset_ids=[201, 202, 203])
Use that carefully. Replacing the membership changes the effective universe for any workflow that depends on that category.
How this connects to VFB
In Virtual Fund Builder, the important field is:
assets_category_unique_id
When that field is present, VFB resolves the traded universe from the asset category.
That is the category-driven pattern.
When it is missing, the signal or other node-level logic is expected to define the universe directly through code.
So the choice is:
- category-driven universe: stable, shared, reusable
- signal-driven universe: local, dynamic, code-defined
Neither is universally better. They solve different problems.
Asset categories vs plain asset lists
This distinction matters.
Plain asset list
Use this when:
- the list is small
- the list is local to one node or one signal
- reuse is not important yet
Asset category
Use this when:
- the universe should be named
- multiple workflows should refer to the same set
- you want universe changes to happen in one place
- the asset universe is part of the business definition
Asset categories vs translation tables
Readers often confuse these because both sit near the universe and price plumbing.
They do different jobs.
Asset category
Answers:
which assets are in scope?
Translation table
Answers:
for a given asset, which upstream time series should be used?
So:
- category = universe definition
- translation table = routing definition
You often need both in the same workflow, especially in portfolio construction.
A practical example
This is the tutorial pattern in its simplest form:
assets = ensure_test_assets()
asset_category = msc.AssetCategory.get_or_create(
display_name="Mock Category Assets Tutorial",
unique_identifier="mock_category_assets_tutorial",
)
asset_category.append_assets(assets=assets)
What this gives you:
- the assets still keep their own identities
- the workflow now also has a named universe object
- later code can reference
mock_category_assets_tutorialinstead of rebuilding the list
Good practices
Keep unique_identifier stable
Treat the category identifier as a real API-level name, not as a temporary label.
Use meaningful boundaries
A category should represent a coherent universe:
- one benchmark
- one strategy universe
- one research basket
- one tutorial test universe
Avoid mixing unrelated universes
If two workflows have different business meanings, they should usually have different categories.
Be careful with shared categories
If many workflows depend on the same category, changing membership can have broad downstream effects.
Common mistakes
1. Treating a category like a translation table
Categories do not route prices.
They only define membership.
2. Treating a category like asset metadata
The category is not where asset identity lives.
Identity still belongs to each Asset.
3. Using one category for unrelated purposes
This makes the category easy to reuse in the short term and confusing in the long term.
4. Rebuilding the same universe everywhere in code
If the same universe keeps appearing in multiple nodes or portfolios, it probably wants to become an asset category.
Rules of thumb
- use assets for identity
- use asset categories for reusable universes
- use translation tables for routing
- use direct asset lists for local or dynamic cases