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: object

A 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:

PortfolioEntity

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: object

Immutable 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 type timestamp(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 as float64.

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 not float64.

  • 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#
class DailyWeights(file_route: str, file_extension: str)[source]#

Bases: object

data: DataFrame#
file_extension: str#
file_route: str#
read_csv() → DataFrame[source]#

Reads the CSV file and returns a DataFrame.

Returns:

The data read from the CSV file.

Return type:

DataFrame