{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# 📘 Fincept Notebook — Dollar-Cost Averaging Simulator\n",
        "\n",
        "**Investing · Beginner · ~12 min · Standard library only**\n",
        "\n",
        "Dollar-cost averaging (DCA) means investing a fixed dollar amount on a regular schedule — say $500 every month — regardless of price. You buy more units when prices are low and fewer when prices are high. This notebook simulates DCA over a 24-month price series for an index fund and compares it against putting the same total in as a single lump sum on day one.\n",
        "\n",
        "**What you'll learn**\n",
        "- How to track units, cost basis, and portfolio value month by month under DCA\n",
        "- How DCA's average cost per unit differs from the simple average price\n",
        "- When DCA wins (volatile or declining entry) versus when lump-sum wins (steadily rising market)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. The price series\n",
        "\n",
        "Here is our embedded 24-month month-end price series for a hypothetical index fund. It dips early, bottoms around month 8, then recovers — a realistic 'choppy then rising' path. Every code cell below re-embeds this same list so it can run on its own.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "Price series"
      },
      "outputs": [],
      "source": [
        "# Month-end prices for the index fund (month 0 .. month 23)\n",
        "prices = [\n",
        "    100.00,  98.50,  95.20,  92.80,  90.10,  93.40,  88.70,  85.30,\n",
        "     82.90,  86.50,  89.20,  91.80,  94.30,  92.10,  96.70,  99.40,\n",
        "    101.20, 103.80, 102.50, 105.90, 108.30, 110.10, 112.70, 115.00,\n",
        "]\n",
        "\n",
        "print(f\"Months in series : {len(prices)}\")\n",
        "print(f\"Start price (m0) : ${prices[0]:>7.2f}\")\n",
        "print(f\"Lowest price     : ${min(prices):>7.2f}  (month {prices.index(min(prices))})\")\n",
        "print(f\"Highest price    : ${max(prices):>7.2f}  (month {prices.index(max(prices))})\")\n",
        "print(f\"End price (m23)  : ${prices[-1]:>7.2f}\")\n",
        "\n",
        "simple_avg = sum(prices) / len(prices)\n",
        "print(f\"\\nSimple average price over the 24 months: ${simple_avg:.2f}\")\n",
        "total_change = (prices[-1] / prices[0] - 1) * 100\n",
        "print(f\"Total price change month 0 -> month 23 : {total_change:+.1f}%\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. Run the DCA simulation\n",
        "\n",
        "We invest a fixed **$500 every month**. Each month, units bought = contribution / price. We accumulate units, total invested, and portfolio value (units held × current price). The **average cost per unit** is total invested ÷ units held — the number that really matters.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "DCA month-by-month"
      },
      "outputs": [],
      "source": [
        "prices = [\n",
        "    100.00,  98.50,  95.20,  92.80,  90.10,  93.40,  88.70,  85.30,\n",
        "     82.90,  86.50,  89.20,  91.80,  94.30,  92.10,  96.70,  99.40,\n",
        "    101.20, 103.80, 102.50, 105.90, 108.30, 110.10, 112.70, 115.00,\n",
        "]\n",
        "\n",
        "MONTHLY = 500.00  # fixed dollar contribution each month\n",
        "\n",
        "units = 0.0\n",
        "invested = 0.0\n",
        "\n",
        "header = f\"{'Mo':>3} {'Price':>8} {'Buy$':>7} {'Units+':>8} {'Units':>9} {'Invested':>10} {'Value':>10} {'AvgCost':>8}\"\n",
        "print(header)\n",
        "print(\"-\" * len(header))\n",
        "\n",
        "for m, p in enumerate(prices):\n",
        "    bought = MONTHLY / p\n",
        "    units += bought\n",
        "    invested += MONTHLY\n",
        "    value = units * p\n",
        "    avg_cost = invested / units\n",
        "    # print every 3rd month plus the last, to keep the table compact\n",
        "    if m % 3 == 0 or m == len(prices) - 1:\n",
        "        print(f\"{m:>3} {p:>8.2f} {MONTHLY:>7.0f} {bought:>8.3f} \"\n",
        "              f\"{units:>9.3f} {invested:>10.2f} {value:>10.2f} {avg_cost:>8.2f}\")\n",
        "\n",
        "print(\"-\" * len(header))\n",
        "print(f\"\\nDCA final units held    : {units:.3f}\")\n",
        "print(f\"DCA total invested      : ${invested:,.2f}\")\n",
        "print(f\"DCA average cost / unit : ${invested / units:.2f}\")\n",
        "print(f\"DCA final value (@m23)  : ${units * prices[-1]:,.2f}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. DCA vs lump-sum\n",
        "\n",
        "Now compare. The lump-sum investor puts the **entire 24 months of contributions in at month 0** — 24 × $500 = $12,000 — and holds. Both end with the same dollars contributed; the only difference is *when*. We compare final value, profit, and return.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "DCA vs lump-sum"
      },
      "outputs": [],
      "source": [
        "prices = [\n",
        "    100.00,  98.50,  95.20,  92.80,  90.10,  93.40,  88.70,  85.30,\n",
        "     82.90,  86.50,  89.20,  91.80,  94.30,  92.10,  96.70,  99.40,\n",
        "    101.20, 103.80, 102.50, 105.90, 108.30, 110.10, 112.70, 115.00,\n",
        "]\n",
        "\n",
        "MONTHLY = 500.00\n",
        "n = len(prices)\n",
        "total_capital = MONTHLY * n  # same total dollars for both strategies\n",
        "end_price = prices[-1]\n",
        "\n",
        "# --- DCA ---\n",
        "dca_units = sum(MONTHLY / p for p in prices)\n",
        "dca_value = dca_units * end_price\n",
        "dca_avg_cost = total_capital / dca_units\n",
        "\n",
        "# --- Lump sum: all capital at month 0 price ---\n",
        "ls_units = total_capital / prices[0]\n",
        "ls_value = ls_units * end_price\n",
        "ls_avg_cost = prices[0]\n",
        "\n",
        "def pct(value, cost):\n",
        "    return (value / cost - 1) * 100\n",
        "\n",
        "rows = [\n",
        "    (\"Total invested\",   f\"${total_capital:,.2f}\",            f\"${total_capital:,.2f}\"),\n",
        "    (\"Units acquired\",   f\"{dca_units:,.3f}\",                 f\"{ls_units:,.3f}\"),\n",
        "    (\"Avg cost / unit\",  f\"${dca_avg_cost:,.2f}\",             f\"${ls_avg_cost:,.2f}\"),\n",
        "    (\"Final value\",      f\"${dca_value:,.2f}\",                f\"${ls_value:,.2f}\"),\n",
        "    (\"Profit\",           f\"${dca_value - total_capital:,.2f}\", f\"${ls_value - total_capital:,.2f}\"),\n",
        "    (\"Return\",           f\"{pct(dca_value, total_capital):+.1f}%\", f\"{pct(ls_value, total_capital):+.1f}%\"),\n",
        "]\n",
        "\n",
        "print(f\"{'Metric':<16} {'DCA':>14} {'Lump sum':>14}\")\n",
        "print(\"-\" * 46)\n",
        "for label, a, b in rows:\n",
        "    print(f\"{label:<16} {a:>14} {b:>14}\")\n",
        "\n",
        "print(\"-\" * 46)\n",
        "diff = ls_value - dca_value\n",
        "winner = \"Lump sum\" if diff > 0 else \"DCA\"\n",
        "print(f\"\\nWinner in THIS series: {winner} by ${abs(diff):,.2f}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. When does each strategy win?\n",
        "\n",
        "The result is path-dependent. To see why, we test the same DCA-vs-lump-sum logic on three stylised 12-month paths: a steadily **rising** market, a steadily **declining** market, and a **V-shaped** dip-and-recover. Watch which strategy wins in each.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "fincept_title": "Three market regimes"
      },
      "outputs": [],
      "source": [
        "def simulate(prices, monthly=500.0):\n",
        "    n = len(prices)\n",
        "    capital = monthly * n\n",
        "    end = prices[-1]\n",
        "    dca_units = sum(monthly / p for p in prices)\n",
        "    ls_units = capital / prices[0]\n",
        "    return capital, dca_units * end, ls_units * end\n",
        "\n",
        "# Three 12-month regimes (start at 100 in each)\n",
        "rising    = [100 + 2.0 * i for i in range(12)]                       # straight up\n",
        "declining = [100 - 2.0 * i for i in range(12)]                       # straight down\n",
        "v_shaped  = [100, 95, 90, 85, 80, 78, 80, 85, 90, 95, 100, 105]      # dip then recover\n",
        "\n",
        "scenarios = [(\"Rising\", rising), (\"Declining\", declining), (\"V-shaped\", v_shaped)]\n",
        "\n",
        "print(f\"{'Regime':<11} {'DCA value':>12} {'Lump value':>12} {'Winner':>10}\")\n",
        "print(\"-\" * 47)\n",
        "for name, series in scenarios:\n",
        "    cap, dca_v, ls_v = simulate(series)\n",
        "    winner = \"Lump sum\" if ls_v > dca_v else \"DCA\"\n",
        "    print(f\"{name:<11} {dca_v:>12,.2f} {ls_v:>12,.2f} {winner:>10}\")\n",
        "\n",
        "print(\"\\nTakeaways:\")\n",
        "print(\"  - Rising market : lump sum wins. Money invested earlier rides the whole climb.\")\n",
        "print(\"  - Declining      : DCA wins. Later buys are cheaper, lowering average cost.\")\n",
        "print(\"  - V-shaped       : DCA wins. It loads up near the bottom; lump-sum bought at the top.\")\n",
        "print(\"\\nRule of thumb: lump-sum has the higher EXPECTED return because markets rise\")\n",
        "print(\"more often than they fall, but DCA reduces timing risk and is easier to stick to.\")\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
}
