SECTION 1: LEARNING OBJECTIVES
This final lesson consolidates everything you have learned across the diploma into a comprehensive capstone project. By the end of this lesson, you will be able to:
-
Design and implement an end-to-end financial data analytics solution covering data ingestion, cleaning, analysis, modelling, and deployment.
-
Apply the skills from all previous modules: data warehousing (Module 1), EDA (Module 2), statistics (Module 3), machine learning (Module 4), risk analytics (Module 5), and advanced topics (Module 6).
-
Build a credit risk assessment dashboard for a bank – from raw data to interactive visualisation.
-
Integrate multiple data sources – structured customer data, transactional data, and external economic indicators.
-
Develop and validate a predictive model (logistic regression / XGBoost) for loan default prediction.
-
Calculate expected loss and stress test the portfolio under macroeconomic scenarios.
-
Deploy a simple interactive dashboard using Streamlit or a similar tool.
-
Document the project as a portfolio piece to demonstrate your skills.
SECTION 2: PROJECT OVERVIEW – CREDIT RISK ASSESSMENT DASHBOARD
Scenario: You are a data analyst at a mid-sized bank. The Credit Risk team wants a dashboard that:
-
Aggregates customer and loan data.
-
Predicts the probability of default (PD) for each loan.
-
Calculates Expected Loss (EL) at the portfolio level.
-
Performs scenario analysis (stress testing) for different economic conditions.
-
Allows credit officers to drill down into individual loan details.
Deliverables:
-
A Python script that processes data, builds the model, and generates a report.
-
An interactive dashboard (Streamlit/Plotly Dash) with:
-
Portfolio overview (total exposure, EL, PD distribution).
-
Individual loan-level predictions and explanations (SHAP).
-
Scenario analysis sliders (GDP growth, unemployment).
-
Data Sources:
-
Internal loan data (simulated): customer demographics, financial ratios, loan terms.
-
External economic indicators (simulated): GDP growth, unemployment rate, inflation.
SECTION 3: PROJECT STEPS
| Step | Description | Relevant Module |
|---|---|---|
| 1. Data Ingestion | Load data from multiple sources (CSV, API, database). | Module 1 |
| 2. Data Cleaning | Handle missing values, outliers, and inconsistencies. | Module 2 |
| 3. Feature Engineering | Create new features (e.g., debt-to-income ratio, credit utilisation). | Module 2 |
| 4. Exploratory Analysis | Understand distributions, correlations, and relationships. | Module 2 |
| 5. Model Building | Train a logistic regression / XGBoost model. | Module 4 |
| 6. Model Evaluation | Validate model (AUC, KS, calibration, fairness). | Modules 4, 5, 6 |
| 7. Risk Calculation | Compute PD, LGD, EAD, EL, and economic capital. | Module 5 |
| 8. Stress Testing | Apply macroeconomic shocks to portfolio. | Module 5 |
| 9. Dashboard | Visualise results with interactive dashboard. | All modules |
| 10. Documentation | Write a comprehensive report. | All modules |
SECTION 4: IMPLEMENTATION IN PYTHON – CAPSTONE PROJECT
Given the length constraints, we provide the core implementation framework. The full code is available in the course repository.
# =================================================================== # MODULE 6, LESSON 8: CAPSTONE PROJECT – CREDIT RISK DASHBOARD # =================================================================== """ Project Structure: ├── data/ │ ├── loan_data.csv │ ├── macro_data.csv ├── src/ │ ├── data_prep.py │ ├── model.py │ ├── risk.py │ ├── dashboard.py ├── notebooks/ │ ├── eda.ipynb │ ├── modeling.ipynb ├── requirements.txt ├── README.md └── run.py """ # We'll provide the main components as code cells. import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier import xgboost as xgb from sklearn.metrics import roc_auc_score, classification_report import shap import streamlit as st from io import StringIO import warnings warnings.filterwarnings('ignore') print("="*70) print("CAPSTONE PROJECT – CREDIT RISK DASHBOARD") print("="*70) # ---------------------------------------------------------------- # PART A: DATA GENERATION (simulate internal and external data) # ---------------------------------------------------------------- def generate_data(n=10000): """Generate synthetic loan and macro data.""" # Internal loan data np.random.seed(42) df = pd.DataFrame({ 'loan_id': range(1, n+1), 'age': np.random.normal(45, 12, n).clip(22, 75).astype(int), 'income': np.random.gamma(5, 15, n) + 20, 'dti': np.random.beta(2, 5, n) * 60, 'credit_score': np.random.normal(700, 50, n).clip(550, 850).astype(int), 'loan_amount': np.random.gamma(4, 50, n) + 30, 'loan_term': np.random.choice([12, 24, 36, 48, 60, 72], n), 'employment_years': np.random.gamma(3, 5, n).clip(0, 30).astype(int), 'home_owner': np.random.binomial(1, 0.65, n), 'marital_status': np.random.choice([0, 1, 2], n, p=[0.4, 0.4, 0.2]), # 0: single, 1: married, 2: other 'education': np.random.choice([0, 1, 2, 3], n, p=[0.2, 0.3, 0.3, 0.2]) # 0: high school, 1: some college, 2: bachelor, 3: post-grad }) # Economic indicators (add as columns) # Simulate time-varying macro data (quarterly) quarters = np.repeat(np.arange(1, 21), n/20) # 20 quarters quarters = quarters[:n] # GDP growth, unemployment, inflation gdp = np.random.normal(0.025, 0.01, 20)[quarters.astype(int)-1] unemp = np.random.normal(0.05, 0.005, 20)[quarters.astype(int)-1] inf = np.random.normal(0.02, 0.005, 20)[quarters.astype(int)-1] df['gdp'] = gdp df['unemployment'] = unemp df['inflation'] = inf # Generate default based on features (logistic) log_odds = (-4.5 + 0.04*df['dti'] - 0.005*df['credit_score'] + 0.01*(df['loan_amount']/1000) + 0.02*df['employment_years'] - 0.01*df['age'] + 0.3*df['home_owner'] - 0.5*df['marital_status'] - 0.3*df['education'] - 1.5*df['gdp'] + 3*df['unemployment']) prob = 1/(1+np.exp(-log_odds)) df['default'] = np.random.binomial(1, prob) # LGD (loss given default) df['lgd'] = np.where(df['home_owner'] == 1, np.random.normal(0.35, 0.10, n).clip(0.05, 0.80), np.random.normal(0.65, 0.10, n).clip(0.05, 0.95)) # EAD = loan amount (for term loans) df['ead'] = df['loan_amount'] return df # Generate data df_loan = generate_data(10000) # Save to CSV for dashboard df_loan.to_csv('loan_data.csv', index=False) print("Data generated and saved.") print(f"Shape: {df_loan.shape}") print(f"Default rate: {df_loan['default'].mean():.4f}") # ---------------------------------------------------------------- # PART B: DATA PREPARATION AND MODEL BUILDING # ---------------------------------------------------------------- def build_model(df): """Train a default prediction model.""" features = ['age', 'income', 'dti', 'credit_score', 'loan_amount', 'loan_term', 'employment_years', 'home_owner', 'marital_status', 'education', 'gdp', 'unemployment', 'inflation'] X = df[features] y = df['default'] # Train-test split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # Scale scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # Model (XGBoost) model = xgb.XGBClassifier(n_estimators=100, max_depth=5, learning_rate=0.1, random_state=42, use_label_encoder=False, eval_metric='logloss') model.fit(X_train_scaled, y_train) # Predictions y_pred_proba = model.predict_proba(X_test_scaled)[:, 1] auc = roc_auc_score(y_test, y_pred_proba) print(f"Model AUC: {auc:.4f}") # Save model and scaler import joblib joblib.dump(model, 'model.pkl') joblib.dump(scaler, 'scaler.pkl') joblib.dump(features, 'features.pkl') return model, scaler, features, X_test, y_test, y_pred_proba # Build the model model, scaler, features, X_test, y_test, y_pred_proba = build_model(df_loan) # ---------------------------------------------------------------- # PART C: RISK CALCULATION # ---------------------------------------------------------------- def calculate_risk(df, model, scaler, features): """Compute PD, EL, and economic capital.""" X = df[features] X_scaled = scaler.transform(X) pd_pred = model.predict_proba(X_scaled)[:, 1] df['pd'] = pd_pred df['el'] = df['pd'] * df['lgd'] * df['ead'] # Economic capital (Vasicek) from scipy.stats import norm rho = 0.12 z = norm.ppf(0.999) pd_adj = norm.cdf((norm.ppf(df['pd']) + np.sqrt(rho) * z) / np.sqrt(1 - rho)) df['ul'] = df['lgd'] * (pd_adj - df['pd']) * df['ead'] df['ul'] = np.maximum(df['ul'], 0) return df df_risk = calculate_risk(df_loan, model, scaler, features) print(f"Total EL: ${df_risk['el'].sum():,.2f}") print(f"Total UL (99.9%): ${df_risk['ul'].sum():,.2f}") print(f"EL as % of portfolio: {df_risk['el'].sum() / df_risk['ead'].sum() * 100:.2f}%") # ---------------------------------------------------------------- # PART D: STRESS TESTING # ---------------------------------------------------------------- def stress_test(df, model, scaler, features, gdp_shock, unemp_shock): """Apply macroeconomic shocks and recalc EL.""" df_stressed = df.copy() # Apply shocks: GDP shock shifts the gdp feature, unemployment shock shifts unemployment df_stressed['gdp'] = df_stressed['gdp'] + gdp_shock df_stressed['unemployment'] = df_stressed['unemployment'] + unemp_shock # Recalculate PD X_stressed = df_stressed[features] X_scaled_stressed = scaler.transform(X_stressed) pd_stressed = model.predict_proba(X_scaled_stressed)[:, 1] df_stressed['pd_stressed'] = pd_stressed df_stressed['el_stressed'] = pd_stressed * df_stressed['lgd'] * df_stressed['ead'] return df_stressed # Define scenarios scenarios = { 'Baseline': (0.0, 0.0), 'Adverse': (-0.02, 0.03), 'Severely Adverse': (-0.05, 0.06) } stress_results = [] for name, (gdp_shock, unemp_shock) in scenarios.items(): df_scenario = stress_test(df_risk, model, scaler, features, gdp_shock, unemp_shock) el_total = df_scenario['el_stressed'].sum() stress_results.append({'Scenario': name, 'EL': el_total, 'Increase %': (el_total/df_risk['el'].sum() - 1)*100}) print("\nStress Test Results:") for res in stress_results: print(f"{res['Scenario']}: EL = ${res['EL']:,.2f} (increase {res['Increase %']:.1f}%)") # ---------------------------------------------------------------- # PART E: DASHBOARD (Streamlit) – Code Snippet # ---------------------------------------------------------------- st.markdown(""" # Credit Risk Dashboard ## Portfolio Overview """) # Load data (in real dashboard, use st.cache) df = pd.read_csv('loan_data.csv') model = joblib.load('model.pkl') scaler = joblib.load('scaler.pkl') features = joblib.load('features.pkl') # Compute metrics total_exposure = df['ead'].sum() avg_pd = df['pd'].mean() total_el = df['el'].sum() ul_99 = df['ul'].sum() col1, col2, col3, col4 = st.columns(4) col1.metric("Total Exposure", f"${total_exposure:,.0f}") col2.metric("Average PD", f"{avg_pd:.2%}") col3.metric("Expected Loss", f"${total_el:,.0f}") col4.metric("Economic Capital (99.9%)", f"${ul_99:,.0f}") # PD Distribution st.subheader("PD Distribution") fig, ax = plt.subplots() ax.hist(df['pd'], bins=50, edgecolor='black', alpha=0.7) st.pyplot(fig) # Scenario Analysis st.subheader("Scenario Analysis") gdp_slider = st.slider("GDP Shock (%)", -5.0, 5.0, 0.0, 0.1) unemp_slider = st.slider("Unemployment Shock (pp)", -2.0, 5.0, 0.0, 0.1) if st.button("Apply Stress"): df_stressed = stress_test(df, model, scaler, features, gdp_slider/100, unemp_slider/100) new_el = df_stressed['el_stressed'].sum() st.metric("Stressed EL", f"${new_el:,.0f}", delta=f"{new_el/total_el - 1:.1%}") # Individual Loan Lookup st.subheader("Loan Level Detail") loan_id = st.number_input("Enter Loan ID", min_value=1, max_value=len(df)) loan = df[df['loan_id'] == loan_id] if not loan.empty: st.write(loan[['loan_id', 'pd', 'el', 'lgd', 'ead', 'default']].iloc[0]) # SHAP explanation (would need to compute SHAP for the loan) st.write("SHAP explanation not shown in this snippet.")
SECTION 5: PROJECT DELIVERABLES CHECKLIST
| Deliverable | Description | Completed |
|---|---|---|
| Code Repository | All Python scripts, notebooks, and data. | ☐ |
| Data Pipeline | Ingestion, cleaning, feature engineering. | ☐ |
| Model | Trained and validated predictive model. | ☐ |
| Risk Calculations | PD, LGD, EAD, EL, UL. | ☐ |
| Stress Testing | Scenario analysis. | ☐ |
| Dashboard | Interactive Streamlit/Dash app. | ☐ |
| Documentation | README, project report, and model card. | ☐ |
| Presentation | 10-minute video or slide deck. | ☐ |
SECTION 6: SUMMARY AND NEXT STEPS
Congratulations! You have completed the Diploma in Financial Data Analytics. You have covered:
-
Module 1: Data landscape, warehousing, SQL, ETL, governance.
-
Module 2: EDA, data preprocessing, feature engineering.
-
Module 3: Probability, statistics, hypothesis testing, MLE, Bayesian methods.
-
Module 4: Machine learning (logistic regression, decision trees, SVM, GBM, deep learning, clustering).
-
Module 5: Financial risk (VaR, GARCH, model validation, credit risk, operational risk, market risk, ALM).
-
Module 6: Advanced topics (NLP, Generative AI, XAI, RL, AI governance, blockchain, quantum).
You are now equipped to:
-
Lead data analytics projects in banking and finance.
-
Build and validate predictive models.
-
Manage financial risk with quantitative methods.
-
Apply cutting-edge AI and emerging technologies.
-
Ensure compliance and ethical use of AI.
Next Steps:
-
Build your portfolio with real-world projects.
-
Pursue certifications (CFA, FRM, or data science certifications).
-
Stay updated with industry trends (AI, blockchain, quantum).
-
Network with professionals in fintech and banking.
-
Consider further studies (MSc in Financial Data Analytics, etc.).
Good luck on your journey!