CodeForFinance
Python

Dividend Income Calculator and DRIP Simulator

Dividend investing is a popular strategy for building passive income. But how much will your dividends grow over time with reinvestment? In this tutorial, we build a dividend calculator that models DRIP (Dividend Reinvestment Plan) compounding, projects future income, and visualises the snowball effect.

Prerequisites

  • Basic Python
  • Understanding of dividends and compound interest

Step 1.Install dependencies

Standard data science stack for calculations and charts.

pip install pandas matplotlib numpy

Step 2.Define your dividend holdings

Each holding has the current dividend per share and an estimated annual dividend growth rate.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

holdings = [
    {"ticker": "JNJ", "shares": 50, "price": 155.0, "annual_div": 4.76, "div_growth": 0.06},
    {"ticker": "KO", "shares": 100, "price": 62.0, "annual_div": 1.94, "div_growth": 0.05},
    {"ticker": "PG", "shares": 40, "price": 165.0, "annual_div": 3.76, "div_growth": 0.07},
    {"ticker": "O", "shares": 75, "price": 55.0, "annual_div": 3.08, "div_growth": 0.04},
]

total_income = sum(h["shares"] * h["annual_div"] for h in holdings)
print(f"Current annual dividend income: ${total_income:,.2f}")
print(f"Monthly income: ${total_income/12:,.2f}")

Step 3.Simulate DRIP over 20 years

Each year: collect dividends, buy more shares at current price, then grow the dividend and price.

def simulate_drip(initial_holdings, years=20):
    yearly_data = []
    current = [dict(h) for h in initial_holdings]
    original_costs = [h["shares"] * h["price"] for h in initial_holdings]

    for year in range(years + 1):
        annual_income = sum(h["shares"] * h["annual_div"] for h in current)
        portfolio_value = sum(h["shares"] * h["price"] for h in current)
        total_cost = sum(original_costs)
        yoc = (annual_income / total_cost) * 100 if total_cost > 0 else 0
        yearly_data.append({
            "Year": year,
            "Annual Income": round(annual_income, 2),
            "Portfolio Value": round(portfolio_value, 2),
            "Yield on Cost": round(yoc, 2)
        })

        for h in current:
            new_shares = (h["annual_div"] * h["shares"]) / h["price"]
            h["shares"] += new_shares
            h["annual_div"] *= (1 + h["div_growth"])
            h["price"] *= 1.05  # Assume 5% price appreciation

    return pd.DataFrame(yearly_data)

df = simulate_drip(holdings, 20)
print(df.to_string(index=False))

Step 4.Compare DRIP vs no DRIP

DRIP creates a compounding snowball. The difference grows dramatically over time.

def simulate_no_drip(initial_holdings, years=20):
    yearly_data = []
    current = [dict(h) for h in initial_holdings]

    for year in range(years + 1):
        annual_income = sum(h["shares"] * h["annual_div"] for h in current)
        portfolio_value = sum(h["shares"] * h["price"] for h in current)
        yearly_data.append({"Year": year, "Annual Income": round(annual_income, 2), "Portfolio Value": round(portfolio_value, 2)})
        for h in current:
            h["annual_div"] *= (1 + h["div_growth"])
            h["price"] *= 1.05
    return pd.DataFrame(yearly_data)

df_no_drip = simulate_no_drip(holdings, 20)
print(f"Year 20 with DRIP:    ${df.iloc[-1]['Annual Income']:,.0f}/year income")
print(f"Year 20 without DRIP: ${df_no_drip.iloc[-1]['Annual Income']:,.0f}/year income")

Step 5.Visualise the dividend snowball

Side-by-side comparison showing the power of reinvesting dividends over time.

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))

ax1.bar(df["Year"], df["Annual Income"], color="cyan", alpha=0.7, label="With DRIP")
ax1.bar(df_no_drip["Year"], df_no_drip["Annual Income"], color="gray", alpha=0.4, label="Without DRIP")
ax1.set_xlabel("Year")
ax1.set_ylabel("Annual Dividend Income ($)")
ax1.set_title("Dividend Income Growth")
ax1.legend()

ax2.plot(df["Year"], df["Portfolio Value"], color="cyan", linewidth=2, label="With DRIP")
ax2.plot(df_no_drip["Year"], df_no_drip["Portfolio Value"], color="gray", linewidth=2, label="Without DRIP")
ax2.set_xlabel("Year")
ax2.set_ylabel("Portfolio Value ($)")
ax2.set_title("Portfolio Value Growth")
ax2.legend()

plt.tight_layout()
plt.savefig("dividend_drip.png", dpi=150)
plt.show()

Expected Output

Current annual dividend income: $1,003.00
Monthly income: $83.58
Year 20 with DRIP:    $5,842/year income
Year 20 without DRIP: $3,215/year income

Next Steps

  • Add tax impact modelling (ISA vs GIA)
  • Include monthly contribution additions
  • Build a dividend calendar showing payment dates

Recommended Reading

The Single Best Investment

As an Amazon Associate we may earn from qualifying purchases.

Browse All Tutorials →

Developer Essentials

As an Amazon Associate we may earn from qualifying purchases.