Entities#
Data entities for portfolio and benchmark weight allocations.
Portfolio entity for managing asset weight allocations over time.
This module provides PortfolioEntity, an immutable dataclass
that stores portfolio weights in a PyArrow table. The first column
holds ticker symbols and every subsequent column represents weights at
a rebalancing date. Comprehensive validation ensures date formats,
non-negative weights, per-date weight sums, and data-type correctness.
- class PortfolioEntity(portfolio_table: Table)[source]#
Bases:
objectA portfolio entity that manages asset weights over time using PyArrow tables.
This class represents a portfolio with asset allocations that can change over time. It stores portfolio data in a PyArrow table format where the first column contains ticker symbols and subsequent columns represent portfolio weights at different dates.
The class ensures data integrity through comprehensive validation including: - Date format validation (YYYY-MM-DD or DD/MM/YYYY) - Weight validation (non-negative, sum <= 1.0 per date) - Data type validation for all columns - Null value detection and prevention
- portfolio_table#
PyArrow table containing portfolio data with ticker symbols in the first column and date-based weights in subsequent columns.
- Type:
pa.Table
Examples
>>> weights = { ... "2020-01-01": {"AAPL": 0.6, "MSFT": 0.4}, ... "2020-06-01": {"AAPL": 0.5, "MSFT": 0.5} ... } >>> portfolio = PortfolioEntity.from_dict(weights) >>> tickers = portfolio.get_tickers() >>> dates = portfolio.get_date_columns()
- classmethod from_dict(weights_dict: dict[str, dict[str, float]]) PortfolioEntity[source]#
Create PortfolioEntity from dictionary of weights with full validation.
- Parameters:
weights_dict (dict[str, dict[str, float]]) –
Dictionary with date keys and ticker-weight mappings. Example: {
”2020-01-01”: {“AAPL”: 0.6, “MSFT”: 0.4}, “2020-06-01”: {“AAPL”: 0.5, “MSFT”: 0.5}, “2021-01-01”: {“AAPL”: 0.7, “MSFT”: 0.3}
}
- Returns:
Validated PortfolioEntity instance.
- Return type:
- Raises:
PortfolioEntityError – If validation fails.
- get_date_columns() list[str][source]#
Retrieve the list of date column names in the portfolio.
Returns all date column names from the portfolio table, excluding the first column which contains ticker symbols. Each date column represents a specific rebalancing date with corresponding portfolio weights.
- Returns:
A list of date column names in string format (e.g., [‘2020-01-01’, ‘2020-06-01’, ‘2021-01-01’]). The dates are in the format YYYY-MM-DD or DD/MM/YYYY as originally provided during portfolio creation.
- Return type:
list[str]
Examples
>>> portfolio = PortfolioEntity.from_dict({ ... "2020-01-01": {"AAPL": 0.6, "MSFT": 0.4}, ... "2020-06-01": {"AAPL": 0.5, "MSFT": 0.5} ... }) >>> dates = portfolio.get_date_columns() >>> print(dates) ['2020-01-01', '2020-06-01']
- get_tickers() list[str][source]#
Retrieve the list of ticker symbols in the portfolio.
Returns all ticker symbols stored in the first column of the portfolio table. The tickers are returned in the same order as they appear in the table.
- Returns:
A list of ticker symbols (e.g., [‘AAPL’, ‘MSFT’, ‘GOOGL’]). The list maintains the original ordering from the portfolio table.
- Return type:
list[str]
Examples
>>> portfolio = PortfolioEntity.from_dict({ ... "2020-01-01": {"AAPL": 0.6, "MSFT": 0.4} ... }) >>> tickers = portfolio.get_tickers() >>> print(tickers) ['AAPL', 'MSFT']
- get_weights_for_date_column(date_column: str) list[float][source]#
Retrieve portfolio weights for a specific date column.
Returns the portfolio weights for all tickers at the specified date. The weights are returned in the same order as the tickers from get_tickers().
- Parameters:
date_column (str) – The date column name to retrieve weights for. Must be one of the date columns returned by get_date_columns. Format should match the original date format (YYYY-MM-DD or DD/MM/YYYY).
- Returns:
A list of portfolio weights corresponding to each ticker for the specified date. The weights are in the same order as the tickers returned by get_tickers.
- Return type:
list[float]
- Raises:
PortfolioEntityError – If the specified date_column is not found in the portfolio.
Examples
>>> portfolio = PortfolioEntity.from_dict({ ... "2020-01-01": {"AAPL": 0.6, "MSFT": 0.4}, ... "2020-06-01": {"AAPL": 0.5, "MSFT": 0.5} ... }) >>> weights = portfolio.get_weights_for_date_column("2020-01-01") >>> tickers = portfolio.get_tickers() >>> dict(zip(tickers, weights)) {'AAPL': 0.6, 'MSFT': 0.4}
- portfolio_table: Table#
- class PortfolioDailyWeights(daily_weights: pandas.DataFrame)[source]#
Bases:
object- daily_weights: DataFrame#
Immutable entity for portfolio / benchmark weight allocations as PyArrow tables.
This module provides PortfolioWeights, a frozen dataclass that
wraps a pa.Table of asset weights in the transposed attribution
format (one column per asset, rows indexed by date). Schema and data
integrity validations run automatically on construction.
- class PortfolioWeights(table: Table)[source]#
Bases:
objectImmutable wrapper around a PyArrow table of portfolio or benchmark weights.
The expected schema is:
[date: timestamp(ns), ASSET1: float64, ASSET2: float64, ...]
Validation is performed automatically on construction via
__post_init__and includes:The table must contain at least one row.
A
"date"column of typetimestamp(ns)must be present.Every non-date column must be
float64.No null values are allowed in asset columns.
If the table spans at least a year, the observation density near the start and end of the series must be consistent with a daily series (roughly 240-260 rows per year).
- Parameters:
table (pa.Table) – PyArrow table whose first column is a
timestamp(ns)date column named"date"and whose remaining columns hold per-asset weight allocations asfloat64.- Raises:
DataValidationError – If the table is empty.
MissingColumnError – If the
"date"column is absent.EntityTypeError – If the
"date"column is not a timestamp or any asset column is notfloat64.NullError – If any asset column contains null values.
DataIntegrityError – If the table spans at least a year but the number of observations in the first or last 1-year window falls outside
[MIN_ANNUAL_OBSERVATIONS, MAX_ANNUAL_OBSERVATIONS], suggesting the series does not have daily granularity (e.g. rebalance-only weights passed directly instead of a full daily series).
Examples
>>> import pyarrow as pa >>> tbl = pa.table({ ... "date": pa.array(["2024-01-02", "2024-01-03"]).cast(pa.timestamp("ns")), ... "AAPL": pa.array([0.6, 0.5], type=pa.float64()), ... "MSFT": pa.array([0.4, 0.5], type=pa.float64()), ... }) >>> pw = PortfolioWeights(table=tbl) >>> pw.asset_names ['AAPL', 'MSFT']
- property asset_names: list[str]#
Return the list of asset column names (excludes
"date").- Returns:
Ordered list of asset ticker names present in the table.
- Return type:
list[str]
- table: Table#