{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Portfolios\n",
        "\n",
        "**Prerequisites**\n",
        "* *Uploaders API* Tutorial\n",
        "\n",
        "\n",
        "In this tutorial we are going to demonstrate the usage of the Bayesline Portfolios API. The Portfolios API provides a unified access mechanism to bring portfolio holdings data into the system such that it can be used downstream (e.g. for risk analytics).\n",
        "\n",
        "Specifically, we will introduce and explore:\n",
        "* *Portfolio Sources*\n",
        "* Listing existing portfolio sources\n",
        "* Uploading new portfolio data (adding to a source, creating a new source) \n",
        "* Reading portfolio data\n",
        "* Forward filling holdings data with drift correction\n",
        "* Funds of funds structures\n",
        "* *Portfolio Schemas* (advanced)"
      ]
    },
    {
      "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",
        "    PortfolioSettings,\n",
        "    PortfolioOrganizerSettings,\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.portfolios`. All portfolios functionality can be reached from here on out.\n",
        "\n",
        "See here for relevant docs:\n",
        "* [Portfolios API Summary](https://docs.bayesline.com/0.12.1/_autosummary/bayesline.api.equity.AsyncPortfolioApi.html)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [],
      "source": [
        "portfolios_loader = bln.equity.portfolios"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Portfolio Sources\n",
        "\n",
        "A portfolio source is an isolated dataset that contains holdings information for different portfolios. This could be a system source (e.g. from a database) or user uploaded data.\n",
        "\n",
        "For each source it is guaranteed that the data is free of duplications and otherwise consistent.\n",
        "\n",
        "### Listing Available Portfolio Sources\n",
        "Below demonstrates how to obtain the list of available sources using the settings menu."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PortfolioSettingsMenu(sources=[], schemas=[])"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_loader.settings.available_settings()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Uploading a Portfolio Source\n",
        "\n",
        "A new portfolio source can be added using the *portfolios* uploader, which can be obtained through the `uploader` property (note that this is a shortcut for using `bln.equity.uploaders.get_data_type(\"portfolios\")`, which yields the same uploader).\n",
        "\n",
        "Given above showed no existing portfolio sources we should find that the uploader has no datasets."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [],
      "source": [
        "uploader = portfolios_loader.uploader"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[]"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "uploader.get_datasets()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Next let's create a new *portfolios* dataset and upload some sample data. For a more detailed walk through on the uploader infrastructure (including parsers, example inputs, versioning, etc.) see the **Bayesline Uploaders Tutorial**."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [],
      "source": [
        "demo_portfolio_dataset = uploader.get_or_create_dataset(\"demo-portfolios\")"
      ]
    },
    {
      "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: (6, 7)</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>currency</th><th>share_qty</th><th>nav</th></tr><tr><td>str</td><td>str</td><td>str</td><td>date</td><td>str</td><td>f64</td><td>i64</td></tr></thead><tbody><tr><td>&quot;Test-Portfolio&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>2026-01-01</td><td>null</td><td>null</td><td>100</td></tr><tr><td>&quot;Test-Portfolio&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;cusip9&quot;</td><td>2026-01-31</td><td>null</td><td>null</td><td>110</td></tr><tr><td>&quot;Test-Portfolio&quot;</td><td>&quot;2588173&quot;</td><td>&quot;sedol7&quot;</td><td>2026-01-15</td><td>null</td><td>null</td><td>200</td></tr><tr><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;85371710&quot;</td><td>&quot;cusip8&quot;</td><td>2026-01-01</td><td>null</td><td>null</td><td>50</td></tr><tr><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;85371710&quot;</td><td>&quot;cusip8&quot;</td><td>2026-01-13</td><td>null</td><td>null</td><td>55</td></tr><tr><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;85371710&quot;</td><td>&quot;cusip8&quot;</td><td>2026-02-15</td><td>null</td><td>null</td><td>54</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (6, 7)\n",
              "┌──────────────────┬───────────┬───────────────┬────────────┬──────────┬───────────┬─────┐\n",
              "│ portfolio_id     ┆ asset_id  ┆ asset_id_type ┆ date       ┆ currency ┆ share_qty ┆ nav │\n",
              "│ ---              ┆ ---       ┆ ---           ┆ ---        ┆ ---      ┆ ---       ┆ --- │\n",
              "│ str              ┆ str       ┆ str           ┆ date       ┆ str      ┆ f64       ┆ i64 │\n",
              "╞══════════════════╪═══════════╪═══════════════╪════════════╪══════════╪═══════════╪═════╡\n",
              "│ Test-Portfolio   ┆ 02079K305 ┆ cusip9        ┆ 2026-01-01 ┆ null     ┆ null      ┆ 100 │\n",
              "│ Test-Portfolio   ┆ 02079K305 ┆ cusip9        ┆ 2026-01-31 ┆ null     ┆ null      ┆ 110 │\n",
              "│ Test-Portfolio   ┆ 2588173   ┆ sedol7        ┆ 2026-01-15 ┆ null     ┆ null      ┆ 200 │\n",
              "│ Test-Portfolio-2 ┆ 85371710  ┆ cusip8        ┆ 2026-01-01 ┆ null     ┆ null      ┆ 50  │\n",
              "│ Test-Portfolio-2 ┆ 85371710  ┆ cusip8        ┆ 2026-01-13 ┆ null     ┆ null      ┆ 55  │\n",
              "│ Test-Portfolio-2 ┆ 85371710  ┆ cusip8        ┆ 2026-02-15 ┆ null     ┆ null      ┆ 54  │\n",
              "└──────────────────┴───────────┴───────────────┴────────────┴──────────┴───────────┴─────┘"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "df = pl.DataFrame({\n",
        "    \"portfolio_id\": [\n",
        "        \"Test-Portfolio\", \"Test-Portfolio\", \"Test-Portfolio\",\n",
        "        \"Test-Portfolio-2\", \"Test-Portfolio-2\", \"Test-Portfolio-2\",\n",
        "    ],\n",
        "    \"asset_id\": [\n",
        "        \"02079K305\", \"02079K305\", \"2588173\",\n",
        "        \"85371710\", \"85371710\", \"85371710\"\n",
        "    ],\n",
        "    \"asset_id_type\": [\n",
        "        \"cusip9\", \"cusip9\", \"sedol7\",\n",
        "        \"cusip8\", \"cusip8\", \"cusip8\"\n",
        "    ],\n",
        "    \"date\": [\n",
        "        dt.date(2026, 1, 1), dt.date(2026, 1, 31), dt.date(2026, 1, 15),\n",
        "        dt.date(2026, 1, 1), dt.date(2026, 1, 13), dt.date(2026, 2, 15),\n",
        "    ],\n",
        "    \"currency\": [None]*6,\n",
        "    \"share_qty\": [None]*6,\n",
        "    \"nav\": [\n",
        "        100, 110, 200,\n",
        "        50, 55, 54\n",
        "    ]\n",
        "}).with_columns(pl.col(\"currency\").cast(pl.String), pl.col(\"share_qty\").cast(pl.Float64))\n",
        "\n",
        "df"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "UploadCommitResult(version=1, committed_names=[])"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "demo_portfolio_dataset.fast_commit(df, mode=\"append\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Reading Portfolio Data\n",
        "\n",
        "Having uploaded a portfolio source we can now use the portfolio loader to obtain the portfolio."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PortfolioSettingsMenu(sources=['demo-portfolios'], schemas=[])"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_loader.settings.available_settings()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [],
      "source": [
        "portfolios_api = portfolios_loader.load(PortfolioSettings.from_source(\"demo-portfolios\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Why use a separate *Portfolios API* to obtain this data, as opposed to just using the uploader infrastructure to read the data back? \n",
        "The *Portfolios API* adds plenty of functionality that is specific to the domain of portfolios, e.g. coverage statistics, forward filling and drifting, etc.\n",
        "\n",
        "Below demonstrates this functionality."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "['Test-Portfolio', 'Test-Portfolio-2']"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_portfolio_names()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'Test-Portfolio': [datetime.date(2026, 1, 1),\n",
              "  datetime.date(2026, 1, 15),\n",
              "  datetime.date(2026, 1, 31)],\n",
              " 'Test-Portfolio-2': [datetime.date(2026, 1, 1),\n",
              "  datetime.date(2026, 1, 13),\n",
              "  datetime.date(2026, 2, 15)]}"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_dates()"
      ]
    },
    {
      "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: (6, 6)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>portfolio_group</th><th>portfolio_id</th><th>date</th><th>asset_id_type</th><th>input</th><th>bayesid</th></tr><tr><td>str</td><td>str</td><td>date</td><td>str</td><td>u32</td><td>u32</td></tr></thead><tbody><tr><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>2026-01-01</td><td>&quot;cusip9&quot;</td><td>1</td><td>1</td></tr><tr><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>2026-01-15</td><td>&quot;sedol7&quot;</td><td>1</td><td>1</td></tr><tr><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>2026-01-31</td><td>&quot;cusip9&quot;</td><td>1</td><td>1</td></tr><tr><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>2026-01-01</td><td>&quot;cusip8&quot;</td><td>1</td><td>0</td></tr><tr><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>2026-01-13</td><td>&quot;cusip8&quot;</td><td>1</td><td>0</td></tr><tr><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>2026-02-15</td><td>&quot;cusip8&quot;</td><td>1</td><td>0</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (6, 6)\n",
              "┌─────────────────┬──────────────────┬────────────┬───────────────┬───────┬─────────┐\n",
              "│ portfolio_group ┆ portfolio_id     ┆ date       ┆ asset_id_type ┆ input ┆ bayesid │\n",
              "│ ---             ┆ ---              ┆ ---        ┆ ---           ┆ ---   ┆ ---     │\n",
              "│ str             ┆ str              ┆ date       ┆ str           ┆ u32   ┆ u32     │\n",
              "╞═════════════════╪══════════════════╪════════════╪═══════════════╪═══════╪═════════╡\n",
              "│ demo-portfolios ┆ Test-Portfolio   ┆ 2026-01-01 ┆ cusip9        ┆ 1     ┆ 1       │\n",
              "│ demo-portfolios ┆ Test-Portfolio   ┆ 2026-01-15 ┆ sedol7        ┆ 1     ┆ 1       │\n",
              "│ demo-portfolios ┆ Test-Portfolio   ┆ 2026-01-31 ┆ cusip9        ┆ 1     ┆ 1       │\n",
              "│ demo-portfolios ┆ Test-Portfolio-2 ┆ 2026-01-01 ┆ cusip8        ┆ 1     ┆ 0       │\n",
              "│ demo-portfolios ┆ Test-Portfolio-2 ┆ 2026-01-13 ┆ cusip8        ┆ 1     ┆ 0       │\n",
              "│ demo-portfolios ┆ Test-Portfolio-2 ┆ 2026-02-15 ┆ cusip8        ┆ 1     ┆ 0       │\n",
              "└─────────────────┴──────────────────┴────────────┴───────────────┴───────┴─────────┘"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_coverage()"
      ]
    },
    {
      "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, 8)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_group</th><th>portfolio_id</th><th>input_asset_id_type</th><th>input_asset_id</th><th>currency</th><th>share_qty</th><th>nav</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>2026-01-01</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>99.999992</td></tr><tr><td>2026-01-15</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;sedol7&quot;</td><td>&quot;2588173&quot;</td><td>&quot;USD&quot;</td><td>0.437963</td><td>200.0</td></tr><tr><td>2026-01-31</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325444</td><td>110.0</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (3, 8)\n",
              "┌────────────┬────────────┬────────────┬────────────┬───────────┬──────────┬───────────┬───────────┐\n",
              "│ date       ┆ portfolio_ ┆ portfolio_ ┆ input_asse ┆ input_ass ┆ currency ┆ share_qty ┆ nav       │\n",
              "│ ---        ┆ group      ┆ id         ┆ t_id_type  ┆ et_id     ┆ ---      ┆ ---       ┆ ---       │\n",
              "│ date       ┆ ---        ┆ ---        ┆ ---        ┆ ---       ┆ str      ┆ f32       ┆ f32       │\n",
              "│            ┆ str        ┆ str        ┆ str        ┆ str       ┆          ┆           ┆           │\n",
              "╞════════════╪════════════╪════════════╪════════════╪═══════════╪══════════╪═══════════╪═══════════╡\n",
              "│ 2026-01-01 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 99.999992 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-01-15 ┆ demo-portf ┆ Test-Portf ┆ sedol7     ┆ 2588173   ┆ USD      ┆ 0.437963  ┆ 200.0     │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-01-31 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325444  ┆ 110.0     │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "└────────────┴────────────┴────────────┴────────────┴───────────┴──────────┴───────────┴───────────┘"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_portfolio(names=[\"Test-Portfolio\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note how above the ID space is the input ID space, i.e. the IDs that were uploaded. These IDs can be mapped to one of the supported target ID spaces. Note that if IDs cannot be mapped to output values will be `None`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'Test-Portfolio': ['bayesid'], 'Test-Portfolio-2': ['bayesid']}"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_id_types()"
      ]
    },
    {
      "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: (3, 10)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_group</th><th>portfolio_id</th><th>input_asset_id_type</th><th>input_asset_id</th><th>asset_id_type</th><th>asset_id</th><th>currency</th><th>share_qty</th><th>nav</th></tr><tr><td>date</td><td>str</td><td>str</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>2026-01-01</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;bayesid&quot;</td><td>&quot;ICA17F00B9&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>99.999992</td></tr><tr><td>2026-01-15</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;sedol7&quot;</td><td>&quot;2588173&quot;</td><td>&quot;bayesid&quot;</td><td>&quot;ICF982536B&quot;</td><td>&quot;USD&quot;</td><td>0.437963</td><td>200.0</td></tr><tr><td>2026-01-31</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;bayesid&quot;</td><td>&quot;ICA17F00B9&quot;</td><td>&quot;USD&quot;</td><td>0.325444</td><td>110.0</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (3, 10)\n",
              "┌───────────┬───────────┬───────────┬───────────┬───┬───────────┬──────────┬───────────┬───────────┐\n",
              "│ date      ┆ portfolio ┆ portfolio ┆ input_ass ┆ … ┆ asset_id  ┆ currency ┆ share_qty ┆ nav       │\n",
              "│ ---       ┆ _group    ┆ _id       ┆ et_id_typ ┆   ┆ ---       ┆ ---      ┆ ---       ┆ ---       │\n",
              "│ date      ┆ ---       ┆ ---       ┆ e         ┆   ┆ str       ┆ str      ┆ f32       ┆ f32       │\n",
              "│           ┆ str       ┆ str       ┆ ---       ┆   ┆           ┆          ┆           ┆           │\n",
              "│           ┆           ┆           ┆ str       ┆   ┆           ┆          ┆           ┆           │\n",
              "╞═══════════╪═══════════╪═══════════╪═══════════╪═══╪═══════════╪══════════╪═══════════╪═══════════╡\n",
              "│ 2026-01-0 ┆ demo-port ┆ Test-Port ┆ cusip9    ┆ … ┆ ICA17F00B ┆ USD      ┆ 0.319489  ┆ 99.999992 │\n",
              "│ 1         ┆ folios    ┆ folio     ┆           ┆   ┆ 9         ┆          ┆           ┆           │\n",
              "│ 2026-01-1 ┆ demo-port ┆ Test-Port ┆ sedol7    ┆ … ┆ ICF982536 ┆ USD      ┆ 0.437963  ┆ 200.0     │\n",
              "│ 5         ┆ folios    ┆ folio     ┆           ┆   ┆ B         ┆          ┆           ┆           │\n",
              "│ 2026-01-3 ┆ demo-port ┆ Test-Port ┆ cusip9    ┆ … ┆ ICA17F00B ┆ USD      ┆ 0.325444  ┆ 110.0     │\n",
              "│ 1         ┆ folios    ┆ folio     ┆           ┆   ┆ 9         ┆          ┆           ┆           │\n",
              "└───────────┴───────────┴───────────┴───────────┴───┴───────────┴──────────┴───────────┴───────────┘"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_portfolio(names=[\"Test-Portfolio\"], id_type=\"bayesid\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Forward Filling and Drift Correction\n",
        "\n",
        "The demo portfolio data was uploaded with a monthly frequency. Obtaining them back will yield the data unaltered. We can pass settings to automatically forward fill and drift correct the data. We do this through the `ffill` parameter."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "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: (90, 8)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_group</th><th>portfolio_id</th><th>input_asset_id_type</th><th>input_asset_id</th><th>currency</th><th>share_qty</th><th>nav</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>2026-01-01</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>99.999992</td></tr><tr><td>2026-01-02</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>100.686852</td></tr><tr><td>2026-01-03</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>100.686852</td></tr><tr><td>2026-01-04</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>100.686852</td></tr><tr><td>2026-01-05</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>101.130997</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>2026-03-27</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325667</td><td>89.343521</td></tr><tr><td>2026-03-28</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325667</td><td>89.343521</td></tr><tr><td>2026-03-29</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325667</td><td>89.343521</td></tr><tr><td>2026-03-30</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325667</td><td>89.069923</td></tr><tr><td>2026-03-31</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325667</td><td>93.648842</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (90, 8)\n",
              "┌────────────┬────────────┬────────────┬────────────┬───────────┬──────────┬───────────┬───────────┐\n",
              "│ date       ┆ portfolio_ ┆ portfolio_ ┆ input_asse ┆ input_ass ┆ currency ┆ share_qty ┆ nav       │\n",
              "│ ---        ┆ group      ┆ id         ┆ t_id_type  ┆ et_id     ┆ ---      ┆ ---       ┆ ---       │\n",
              "│ date       ┆ ---        ┆ ---        ┆ ---        ┆ ---       ┆ str      ┆ f32       ┆ f32       │\n",
              "│            ┆ str        ┆ str        ┆ str        ┆ str       ┆          ┆           ┆           │\n",
              "╞════════════╪════════════╪════════════╪════════════╪═══════════╪══════════╪═══════════╪═══════════╡\n",
              "│ 2026-01-01 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 99.999992 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-01-02 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 100.68685 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆ 2         │\n",
              "│ 2026-01-03 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 100.68685 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆ 2         │\n",
              "│ 2026-01-04 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 100.68685 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆ 2         │\n",
              "│ 2026-01-05 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 101.13099 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆ 7         │\n",
              "│ …          ┆ …          ┆ …          ┆ …          ┆ …         ┆ …        ┆ …         ┆ …         │\n",
              "│ 2026-03-27 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325667  ┆ 89.343521 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-03-28 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325667  ┆ 89.343521 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-03-29 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325667  ┆ 89.343521 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-03-30 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325667  ┆ 89.069923 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-03-31 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325667  ┆ 93.648842 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "└────────────┴────────────┴────────────┴────────────┴───────────┴──────────┴───────────┴───────────┘"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_portfolio(names=[\"Test-Portfolio\"], ffill=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Funds of Funds\n",
        "\n",
        "We can represent fund of funds structures easily by using the portfolio-id as the respective asset ids and `portfolio_id` as the asset id type.\n",
        "\n",
        "Below we're uploading a fund of funds portfolios to our existing dataset by *appending* it to the existing data."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "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: (4, 7)</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>currency</th><th>share_qty</th><th>nav</th></tr><tr><td>str</td><td>str</td><td>str</td><td>date</td><td>str</td><td>f64</td><td>f64</td></tr></thead><tbody><tr><td>&quot;My-FOF&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;portfolio_id&quot;</td><td>2026-01-01</td><td>null</td><td>null</td><td>0.5</td></tr><tr><td>&quot;My-FOF&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;portfolio_id&quot;</td><td>2026-01-31</td><td>null</td><td>null</td><td>0.55</td></tr><tr><td>&quot;My-FOF&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;portfolio_id&quot;</td><td>2026-01-01</td><td>null</td><td>null</td><td>0.5</td></tr><tr><td>&quot;My-FOF&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;portfolio_id&quot;</td><td>2026-01-31</td><td>null</td><td>null</td><td>0.45</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (4, 7)\n",
              "┌──────────────┬──────────────────┬───────────────┬────────────┬──────────┬───────────┬──────┐\n",
              "│ portfolio_id ┆ asset_id         ┆ asset_id_type ┆ date       ┆ currency ┆ share_qty ┆ nav  │\n",
              "│ ---          ┆ ---              ┆ ---           ┆ ---        ┆ ---      ┆ ---       ┆ ---  │\n",
              "│ str          ┆ str              ┆ str           ┆ date       ┆ str      ┆ f64       ┆ f64  │\n",
              "╞══════════════╪══════════════════╪═══════════════╪════════════╪══════════╪═══════════╪══════╡\n",
              "│ My-FOF       ┆ Test-Portfolio   ┆ portfolio_id  ┆ 2026-01-01 ┆ null     ┆ null      ┆ 0.5  │\n",
              "│ My-FOF       ┆ Test-Portfolio   ┆ portfolio_id  ┆ 2026-01-31 ┆ null     ┆ null      ┆ 0.55 │\n",
              "│ My-FOF       ┆ Test-Portfolio-2 ┆ portfolio_id  ┆ 2026-01-01 ┆ null     ┆ null      ┆ 0.5  │\n",
              "│ My-FOF       ┆ Test-Portfolio-2 ┆ portfolio_id  ┆ 2026-01-31 ┆ null     ┆ null      ┆ 0.45 │\n",
              "└──────────────┴──────────────────┴───────────────┴────────────┴──────────┴───────────┴──────┘"
            ]
          },
          "execution_count": 19,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "fof_portfolio_df = pl.DataFrame({\n",
        "    \"portfolio_id\": [\n",
        "        \"My-FOF\", \"My-FOF\", \"My-FOF\", \"My-FOF\"\n",
        "    ],\n",
        "    \"asset_id\": [\n",
        "        \"Test-Portfolio\", \"Test-Portfolio\", \"Test-Portfolio-2\", \"Test-Portfolio-2\"\n",
        "    ],\n",
        "    \"asset_id_type\": [\n",
        "        \"portfolio_id\", \"portfolio_id\", \"portfolio_id\", \"portfolio_id\"\n",
        "    ],\n",
        "    \"date\": [\n",
        "        dt.date(2026, 1, 1), dt.date(2026, 1, 31), dt.date(2026, 1, 1), dt.date(2026, 1, 31)\n",
        "    ],\n",
        "    \"currency\": [None]*4,\n",
        "    \"share_qty\": [None]*4,\n",
        "    \"nav\": [\n",
        "        .5, .55, .5, .45\n",
        "    ]\n",
        "}).with_columns(pl.col(\"currency\").cast(pl.String), pl.col(\"share_qty\").cast(pl.Float64))\n",
        "\n",
        "fof_portfolio_df"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "UploadCommitResult(version=2, committed_names=[])"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "demo_portfolio_dataset.fast_commit(fof_portfolio_df, mode=\"append\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Below we're obtaining the fund of funds portfolio without any alterations."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "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: (2, 8)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_group</th><th>portfolio_id</th><th>input_asset_id_type</th><th>input_asset_id</th><th>currency</th><th>share_qty</th><th>nav</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>2026-01-01</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;portfolio_id&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>null</td><td>0.005</td><td>0.5</td></tr><tr><td>2026-01-31</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;portfolio_id&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>null</td><td>0.005</td><td>0.55</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (2, 8)\n",
              "┌────────────┬─────────────┬─────────────┬─────────────┬─────────────┬──────────┬───────────┬──────┐\n",
              "│ date       ┆ portfolio_g ┆ portfolio_i ┆ input_asset ┆ input_asset ┆ currency ┆ share_qty ┆ nav  │\n",
              "│ ---        ┆ roup        ┆ d           ┆ _id_type    ┆ _id         ┆ ---      ┆ ---       ┆ ---  │\n",
              "│ date       ┆ ---         ┆ ---         ┆ ---         ┆ ---         ┆ str      ┆ f32       ┆ f32  │\n",
              "│            ┆ str         ┆ str         ┆ str         ┆ str         ┆          ┆           ┆      │\n",
              "╞════════════╪═════════════╪═════════════╪═════════════╪═════════════╪══════════╪═══════════╪══════╡\n",
              "│ 2026-01-01 ┆ demo-portfo ┆ My-FOF      ┆ portfolio_i ┆ Test-Portfo ┆ null     ┆ 0.005     ┆ 0.5  │\n",
              "│            ┆ lios        ┆             ┆ d           ┆ lio         ┆          ┆           ┆      │\n",
              "│ 2026-01-31 ┆ demo-portfo ┆ My-FOF      ┆ portfolio_i ┆ Test-Portfo ┆ null     ┆ 0.005     ┆ 0.55 │\n",
              "│            ┆ lios        ┆             ┆ d           ┆ lio         ┆          ┆           ┆      │\n",
              "└────────────┴─────────────┴─────────────┴─────────────┴─────────────┴──────────┴───────────┴──────┘"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api = portfolios_loader.load(\n",
        "    PortfolioSettings.from_source(\"demo-portfolios\")\n",
        ")\n",
        "portfolios_api.get_portfolio(names=[\"My-FOF\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can also configure to unpack the fund of funds structure and forward fill with drift. This is an essential piece of functioanlity to provide fund of fund level analytics. When unpacking, the target currency is required."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "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: (90, 8)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_group</th><th>portfolio_id</th><th>input_asset_id_type</th><th>input_asset_id</th><th>currency</th><th>share_qty</th><th>nav</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>2026-01-01</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001597</td><td>0.5</td></tr><tr><td>2026-01-02</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001597</td><td>0.503434</td></tr><tr><td>2026-01-03</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001597</td><td>0.503434</td></tr><tr><td>2026-01-04</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001597</td><td>0.503434</td></tr><tr><td>2026-01-05</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001597</td><td>0.505655</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>2026-03-27</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001628</td><td>0.446718</td></tr><tr><td>2026-03-28</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001628</td><td>0.446718</td></tr><tr><td>2026-03-29</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001628</td><td>0.446718</td></tr><tr><td>2026-03-30</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001628</td><td>0.44535</td></tr><tr><td>2026-03-31</td><td>&quot;demo-portfolios&quot;</td><td>&quot;My-FOF&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.001628</td><td>0.468244</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (90, 8)\n",
              "┌────────────┬────────────┬────────────┬────────────┬────────────┬──────────┬───────────┬──────────┐\n",
              "│ date       ┆ portfolio_ ┆ portfolio_ ┆ input_asse ┆ input_asse ┆ currency ┆ share_qty ┆ nav      │\n",
              "│ ---        ┆ group      ┆ id         ┆ t_id_type  ┆ t_id       ┆ ---      ┆ ---       ┆ ---      │\n",
              "│ date       ┆ ---        ┆ ---        ┆ ---        ┆ ---        ┆ str      ┆ f32       ┆ f32      │\n",
              "│            ┆ str        ┆ str        ┆ str        ┆ str        ┆          ┆           ┆          │\n",
              "╞════════════╪════════════╪════════════╪════════════╪════════════╪══════════╪═══════════╪══════════╡\n",
              "│ 2026-01-01 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001597  ┆ 0.5      │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-01-02 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001597  ┆ 0.503434 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-01-03 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001597  ┆ 0.503434 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-01-04 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001597  ┆ 0.503434 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-01-05 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001597  ┆ 0.505655 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ …          ┆ …          ┆ …          ┆ …          ┆ …          ┆ …        ┆ …         ┆ …        │\n",
              "│ 2026-03-27 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001628  ┆ 0.446718 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-03-28 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001628  ┆ 0.446718 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-03-29 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001628  ┆ 0.446718 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-03-30 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001628  ┆ 0.44535  │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "│ 2026-03-31 ┆ demo-portf ┆ My-FOF     ┆ cusip9     ┆ 02079K305  ┆ USD      ┆ 0.001628  ┆ 0.468244 │\n",
              "│            ┆ olios      ┆            ┆            ┆            ┆          ┆           ┆          │\n",
              "└────────────┴────────────┴────────────┴────────────┴────────────┴──────────┴───────────┴──────────┘"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api = portfolios_loader.load(PortfolioSettings.from_source(\"demo-portfolios\"))\n",
        "portfolios_api.get_portfolio(names=[\"My-FOF\"], ffill=True, unpack=True, currency=\"USD\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Portfolio Schemas\n",
        "\n",
        "*Portfolio Schemas* are an advanced topic that sit on top of the *Portfolio Sources* we previously explored. \n",
        "\n",
        "Using *Portfolio Schemas* we can cherry-pick which portfolios should be sourced from what underlying portfolio source. We do this by providing a mapping from `portfolio id -> portfolio source`. This flexibility allows for a powerful means to arbitrarily override portfolio holdings while keeping everything else the same, e.g. to perform what-if analyses.\n",
        "\n",
        "Below we are creating a new `what-if` portfolio dataset where we override holdings for our `Test-Portfolio-2`. We then create a *Portfolio Schema* where we source `Test-Portfolio-2` from our new dataset, keeping the rest the same. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {},
      "outputs": [],
      "source": [
        "what_if_dataset = uploader.create_dataset(\"what-if\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "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: (2, 7)</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>currency</th><th>share_qty</th><th>nav</th></tr><tr><td>str</td><td>str</td><td>str</td><td>date</td><td>str</td><td>f64</td><td>i64</td></tr></thead><tbody><tr><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>2026-01-01</td><td>null</td><td>null</td><td>100</td></tr><tr><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;cusip8&quot;</td><td>2026-01-31</td><td>null</td><td>null</td><td>125</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (2, 7)\n",
              "┌──────────────────┬──────────┬───────────────┬────────────┬──────────┬───────────┬─────┐\n",
              "│ portfolio_id     ┆ asset_id ┆ asset_id_type ┆ date       ┆ currency ┆ share_qty ┆ nav │\n",
              "│ ---              ┆ ---      ┆ ---           ┆ ---        ┆ ---      ┆ ---       ┆ --- │\n",
              "│ str              ┆ str      ┆ str           ┆ date       ┆ str      ┆ f64       ┆ i64 │\n",
              "╞══════════════════╪══════════╪═══════════════╪════════════╪══════════╪═══════════╪═════╡\n",
              "│ Test-Portfolio-2 ┆ 67066G10 ┆ cusip8        ┆ 2026-01-01 ┆ null     ┆ null      ┆ 100 │\n",
              "│ Test-Portfolio-2 ┆ 67066G10 ┆ cusip8        ┆ 2026-01-31 ┆ null     ┆ null      ┆ 125 │\n",
              "└──────────────────┴──────────┴───────────────┴────────────┴──────────┴───────────┴─────┘"
            ]
          },
          "execution_count": 24,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "what_if_portfolio_df = pl.DataFrame({\n",
        "    \"portfolio_id\": [\"Test-Portfolio-2\", \"Test-Portfolio-2\"],\n",
        "    \"asset_id\": [\"67066G10\", \"67066G10\"],\n",
        "    \"asset_id_type\": [\"cusip8\", \"cusip8\"],\n",
        "    \"date\": [dt.date(2026, 1, 1), dt.date(2026, 1, 31)],\n",
        "    \"currency\": [None]*2,\n",
        "    \"share_qty\": [None]*2,\n",
        "    \"nav\": [100, 125],\n",
        "}).with_columns(pl.col(\"currency\").cast(pl.String), pl.col(\"share_qty\").cast(pl.Float64))\n",
        "\n",
        "what_if_portfolio_df"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "UploadCommitResult(version=1, committed_names=[])"
            ]
          },
          "execution_count": 25,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "what_if_dataset.fast_commit(what_if_portfolio_df, mode=\"append\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PortfolioSettingsMenu(sources=['demo-portfolios', 'what-if'], schemas=[])"
            ]
          },
          "execution_count": 26,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_loader.settings.available_settings()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "metadata": {},
      "outputs": [],
      "source": [
        "schema = PortfolioOrganizerSettings(\n",
        "    enabled_portfolios={\n",
        "        \"Test-Portfolio\": \"demo-portfolios\",\n",
        "        \"Test-Portfolio-2\": \"what-if\"\n",
        "    }\n",
        ")\n",
        "\n",
        "portfolios_api = portfolios_loader.load(PortfolioSettings(portfolio_schema=schema))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "['Test-Portfolio', 'Test-Portfolio-2']"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_portfolio_names()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note below how `Test-Portfolio-2` has the updated values."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "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: (180, 8)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>date</th><th>portfolio_group</th><th>portfolio_id</th><th>input_asset_id_type</th><th>input_asset_id</th><th>currency</th><th>share_qty</th><th>nav</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>2026-01-01</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>99.999992</td></tr><tr><td>2026-01-01</td><td>&quot;what-if&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;USD&quot;</td><td>0.536193</td><td>99.999992</td></tr><tr><td>2026-01-02</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>100.686852</td></tr><tr><td>2026-01-02</td><td>&quot;what-if&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;USD&quot;</td><td>0.536193</td><td>101.260048</td></tr><tr><td>2026-01-03</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.319489</td><td>100.686852</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>2026-03-29</td><td>&quot;what-if&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;USD&quot;</td><td>0.65404</td><td>109.564827</td></tr><tr><td>2026-03-30</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325667</td><td>89.069923</td></tr><tr><td>2026-03-30</td><td>&quot;what-if&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;USD&quot;</td><td>0.65404</td><td>108.027832</td></tr><tr><td>2026-03-31</td><td>&quot;demo-portfolios&quot;</td><td>&quot;Test-Portfolio&quot;</td><td>&quot;cusip9&quot;</td><td>&quot;02079K305&quot;</td><td>&quot;USD&quot;</td><td>0.325667</td><td>93.648842</td></tr><tr><td>2026-03-31</td><td>&quot;what-if&quot;</td><td>&quot;Test-Portfolio-2&quot;</td><td>&quot;cusip8&quot;</td><td>&quot;67066G10&quot;</td><td>&quot;USD&quot;</td><td>0.65404</td><td>114.064621</td></tr></tbody></table></div>"
            ],
            "text/plain": [
              "shape: (180, 8)\n",
              "┌────────────┬────────────┬────────────┬────────────┬───────────┬──────────┬───────────┬───────────┐\n",
              "│ date       ┆ portfolio_ ┆ portfolio_ ┆ input_asse ┆ input_ass ┆ currency ┆ share_qty ┆ nav       │\n",
              "│ ---        ┆ group      ┆ id         ┆ t_id_type  ┆ et_id     ┆ ---      ┆ ---       ┆ ---       │\n",
              "│ date       ┆ ---        ┆ ---        ┆ ---        ┆ ---       ┆ str      ┆ f32       ┆ f32       │\n",
              "│            ┆ str        ┆ str        ┆ str        ┆ str       ┆          ┆           ┆           │\n",
              "╞════════════╪════════════╪════════════╪════════════╪═══════════╪══════════╪═══════════╪═══════════╡\n",
              "│ 2026-01-01 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 99.999992 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-01-01 ┆ what-if    ┆ Test-Portf ┆ cusip8     ┆ 67066G10  ┆ USD      ┆ 0.536193  ┆ 99.999992 │\n",
              "│            ┆            ┆ olio-2     ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-01-02 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 100.68685 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆ 2         │\n",
              "│ 2026-01-02 ┆ what-if    ┆ Test-Portf ┆ cusip8     ┆ 67066G10  ┆ USD      ┆ 0.536193  ┆ 101.26004 │\n",
              "│            ┆            ┆ olio-2     ┆            ┆           ┆          ┆           ┆ 8         │\n",
              "│ 2026-01-03 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.319489  ┆ 100.68685 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆ 2         │\n",
              "│ …          ┆ …          ┆ …          ┆ …          ┆ …         ┆ …        ┆ …         ┆ …         │\n",
              "│ 2026-03-29 ┆ what-if    ┆ Test-Portf ┆ cusip8     ┆ 67066G10  ┆ USD      ┆ 0.65404   ┆ 109.56482 │\n",
              "│            ┆            ┆ olio-2     ┆            ┆           ┆          ┆           ┆ 7         │\n",
              "│ 2026-03-30 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325667  ┆ 89.069923 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-03-30 ┆ what-if    ┆ Test-Portf ┆ cusip8     ┆ 67066G10  ┆ USD      ┆ 0.65404   ┆ 108.02783 │\n",
              "│            ┆            ┆ olio-2     ┆            ┆           ┆          ┆           ┆ 2         │\n",
              "│ 2026-03-31 ┆ demo-portf ┆ Test-Portf ┆ cusip9     ┆ 02079K305 ┆ USD      ┆ 0.325667  ┆ 93.648842 │\n",
              "│            ┆ olios      ┆ olio       ┆            ┆           ┆          ┆           ┆           │\n",
              "│ 2026-03-31 ┆ what-if    ┆ Test-Portf ┆ cusip8     ┆ 67066G10  ┆ USD      ┆ 0.65404   ┆ 114.06462 │\n",
              "│            ┆            ┆ olio-2     ┆            ┆           ┆          ┆           ┆ 1         │\n",
              "└────────────┴────────────┴────────────┴────────────┴───────────┴──────────┴───────────┴───────────┘"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api.get_portfolio(names=['Test-Portfolio', \"Test-Portfolio-2\"], ffill=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Saving the Schema\n",
        "\n",
        "We can also save a portfolio schema for later use. \n",
        "\n",
        "Below we demonstrate how to save the schema we previosly created and then use downstream."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "1"
            ]
          },
          "execution_count": 30,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_loader.organizer_settings.save(\"my-schema\", schema)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "['Test-Portfolio', 'Test-Portfolio-2']"
            ]
          },
          "execution_count": 31,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "portfolios_api = portfolios_loader.load(\n",
        "    PortfolioSettings(portfolio_schema=\"my-schema\")\n",
        ")\n",
        "\n",
        "portfolios_api.get_portfolio_names()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Housekeeping\n",
        "\n",
        "Lastly, we clean up by deleting our portfolio datasets."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "metadata": {},
      "outputs": [],
      "source": [
        "portfolios_loader.organizer_settings.delete(\"my-schema\")\n",
        "uploader.get_dataset(\"demo-portfolios\").destroy()\n",
        "uploader.get_dataset(\"what-if\").destroy()"
      ]
    }
  ],
  "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.15"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}