{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Portfolio Hierarchies\n",
        "\n",
        "**Prerequisites**\n",
        "* *Portfolios API* Tutorial\n",
        "* *Uploaders API* Tutorial\n",
        "\n",
        "In this tutorial we are going to demonstrate the usage of the Bayesline Portfolio Hierarchies API. The Portfolio Hierarchies API builds on the Portfolios API (see tutorial for an in depth walk through). It allows to combine portfolios with benchmarks and other groupings and by default applies forward filling and drift correction. This forms the foundation for portfolio analytics, where we can simply link a *Portfolio Hierarchy* and obtain analytics (e.g. a return attribution report) for different portfolios with optional benchmarks and other groupings.\n",
        "\n",
        "Specifically, we will introduce and explore:\n",
        "* *Portfolio Hierarchies*\n",
        "* Creating a new *Portfolio Hierarchy*\n",
        "    * Only portfolios\n",
        "    * With benchmarks\n",
        "    * Additional Groupings \n",
        "* Obtaining data\n",
        "* Housekeeping "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Imports & Setup\n",
        "\n",
        "For this tutorial notebook, you will need to import the following packages."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [],
      "source": [
        "import datetime as dt\n",
        "\n",
        "import polars as pl\n",
        "\n",
        "from bayesline.apiclient import BayeslineApiClient\n",
        "\n",
        "from bayesline.api.equity import (\n",
        "    PortfolioHierarchySettings,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We will also need to have a Bayesline API client configured."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "tags": [
          "skip-execution"
        ]
      },
      "outputs": [],
      "source": [
        "bln = BayeslineApiClient.new_client(\n",
        "    endpoint=\"https://[ENDPOINT]\",\n",
        "    api_key=\"[API-KEY]\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The main entrypoint for the Portfolios API sits on `bln.equity.portfoliohierarchies`. All portfolio hierarchies functionality can be reached from here on out.\n",
        "\n",
        "See here for relevant docs:\n",
        "* [Portfolio Hierarchies](https://docs.bayesline.com/0.9.2/_autosummary/bayesline.api.equity.AsyncPortfolioHierarchyApi.html)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [],
      "source": [
        "ph_loader = bln.equity.portfoliohierarchies"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Portfolio Hierarchies\n",
        "\n",
        "In its most plain form a *PortfolioHierarchy* solely consists of a list of portfolio ids and a source or schema that describes where to obtain the data from.\n",
        "\n",
        "Below we create some demo portfolio data and upload it to the system such that it can be used for this demo. See the *Uploaders API* tutorial for an in depth walk through on how to bring data into the Bayesline ecosystem. \n",
        "\n",
        "In this toy example we create four portfolios with 2 holdings prints, 2025-01-01 and 2025-01-31:\n",
        "1. `AGTHX` with two holdings\n",
        "2. `FCNTX` with one holding\n",
        "3. `VADGX` with three holdings\n",
        "3. `SPX` with four holdings"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [],
      "source": [
        "uploader = bln.equity.uploaders.get_data_type(\"portfolios\").get_or_create_dataset(\"portfolio-hierarchies-demo\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div><style>\n",
              ".dataframe > thead > tr,\n",
              ".dataframe > tbody > tr {\n",
              "  text-align: right;\n",
              "  white-space: pre-wrap;\n",
              "}\n",
              "</style>\n",
              "<small>shape: (20, 5)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>portfolio_id</th><th>asset_id</th><th>asset_id_type</th><th>date</th><th>value</th></tr><tr><td>str</td><td>str</td><td>str</td><td>date</td><td>f64</td></tr></thead><tbody><tr><td>&quot;AGTHX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>2025-01-01</td><td>0.5</td></tr><tr><td>&quot;AGTHX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>2025-01-31</td><td>0.55</td></tr><tr><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>2025-01-01</td><td>0.5</td></tr><tr><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>2025-01-31</td><td>0.45</td></tr><tr><td>&quot;FCNTX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>2025-01-01</td><td>1.0</td></tr><tr><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td></tr><tr><td>&quot;SPX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>2025-01-31</td><td>0.2</td></tr><tr><td>&quot;SPX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>2025-01-01</td><td>0.3</td></tr><tr><td>&quot;SPX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>2025-01-31</td><td>0.25</td></tr><tr><td>&quot;SPX&quot;</td><td>&quot;85371710&quot;</td><td>&quot;cusip8&quot;</td><td>2025-01-01</td><td>0.3</td></tr><tr><td>&quot;SPX&quot;</td><td>&quot;85371710&quot;</td><td>&quot;cusip8&quot;</td><td>2025-01-31</td><td>0.25</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (20, 5)\n",
              "┌──────────────┬───────────┬───────────────┬────────────┬───────┐\n",
              "│ portfolio_id ┆ asset_id  ┆ asset_id_type ┆ date       ┆ value │\n",
              "│ ---          ┆ ---       ┆ ---           ┆ ---        ┆ ---   │\n",
              "│ str          ┆ str       ┆ str           ┆ date       ┆ f64   │\n",
              "╞══════════════╪═══════════╪═══════════════╪════════════╪═══════╡\n",
              "│ AGTHX        ┆ 02079K305 ┆ cusip9        ┆ 2025-01-01 ┆ 0.5   │\n",
              "│ AGTHX        ┆ 02079K305 ┆ cusip9        ┆ 2025-01-31 ┆ 0.55  │\n",
              "│ AGTHX        ┆ 2588173   ┆ sedol7        ┆ 2025-01-01 ┆ 0.5   │\n",
              "│ AGTHX        ┆ 2588173   ┆ sedol7        ┆ 2025-01-31 ┆ 0.45  │\n",
              "│ FCNTX        ┆ 67066G10  ┆ cusip8        ┆ 2025-01-01 ┆ 1.0   │\n",
              "│ …            ┆ …         ┆ …             ┆ …          ┆ …     │\n",
              "│ SPX          ┆ 2588173   ┆ sedol7        ┆ 2025-01-31 ┆ 0.2   │\n",
              "│ SPX          ┆ 67066G10  ┆ cusip8        ┆ 2025-01-01 ┆ 0.3   │\n",
              "│ SPX          ┆ 67066G10  ┆ cusip8        ┆ 2025-01-31 ┆ 0.25  │\n",
              "│ SPX          ┆ 85371710  ┆ cusip8        ┆ 2025-01-01 ┆ 0.3   │\n",
              "│ SPX          ┆ 85371710  ┆ cusip8        ┆ 2025-01-31 ┆ 0.25  │\n",
              "└──────────────┴───────────┴───────────────┴────────────┴───────┘"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolio_df = pl.DataFrame({\n",
        "    \"portfolio_id\": [\n",
        "        \"AGTHX\", \"AGTHX\", \"AGTHX\", \"AGTHX\",\n",
        "        \"FCNTX\", \"FCNTX\",\n",
        "        \"VADGX\", \"VADGX\", \"VADGX\", \"VADGX\", \"VADGX\", \"VADGX\",\n",
        "        \"SPX\", \"SPX\", \"SPX\", \"SPX\", \"SPX\", \"SPX\", \"SPX\", \"SPX\",\n",
        "    ],\n",
        "    \"asset_id\": [\n",
        "        # AGTHX\n",
        "        \"02079K305\", \"02079K305\", \n",
        "        \"2588173\", \"2588173\",\n",
        "\n",
        "        # FCNTX\n",
        "        \"67066G10\", \"67066G10\",\n",
        "\n",
        "        # VADGX\n",
        "        \"02079K305\", \"02079K305\", \n",
        "        \"2588173\", \"2588173\", \n",
        "        \"67066G10\", \"67066G10\",\n",
        "\n",
        "        # SPX\n",
        "        \"02079K305\", \"02079K305\", \n",
        "        \"2588173\", \"2588173\",\n",
        "        \"67066G10\", \"67066G10\",\n",
        "        \"85371710\", \"85371710\",\n",
        "\n",
        "    ],  \n",
        "    \"asset_id_type\": [\n",
        "        # AGTHX\n",
        "        \"cusip9\", \"cusip9\", \"sedol7\", \"sedol7\",\n",
        "\n",
        "        # FCNTX\n",
        "        \"cusip8\", \"cusip8\",\n",
        "\n",
        "        # VADGX\n",
        "        \"cusip9\", \"cusip9\", \"sedol7\", \"sedol7\", \"cusip8\", \"cusip8\",\n",
        "\n",
        "        # SPX\n",
        "        \"cusip9\", \"cusip9\", \"sedol7\", \"sedol7\", \"cusip8\", \"cusip8\", \"cusip8\", \"cusip8\",\n",
        "        \n",
        "    ],\n",
        "    \"date\": [\n",
        "        # AGTHX\n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31),\n",
        "\n",
        "        # FCNTX\n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31),\n",
        "\n",
        "        # VADGX\n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "        \n",
        "        # SPX\n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "        dt.date(2025, 1, 1), dt.date(2025, 1, 31), \n",
        "    ],\n",
        "    \"value\": [\n",
        "        # AGTHX\n",
        "        0.5, 0.55, \n",
        "        0.5, 0.45,\n",
        "\n",
        "        # FCNTX\n",
        "        1.0, 1.0,\n",
        "\n",
        "        # VADGX\n",
        "        0.3, 0.5, \n",
        "        0.4, 0.2,\n",
        "        0.3, 0.25,\n",
        "\n",
        "        # SPX\n",
        "        0.3, 0.3, \n",
        "        0.4, 0.2,\n",
        "        0.3, 0.25,\n",
        "        0.3, 0.25,\n",
        "    ],\n",
        "})\n",
        "\n",
        "portfolio_df"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "UploadCommitResult(version=1, committed_names=[])"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "uploader.fast_commit(portfolio_df, mode=\"append\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Hierarchy with only Portfolios \n",
        "\n",
        "Next up we create a portfolio hierarchy with only two of the portfolios we created. This is the first use case of the hierarchy, to subset a larger source of portfolios to only those that are relevant for a specific analysis.\n",
        "\n",
        "Note that the `portfolio_ids` must be unique for a hierarchy to be valid."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [],
      "source": [
        "ph_settings = PortfolioHierarchySettings.from_source(\n",
        "    source=\"portfolio-hierarchies-demo\",\n",
        "    portfolio_ids=[\"AGTHX\", \"SPX\"],\n",
        ")\n",
        "\n",
        "ph_api = ph_loader.load(ph_settings)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Below we obtain holdings data for our setup and we find that:\n",
        "\n",
        "1. Only our two specified portfolios are present\n",
        "2. The values have been forward filled and drift corrected\n",
        "3. The benchmark is unset (as specified)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div><style>\n",
              ".dataframe > thead > tr,\n",
              ".dataframe > tbody > tr {\n",
              "  text-align: right;\n",
              "  white-space: pre-wrap;\n",
              "}\n",
              "</style>\n",
              "<small>shape: (155, 6)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_id</th><th>input_asset_id</th><th>input_asset_id_type</th><th>value</th><th>value_bench</th></tr><tr><td>date</td><td>str</td><td>str</td><td>str</td><td>f32</td><td>f32</td></tr></thead><tbody><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.5</td><td>null</td></tr><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.5</td><td>null</td></tr><tr><td>2025-01-01</td><td>&quot;SPX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.3</td><td>null</td></tr><tr><td>2025-01-01</td><td>&quot;SPX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.4</td><td>null</td></tr><tr><td>2025-01-01</td><td>&quot;SPX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>0.3</td><td>null</td></tr><tr><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td></tr><tr><td>2025-01-31</td><td>&quot;AGTHX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.55</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.45</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.3</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.2</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>0.25</td><td>null</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (155, 6)\n",
              "┌────────────┬──────────────┬────────────────┬─────────────────────┬───────┬─────────────┐\n",
              "│ date       ┆ portfolio_id ┆ input_asset_id ┆ input_asset_id_type ┆ value ┆ value_bench │\n",
              "│ ---        ┆ ---          ┆ ---            ┆ ---                 ┆ ---   ┆ ---         │\n",
              "│ date       ┆ str          ┆ str            ┆ str                 ┆ f32   ┆ f32         │\n",
              "╞════════════╪══════════════╪════════════════╪═════════════════════╪═══════╪═════════════╡\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 02079K305      ┆ cusip9              ┆ 0.5   ┆ null        │\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 2588173        ┆ sedol7              ┆ 0.5   ┆ null        │\n",
              "│ 2025-01-01 ┆ SPX          ┆ 02079K305      ┆ cusip9              ┆ 0.3   ┆ null        │\n",
              "│ 2025-01-01 ┆ SPX          ┆ 2588173        ┆ sedol7              ┆ 0.4   ┆ null        │\n",
              "│ 2025-01-01 ┆ SPX          ┆ 67066G10       ┆ cusip8              ┆ 0.3   ┆ null        │\n",
              "│ …          ┆ …            ┆ …              ┆ …                   ┆ …     ┆ …           │\n",
              "│ 2025-01-31 ┆ AGTHX        ┆ 02079K305      ┆ cusip9              ┆ 0.55  ┆ null        │\n",
              "│ 2025-01-31 ┆ AGTHX        ┆ 2588173        ┆ sedol7              ┆ 0.45  ┆ null        │\n",
              "│ 2025-01-31 ┆ SPX          ┆ 02079K305      ┆ cusip9              ┆ 0.3   ┆ null        │\n",
              "│ 2025-01-31 ┆ SPX          ┆ 2588173        ┆ sedol7              ┆ 0.2   ┆ null        │\n",
              "│ 2025-01-31 ┆ SPX          ┆ 67066G10       ┆ cusip8              ┆ 0.25  ┆ null        │\n",
              "└────────────┴──────────────┴────────────────┴─────────────────────┴───────┴─────────────┘"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ph_api.get(start_date=dt.date(2025, 1, 1), end_date=dt.date(2025, 1, 31))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adding Benchmarks\n",
        "\n",
        "We can add a benchmark to our hierarchy which will populate the benchmark column. Note that we can selectively specify a benchmark, for instance we could benchmark `AGTHX` against `SPX` but leave `SPX` itself unbenchmarked."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {},
      "outputs": [],
      "source": [
        "ph_settings = PortfolioHierarchySettings.from_source(\n",
        "    source=\"portfolio-hierarchies-demo\",\n",
        "    portfolio_ids=[\"AGTHX\", \"SPX\"],\n",
        "    benchmark_ids=[\"SPX\", None],\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {},
      "outputs": [],
      "source": [
        "ph_api = ph_loader.load(ph_settings)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note that the `null` observations are cases where either portfolio or benchmark as a holding but the other does not (in its respective id space)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div><style>\n",
              ".dataframe > thead > tr,\n",
              ".dataframe > tbody > tr {\n",
              "  text-align: right;\n",
              "  white-space: pre-wrap;\n",
              "}\n",
              "</style>\n",
              "<small>shape: (186, 6)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_id</th><th>input_asset_id</th><th>input_asset_id_type</th><th>value</th><th>value_bench</th></tr><tr><td>date</td><td>str</td><td>str</td><td>str</td><td>f32</td><td>f32</td></tr></thead><tbody><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.5</td><td>0.3</td></tr><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.5</td><td>0.4</td></tr><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>null</td><td>0.3</td></tr><tr><td>2025-01-01</td><td>&quot;SPX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.3</td><td>null</td></tr><tr><td>2025-01-01</td><td>&quot;SPX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.4</td><td>null</td></tr><tr><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td></tr><tr><td>2025-01-31</td><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.45</td><td>0.2</td></tr><tr><td>2025-01-31</td><td>&quot;AGTHX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>null</td><td>0.25</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.3</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.2</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>0.25</td><td>null</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (186, 6)\n",
              "┌────────────┬──────────────┬────────────────┬─────────────────────┬───────┬─────────────┐\n",
              "│ date       ┆ portfolio_id ┆ input_asset_id ┆ input_asset_id_type ┆ value ┆ value_bench │\n",
              "│ ---        ┆ ---          ┆ ---            ┆ ---                 ┆ ---   ┆ ---         │\n",
              "│ date       ┆ str          ┆ str            ┆ str                 ┆ f32   ┆ f32         │\n",
              "╞════════════╪══════════════╪════════════════╪═════════════════════╪═══════╪═════════════╡\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 02079K305      ┆ cusip9              ┆ 0.5   ┆ 0.3         │\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 2588173        ┆ sedol7              ┆ 0.5   ┆ 0.4         │\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 67066G10       ┆ cusip8              ┆ null  ┆ 0.3         │\n",
              "│ 2025-01-01 ┆ SPX          ┆ 02079K305      ┆ cusip9              ┆ 0.3   ┆ null        │\n",
              "│ 2025-01-01 ┆ SPX          ┆ 2588173        ┆ sedol7              ┆ 0.4   ┆ null        │\n",
              "│ …          ┆ …            ┆ …              ┆ …                   ┆ …     ┆ …           │\n",
              "│ 2025-01-31 ┆ AGTHX        ┆ 2588173        ┆ sedol7              ┆ 0.45  ┆ 0.2         │\n",
              "│ 2025-01-31 ┆ AGTHX        ┆ 67066G10       ┆ cusip8              ┆ null  ┆ 0.25        │\n",
              "│ 2025-01-31 ┆ SPX          ┆ 02079K305      ┆ cusip9              ┆ 0.3   ┆ null        │\n",
              "│ 2025-01-31 ┆ SPX          ┆ 2588173        ┆ sedol7              ┆ 0.2   ┆ null        │\n",
              "│ 2025-01-31 ┆ SPX          ┆ 67066G10       ┆ cusip8              ┆ 0.25  ┆ null        │\n",
              "└────────────┴──────────────┴────────────────┴─────────────────────┴───────┴─────────────┘"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ph_api.get(start_date=dt.date(2025, 1, 1), end_date=dt.date(2025, 1, 31))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can pass one of the supported output id types to map ids on the fly."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'AGTHX': ['bayesid'], 'SPX': ['bayesid']}"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ph_api.get_id_types()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div><style>\n",
              ".dataframe > thead > tr,\n",
              ".dataframe > tbody > tr {\n",
              "  text-align: right;\n",
              "  white-space: pre-wrap;\n",
              "}\n",
              "</style>\n",
              "<small>shape: (186, 8)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_id</th><th>input_asset_id</th><th>input_asset_id_type</th><th>asset_id</th><th>asset_id_type</th><th>value</th><th>value_bench</th></tr><tr><td>date</td><td>str</td><td>str</td><td>str</td><td>str</td><td>str</td><td>f32</td><td>f32</td></tr></thead><tbody><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;ICA17F00B9&quot;</td><td>&quot;bayesid&quot;</td><td>0.5</td><td>0.3</td></tr><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>&quot;ICF982536B&quot;</td><td>&quot;bayesid&quot;</td><td>0.5</td><td>0.4</td></tr><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;IC46D464E0&quot;</td><td>&quot;bayesid&quot;</td><td>null</td><td>0.3</td></tr><tr><td>2025-01-01</td><td>&quot;SPX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;ICA17F00B9&quot;</td><td>&quot;bayesid&quot;</td><td>0.3</td><td>null</td></tr><tr><td>2025-01-01</td><td>&quot;SPX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>&quot;ICF982536B&quot;</td><td>&quot;bayesid&quot;</td><td>0.4</td><td>null</td></tr><tr><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td></tr><tr><td>2025-01-31</td><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>&quot;ICF982536B&quot;</td><td>&quot;bayesid&quot;</td><td>0.45</td><td>0.2</td></tr><tr><td>2025-01-31</td><td>&quot;AGTHX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;IC46D464E0&quot;</td><td>&quot;bayesid&quot;</td><td>null</td><td>0.25</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;ICA17F00B9&quot;</td><td>&quot;bayesid&quot;</td><td>0.3</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>&quot;ICF982536B&quot;</td><td>&quot;bayesid&quot;</td><td>0.2</td><td>null</td></tr><tr><td>2025-01-31</td><td>&quot;SPX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;IC46D464E0&quot;</td><td>&quot;bayesid&quot;</td><td>0.25</td><td>null</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (186, 8)\n",
              "┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬───────┬────────────┐\n",
              "│ date       ┆ portfolio_ ┆ input_asse ┆ input_asse ┆ asset_id   ┆ asset_id_t ┆ value ┆ value_benc │\n",
              "│ ---        ┆ id         ┆ t_id       ┆ t_id_type  ┆ ---        ┆ ype        ┆ ---   ┆ h          │\n",
              "│ date       ┆ ---        ┆ ---        ┆ ---        ┆ str        ┆ ---        ┆ f32   ┆ ---        │\n",
              "│            ┆ str        ┆ str        ┆ str        ┆            ┆ str        ┆       ┆ f32        │\n",
              "╞════════════╪════════════╪════════════╪════════════╪════════════╪════════════╪═══════╪════════════╡\n",
              "│ 2025-01-01 ┆ AGTHX      ┆ 02079K305  ┆ cusip9     ┆ ICA17F00B9 ┆ bayesid    ┆ 0.5   ┆ 0.3        │\n",
              "│ 2025-01-01 ┆ AGTHX      ┆ 2588173    ┆ sedol7     ┆ ICF982536B ┆ bayesid    ┆ 0.5   ┆ 0.4        │\n",
              "│ 2025-01-01 ┆ AGTHX      ┆ 67066G10   ┆ cusip8     ┆ IC46D464E0 ┆ bayesid    ┆ null  ┆ 0.3        │\n",
              "│ 2025-01-01 ┆ SPX        ┆ 02079K305  ┆ cusip9     ┆ ICA17F00B9 ┆ bayesid    ┆ 0.3   ┆ null       │\n",
              "│ 2025-01-01 ┆ SPX        ┆ 2588173    ┆ sedol7     ┆ ICF982536B ┆ bayesid    ┆ 0.4   ┆ null       │\n",
              "│ …          ┆ …          ┆ …          ┆ …          ┆ …          ┆ …          ┆ …     ┆ …          │\n",
              "│ 2025-01-31 ┆ AGTHX      ┆ 2588173    ┆ sedol7     ┆ ICF982536B ┆ bayesid    ┆ 0.45  ┆ 0.2        │\n",
              "│ 2025-01-31 ┆ AGTHX      ┆ 67066G10   ┆ cusip8     ┆ IC46D464E0 ┆ bayesid    ┆ null  ┆ 0.25       │\n",
              "│ 2025-01-31 ┆ SPX        ┆ 02079K305  ┆ cusip9     ┆ ICA17F00B9 ┆ bayesid    ┆ 0.3   ┆ null       │\n",
              "│ 2025-01-31 ┆ SPX        ┆ 2588173    ┆ sedol7     ┆ ICF982536B ┆ bayesid    ┆ 0.2   ┆ null       │\n",
              "│ 2025-01-31 ┆ SPX        ┆ 67066G10   ┆ cusip8     ┆ IC46D464E0 ┆ bayesid    ┆ 0.25  ┆ null       │\n",
              "└────────────┴────────────┴────────────┴────────────┴────────────┴────────────┴───────┴────────────┘"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ph_api.get(start_date=dt.date(2025, 1, 1), end_date=dt.date(2025, 1, 31), id_type=\"bayesid\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adding Additional Groupings\n",
        "\n",
        "We can add additional groupings that downstream can be used to provide additional aggregations for these groupings. For instance we might assign portfolios to managers or a specific investment style.\n",
        "\n",
        "Below we're assigning hypothetical managers *Alex* and *Joanna*. Note that these groupings have no effect on the output of the `ph_loader.get` method but they are picked up downstream by the *Reports API*."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div><style>\n",
              ".dataframe > thead > tr,\n",
              ".dataframe > tbody > tr {\n",
              "  text-align: right;\n",
              "  white-space: pre-wrap;\n",
              "}\n",
              "</style>\n",
              "<small>shape: (3, 3)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>Manager</th><th>portfolio_id</th><th>benchmark_id</th></tr><tr><td>str</td><td>str</td><td>str</td></tr></thead><tbody><tr><td>&quot;Alice&quot;</td><td>&quot;AGTHX&quot;</td><td>&quot;SPX&quot;</td></tr><tr><td>&quot;Bob&quot;</td><td>&quot;FCNTX&quot;</td><td>&quot;SPX&quot;</td></tr><tr><td>&quot;Charlie&quot;</td><td>&quot;VADGX&quot;</td><td>&quot;VADGX&quot;</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (3, 3)\n",
              "┌─────────┬──────────────┬──────────────┐\n",
              "│ Manager ┆ portfolio_id ┆ benchmark_id │\n",
              "│ ---     ┆ ---          ┆ ---          │\n",
              "│ str     ┆ str          ┆ str          │\n",
              "╞═════════╪══════════════╪══════════════╡\n",
              "│ Alice   ┆ AGTHX        ┆ SPX          │\n",
              "│ Bob     ┆ FCNTX        ┆ SPX          │\n",
              "│ Charlie ┆ VADGX        ┆ VADGX        │\n",
              "└─────────┴──────────────┴──────────────┘"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ph_settings = PortfolioHierarchySettings.from_source(\n",
        "    source=\"portfolio-hierarchies-demo\",\n",
        "    portfolio_ids=[\"AGTHX\", \"FCNTX\", \"VADGX\"],\n",
        "    benchmark_ids=[\"SPX\", \"SPX\", \"VADGX\"],\n",
        "    groupings={\"Manager\": [\"Alice\", \"Bob\", \"Charlie\"]},\n",
        ")\n",
        "\n",
        "\n",
        "ph_settings.to_polars()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Creating from Polars\n",
        "\n",
        "As a shorthand it might be more convenient to create a hierarchy from a data frame instead of manually specifying the Pydantic object."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div><style>\n",
              ".dataframe > thead > tr,\n",
              ".dataframe > tbody > tr {\n",
              "  text-align: right;\n",
              "  white-space: pre-wrap;\n",
              "}\n",
              "</style>\n",
              "<small>shape: (3, 3)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>Manager</th><th>portfolio_id</th><th>benchmark_id</th></tr><tr><td>str</td><td>str</td><td>str</td></tr></thead><tbody><tr><td>&quot;Alice&quot;</td><td>&quot;AGTHX&quot;</td><td>&quot;SPX&quot;</td></tr><tr><td>&quot;Bob&quot;</td><td>&quot;FCNTX&quot;</td><td>&quot;SPX&quot;</td></tr><tr><td>&quot;Charlie&quot;</td><td>&quot;VADGX&quot;</td><td>&quot;VADGX&quot;</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (3, 3)\n",
              "┌─────────┬──────────────┬──────────────┐\n",
              "│ Manager ┆ portfolio_id ┆ benchmark_id │\n",
              "│ ---     ┆ ---          ┆ ---          │\n",
              "│ str     ┆ str          ┆ str          │\n",
              "╞═════════╪══════════════╪══════════════╡\n",
              "│ Alice   ┆ AGTHX        ┆ SPX          │\n",
              "│ Bob     ┆ FCNTX        ┆ SPX          │\n",
              "│ Charlie ┆ VADGX        ┆ VADGX        │\n",
              "└─────────┴──────────────┴──────────────┘"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ph_settings_df = ph_settings.to_polars()\n",
        "\n",
        "ph_settings_df"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {},
      "outputs": [],
      "source": [
        "ph_settings = PortfolioHierarchySettings.from_polars(\n",
        "    ph_settings_df, \n",
        "    portfolio_source=\"portfolio-hierarchies-demo\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div><style>\n",
              ".dataframe > thead > tr,\n",
              ".dataframe > tbody > tr {\n",
              "  text-align: right;\n",
              "  white-space: pre-wrap;\n",
              "}\n",
              "</style>\n",
              "<small>shape: (279, 6)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_id</th><th>input_asset_id</th><th>input_asset_id_type</th><th>value</th><th>value_bench</th></tr><tr><td>date</td><td>str</td><td>str</td><td>str</td><td>f32</td><td>f32</td></tr></thead><tbody><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.5</td><td>0.3</td></tr><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.5</td><td>0.4</td></tr><tr><td>2025-01-01</td><td>&quot;AGTHX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>null</td><td>0.3</td></tr><tr><td>2025-01-01</td><td>&quot;FCNTX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>null</td><td>0.3</td></tr><tr><td>2025-01-01</td><td>&quot;FCNTX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>null</td><td>0.4</td></tr><tr><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td></tr><tr><td>2025-01-31</td><td>&quot;FCNTX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>null</td><td>0.2</td></tr><tr><td>2025-01-31</td><td>&quot;FCNTX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>1.0</td><td>0.25</td></tr><tr><td>2025-01-31</td><td>&quot;VADGX&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>0.5</td><td>0.5</td></tr><tr><td>2025-01-31</td><td>&quot;VADGX&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>0.2</td><td>0.2</td></tr><tr><td>2025-01-31</td><td>&quot;VADGX&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>0.25</td><td>0.25</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (279, 6)\n",
              "┌────────────┬──────────────┬────────────────┬─────────────────────┬───────┬─────────────┐\n",
              "│ date       ┆ portfolio_id ┆ input_asset_id ┆ input_asset_id_type ┆ value ┆ value_bench │\n",
              "│ ---        ┆ ---          ┆ ---            ┆ ---                 ┆ ---   ┆ ---         │\n",
              "│ date       ┆ str          ┆ str            ┆ str                 ┆ f32   ┆ f32         │\n",
              "╞════════════╪══════════════╪════════════════╪═════════════════════╪═══════╪═════════════╡\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 02079K305      ┆ cusip9              ┆ 0.5   ┆ 0.3         │\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 2588173        ┆ sedol7              ┆ 0.5   ┆ 0.4         │\n",
              "│ 2025-01-01 ┆ AGTHX        ┆ 67066G10       ┆ cusip8              ┆ null  ┆ 0.3         │\n",
              "│ 2025-01-01 ┆ FCNTX        ┆ 02079K305      ┆ cusip9              ┆ null  ┆ 0.3         │\n",
              "│ 2025-01-01 ┆ FCNTX        ┆ 2588173        ┆ sedol7              ┆ null  ┆ 0.4         │\n",
              "│ …          ┆ …            ┆ …              ┆ …                   ┆ …     ┆ …           │\n",
              "│ 2025-01-31 ┆ FCNTX        ┆ 2588173        ┆ sedol7              ┆ null  ┆ 0.2         │\n",
              "│ 2025-01-31 ┆ FCNTX        ┆ 67066G10       ┆ cusip8              ┆ 1.0   ┆ 0.25        │\n",
              "│ 2025-01-31 ┆ VADGX        ┆ 02079K305      ┆ cusip9              ┆ 0.5   ┆ 0.5         │\n",
              "│ 2025-01-31 ┆ VADGX        ┆ 2588173        ┆ sedol7              ┆ 0.2   ┆ 0.2         │\n",
              "│ 2025-01-31 ┆ VADGX        ┆ 67066G10       ┆ cusip8              ┆ 0.25  ┆ 0.25        │\n",
              "└────────────┴──────────────┴────────────────┴─────────────────────┴───────┴─────────────┘"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ph_api = ph_loader.load(ph_settings)\n",
        "\n",
        "ph_api.get(start_date=dt.date(2025, 1, 1), end_date=dt.date(2025, 1, 31))"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": ".venv",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.14"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}