SECTION 1: LEARNING OBJECTIVES
By the end of this lesson, you will be able to:
-
Define Digital Twins and understand their application in finance – simulating customers, portfolios, markets, and entire banking ecosystems.
-
Distinguish between different types of digital twins – product twins, process twins, system twins, and customer twins.
-
Understand the architecture of a financial digital twin – data ingestion, simulation engine, analytics, and visualisation.
-
Apply agent-based modelling to simulate customer behaviour, market dynamics, and portfolio risk.
-
Use digital twins for stress testing, scenario analysis, and business strategy optimisation.
-
Build a simple digital twin for a bank’s deposit portfolio using Python.
-
Understand the challenges – data requirements, model complexity, computational cost, and validation.
-
Identify the business value – improved decision-making, reduced risk, and enhanced customer experience.
SECTION 2: WHAT IS A DIGITAL TWIN?
Digital Twin is a virtual representation of a physical object, system, or process that mirrors its real-world counterpart in real-time or near-real-time.
Components of a Digital Twin:
-
Physical Entity: The real-world system (e.g., a bank, a customer base, a portfolio).
-
Data: Real-time and historical data from sensors, transactions, and external sources.
-
Model: A simulation or analytical model that replicates the behaviour of the physical entity.
-
Analytics: Algorithms that extract insights, predict outcomes, and optimise decisions.
-
Visualisation: Interactive dashboards and 3D representations.
Types of Digital Twins:
| Type | Description | Financial Example |
|---|---|---|
| Product Twin | Simulates a specific product. | Loan product performance simulation. |
| Process Twin | Simulates a business process. | Loan origination process simulation. |
| System Twin | Simulates an entire system. | The entire banking system (deposits, loans, investments). |
| Customer Twin | Simulates a customer’s behaviour. | Customer lifecycle and churn prediction. |
| Market Twin | Simulates financial markets. | Market dynamics, price movements, volatility. |
| Portfolio Twin | Simulates a portfolio of assets. | Portfolio risk and return simulation. |
SECTION 3: DIGITAL TWINS IN FINANCE – USE CASES
3.1 Customer Digital Twins
| Application | Description | Benefit |
|---|---|---|
| Customer Journey Simulation | Model how customers interact with the bank. | Optimise customer experience. |
| Churn Prediction | Simulate customer behaviour to predict churn. | Proactive retention strategies. |
| Personalised Offers | Simulate customer response to different offers. | Increase conversion rates. |
| Lifecycle Management | Model customer value over time. | Optimise product recommendations. |
3.2 Portfolio Digital Twins
| Application | Description | Benefit |
|---|---|---|
| Risk Simulation | Simulate portfolio performance under stress. | Better risk management. |
| Asset Allocation | Test different allocation strategies. | Optimise returns for a given risk. |
| Liquidity Management | Simulate cash flows and liquidity needs. | Optimise balance sheet management. |
| Performance Attribution | Identify sources of portfolio returns. | Better investment decisions. |
3.3 Market Digital Twins
| Application | Description | Benefit |
|---|---|---|
| Market Microstructure | Simulate order book dynamics. | Better trading strategies. |
| Price Discovery | Simulate price formation mechanisms. | Improved market predictions. |
| Systemic Risk | Simulate interbank contagion. | Better systemic risk management. |
3.4 Operational Digital Twins
| Application | Description | Benefit |
|---|---|---|
| Branch Operations | Simulate branch traffic, staffing, and service. | Optimise branch operations. |
| Fraud Detection | Simulate fraud patterns. | Better fraud detection systems. |
| Process Optimisation | Simulate loan origination, approval, and servicing. | Reduce processing time and costs. |
SECTION 4: AGENT-BASED MODELLING FOR DIGITAL TWINS
Agent-Based Modelling (ABM) is a common technique for building digital twins of financial systems.
Key Concepts:
-
Agents: Autonomous entities with behaviours and interactions (customers, banks, regulators).
-
Environment: The context in which agents operate (market conditions, economic factors).
-
Rules: The logic governing agent behaviour.
-
Emergence: System-level outcomes emerge from individual agent interactions.
Advantages of ABM:
-
Captures heterogeneity (different customer types).
-
Models complex interactions and feedback loops.
-
Enables “what-if” scenario analysis.
-
Can incorporate machine learning for agent behaviour.
SECTION 5: IMPLEMENTATION IN PYTHON – DIGITAL TWIN FOR A BANK’S DEPOSIT PORTFOLIO
We’ll build a simplified digital twin for a bank’s deposit portfolio, simulating customer behaviour, interest rates, and deposit flows.
# =================================================================== # MODULE 7, LESSON 4: DIGITAL TWINS IN FINANCE # =================================================================== import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from dataclasses import dataclass from typing import List, Dict from scipy.stats import norm, lognorm, weibull_min import warnings warnings.filterwarnings('ignore') # Set style sns.set_style("whitegrid") np.random.seed(42) print("="*70) print("DIGITAL TWINS IN FINANCE – DEPOSIT PORTFOLIO SIMULATION") print("="*70) # ---------------------------------------------------------------- # PART A: DEFINE AGENT CLASSES # ---------------------------------------------------------------- @dataclass class Customer: """Represents a customer in the digital twin.""" id: int age: float income: float balance: float deposit_elasticity: float # Sensitivity to interest rates withdrawal_probability: float # Base probability of withdrawal churn_probability: float # Probability of leaving the bank def update_balance(self, interest_rate, market_rate, time_step=1): """ Update customer balance based on interest rates and behaviour. """ # Interest earned interest = self.balance * interest_rate * time_step # Withdrawal based on rate differential (higher rates -> lower withdrawals) rate_diff = market_rate - interest_rate withdrawal_effect = 1 - self.deposit_elasticity * max(0, rate_diff) withdrawal_effect = max(0.5, min(1.5, withdrawal_effect)) # Base withdrawal withdrawal_prob = self.withdrawal_probability * withdrawal_effect withdrawal = np.random.binomial(1, withdrawal_prob * time_step) if withdrawal: withdrawal_amount = self.balance * np.random.uniform(0.05, 0.3) self.balance -= withdrawal_amount # New deposits (savings from income) savings_rate = 0.1 * (1 + rate_diff * 0.5) savings_rate = max(0.02, min(0.2, savings_rate)) new_deposit = self.income * savings_rate * time_step * np.random.uniform(0.5, 1.5) self.balance += new_deposit # Churn if np.random.random() < self.churn_probability * time_step: self.balance = 0 return False # Customer churned return True # Customer active class Bank: """Represents the bank in the digital twin.""" def __init__(self, name): self.name = name self.customers: List[Customer] = [] self.deposit_rate = 0.02 # 2% annual deposit rate self.market_rate = 0.05 # 5% market rate def add_customer(self, customer: Customer): self.customers.append(customer) def set_rates(self, deposit_rate: float, market_rate: float): self.deposit_rate = deposit_rate self.market_rate = market_rate def simulate_month(self): """ Simulate one month of activity. """ active_customers = [] total_balance = 0 total_withdrawals = 0 total_deposits = 0 for customer in self.customers: if customer.balance > 0: active = customer.update_balance( self.deposit_rate / 12, # Monthly rate self.market_rate / 12, time_step=1 ) if active: active_customers.append(customer) total_balance += customer.balance self.customers = active_customers return { 'total_balance': total_balance, 'num_customers': len(active_customers), 'avg_balance': total_balance / len(active_customers) if active_customers else 0 } # ---------------------------------------------------------------- # PART B: GENERATE SYNTHETIC CUSTOMER BASE # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART B: Generating Synthetic Customer Base") print("-"*60) def generate_customer_base(n_customers=5000): """Generate a diverse customer base.""" customers = [] for i in range(n_customers): age = np.random.normal(45, 15).clip(18, 80) income = np.random.gamma(5, 15).clip(10, 150) * 1000 # Annual income balance = np.random.gamma(3, 50).clip(0, 50000) # Initial balance # Different segments segment = np.random.choice(['mass', 'affluent', 'premium'], p=[0.6, 0.3, 0.1]) if segment == 'affluent': income *= 1.5 balance *= 2 elif segment == 'premium': income *= 2.5 balance *= 4 # Behavioural parameters deposit_elasticity = np.random.beta(2, 5).clip(0.1, 0.8) withdrawal_probability = np.random.beta(2, 8).clip(0.01, 0.2) churn_probability = np.random.beta(1, 10).clip(0.001, 0.05) customer = Customer( id=i, age=age, income=income, balance=balance, deposit_elasticity=deposit_elasticity, withdrawal_probability=withdrawal_probability, churn_probability=churn_probability ) customers.append(customer) return customers # Generate customer base customers = generate_customer_base(5000) print(f"Generated {len(customers)} customers.") # Customer statistics balances = [c.balance for c in customers] ages = [c.age for c in customers] incomes = [c.income for c in customers] print(f"Average balance: ${np.mean(balances):,.2f}") print(f"Median balance: ${np.median(balances):,.2f}") print(f"Average age: {np.mean(ages):.1f}") print(f"Average income: ${np.mean(incomes):,.2f}") # ---------------------------------------------------------------- # PART C: INITIALISE AND RUN DIGITAL TWIN # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART C: Running the Digital Twin") print("-"*60) # Create bank bank = Bank("Digital Bank") for customer in customers: bank.add_customer(customer) # Initial state initial_balance = sum(c.balance for c in bank.customers) initial_customers = len(bank.customers) print(f"Initial total deposits: ${initial_balance:,.2f}") print(f"Initial customers: {initial_customers}") # Simulate 36 months (3 years) n_months = 36 history = [] # Different rate scenarios scenarios = { 'Baseline': {'deposit_rate': 0.02, 'market_rate': 0.05}, 'High Rate': {'deposit_rate': 0.04, 'market_rate': 0.06}, 'Low Rate': {'deposit_rate': 0.01, 'market_rate': 0.03}, 'Rising Rate': {'deposit_rate': 0.02, 'market_rate': 0.05}, } scenario_results = {} for scenario_name, rates in scenarios.items(): print(f"\nRunning scenario: {scenario_name}") # Reset bank for each scenario bank2 = Bank("Digital Bank") for customer in customers: # Reset customer balances customer.balance = balances[customers.index(customer)] bank2.add_customer(customer) bank2.set_rates(rates['deposit_rate'], rates['market_rate']) history_scenario = [] for month in range(n_months): result = bank2.simulate_month() result['month'] = month history_scenario.append(result) scenario_results[scenario_name] = pd.DataFrame(history_scenario) # ---------------------------------------------------------------- # PART D: ANALYSE AND VISUALISE RESULTS # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART D: Digital Twin Results") print("-"*60) fig, axes = plt.subplots(2, 2, figsize=(15, 10)) for idx, (scenario_name, df_scenario) in enumerate(scenario_results.items()): ax = axes[idx // 2, idx % 2] # Plot total balance over time ax.plot(df_scenario['month'], df_scenario['total_balance'] / 1e6, 'b-', linewidth=2) ax.set_xlabel('Month') ax.set_ylabel('Total Deposits ($M)') ax.set_title(f'Scenario: {scenario_name}') ax.grid(True, alpha=0.3) # Add customer count as secondary axis ax2 = ax.twinx() ax2.plot(df_scenario['month'], df_scenario['num_customers'], 'r--', linewidth=1) ax2.set_ylabel('Number of Customers', color='red') ax2.tick_params(axis='y', labelcolor='red') # Final stats final_balance = df_scenario['total_balance'].iloc[-1] final_customers = df_scenario['num_customers'].iloc[-1] ax.text(0.95, 0.05, f'Final: ${final_balance/1e6:.1f}M\nCustomers: {final_customers}', transform=ax.transAxes, ha='right', va='bottom', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) plt.tight_layout() plt.savefig('digital_twin_results.png', dpi=300) plt.show() # ---------------------------------------------------------------- # PART E: COMPARISON OF SCENARIOS # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART E: Scenario Comparison") print("-"*60) comparison_data = [] for scenario_name, df_scenario in scenario_results.items(): final_balance = df_scenario['total_balance'].iloc[-1] final_customers = df_scenario['num_customers'].iloc[-1] growth = (final_balance - initial_balance) / initial_balance * 100 comparison_data.append({ 'Scenario': scenario_name, 'Final Deposits ($M)': final_balance / 1e6, 'Final Customers': final_customers, 'Growth (%)': growth, 'Rate Differential (%)': (scenarios[scenario_name]['market_rate'] - scenarios[scenario_name]['deposit_rate']) * 100 }) comparison_df = pd.DataFrame(comparison_data) print(comparison_df.to_string(index=False)) # Visualise comparison fig, axes = plt.subplots(1, 2, figsize=(14, 5)) ax = axes[0] ax.bar(comparison_df['Scenario'], comparison_df['Growth (%)'], color='blue', alpha=0.7) ax.set_xlabel('Scenario') ax.set_ylabel('Deposit Growth (%)') ax.set_title('Portfolio Growth by Scenario') ax.axhline(0, color='black', linestyle='-', alpha=0.3) ax.grid(True, alpha=0.3) ax = axes[1] ax.bar(comparison_df['Scenario'], comparison_df['Final Customers'], color='green', alpha=0.7) ax.set_xlabel('Scenario') ax.set_ylabel('Final Customer Count') ax.set_title('Customer Retention by Scenario') ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('scenario_comparison.png', dpi=300) plt.show() # ---------------------------------------------------------------- # PART F: ADVANCED – AGENT-BASED MARKET SIMULATION # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART F: Agent-Based Market Simulation (Extended)") print("-"*60) class MarketAgent: """Represents a market participant (trader/investor).""" def __init__(self, id, initial_cash, risk_tolerance, strategy): self.id = id self.cash = initial_cash self.holdings = 0 self.risk_tolerance = risk_tolerance self.strategy = strategy # 'momentum', 'value', 'noise' self.trades = [] def trade(self, price, volatility): """Decide whether to buy, sell, or hold.""" if self.strategy == 'momentum': # Buy if price is rising if np.random.random() < 0.6: return 'buy', self.cash * 0.1 elif self.strategy == 'value': # Buy if price is low (below intrinsic value) intrinsic_value = 100 if price < intrinsic_value * 0.9: return 'buy', self.cash * 0.15 elif self.strategy == 'noise': # Random trading if np.random.random() < 0.2: return 'buy', self.cash * 0.05 if np.random.random() < 0.2: return 'sell', self.holdings * 0.1 return 'hold', 0 # Simulate a market with agents n_agents = 100 agents = [] for i in range(n_agents): cash = np.random.uniform(1000, 10000) risk_tolerance = np.random.uniform(0.3, 0.9) strategy = np.random.choice(['momentum', 'value', 'noise'], p=[0.4, 0.3, 0.3]) agents.append(MarketAgent(i, cash, risk_tolerance, strategy)) # Market simulation n_days = 200 price = 100 price_history = [price] volatility = 0.02 for day in range(n_days): # Random price movement price *= 1 + np.random.normal(0, volatility) price = max(price, 50) price_history.append(price) # Agents trade for agent in agents: action, amount = agent.trade(price, volatility) if action == 'buy' and agent.cash >= amount: shares = amount / price agent.holdings += shares agent.cash -= amount agent.trades.append(('buy', day, price, shares)) elif action == 'sell' and agent.holdings > 0: shares = min(amount / price, agent.holdings) agent.cash += shares * price agent.holdings -= shares agent.trades.append(('sell', day, price, shares)) # Visualise market simulation fig, axes = plt.subplots(1, 2, figsize=(14, 5)) ax = axes[0] ax.plot(price_history, 'b-', linewidth=1.5) ax.set_xlabel('Day') ax.set_ylabel('Price') ax.set_title('Simulated Market Price (Agent-Based)') ax.grid(True, alpha=0.3) # Agent distribution ax = axes[1] agent_types = [agent.strategy for agent in agents] type_counts = pd.Series(agent_types).value_counts() ax.bar(type_counts.index, type_counts.values, color=['blue', 'green', 'orange']) ax.set_xlabel('Strategy Type') ax.set_ylabel('Number of Agents') ax.set_title('Agent Strategy Distribution') ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('market_simulation.png', dpi=300) plt.show() print("Market simulation completed.") # ---------------------------------------------------------------- # PART G: DIGITAL TWIN CHALLENGES AND BEST PRACTICES # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART G: Digital Twin Challenges and Best Practices") print("-"*60) print(""" Key Challenges: 1. Data Requirements: - Need large volumes of high-quality data. - Data must be real-time or near-real-time. - Historical data for model calibration. 2. Model Complexity: - Capturing all relevant behaviours is difficult. - Trade-off between realism and computability. - Model validation is challenging. 3. Computational Cost: - Agent-based simulations can be expensive. - Need scalable infrastructure (cloud, parallel computing). 4. Validation: - How do we know the twin is accurate? - Need continuous calibration against real-world data. - Uncertainty quantification is essential. 5. Governance: - Who owns the twin? - How are decisions made using twin outputs? - Regulatory acceptance is evolving. Best Practices: - Start with a focused problem (e.g., deposit portfolio). - Use hybrid modelling (agent-based + machine learning). - Validate against historical data and expert judgment. - Build incrementally – add complexity over time. - Use the twin for "what-if" analysis, not just prediction. - Document assumptions and limitations. - Engage stakeholders early in the design process. """) # ---------------------------------------------------------------- # PART H: BUSINESS VALUE AND ROI # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART H: Business Value of Digital Twins in Banking") print("-"*60) print(""" Business Value: 1. Better Decision-Making: - Test strategies before implementing them. - Reduce risk of poor decisions. 2. Risk Reduction: - Identify vulnerabilities before they materialise. - Stress test portfolios and systems. 3. Customer Experience: - Simulate customer journeys and optimise touchpoints. - Personalise offers based on simulated behaviour. 4. Operational Efficiency: - Optimise processes (e.g., loan origination). - Reduce costs through better resource allocation. 5. Innovation: - Experiment with new products and services. - Test market responses without real-world risk. ROI Estimation: - Typical ROI: 3-10x over 3-5 years. - Faster time-to-market for new products. - Reduced losses from avoided risks. - Improved customer retention and acquisition. Case Study: - A large European bank used a digital twin for liquidity management. - Simulated 100,000 customer scenarios daily. - Reduced liquidity buffer by 15% (saving €500M). - Increased NII by €50M/year. """) # ---------------------------------------------------------------- # PART I: SUMMARY AND RECOMMENDATIONS # ---------------------------------------------------------------- print("\n" + "="*70) print("PART I: Summary and Recommendations") print("="*70) print(""" Digital Twins in Finance – Key Takeaways: 1. Digital twins are virtual replicas of financial systems, customers, or processes. 2. Agent-based modelling is a common approach for financial digital twins. 3. Use cases: customer behaviour simulation, portfolio risk, market dynamics, and operational optimisation. 4. Benefits: better decisions, reduced risk, improved customer experience, and innovation. 5. Challenges: data requirements, model complexity, computational cost, and validation. 6. Business value: 3-10x ROI over 3-5 years. 7. Best practice: start focused, validate rigorously, and scale incrementally. Recommendations: - Start with a pilot for a specific business problem. - Use hybrid modelling (agent-based + ML) for richer behaviour. - Invest in data infrastructure (real-time data feeds). - Build a dedicated digital twin team (data scientists, domain experts, engineers). - Collaborate with academia and technology partners. - Develop a roadmap for scaling digital twins across the organisation. """) print("="*70) print("END OF LESSON 4 – MODULE 7") print("="*70)
SECTION 6: SUMMARY FOR THE DATA PRACTITIONER
-
Digital twins create virtual replicas of financial systems for simulation and analysis.
-
Agent-based modelling captures heterogeneous behaviours and emergent outcomes.
-
Key applications include customer simulation, portfolio risk, market dynamics, and operational optimisation.
-
Challenges include data requirements, model complexity, computational cost, and validation.
-
Business value includes better decisions, risk reduction, and improved customer experience.
-
Best practices: start focused, validate rigorously, and scale incrementally.
SECTION 7: RECOMMENDED NEXT STEPS
-
Build a simple agent-based model for a financial use case.
-
Explore AnyLogic, Mesa, or NetLogo for agent-based simulation.
-
Investigate digital twin platforms (e.g., Azure Digital Twins, AWS IoT TwinMaker).
-
Study hybrid modelling (ABM + ML) for richer simulations.
-
Prepare for the next lesson on Web3 and the Future of Finance.
[END OF LESSON 4 – MODULE 7]