SECTION 1: LEARNING OBJECTIVES
By the end of this lesson, you will be able to:
-
Understand the fundamental concepts of descriptive statistics and their application in financial data analysis.
-
Calculate and interpret measures of central tendency (mean, median, mode) for financial variables.
-
Calculate and interpret measures of dispersion (variance, standard deviation, range, IQR) for risk assessment.
-
Understand probability theory fundamentals and their application in financial decision-making.
-
Apply probability rules (addition, multiplication, conditional probability) to financial scenarios.
-
Calculate and interpret expected values for financial outcomes.
-
Apply Bayes’ Theorem to update financial beliefs with new information.
-
Use Python to calculate descriptive statistics and probabilities for banking data.
SECTION 2: MEASURES OF CENTRAL TENDENCY
2.1 The Mean (Arithmetic Average)
The mean is the sum of all values divided by the number of values. In banking, the mean is used for average transaction amounts, average account balances, and average credit scores.
# ============= MEASURES OF CENTRAL TENDENCY ============= import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from scipy import stats # Create sample banking data np.random.seed(42) n = 1000 # Generate realistic banking data account_balances = np.random.lognormal(10, 1.2, n) # Right-skewed transaction_amounts = np.random.lognormal(4, 0.8, n) # Right-skewed credit_scores = np.random.normal(700, 50, n) # Approximately normal loan_amounts = np.random.lognormal(11, 0.5, n) # Right-skewed # Create DataFrame banking_stats = pd.DataFrame({ 'account_balance': account_balances, 'transaction_amount': transaction_amounts, 'credit_score': credit_scores, 'loan_amount': loan_amounts }) print("="*60) print("MEASURES OF CENTRAL TENDENCY") print("="*60) for col in banking_stats.columns: data = banking_stats[col] print(f"\n📊 {col.replace('_', ' ').title()}:") print(f" Mean: ${data.mean():,.2f}" if 'balance' in col or 'amount' in col else f" Mean: {data.mean():.2f}") print(f" Median: ${data.median():,.2f}" if 'balance' in col or 'amount' in col else f" Median: {data.median():.2f}") print(f" Mode: ${data.mode().iloc[0]:,.2f}" if 'balance' in col or 'amount' in col else f" Mode: {data.mode().iloc[0]:.2f}") print(f" Skewness: {data.skew():.3f}") # Visualize central tendency fig, axes = plt.subplots(2, 2, figsize=(14, 10)) for i, col in enumerate(banking_stats.columns): row = i // 2 col_idx = i % 2 data = banking_stats[col] ax = axes[row, col_idx] # Histogram data.hist(bins=50, ax=ax, edgecolor='black', alpha=0.7) # Add vertical lines for mean and median ax.axvline(data.mean(), color='red', linestyle='--', linewidth=2, label=f'Mean: {data.mean():.2f}') ax.axvline(data.median(), color='blue', linestyle='--', linewidth=2, label=f'Median: {data.median():.2f}') ax.set_title(f'{col.replace("_", " ").title()}', fontsize=12) ax.set_xlabel('Value') ax.set_ylabel('Frequency') ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('central_tendency.png', dpi=300) plt.show()
2.2 When to Use Mean vs Median
| Situation | Use Mean | Use Median |
|---|---|---|
| Normal Distribution | ✓ | ✓ |
| Skewed Distribution | ✗ | ✓ |
| Outliers Present | ✗ | ✓ |
| Financial Reporting | Often | Sometimes |
| Risk Assessment | Sometimes | Often |
Banking Example: Average Account Balance
# ============= MEAN VS MEDIAN IN BANKING ============= # Create a dataset with outliers (high net worth individuals) np.random.seed(42) regular_balances = np.random.lognormal(9, 0.8, 1000) high_net_worth = np.random.lognormal(13, 0.5, 50) all_balances = np.concatenate([regular_balances, high_net_worth]) print("\n" + "="*60) print("MEAN VS MEDIAN: ACCOUNT BALANCES") print("="*60) print(f"\n📊 Account Balance Statistics:") print(f" Mean: ${all_balances.mean():,.2f}") print(f" Median: ${np.median(all_balances):,.2f}") print(f" Difference: ${all_balances.mean() - np.median(all_balances):,.2f}") print(f" Skewness: {pd.Series(all_balances).skew():.3f}") print(f" High Net Worth Customers: {len(high_net_worth)} ({len(high_net_worth)/len(all_balances)*100:.1f}%)") # Visualize fig, ax = plt.subplots(figsize=(10, 6)) pd.Series(all_balances).hist(bins=50, ax=ax, edgecolor='black', alpha=0.7) ax.axvline(np.mean(all_balances), color='red', linestyle='--', linewidth=2, label=f'Mean: ${np.mean(all_balances):,.0f}') ax.axvline(np.median(all_balances), color='blue', linestyle='--', linewidth=2, label=f'Median: ${np.median(all_balances):,.0f}') ax.set_title('Account Balance Distribution - Mean vs Median', fontsize=14) ax.set_xlabel('Account Balance ($)') ax.set_ylabel('Frequency') ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('mean_vs_median.png', dpi=300) plt.show() print("\n💡 Insight:") print(" • Mean is pulled upward by high-net-worth customers") print(" • Median better represents the 'typical' customer") print(" • For regulatory reporting, banks often use both")
SECTION 3: MEASURES OF DISPERSION
3.1 Variance and Standard Deviation
Variance and standard deviation measure how spread out data is. In banking, these are used to measure risk and volatility.
# ============= MEASURES OF DISPERSION ============= print("\n" + "="*60) print("MEASURES OF DISPERSION") print("="*60) # Calculate dispersion measures for col in banking_stats.columns: data = banking_stats[col] print(f"\n📊 {col.replace('_', ' ').title()}:") print(f" Variance: {data.var():,.2f}") print(f" Standard Deviation: {data.std():,.2f}") print(f" Range: {data.max() - data.min():,.2f}") print(f" IQR: {data.quantile(0.75) - data.quantile(0.25):,.2f}") print(f" CV (Coefficient of Variation): {data.std() / data.mean():.3f}") # Coefficient of Variation (CV) measures relative variability # Useful for comparing risk across different scales print("\n" + "="*60) print("RISK ASSESSMENT USING DISPERSION") print("="*60) # Simulate two investment portfolios np.random.seed(42) portfolio_a_returns = np.random.normal(0.10, 0.15, 1000) # Mean 10%, Std 15% portfolio_b_returns = np.random.normal(0.08, 0.08, 1000) # Mean 8%, Std 8% portfolios = pd.DataFrame({ 'Portfolio A': portfolio_a_returns, 'Portfolio B': portfolio_b_returns }) print("\n📊 Portfolio Comparison:") for col in portfolios.columns: data = portfolios[col] print(f"\n{col}:") print(f" Mean Return: {data.mean():.2%}") print(f" Std Deviation: {data.std():.2%}") print(f" CV: {data.std() / data.mean():.3f}") print(f" Range: {data.max() - data.min():.2%}") print(f" VaR (5%): {data.quantile(0.05):.2%}") # Visualize fig, ax = plt.subplots(figsize=(10, 6)) portfolios.hist(bins=50, ax=ax, alpha=0.6, edgecolor='black') ax.set_title('Portfolio Return Distributions', fontsize=14) ax.set_xlabel('Return') ax.set_ylabel('Frequency') ax.axvline(0, color='black', linestyle='-', alpha=0.3) ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('portfolio_comparison.png', dpi=300) plt.show() print("\n💡 Insight:") print(" • Portfolio A has higher average return but higher volatility (CV: 1.50)") print(" • Portfolio B has lower average return but much lower volatility (CV: 1.00)") print(" • Risk-averse investors may prefer Portfolio B")
3.2 Interquartile Range (IQR)
IQR measures the spread of the middle 50% of data. It is robust to outliers.
# ============= IQR FOR OUTLIER DETECTION ============= def detect_outliers_iqr(data, multiplier=1.5): """Detect outliers using IQR method.""" q1 = data.quantile(0.25) q3 = data.quantile(0.75) iqr = q3 - q1 lower_bound = q1 - multiplier * iqr upper_bound = q3 + multiplier * iqr outliers = data[(data < lower_bound) | (data > upper_bound)] return outliers, lower_bound, upper_bound # Test on transaction amounts transactions = banking_stats['transaction_amount'] outliers, lower_bound, upper_bound = detect_outliers_iqr(transactions) print("\n" + "="*60) print("IQR - TRANSACTION AMOUNT OUTLIER DETECTION") print("="*60) print(f"\n📊 Transaction Amounts:") print(f" Q1 (25th percentile): ${transactions.quantile(0.25):.2f}") print(f" Q3 (75th percentile): ${transactions.quantile(0.75):.2f}") print(f" IQR: ${transactions.quantile(0.75) - transactions.quantile(0.25):.2f}") print(f" Normal Range: [${lower_bound:.2f}, ${upper_bound:.2f}]") print(f" Outliers: {len(outliers)} ({len(outliers)/len(transactions)*100:.1f}%)") print(f" Outlier Range: [${outliers.min():.2f}, ${outliers.max():.2f}]") # Visualize IQR fig, axes = plt.subplots(1, 2, figsize=(14, 5)) # Box plot ax1 = axes[0] sns.boxplot(y=transactions, ax=ax1) ax1.set_title('Box Plot - Transaction Amounts', fontsize=12) ax1.set_ylabel('Amount ($)') ax1.axhline(lower_bound, color='red', linestyle='--', label=f'Lower Bound: ${lower_bound:.2f}') ax1.axhline(upper_bound, color='red', linestyle='--', label=f'Upper Bound: ${upper_bound:.2f}') ax1.legend() # Histogram with outlier boundaries ax2 = axes[1] transactions.hist(bins=50, ax=ax2, edgecolor='black', alpha=0.7) ax2.axvline(lower_bound, color='red', linestyle='--', label=f'Lower Bound: ${lower_bound:.2f}') ax2.axvline(upper_bound, color='red', linestyle='--', label=f'Upper Bound: ${upper_bound:.2f}') ax2.set_title('Transaction Amount Distribution with Boundaries', fontsize=12) ax2.set_xlabel('Amount ($)') ax2.set_ylabel('Frequency') ax2.legend() plt.tight_layout() plt.savefig('iqr_analysis.png', dpi=300) plt.show()
SECTION 4: PROBABILITY FUNDAMENTALS
4.1 Basic Probability Concepts
Probability is the foundation of risk assessment, fraud detection, and financial modeling.
# ============= PROBABILITY FUNDAMENTALS ============= print("\n" + "="*60) print("PROBABILITY FUNDAMENTALS") print("="*60) # Example: Loan default probability # Historical data shows: # - 5% of all loans default # - 60% of defaults are from customers with credit score < 650 # - 20% of non-defaults are from customers with credit score < 650 # Calculate: # P(Default) = 0.05 # P(Low Credit | Default) = 0.60 # P(Low Credit | No Default) = 0.20 # 1. Joint Probability p_default = 0.05 p_no_default = 0.95 p_low_credit_given_default = 0.60 p_low_credit_given_no_default = 0.20 p_default_and_low_credit = p_default * p_low_credit_given_default p_no_default_and_low_credit = p_no_default * p_low_credit_given_no_default p_low_credit = p_default_and_low_credit + p_no_default_and_low_credit print("\n📊 Loan Default Analysis:") print(f" P(Default) = {p_default:.2%}") print(f" P(Low Credit | Default) = {p_low_credit_given_default:.2%}") print(f" P(Low Credit | No Default) = {p_low_credit_given_no_default:.2%}") print(f" P(Default ∩ Low Credit) = {p_default_and_low_credit:.2%}") print(f" P(Low Credit) = {p_low_credit:.2%}") # 2. Conditional Probability # What is P(Default | Low Credit)? p_default_given_low_credit = p_default_and_low_credit / p_low_credit print(f"\n P(Default | Low Credit) = {p_default_given_low_credit:.2%}") # 3. Bayes' Theorem print("\n📊 Bayes' Theorem Application:") print(f" P(Default | Low Credit) = P(Low Credit | Default) * P(Default) / P(Low Credit)") print(f" = {p_low_credit_given_default:.2%} * {p_default:.2%} / {p_low_credit:.2%}") print(f" = {p_default_given_low_credit:.2%}") # Create a function for probability calculations def calculate_conditional_probability(p_a, p_b_given_a, p_b_given_not_a): """Calculate P(A|B) using Bayes' Theorem.""" p_a_and_b = p_a * p_b_given_a p_not_a = 1 - p_a p_b = p_a_and_b + p_not_a * p_b_given_not_a p_a_given_b = p_a_and_b / p_b return p_a_given_b # Example: Credit card fraud detection p_fraud = 0.01 # 1% of transactions are fraudulent p_anomaly_given_fraud = 0.85 # 85% of fraud transactions are flagged p_anomaly_given_legit = 0.05 # 5% of legit transactions are flagged p_fraud_given_anomaly = calculate_conditional_probability( p_fraud, p_anomaly_given_fraud, p_anomaly_given_legit ) print("\n" + "="*60) print("FRAUD DETECTION EXAMPLE") print("="*60) print(f"\n📊 Credit Card Fraud Detection:") print(f" P(Fraud) = {p_fraud:.2%}") print(f" P(Anomaly | Fraud) = {p_anomaly_given_fraud:.2%}") print(f" P(Anomaly | Legit) = {p_anomaly_given_legit:.2%}") print(f" P(Fraud | Anomaly) = {p_fraud_given_anomaly:.2%}") print("\n💡 Insight:") print(" • Even with high detection rate (85%),") print(" • Only {:.1f}% of flagged transactions are actually fraud".format(p_fraud_given_anomaly*100)) print(" • This is why banks combine multiple signals for fraud detection")
4.2 Probability Distributions
# ============= PROBABILITY DISTRIBUTIONS ============= from scipy.stats import norm, lognorm, binom, poisson, expon print("\n" + "="*60) print("PROBABILITY DISTRIBUTIONS IN FINANCE") print("="*60) # 1. Normal Distribution - Stock Returns print("\n📊 Normal Distribution - Stock Returns:") mu = 0.05 # Expected return sigma = 0.15 # Volatility # Calculate probabilities x = np.linspace(mu - 4*sigma, mu + 4*sigma, 1000) y = norm.pdf(x, mu, sigma) print(f" Mean Return: {mu:.2%}") print(f" Volatility: {sigma:.2%}") print(f" P(Return < 0) = {norm.cdf(0, mu, sigma):.2%}") print(f" P(Return > 0.10) = {1 - norm.cdf(0.10, mu, sigma):.2%}") print(f" P(Return between -5% and 15%) = {norm.cdf(0.15, mu, sigma) - norm.cdf(-0.05, mu, sigma):.2%}") # 2. Log-normal Distribution - Asset Prices print("\n📊 Log-normal Distribution - Asset Prices:") price_mu = 100 # Current price price_sigma = 0.20 # Volatility # Calculate distribution parameters log_mu = np.log(price_mu) - 0.5 * price_sigma**2 # Calculate probabilities x_price = np.linspace(50, 200, 1000) y_price = lognorm.pdf(x_price, price_sigma, scale=np.exp(log_mu)) print(f" Current Price: ${price_mu:.2f}") print(f" Volatility: {price_sigma:.2%}") print(f" P(Price < $80) = {lognorm.cdf(80, price_sigma, scale=np.exp(log_mu)):.2%}") print(f" P(Price > $120) = {1 - lognorm.cdf(120, price_sigma, scale=np.exp(log_mu)):.2%}") # 3. Binomial Distribution - Loan Defaults print("\n📊 Binomial Distribution - Loan Defaults:") n_loans = 100 p_default = 0.05 # Calculate probabilities k = np.arange(0, 25) y_binom = binom.pmf(k, n_loans, p_default) print(f" Number of Loans: {n_loans}") print(f" Default Probability: {p_default:.2%}") print(f" Expected Defaults: {n_loans * p_default:.1f}") print(f" P(0 Defaults) = {binom.pmf(0, n_loans, p_default):.2%}") print(f" P(5 Defaults) = {binom.pmf(5, n_loans, p_default):.2%}") print(f" P(>5 Defaults) = {1 - binom.cdf(5, n_loans, p_default):.2%}") # 4. Poisson Distribution - Transaction Arrivals print("\n📊 Poisson Distribution - Transaction Arrivals:") lambda_rate = 10 # Average 10 transactions per minute print(f" Average Rate: {lambda_rate} transactions/minute") print(f" P(5 transactions) = {poisson.pmf(5, lambda_rate):.2%}") print(f" P(10 transactions) = {poisson.pmf(10, lambda_rate):.2%}") print(f" P(>15 transactions) = {1 - poisson.cdf(15, lambda_rate):.2%}") # 5. Exponential Distribution - Time Between Events print("\n📊 Exponential Distribution - Time Between Events:") mean_time = 6 # Average 6 minutes between transactions print(f" Average Time: {mean_time} minutes") print(f" P(Time < 1 min) = {expon.cdf(1, scale=mean_time):.2%}") print(f" P(Time > 10 min) = {1 - expon.cdf(10, scale=mean_time):.2%}") # Visualize distributions fig, axes = plt.subplots(2, 3, figsize=(15, 10)) # 1. Normal Distribution ax = axes[0, 0] ax.plot(x, y, 'b-', linewidth=2) ax.fill_between(x, 0, y, where=(x <= 0), color='red', alpha=0.3, label='Loss Region') ax.set_title('Normal Distribution - Stock Returns', fontsize=12) ax.set_xlabel('Return') ax.set_ylabel('Density') ax.axvline(0, color='black', linestyle='-', alpha=0.3) ax.grid(True, alpha=0.3) ax.legend() # 2. Log-normal Distribution ax = axes[0, 1] ax.plot(x_price, y_price, 'b-', linewidth=2) ax.axvline(price_mu, color='red', linestyle='--', label=f'Current Price: ${price_mu:.0f}') ax.set_title('Log-normal Distribution - Asset Prices', fontsize=12) ax.set_xlabel('Price ($)') ax.set_ylabel('Density') ax.grid(True, alpha=0.3) ax.legend() # 3. Binomial Distribution ax = axes[0, 2] ax.bar(k, y_binom, alpha=0.7, color='steelblue') ax.set_title('Binomial Distribution - Loan Defaults', fontsize=12) ax.set_xlabel('Number of Defaults') ax.set_ylabel('Probability') ax.grid(True, alpha=0.3) # 4. Poisson Distribution ax = axes[1, 0] k_poisson = np.arange(0, 30) y_poisson = poisson.pmf(k_poisson, lambda_rate) ax.bar(k_poisson, y_poisson, alpha=0.7, color='green') ax.set_title('Poisson Distribution - Transaction Arrivals', fontsize=12) ax.set_xlabel('Number of Transactions') ax.set_ylabel('Probability') ax.axvline(lambda_rate, color='red', linestyle='--', label=f'Mean: {lambda_rate}') ax.grid(True, alpha=0.3) ax.legend() # 5. Exponential Distribution ax = axes[1, 1] x_exp = np.linspace(0, 30, 1000) y_exp = expon.pdf(x_exp, scale=mean_time) ax.plot(x_exp, y_exp, 'b-', linewidth=2) ax.set_title('Exponential Distribution - Time Between Events', fontsize=12) ax.set_xlabel('Time (minutes)') ax.set_ylabel('Density') ax.axvline(mean_time, color='red', linestyle='--', label=f'Mean: {mean_time} min') ax.grid(True, alpha=0.3) ax.legend() # 6. Comparison of distributions ax = axes[1, 2] # Generate samples from each distribution normal_samples = np.random.normal(0, 1, 1000) lognormal_samples = np.random.lognormal(0, 0.5, 1000) poisson_samples = np.random.poisson(5, 1000) ax.hist(normal_samples, bins=30, alpha=0.5, label='Normal', density=True) ax.hist(lognormal_samples, bins=30, alpha=0.5, label='Log-normal', density=True) ax.hist(poisson_samples, bins=30, alpha=0.5, label='Poisson', density=True) ax.set_title('Distribution Comparison', fontsize=12) ax.set_xlabel('Value') ax.set_ylabel('Density') ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('probability_distributions.png', dpi=300) plt.show()
SECTION 5: EXPECTED VALUE AND RISK METRICS
5.1 Expected Value
Expected value is the weighted average of possible outcomes. In banking, it’s used for pricing loans, insurance, and derivatives.
# ============= EXPECTED VALUE ============= print("\n" + "="*60) print("EXPECTED VALUE IN FINANCE") print("="*60) # Example 1: Loan Expected Return print("\n📊 Loan Expected Return:") loan_amount = 100000 interest_rate = 0.08 default_prob = 0.05 recovery_rate = 0.40 # Outcomes if_no_default = loan_amount * (1 + interest_rate) if_default = loan_amount * recovery_rate expected_return = (1 - default_prob) * if_no_default + default_prob * if_default expected_profit = expected_return - loan_amount expected_return_pct = expected_return / loan_amount - 1 print(f" Loan Amount: ${loan_amount:,.0f}") print(f" Interest Rate: {interest_rate:.2%}") print(f" Default Probability: {default_prob:.2%}") print(f" Recovery Rate: {recovery_rate:.2%}") print(f" Expected Return: ${expected_return:,.2f}") print(f" Expected Profit: ${expected_profit:,.2f}") print(f" Expected Return %: {expected_return_pct:.2%}") # Example 2: Credit Card Expected Revenue print("\n📊 Credit Card Expected Revenue:") avg_balance = 5000 annual_fee = 95 interchange_rate = 0.02 avg_spend = 12000 default_prob_card = 0.03 loss_given_default = 0.50 revenue_from_fees = annual_fee revenue_from_interchange = avg_spend * interchange_rate loss_from_default = avg_balance * default_prob_card * loss_given_default expected_revenue = revenue_from_fees + revenue_from_interchange - loss_from_default print(f" Average Balance: ${avg_balance:,.0f}") print(f" Annual Fee: ${annual_fee:.0f}") print(f" Interchange Rate: {interchange_rate:.2%}") print(f" Average Annual Spend: ${avg_spend:,.0f}") print(f" Expected Revenue: ${expected_revenue:.2f}") print(f" Revenue Breakdown:") print(f" • Annual Fees: ${revenue_from_fees:.2f}") print(f" • Interchange: ${revenue_from_interchange:.2f}") print(f" • Expected Loss: -${loss_from_default:.2f}") # Example 3: Investment Portfolio Expected Return print("\n📊 Portfolio Expected Return:") # Two-asset portfolio weights = [0.60, 0.40] expected_returns = [0.10, 0.06] volatilities = [0.15, 0.08] correlation = 0.30 portfolio_return = sum(w * r for w, r in zip(weights, expected_returns)) # Portfolio variance (2-asset case) var1 = volatilities[0]**2 var2 = volatilities[1]**2 cov12 = correlation * volatilities[0] * volatilities[1] portfolio_variance = (weights[0]**2 * var1 + weights[1]**2 * var2 + 2 * weights[0] * weights[1] * cov12) portfolio_volatility = np.sqrt(portfolio_variance) print(f" Asset 1: Expected Return {expected_returns[0]:.2%}, Volatility {volatilities[0]:.2%}") print(f" Asset 2: Expected Return {expected_returns[1]:.2%}, Volatility {volatilities[1]:.2%}") print(f" Correlation: {correlation:.2%}") print(f" Portfolio Expected Return: {portfolio_return:.2%}") print(f" Portfolio Volatility: {portfolio_volatility:.2%}") print(f" Sharpe Ratio (risk-free=2%): {(portfolio_return - 0.02) / portfolio_volatility:.3f}")
5.2 Value at Risk (VaR)
VaR measures the maximum expected loss over a given time period at a given confidence level.
# ============= VALUE AT RISK (VaR) ============= def calculate_var(returns, confidence_level=0.95, method='historical'): """Calculate Value at Risk using different methods.""" if method == 'historical': # Historical VaR var = -np.percentile(returns, (1 - confidence_level) * 100) return var elif method == 'parametric': # Parametric VaR (assuming normal distribution) mu = returns.mean() sigma = returns.std() z_score = stats.norm.ppf(confidence_level) var = -(mu - z_score * sigma) return var elif method == 'monte_carlo': # Monte Carlo VaR (simplified) n_simulations = 10000 mu = returns.mean() sigma = returns.std() simulated = np.random.normal(mu, sigma, n_simulations) var = -np.percentile(simulated, (1 - confidence_level) * 100) return var # Generate sample returns np.random.seed(42) daily_returns = np.random.normal(0.0005, 0.02, 1000) # Mean 0.05%, Std 2% portfolio_value = 1000000 print("\n" + "="*60) print("VALUE AT RISK (VaR) CALCULATION") print("="*60) print(f"\n📊 Portfolio Value: ${portfolio_value:,.0f}") print(f" Daily Returns: Mean={daily_returns.mean():.4%}, Std={daily_returns.std():.4%}") # Calculate VaR using different methods confidence_levels = [0.90, 0.95, 0.99] print("\nVaR Estimates (1-day):") for confidence in confidence_levels: var_historical = calculate_var(daily_returns, confidence, 'historical') var_parametric = calculate_var(daily_returns, confidence, 'parametric') var_mc = calculate_var(daily_returns, confidence, 'monte_carlo') print(f"\n {confidence:.0%} Confidence Level:") print(f" Historical VaR: ${var_historical * portfolio_value:,.2f} ({var_historical:.2%})") print(f" Parametric VaR: ${var_parametric * portfolio_value:,.2f} ({var_parametric:.2%})") print(f" Monte Carlo VaR: ${var_mc * portfolio_value:,.2f} ({var_mc:.2%})") # Conditional VaR (Expected Shortfall) def calculate_cvar(returns, confidence_level=0.95): """Calculate Conditional VaR (Expected Shortfall).""" var = -np.percentile(returns, (1 - confidence_level) * 100) cvar = -returns[returns < -var].mean() return cvar cvar_95 = calculate_cvar(daily_returns, 0.95) print(f"\n📊 Conditional VaR (Expected Shortfall) at 95%:") print(f" CVaR: ${cvar_95 * portfolio_value:,.2f} ({cvar_95:.2%})") print(f" This is the expected loss given VaR is exceeded")
SECTION 6: BUSINESS RISK & FINANCIAL IMPACT
6.1 Regulatory Context
# ============= REGULATORY CONTEXT ============= print("\n" + "="*60) print("REGULATORY CONTEXT FOR STATISTICS") print("="*60) regulatory_requirements = { 'BASEL III': { 'description': 'Capital requirements based on risk metrics', 'metrics': ['VaR', 'Expected Shortfall', 'Probability of Default'] }, 'SR 11-7': { 'description': 'Model risk management', 'metrics': ['Mean', 'Variance', 'Confidence Intervals'] }, 'IFRS 9': { 'description': 'Expected credit loss calculation', 'metrics': ['Probability of Default', 'Loss Given Default', 'Exposure at Default'] }, 'CCAR': { 'description': 'Stress testing requirements', 'metrics': ['VaR', 'Expected Shortfall', 'Scenario Analysis'] } } for reg, details in regulatory_requirements.items(): print(f"\n📋 {reg}:") print(f" {details['description']}") print(f" Key Statistical Concepts: {', '.join(details['metrics'])}")
SECTION 7: SUMMARY FOR THE DATA PRACTITIONER
7.1 The 1-Minute Elevator Pitch
“Descriptive statistics and probability are the foundation of financial data analytics. We use measures of central tendency (mean, median) and dispersion (variance, standard deviation, IQR) to understand data distributions. Probability theory helps us quantify risk and uncertainty – from loan defaults to market movements. Expected value calculations guide pricing decisions, while VaR measures portfolio risk. These concepts are essential for regulatory compliance (BASEL III, SR 11-7) and informed financial decision-making.”
7.2 Key Takeaways
-
Mean is sensitive to outliers; median is robust for skewed data.
-
Standard deviation measures risk (volatility) in financial returns.
-
IQRÂ is robust for outlier detection and understanding data spread.
-
Bayes’ Theorem updates probabilities with new information.
-
Expected value guides pricing and investment decisions.
-
VaRÂ quantifies maximum expected loss at a confidence level.
-
Normal distribution models returns; lognormal models prices.
-
Binomial distribution models number of defaults; Poisson models arrivals.
-
Regulatory compliance requires understanding of statistical concepts.
-
Python enables calculation and visualization of statistics.
7.3 Recommended Next Steps
-
Practice calculating statistics on your banking datasets
-
Build risk models using VaR and expected value
-
Apply Bayesian methods to fraud detection
-
Understand regulatory requirements for statistics
-
Create dashboards showing key statistical metrics
[END OF LESSON 1]