Python3 IRA Calculator
yes, calculators are boring. I wanted one anyway.
Demo
Python Source Code
import tkinter as tk
from tkinter import ttk
import pandas as pd
from decimal import Decimal, ROUND_HALF_UP
def calculate_growth():
try:
# User inputs - convert to Decimal for precise calculation
monthly_contribution = Decimal(monthly_contribution_entry.get())
overhead_rate = Decimal(overhead_rate_entry.get()) / Decimal('100')
annual_rate = Decimal(interest_rate_entry.get()) / Decimal('100')
monthly_rate = annual_rate / Decimal('12')
years = int(years_entry.get())
# Variables for calculations
table_data = [] # Yearly summary
monthly_detail = [] # Monthly breakdown
total_contribution = Decimal('0')
total_overhead = Decimal('0')
cash_value = Decimal('0')
# Monthly compounding logic with detailed tracking
for year in range(1, years + 1):
yearly_interest = Decimal('0')
for month in range(1, 13):
# Add monthly contribution
total_contribution += monthly_contribution
# Calculate and add overhead
monthly_overhead = monthly_contribution * overhead_rate
total_overhead += monthly_overhead
# Calculate cash value addition (contribution minus overhead)
cash_addition = monthly_contribution - monthly_overhead
cash_value += cash_addition
# Calculate interest on cash value
month_interest = (cash_value * monthly_rate).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
# Add interest to cash value
cash_value += month_interest
# Track yearly interest
yearly_interest += month_interest
# Track monthly details
monthly_detail.append({
'Year': year,
'Month': month,
'Total_Contribution': float(total_contribution),
'Total_Overhead': float(total_overhead),
'Cash_Value': float(cash_value),
'Monthly_Interest': float(month_interest),
'Interest_Rate': float(annual_rate * 100)
})
# Add year's summary to table
table_data.append((
year,
f"${float(total_contribution):,.2f}",
f"${float(total_overhead):,.2f}",
f"${float(cash_value):,.2f}",
f"${float(yearly_interest):,.2f}"
))
# Update Treeview
for row in tree.get_children():
tree.delete(row)
for row_data in table_data:
tree.insert("", "end", values=row_data)
# Save both summary and detailed data for copying
global clipboard_data, monthly_detail_data
clipboard_data = pd.DataFrame(
table_data,
columns=["Year", "Total Contribution", "Total Overhead", "Cash Value", "Yearly Interest"]
)
monthly_detail_data = pd.DataFrame(monthly_detail)
# Show first month's calculation in info label
first_month = monthly_detail[0]
detail_text = (
f"First Month Example:\n"
f"Contribution: ${monthly_contribution:.2f}\n"
f"Overhead: ${monthly_contribution * overhead_rate:.2f}\n"
f"Cash Value: ${first_month['Cash_Value']:.2f}\n"
f"Interest Earned: ${first_month['Monthly_Interest']:.2f}"
)
error_label.config(text=detail_text)
except ValueError as e:
error_label.config(text=f"Please enter valid numbers. Error: {str(e)}")
def copy_to_clipboard():
if clipboard_data is not None:
root.clipboard_clear()
clipboard_data.to_clipboard(index=False, header=True, sep="\t")
error_label.config(text="Yearly summary copied to clipboard!")
else:
error_label.config(text="No data to copy!")
def copy_monthly_detail():
if monthly_detail_data is not None:
monthly_detail_data.to_clipboard(index=False, header=True, sep="\t")
error_label.config(text="Monthly detail copied to clipboard!")
else:
error_label.config(text="No monthly detail available!")
# Create the main window
root = tk.Tk()
root.title("Life Insurance Policy Calculator")
# Initialize data variables
clipboard_data = None
monthly_detail_data = None
# Create and configure a main frame with padding
main_frame = ttk.Frame(root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# Input fields with improved layout
input_frame = ttk.LabelFrame(main_frame, text="Policy Parameters", padding="5")
input_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=5)
ttk.Label(input_frame, text="Monthly Contribution ($):").grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
monthly_contribution_entry = ttk.Entry(input_frame)
monthly_contribution_entry.insert(0, "100") # Default value
monthly_contribution_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
ttk.Label(input_frame, text="Overhead Rate (%):").grid(row=1, column=0, sticky=tk.W, padx=5, pady=2)
overhead_rate_entry = ttk.Entry(input_frame)
overhead_rate_entry.insert(0, "50") # Default value
overhead_rate_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
ttk.Label(input_frame, text="Annual Interest Rate (%):").grid(row=2, column=0, sticky=tk.W, padx=5, pady=2)
interest_rate_entry = ttk.Entry(input_frame)
interest_rate_entry.insert(0, "11") # Default value
interest_rate_entry.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
ttk.Label(input_frame, text="Number of Years:").grid(row=3, column=0, sticky=tk.W, padx=5, pady=2)
years_entry = ttk.Entry(input_frame)
years_entry.insert(0, "25") # Default value
years_entry.grid(row=3, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
# Buttons frame
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=1, column=0, pady=5)
calculate_button = ttk.Button(button_frame, text="Calculate", command=calculate_growth)
calculate_button.grid(row=0, column=0, padx=5)
copy_button = ttk.Button(button_frame, text="Copy Yearly Summary", command=copy_to_clipboard)
copy_button.grid(row=0, column=1, padx=5)
copy_detail_button = ttk.Button(button_frame, text="Copy Monthly Detail", command=copy_monthly_detail)
copy_detail_button.grid(row=0, column=2, padx=5)
# Error/info label with more space
error_label = ttk.Label(main_frame, text="", foreground="blue", wraplength=400)
error_label.grid(row=2, column=0, pady=5)
# Treeview for displaying yearly results
tree = ttk.Treeview(main_frame,
columns=("Year", "Total Contribution", "Total Overhead", "Cash Value", "Yearly Interest"),
show="headings",
height=25) # Increased height to show all 25 years
# Configure column headings
for col in tree["columns"]:
tree.heading(col, text=col, anchor="w")
tree.column(col, anchor="w")
tree.grid(row=3, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
# Add scrollbar
scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=tree.yview)
scrollbar.grid(row=3, column=1, sticky=(tk.N, tk.S))
tree.configure(yscrollcommand=scrollbar.set)
# Configure grid weights for resizing
main_frame.columnconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
# Automatically calculate on startup
root.after(100, calculate_growth)
# Run the application
root.mainloop()
Requirements
numpy==2.2.0
pandas==2.2.3
python-dateutil==2.9.0.post0
pytz==2024.2
six==1.17.0
tzdata==2024.2