Introduction
An expense tracker is the project where Python stops being a hobby and starts saving you money. In about 120 lines you get a form for logging expenses, automatic monthly summaries, and charts that make overspending painfully obvious. It combines the state handling of the to-do list app with the data-crunching patterns from the Pandas dashboard.
The trick that keeps this app simple is Pandas as the storage engine: expenses live in a CSV file, so filtering by month, grouping by category, and summing totals are one-liners instead of hand-rolled loops.
Features
Prerequisites
pip install streamlit pandas matplotlibStep 1: Create the Script
Save this as expense_tracker.py:
import streamlit as st
import pandas as pd
from datetime import date
import os
CSV_FILE = "expenses.csv"
CATEGORIES = ["Food", "Travel", "Bills", "Shopping", "Fun", "Other"]
def load_expenses():
if os.path.exists(CSV_FILE):
return pd.read_csv(CSV_FILE, parse_dates=["date"])
return pd.DataFrame(columns=["date", "amount", "category", "note"])
st.set_page_config(page_title="Expense Tracker", page_icon="💰", layout="centered")
st.title("💰 Expense Tracker")
expenses = load_expenses()
# --- Add expense form ---
with st.form("add_expense", clear_on_submit=True):
col1, col2 = st.columns(2)
amount = col1.number_input("Amount", min_value=0.01, format="%.2f")
category = col2.selectbox("Category", CATEGORIES)
note = st.text_input("Note (optional)", placeholder="Groceries, fuel, movie...")
submitted = st.form_submit_button("Add Expense")
if submitted:
new_row = pd.DataFrame(
[{"date": date.today(), "amount": amount, "category": category, "note": note}]
)
expenses = pd.concat([new_row, expenses], ignore_index=True)
expenses.to_csv(CSV_FILE, index=False)
st.success(f"Added {category} expense!")
# --- Summary ---
if not expenses.empty:
expenses["month"] = expenses["date"].dt.to_period("M")
this_month = expenses[expenses["month"] == pd.Period(date.today(), "M")]
c1, c2, c3 = st.columns(3)
c1.metric("This month", f"₹{this_month['amount'].sum():,.0f}")
c2.metric("Daily average", f"₹{this_month['amount'].mean():,.0f}")
c3.metric("Entries", len(this_month))
left, right = st.columns(2)
with left:
by_cat = this_month.groupby("category")["amount"].sum()
st.bar_chart(by_cat)
with right:
st.dataframe(
this_month[["date", "amount", "category", "note"]]
.sort_values("date", ascending=False),
use_container_width=True,
hide_index=True,
)
else:
st.info("No expenses logged yet.")Step 2: Run the App
streamlit run expense_tracker.pyLog a few expenses across categories, then watch the bar chart and metrics update instantly.
How It Works
Everything rides on two Pandas idioms. First, df["date"].dt.to_period("M") converts timestamps into month periods, so filtering "this month" is a simple equality check — no manual date-range math. Second, groupby("category")["amount"].sum() produces the aggregated series that st.bar_chart renders directly; Pandas indexes become chart labels for free.
The form uses st.form, which batches its widgets into a single rerun. That matters here: without a form, every keystroke in the amount box would trigger a full script rerun. Forms are the standard answer to *"my Streamlit app reruns too much"*.
Storage stays boring on purpose — a CSV round-tripped with to_csv/read_csv. It has zero setup, opens in Excel, and is trivially portable to the home server you might host it on later.
Common Errors & Fixes
read_csv on a missing file returns an empty frame without columns; the code guards with os.path.exists, so check the file path if you renamed it.NaN amounts; make sure number_input has min_value=0.01 so amounts are always real.to_csv write inside if submitted:.parse_dates=["date"] to read_csv, otherwise .dt accessors fail.Key Concepts
to_period("M") makes month comparisons trivial.What to Try Next
to_csv through st.download_button.st.line_chart on a grouped series.FAQ
Why CSV instead of SQLite?
For one user and a few thousand rows, CSV is simpler, human-readable, and opens in Excel. Switch to SQLite when you need concurrent writes or faster queries on large data.
Can I track income too?
Yes — add a type column ("income"/"expense") with a radio in the form, and compute net savings as income minus expenses per month.
How do I back up my data?
Copy expenses.csv — that's the entire database. For automation, a nightly copy script on your home server works well.