Introduction
The cash flow forecast is an essential tool for any business, helping anticipate future balances based on incoming flows (inflows) and outgoing flows (outflows). In 2026, amid economic volatility, automating these calculations with Python is key to saving time and reducing human errors.
This intermediate tutorial walks you step by step through building a complete web app with Streamlit, Pandas, and Matplotlib. Start from historical data (simple CSV) to project 12 months ahead using a hybrid method: moving average for stability and linear regression for trends. Think of it as a financial GPS—it analyzes your past trajectory to map the road ahead.
Result: an interactive interface where you upload data, adjust parameters, and visualize balances, overdraft alerts, and charts. Perfect for tech-savvy CFOs or finance devs. Estimated time: 30 minutes for a functional prototype.
Prerequisites
- Python 3.10+ installed
- Basic knowledge of Pandas and DataFrames
- pip for dependencies
- Example CSV file with columns:
date(YYYY-MM-DD),entrees(float),sorties(float),solde_initial(optional) - Editor like VS Code
Installing Dependencies
pip install streamlit pandas numpy matplotlib plotly scikit-learnThese packages form the foundation: Streamlit for the web interface, Pandas/NumPy for calculations, Matplotlib/Plotly for interactive visualizations, and scikit-learn for linear regression. Run in the terminal, ideally in a virtual environment (via venv).
Preparing Sample Data
Create a test CSV file to simulate real cash flows. Required columns: date, entrees, sorties. The balance is calculated cumulatively: monthly_balance = previous_balance + entrees - sorties. Use monthly dates to simplify aggregations.
Generating the Sample CSV File
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# Paramètres
start_date = datetime(2025, 1, 1)
dates = pd.date_range(start=start_date, periods=24, freq='MS')
# Flux aléatoires réalistes (entrees > sorties en moyenne)
np.random.seed(42)
entrees = np.random.normal(50000, 10000, 24).clip(20000)
sorties = np.random.normal(40000, 8000, 24).clip(15000)
# Solde initial
df = pd.DataFrame({
'date': dates,
'entrees': entrees,
'sorties': sorties
})
df['solde'] = df['entrees'].cumsum() - df['sorties'].cumsum() + 100000 # Solde initial 100k€
df.to_csv('tresorerie_historique.csv', index=False)
print('Fichier créé : tresorerie_historique.csv')
print(df.head())This script generates 24 months of realistic fictional data (average entrees 50k€, sorties 40k€, initial balance 100k€). The seed ensures reproducibility. Run it to create tresorerie_historique.csv, ready for testing. Pitfall: always clip negative values to avoid absurd flows.
Loading and Cleaning Data
In the Streamlit app, load the CSV, convert date to datetime, aggregate monthly if needed, and calculate cumulative balances. Handle NaNs and validate columns for robustness.
Data Loading and Cleaning Functions
import pandas as pd
import streamlit as st
@st.cache_data
def load_data(uploaded_file):
df = pd.read_csv(uploaded_file)
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values('date').reset_index(drop=True)
# Vérifications
required_cols = ['date', 'entrees', 'sorties']
if not all(col in df.columns for col in required_cols):
st.error('Colonnes manquantes : date, entrees, sorties')
st.stop()
# Nettoyage
df = df.dropna(subset=required_cols)
df['entrees'] = pd.to_numeric(df['entrees'], errors='coerce').fillna(0)
df['sorties'] = pd.to_numeric(df['sorties'], errors='coerce').fillna(0)
# Calcul solde cumulé
solde_initial = st.number_input('Solde initial (€)', value=100000.0)
df['solde'] = solde_initial + df['entrees'].cumsum() - df['sorties'].cumsum()
return df
# Utilisation : df = load_data(st.file_uploader('Upload CSV'))This function loads, validates, and cleans data via Streamlit. @st.cache_data optimizes reloads. Dynamic cumulative balance with user input. Pitfall: ignoring conversion errors can corrupt calculations; always use errors='coerce'. Copy into your main app.
Forecasting Algorithm
Moving average (3-month window) to smooth fluctuations + linear regression on entrees/sorties trends. Project 12 months: for each future month, apply predictions to cumulative balances. Alerts if balance < 10k€.
Forecasting Functions
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from datetime import timedelta
def forecast_tresorerie(df, periods=12):
df_forecast = df.copy()
# Moyenne mobile pour lissage
df['entrees_smooth'] = df['entrees'].rolling(window=3, min_periods=1).mean()
df['sorties_smooth'] = df['sorties'].rolling(window=3, min_periods=1).mean()
# Régression linéaire sur tendances
X = np.arange(len(df)).reshape(-1, 1)
reg_entrees = LinearRegression().fit(X, df['entrees_smooth'])
reg_sorties = LinearRegression().fit(X, df['sorties_smooth'])
# Projection
last_date = df['date'].max()
future_dates = pd.date_range(start=last_date + timedelta(days=1), periods=periods, freq='MS')
future_X = np.arange(len(df), len(df) + periods).reshape(-1, 1)
future_entrees = reg_entrees.predict(future_X)
future_sorties = reg_sorties.predict(future_X)
future_df = pd.DataFrame({
'date': future_dates,
'entrees_pred': future_entrees,
'sorties_pred': future_sorties
})
# Solde projeté
last_solde = df['solde'].iloc[-1]
future_df['solde_pred'] = np.cumsum(future_entrees - future_sorties) + last_solde
df_forecast = pd.concat([df_forecast, future_df], ignore_index=True)
df_forecast['type'] = ['historique'] * len(df) + ['prevision'] * periods
return df_forecast
# Utilisation : df_full = forecast_tresorerie(df)Robust hybrid approach: rolling mean smooths noise, LinearRegression captures growth. freq='MS' for monthly. Cumulative balances reflect real accounting. Pitfall: without min_periods=1, early data is missing; test with fewer than 3 months.
Interactive Visualization
Display charts: balance trends (line), cash flows (bars), with Plotly zoom. Add key metrics: projected minimum balance, average growth percentage.
Visualizations and Metrics
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
def plot_forecast(df_full):
fig = go.Figure()
# Soldes
fig.add_trace(go.Scatter(x=df_full['date'], y=df_full['solde'],
mode='lines+markers', name='Solde historique',
line=dict(color='blue')))
fig.add_trace(go.Scatter(x=df_full[df_full['type']=='prevision']['date'],
y=df_full[df_full['type']=='prevision']['solde_pred'],
mode='lines+markers', name='Solde prévisionnel',
line=dict(color='orange', dash='dash')))
fig.update_layout(title='Évolution de la trésorerie', xaxis_title='Date', yaxis_title='Solde (€)')
st.plotly_chart(fig, use_container_width=True)
# Métriques
solde_min = df_full[df_full['type']=='prevision']['solde_pred'].min()
if solde_min < 10000:
st.error(f'🚨 Alerte : Solde minimum projeté = {solde_min:,.0f}€')
else:
st.success(f'Solde minimum projeté = {solde_min:,.0f}€')
croissance = (df_full['entrees'].tail(12).mean() / df_full['entrees'].head(12).mean() - 1) * 100
st.metric('Croissance moyenne entrees (%)', f'{croissance:.1f}%')
# Utilisation : plot_forecast(df_full)Plotly enables interactivity (zoom, hover). Separate traces for historical vs. forecast. Conditional alerts add real value. Pitfall: without use_container_width=True, charts overflow; always filter by type for clarity.
Complete Streamlit Application
import streamlit as st
from data_loader import load_data
from forecaster import forecast_tresorerie
from visualizer import plot_forecast
st.set_page_config(page_title='Prévision Trésorerie', layout='wide')
st.title('🚀 Prévision de Trésorerie Automatisée')
uploaded_file = st.file_uploader('Choisissez votre fichier CSV', type='csv')
if uploaded_file is not None:
df = load_data(uploaded_file)
st.subheader('Données chargées')
st.dataframe(df.tail())
periods = st.slider('Mois à prévoir', 3, 24, 12)
df_full = forecast_tresorerie(df, periods)
col1, col2 = st.columns(2)
with col1:
plot_forecast(df_full)
with col2:
st.subheader('Tableau prévisionnel')
st.dataframe(df_full[df_full['type']=='prevision'])
st.download_button('Télécharger CSV complet', df_full.to_csv(index=False),
'tresorerie_forecast.csv')
if __name__ == '__main__':
st.info('Exécutez : streamlit run app.py')Fully integrated app: upload, parameters, visualizations, export. Wide layout maximizes space. Modular via imports. Run with streamlit run app.py. Pitfall: without st.cache_data, reloads are slow; test with large CSVs.
Best Practices
- Always validate data: columns, types, NaNs to prevent production crashes.
- Use virtual environments:
venv+requirements.txtfor easy deployment. - Modularize code: separate functions for unit tests (with pytest).
- Secure inputs: limit file sizes, sanitize values.
- Deploy to the cloud: Streamlit Cloud is free for sharing with your finance team.
Common Errors to Avoid
- Forgetting cumulative balances: forecasts become unrealistic if resetting to zero monthly.
- No smoothing: missing moving average amplifies random noise.
- Malformed dates:
pd.to_datetimefails without error handling, crashing the app. - Overly optimistic forecasts: add pessimistic scenarios (e.g., 1.2x factor on sorties).
Next Steps
Integrate advanced ML (Prophet for seasonality) or accounting APIs (QuickBooks). Explore our Python Data Science training and Streamlit Pro. Resources: Pandas Docs, Streamlit Gallery.