Mathew K Analytics

Lesson 5 · Python for Banking and Finance

Effective Error Handling in Banking Systems Using Python for Secure Financial Software

In this lesson, we will learn how to handle errors when processing banking data in Python. Error handling is important in banking because even small…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Error Handling in Banking Systems#

  • In this lesson, we will learn how to handle errors when processing banking data in Python.
  • Error handling is important in banking because even small mistakes in code or data can lead to financial losses and compliance issues.
  • You will discover how to identify, handle, and debug different types of errors that commonly occur in banking applications.
  • By the end, you will be confident writing safer banking code and debugging real-world financial data!
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
np.random.seed(42)

Overview of Data Used in Banking Error Handling#

  • Banking data includes transactions like withdrawals, deposits, transfers, and more.
  • Data is usually structured in tables, with columns for customer IDs, amounts, dates, and transaction types.
  • Beginners often make mistakes like working with missing values, wrong column names, or mismatched data types.
# Create a synthetic banking transactions DataFrame
n_transactions = 1000
n_customers = 200
df = pd.DataFrame({
    'transaction_id': range(1, n_transactions + 1),
    'customer_id': np.random.choice([f'CUST_{i:04d}' for i in range(1, n_customers + 1)], n_transactions),
    'amount': np.round(np.random.normal(150, 60, n_transactions), 2),
    'transaction_type': np.random.choice(['Debit', 'Credit'], n_transactions),
    'channel': np.random.choice(['ATM', 'Online', 'Branch', 'POS'], n_transactions),
    'date': pd.date_range(start='2024-01-01', periods=n_transactions, freq='h')
})
print('Shape:', df.shape)
print(df.head(3))
Shape: (1000, 6)
   transaction_id customer_id  amount transaction_type channel  \
0               1   CUST_0103  238.77           Credit     ATM   
1               2   CUST_0180  269.17           Credit     POS   
2               3   CUST_0093   58.62           Credit  Online   

                 date  
0 2024-01-01 00:00:00  
1 2024-01-01 01:00:00  
2 2024-01-01 02:00:00  
# Example 1: Accessing a non-existent column (beginner error)
try:
    print(df['balance'])
except KeyError as e:
    print('Error:', e)
Error: 'balance'
# Example 2: Division by zero in transaction calculations (beginner error)
try:
    df['percentage'] = df['amount'] / 0
except ZeroDivisionError as e:
    print('Error:', e)
# Example 3: Type conversion errors (beginner error)
df['error_amount'] = ['one hundred'] * len(df)
try:
    df['error_amount'] = df['error_amount'].astype(float)
except ValueError as e:
    print('Error:', e)
Error: could not convert string to float: 'one hundred'
# Example 4: Handling missing data (intermediate error)
df_missing = df.copy()
df_missing.loc[0:10, 'amount'] = np.nan
try:
    total = df_missing['amount'].sum()
    print('Total amount:', total)
except Exception as e:
    print('Error:', e)
Total amount: 150032.92
# Example 5: Ensuring transaction amounts are positive (intermediate error)
negative_rows = df[df['amount'] < 0]
if not negative_rows.empty:
    print('Found negative amounts! Fixing them now...')
    df.loc[df['amount'] < 0, 'amount'] = 0
else:
    print('All amounts are positive.')
Found negative amounts! Fixing them now...
# Example 6: Catching and logging exceptions to a file (intermediate)
import logging
logging.basicConfig(filename='banking_errors.log', level=logging.ERROR, format='%(asctime)s %(levelname)s: %(message)s')

try:
    df['transaction_id'] = df['transaction_id'].astype(float)
    # purposely create a conversion issue
    df.at[0, 'transaction_id'] = 'error_id'
    df['transaction_id'] = df['transaction_id'].astype(int)
except Exception as e:
    print('Error logged to file.')
    logging.error('Problem with transaction_id conversion: %s', e)
Error logged to file.
# Example 7: Using assertions to enforce business rules (intermediate)
assert df['amount'].min() >= 0, 'All transaction amounts must be non-negative!'
print('All transaction amounts are non-negative.')
All transaction amounts are non-negative.
# Example 8: Handling multiple exception types (intermediate)
try:
    # Create a possible index error scenario
    print(df.iloc[5000])
except IndexError as e:
    print('IndexError:', e)
except Exception as e:
    print('Other error:', e)
IndexError: single positional indexer is out-of-bounds
# Example 9: Custom exception for suspicious transactions (advanced)
class SuspiciousTransaction(Exception):
    pass

def validate_transaction(amount):
    if amount > 5000:
        raise SuspiciousTransaction('Transaction above allowable threshold!')

try:
    test_amount = 6000
    validate_transaction(test_amount)
except SuspiciousTransaction as e:
    print('Custom Exception:', e)
Custom Exception: Transaction above allowable threshold!
# Example 10: Handling errors in batch transaction processing (advanced)
def process_transactions(df):
    errors = []
    for idx, row in df.iterrows():
        try:
            if row['amount'] < 0:
                raise ValueError('Negative transaction amount found!')
            # Simulate occasional type error
            if idx == 13:
                x = 'oops' + 10
        except Exception as e:
            errors.append((idx, str(e)))
    return errors

batch_errors = process_transactions(df)
print('Batch errors:', batch_errors[:3])
Batch errors: [(13, 'can only concatenate str (not "int") to str')]
# Example 11: Using finally for important cleanup after error (advanced)
try:
    risky = 10 / 0
except ZeroDivisionError:
    print('Division by zero!')
finally:
    print('This will always run, useful for cleanup.')
Division by zero!
This will always run, useful for cleanup.

Debugging banking data: locating common errors#

  • When your code fails, focus on the error message and the data used for that code.
  • Print() and df.info() are your best tools for finding issues.
  • In banking, errors can waste time or even stop trading or payment batches.
# Debugging: Checking data types and missingness
print(df.info())
print(df.isnull().sum())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 8 columns):
 #   Column            Non-Null Count  Dtype         
---  ------            --------------  -----         
 0   transaction_id    1000 non-null   object        
 1   customer_id       1000 non-null   object        
 2   amount            1000 non-null   float64       
 3   transaction_type  1000 non-null   object        
 4   channel           1000 non-null   object        
 5   date              1000 non-null   datetime64[ns]
 6   percentage        1000 non-null   float64       
 7   error_amount      1000 non-null   object        
dtypes: datetime64[ns](1), float64(2), object(5)
memory usage: 62.6+ KB
None
transaction_id      0
customer_id         0
amount              0
transaction_type    0
channel             0
date                0
percentage          0
error_amount        0
dtype: int64

Best practices for error handling in banking#

  • Always catch exceptions when reading, writing, or transforming financial data.
  • Validate inputs, outputs, and intermediate values.
  • Use logging for serious problems, not just print().
  • Assert business rules early and test boundary cases.
# Pattern: Context-specific error messages for users
try:
    user_input = int('ten')
except ValueError as e:
    print('Invalid entry: please enter a number amount, not text!')
Invalid entry: please enter a number amount, not text!
# Pattern: Using context to provide more helpful error logs
for idx, row in df.iterrows():
    try:
        val = int(row['amount'])
    except Exception as e:
        print(f'Error at transaction {row.transaction_id}: {e}')
        break
# All-in-one practice: Validate uploaded transactions and write to file with error handling
def validate_and_save(df):
    errors = []
    valid = []
    for idx, row in df.iterrows():
        try:
            amount = float(row['amount'])
            assert amount >= 0
            valid.append(row)
        except Exception as e:
            errors.append({'id': row['transaction_id'], 'error': str(e)})
    pd.DataFrame(valid).to_csv('valid_transactions.csv', index=False)
    pd.DataFrame(errors).to_csv('transaction_errors.csv', index=False)

validate_and_save(df)
print('Done validating and writing files.')
Done validating and writing files.
# BONUS: Preview valid transactions file
preview = pd.read_csv('valid_transactions.csv')
print('Preview:', preview.head(3))
Preview:   transaction_id customer_id  amount transaction_type channel  \
0       error_id   CUST_0103  238.77           Credit     ATM   
1            2.0   CUST_0180  269.17           Credit     POS   
2            3.0   CUST_0093   58.62           Credit  Online   

                  date  percentage error_amount  
0  2024-01-01 00:00:00         inf  one hundred  
1  2024-01-01 01:00:00         inf  one hundred  
2  2024-01-01 02:00:00         inf  one hundred  
 

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.