Portfolio Aligned Universes#
In this notebook we’ll demonstrate how to define universes based on existing portfolio data.
For this purpose we will first upload some sample portfolio holdings and subsequently use them to define a universe filter.
import io
import polars as pl
from bayesline.api.equity import (
PortfolioOrganizerSettings,
UniverseSettings,
)
from bayesline.apiclient import BayeslineApiClient
bln = BayeslineApiClient.new_client(
endpoint="https://[ENDPOINT]",
api_key="[API-KEY]",
)
Uploading Portfolios#
We’ll upload two portfolios PORT_1 and PORT_2 which contain assets Apple (IC83A1B819), Microsoft (ICF982536B) and Alphabet (ICA17F00B9).
portfolio_uploaders = bln.equity.uploaders.get_data_type("portfolios")
portfolio_uploader = portfolio_uploaders.create_or_replace_dataset("US-Portfolios")
data_csv = """
portfolio_id date asset_id asset_id_type value
PORT_1 2025-01-01 IC83A1B819 bayesid .3
PORT_1 2025-01-01 ICA17F00B9 bayesid .7
PORT_2 2025-01-01 IC83A1B819 bayesid .4
PORT_2 2025-01-01 ICF982536B bayesid .6
PORT_1 2025-02-01 IC83A1B819 bayesid 1
PORT_2 2025-02-01 ICF982536B bayesid 1
"""
portfolios_df = pl.read_csv(io.StringIO(data_csv.strip()), separator="\t", try_parse_dates=True)
portfolios_df.pivot(index=["date", "portfolio_id"], on="asset_id", values="value").fill_null(0)
| date | portfolio_id | IC83A1B819 | ICA17F00B9 | ICF982536B |
|---|---|---|---|---|
| date | str | f64 | f64 | f64 |
| 2025-01-01 | "PORT_1" | 0.3 | 0.7 | 0.0 |
| 2025-01-01 | "PORT_2" | 0.4 | 0.0 | 0.6 |
| 2025-02-01 | "PORT_1" | 1.0 | 0.0 | 0.0 |
| 2025-02-01 | "PORT_2" | 0.0 | 0.0 | 1.0 |
portfolio_uploader.fast_commit(portfolios_df, mode="append")
UploadCommitResult(version=1, committed_names=[])
Creating the Universe#
Note that below we could still use other filters in addition to the portfolio filter. This way we could for instance express the energy sector within the Russell 3000.
universe_settings = UniverseSettings(
dataset="Bayesline-US-All-1y",
portfolio_filter=PortfolioOrganizerSettings(
# filters against the point in time superset
# of all portfolios contained in `US-Portfolios`
enabled_portfolios="US-Portfolios"
),
)
universe_api = bln.equity.universes.load(universe_settings)
When obtaining the universe data note how even though we only specified holdings for 2025-01-01 and 2025-02-01 (which could be rebalance dates) the resulting universe contains entries for each day. Underneath the holdings are forward filled and delisted assets are dropped.
universe_api.get().to_pandas().assign(value=1.).set_index(["date", "bayesid"]).unstack()
| value | |||
|---|---|---|---|
| bayesid | IC83A1B819 | ICA17F00B9 | ICF982536B |
| date | |||
| 2025-01-01 | 1.0 | 1.0 | 1.0 |
| 2025-01-02 | 1.0 | 1.0 | 1.0 |
| 2025-01-03 | 1.0 | 1.0 | 1.0 |
| 2025-01-04 | 1.0 | 1.0 | 1.0 |
| 2025-01-05 | 1.0 | 1.0 | 1.0 |
| ... | ... | ... | ... |
| 2025-08-21 | 1.0 | NaN | 1.0 |
| 2025-08-22 | 1.0 | NaN | 1.0 |
| 2025-08-23 | 1.0 | NaN | 1.0 |
| 2025-08-24 | 1.0 | NaN | 1.0 |
| 2025-08-25 | 1.0 | NaN | 1.0 |
237 rows × 3 columns
# asset counts over
universe_api.counts().to_pandas().set_index("date")["count"].sort_index().plot()
<Axes: xlabel='date'>