{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# 📘 Fincept Notebook — Dividend Growth & DRIP Compounding\n",
        "\n",
        "**Investing · Intermediate · ~18 min · pandas + numpy**\n",
        "\n",
        "A dividend reinvestment plan (DRIP) automatically uses every dividend payment to buy more shares. Those extra shares then pay their own dividends, which buy still more shares — the snowball that powers long-run equity returns. This notebook models a single dividend-growth stock over 25 years and measures exactly how much the 'reinvest' decision is worth versus taking the cash.\n",
        "\n",
        "**What you'll learn**\n",
        "- How to project shares, price, and dividend-per-share forward year by year\n",
        "- How to build the full DRIP table in pandas and read yield-on-cost\n",
        "- The size of the compounding gap between reinvesting and spending dividends\n",
        "\n",
        "> **Requires** `pandas` and `numpy` (bundled with Fincept Terminal's Python environment).\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. The inputs\n",
        "\n",
        "We model one position with five assumptions: starting shares, starting price, annual price appreciation, the starting dividend yield, and how fast the dividend grows each year. Every code cell re-embeds these so it runs independently.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "Inputs & year-1 math"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "    import numpy as np\n",
        "    import pandas as pd\n",
        "except ImportError:\n",
        "    raise SystemExit(\"Fincept Notebook needs pandas + numpy — install with: pip install pandas numpy\")\n",
        "\n",
        "np.random.seed(7)\n",
        "\n",
        "# --- Model inputs (edit these) ---\n",
        "START_SHARES   = 100.0    # shares owned at year 0\n",
        "START_PRICE    = 50.00    # price per share at year 0 ($)\n",
        "PRICE_GROWTH   = 0.06     # annual price appreciation (6%)\n",
        "START_YIELD    = 0.030    # starting dividend yield (3.0% of price)\n",
        "DIV_GROWTH     = 0.07     # annual dividend-per-share growth (7%)\n",
        "YEARS          = 25\n",
        "\n",
        "start_div_per_share = START_PRICE * START_YIELD\n",
        "cost_basis = START_SHARES * START_PRICE\n",
        "\n",
        "print(\"Starting position\")\n",
        "print(f\"  Shares             : {START_SHARES:,.2f}\")\n",
        "print(f\"  Price / share      : ${START_PRICE:,.2f}\")\n",
        "print(f\"  Market value       : ${cost_basis:,.2f}\")\n",
        "print(f\"  Dividend / share   : ${start_div_per_share:,.3f}  ({START_YIELD:.1%} yield)\")\n",
        "print(f\"  Year-1 dividends   : ${START_SHARES * start_div_per_share:,.2f}\")\n",
        "print()\n",
        "print(\"Growth assumptions\")\n",
        "print(f\"  Price appreciation : {PRICE_GROWTH:.1%} / yr\")\n",
        "print(f\"  Dividend growth    : {DIV_GROWTH:.1%} / yr\")\n",
        "print(f\"  Horizon            : {YEARS} years\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. Build the DRIP table\n",
        "\n",
        "Each year: the dividend per share grows, dividends paid = shares × dividend/share, those dividends buy new shares at **that year's price**, and share count rises. We capture every year in a DataFrame and also compute **yield-on-cost** — this year's dividend income divided by the original cost basis.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "DRIP year-by-year table"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "    import numpy as np\n",
        "    import pandas as pd\n",
        "except ImportError:\n",
        "    raise SystemExit(\"Fincept Notebook needs pandas + numpy — install with: pip install pandas numpy\")\n",
        "\n",
        "np.random.seed(7)\n",
        "\n",
        "START_SHARES, START_PRICE = 100.0, 50.00\n",
        "PRICE_GROWTH, START_YIELD, DIV_GROWTH, YEARS = 0.06, 0.030, 0.07, 25\n",
        "\n",
        "cost_basis = START_SHARES * START_PRICE\n",
        "div_per_share = START_PRICE * START_YIELD\n",
        "shares = START_SHARES\n",
        "price = START_PRICE\n",
        "\n",
        "rows = []\n",
        "for year in range(1, YEARS + 1):\n",
        "    # price and dividend grow at the start of each year\n",
        "    price = price * (1 + PRICE_GROWTH)\n",
        "    div_per_share = div_per_share * (1 + DIV_GROWTH)\n",
        "\n",
        "    dividends = shares * div_per_share\n",
        "    new_shares = dividends / price          # reinvest at this year's price\n",
        "    shares += new_shares\n",
        "    value = shares * price\n",
        "    yield_on_cost = (shares * div_per_share) / cost_basis\n",
        "\n",
        "    rows.append({\n",
        "        \"Year\": year,\n",
        "        \"Price\": round(price, 2),\n",
        "        \"Div/Share\": round(div_per_share, 4),\n",
        "        \"Dividends\": round(dividends, 2),\n",
        "        \"NewShares\": round(new_shares, 4),\n",
        "        \"Shares\": round(shares, 4),\n",
        "        \"Value\": round(value, 2),\n",
        "        \"YieldOnCost\": round(yield_on_cost * 100, 2),\n",
        "    })\n",
        "\n",
        "df = pd.DataFrame(rows)\n",
        "# Show first 5 and last 5 years to keep the table readable\n",
        "show = pd.concat([df.head(5), df.tail(5)])\n",
        "print(\"DRIP projection (years 1-5 and 21-25):\\n\")\n",
        "print(show.to_string(index=False))\n",
        "\n",
        "print(f\"\\nShares grew from {START_SHARES:,.1f} to {df['Shares'].iloc[-1]:,.1f} \"\n",
        "      f\"(+{df['Shares'].iloc[-1] / START_SHARES - 1:.0%}) purely from reinvested dividends.\")\n",
        "print(f\"Yield-on-cost climbed from {START_YIELD:.1%} to {df['YieldOnCost'].iloc[-1]:.1f}%.\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. Reinvest vs take the cash\n",
        "\n",
        "Now the headline comparison. The **WITH reinvestment** path is the DRIP above. The **WITHOUT** path holds the original shares forever and pockets each dividend as cash; we sum that cash and add it to the unchanged stock value. Same business, same dividends — only the reinvest decision differs.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "DRIP vs cash dividends"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "    import numpy as np\n",
        "    import pandas as pd\n",
        "except ImportError:\n",
        "    raise SystemExit(\"Fincept Notebook needs pandas + numpy — install with: pip install pandas numpy\")\n",
        "\n",
        "np.random.seed(7)\n",
        "\n",
        "START_SHARES, START_PRICE = 100.0, 50.00\n",
        "PRICE_GROWTH, START_YIELD, DIV_GROWTH, YEARS = 0.06, 0.030, 0.07, 25\n",
        "\n",
        "cost_basis = START_SHARES * START_PRICE\n",
        "\n",
        "# --- WITH reinvestment (DRIP) ---\n",
        "shares_drip = START_SHARES\n",
        "price = START_PRICE\n",
        "dps = START_PRICE * START_YIELD\n",
        "for _ in range(YEARS):\n",
        "    price *= (1 + PRICE_GROWTH)\n",
        "    dps  *= (1 + DIV_GROWTH)\n",
        "    shares_drip += (shares_drip * dps) / price\n",
        "drip_value = shares_drip * price\n",
        "\n",
        "# --- WITHOUT reinvestment (cash out) ---\n",
        "price = START_PRICE\n",
        "dps = START_PRICE * START_YIELD\n",
        "cash_collected = 0.0\n",
        "for _ in range(YEARS):\n",
        "    price *= (1 + PRICE_GROWTH)\n",
        "    dps  *= (1 + DIV_GROWTH)\n",
        "    cash_collected += START_SHARES * dps      # shares never change\n",
        "stock_value_nocash = START_SHARES * price\n",
        "nocash_total = stock_value_nocash + cash_collected\n",
        "\n",
        "summary = pd.DataFrame([\n",
        "    {\"Strategy\": \"WITH reinvest (DRIP)\", \"FinalShares\": round(shares_drip, 2),\n",
        "     \"StockValue\": round(drip_value, 2), \"CashCollected\": 0.00,\n",
        "     \"TotalWealth\": round(drip_value, 2)},\n",
        "    {\"Strategy\": \"WITHOUT (cash out)\", \"FinalShares\": round(START_SHARES, 2),\n",
        "     \"StockValue\": round(stock_value_nocash, 2), \"CashCollected\": round(cash_collected, 2),\n",
        "     \"TotalWealth\": round(nocash_total, 2)},\n",
        "])\n",
        "print(summary.to_string(index=False))\n",
        "\n",
        "gap = drip_value - nocash_total\n",
        "print(f\"\\nStarting cost basis        : ${cost_basis:,.2f}\")\n",
        "print(f\"Compounding advantage      : ${gap:,.2f}\")\n",
        "print(f\"DRIP total / cash-out total: {drip_value / nocash_total:.2f}x\")\n",
        "print(\"\\nReinvesting wins because each new share itself earns a growing dividend —\")\n",
        "print(\"the cash-out investor's share count (and so dividend income) never grows.\")\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
}
