{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# 📘 Fincept Notebook — Portfolio Weights & Returns\n",
        "\n",
        "**Portfolio · Beginner · ~12 min · Standard library only**\n",
        "\n",
        "A portfolio's weights — each holding's share of total market value — drive its overall return and risk. After a year in which holdings move differently, those weights drift away from your targets, so you rebalance: sell what grew too big, buy what shrank. This notebook walks the full loop on a simple four-asset book.\n",
        "\n",
        "**What you'll learn**\n",
        "- How to turn market values into portfolio weights\n",
        "- How to compute a weighted (blended) portfolio return\n",
        "- How weights drift after a year, and the exact trades to rebalance to target\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. The portfolio\n",
        "\n",
        "Four holdings with their current market values and the return each posted over the past year. Every code cell re-embeds this data so it runs on its own.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "Holdings & weights"
      },
      "outputs": [],
      "source": [
        "# (ticker, market value $, annual return)\n",
        "holdings = [\n",
        "    (\"EQTY\", 40000.0,  0.18),   # equities\n",
        "    (\"BOND\", 30000.0,  0.04),   # bonds\n",
        "    (\"GOLD\", 20000.0, -0.06),   # gold\n",
        "    (\"CASH\", 10000.0,  0.05),   # cash / money market\n",
        "]\n",
        "\n",
        "total = sum(mv for _, mv, _ in holdings)\n",
        "\n",
        "print(f\"{'Asset':<6} {'Value':>12} {'Weight':>9} {'Return':>9}\")\n",
        "print(\"-\" * 38)\n",
        "for tkr, mv, ret in holdings:\n",
        "    w = mv / total\n",
        "    print(f\"{tkr:<6} {mv:>12,.2f} {w:>8.2%} {ret:>+9.2%}\")\n",
        "print(\"-\" * 38)\n",
        "print(f\"{'TOTAL':<6} {total:>12,.2f} {1.0:>8.2%}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. Weighted portfolio return\n",
        "\n",
        "The portfolio return is the sum of each weight times that asset's return — not a plain average. Each holding contributes in proportion to its size, so the big equity sleeve dominates.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "Weighted return"
      },
      "outputs": [],
      "source": [
        "holdings = [\n",
        "    (\"EQTY\", 40000.0,  0.18),\n",
        "    (\"BOND\", 30000.0,  0.04),\n",
        "    (\"GOLD\", 20000.0, -0.06),\n",
        "    (\"CASH\", 10000.0,  0.05),\n",
        "]\n",
        "\n",
        "total = sum(mv for _, mv, _ in holdings)\n",
        "\n",
        "print(f\"{'Asset':<6} {'Weight':>9} {'Return':>9} {'Contribution':>13}\")\n",
        "print(\"-\" * 40)\n",
        "port_return = 0.0\n",
        "for tkr, mv, ret in holdings:\n",
        "    w = mv / total\n",
        "    contrib = w * ret\n",
        "    port_return += contrib\n",
        "    print(f\"{tkr:<6} {w:>8.2%} {ret:>+9.2%} {contrib:>+13.4%}\")\n",
        "print(\"-\" * 40)\n",
        "print(f\"{'PORTFOLIO':<6} {'':>9} {'':>9} {port_return:>+13.4%}\")\n",
        "\n",
        "start_value = total\n",
        "end_value = start_value * (1 + port_return)\n",
        "print(f\"\\nStart value : ${start_value:,.2f}\")\n",
        "print(f\"End value   : ${end_value:,.2f}\")\n",
        "print(f\"Gain        : ${end_value - start_value:,.2f}  ({port_return:+.2%})\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. Weight drift after a year\n",
        "\n",
        "Because each asset returned something different, after a year the new market values imply **new weights**. The winners now take up more of the book than your target allocation, the losers less. We compare drifted weights against the targets.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "Drift vs target"
      },
      "outputs": [],
      "source": [
        "holdings = [\n",
        "    (\"EQTY\", 40000.0,  0.18),\n",
        "    (\"BOND\", 30000.0,  0.04),\n",
        "    (\"GOLD\", 20000.0, -0.06),\n",
        "    (\"CASH\", 10000.0,  0.05),\n",
        "]\n",
        "# Target allocation we want to hold (sums to 1.0)\n",
        "targets = {\"EQTY\": 0.40, \"BOND\": 0.30, \"GOLD\": 0.20, \"CASH\": 0.10}\n",
        "\n",
        "start_total = sum(mv for _, mv, _ in holdings)\n",
        "# market value after a year of returns\n",
        "ending = [(t, mv * (1 + r)) for t, mv, r in holdings]\n",
        "end_total = sum(mv for _, mv in ending)\n",
        "\n",
        "print(f\"{'Asset':<6} {'EndValue':>12} {'NewWt':>8} {'Target':>8} {'Drift':>8}\")\n",
        "print(\"-\" * 46)\n",
        "for tkr, mv in ending:\n",
        "    new_w = mv / end_total\n",
        "    tgt = targets[tkr]\n",
        "    drift = new_w - tgt\n",
        "    print(f\"{tkr:<6} {mv:>12,.2f} {new_w:>7.2%} {tgt:>7.2%} {drift:>+8.2%}\")\n",
        "print(\"-\" * 46)\n",
        "print(f\"{'TOTAL':<6} {end_total:>12,.2f}\")\n",
        "print(\"\\nPositive drift = now OVER-weight (it outperformed); negative = under-weight.\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. Trades to rebalance\n",
        "\n",
        "To return to target, each holding's target dollar value is `target_weight × end_total`. The trade is `target_value − current_value`: a positive number means **buy**, negative means **sell**. The trades net to roughly zero — we fund buys by selling the over-weight winners.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "Rebalancing trades"
      },
      "outputs": [],
      "source": [
        "holdings = [\n",
        "    (\"EQTY\", 40000.0,  0.18),\n",
        "    (\"BOND\", 30000.0,  0.04),\n",
        "    (\"GOLD\", 20000.0, -0.06),\n",
        "    (\"CASH\", 10000.0,  0.05),\n",
        "]\n",
        "targets = {\"EQTY\": 0.40, \"BOND\": 0.30, \"GOLD\": 0.20, \"CASH\": 0.10}\n",
        "\n",
        "ending = [(t, mv * (1 + r)) for t, mv, r in holdings]\n",
        "end_total = sum(mv for _, mv in ending)\n",
        "\n",
        "print(f\"{'Asset':<6} {'Current':>12} {'TargetVal':>12} {'Trade':>12} {'Action':>7}\")\n",
        "print(\"-\" * 54)\n",
        "total_trade = 0.0\n",
        "for tkr, mv in ending:\n",
        "    target_val = targets[tkr] * end_total\n",
        "    trade = target_val - mv\n",
        "    total_trade += trade\n",
        "    action = \"BUY\" if trade > 0 else (\"SELL\" if trade < 0 else \"HOLD\")\n",
        "    print(f\"{tkr:<6} {mv:>12,.2f} {target_val:>12,.2f} {trade:>+12,.2f} {action:>7}\")\n",
        "print(\"-\" * 54)\n",
        "print(f\"{'NET':<6} {end_total:>12,.2f} {end_total:>12,.2f} {total_trade:>+12,.2f}\")\n",
        "print(\"\\nNet trade is ~0: rebalancing is self-funding. Sell the over-weight winners,\")\n",
        "print(\"use the proceeds to top up the under-weight holdings, and weights return to target.\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "---\n",
        "*— Fincept Notebook · part of Fincept Terminal. Edit any cell and press Ctrl+Enter to run.*\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
