1. LEARNING OBJECTIVES

By the end of this lesson, you will be able to:

  • Set up a Python development environment for financial computing.

  • Understand and apply Python’s core data types (int, float, str, bool, list, tuple, dict, set).

  • Implement control flow using conditionals (if-elif-else) and loops (for, while).

  • Write modular code using functions with parameters, return values, and docstrings.

  • Apply exception handling (try-except-finally) to build robust financial applications.

  • Understand and implement Object-Oriented Programming (OOP) concepts: classes, objects, inheritance, polymorphism, encapsulation.

  • Design a simple financial instrument class hierarchy (e.g., Bond, Stock, Option).

  • Write and read files (CSV, JSON, Excel) for financial data processing.


2. SETTING UP THE PYTHON ENVIRONMENT FOR FINANCE

2.1 Installing Python

Download and install Python from python.org. Use version 3.8 or higher for compatibility with financial libraries.

2.2 Package Management with pip

pip is the package installer for Python. Install essential packages:

text
pip install numpy pandas matplotlib scipy scikit-learn statsmodels requests openpyxl

2.3 Virtual Environments

Create isolated environments to manage dependencies:

text
python -m venv fintech_env
source fintech_env/bin/activate  # On Mac/Linux
fintech_env\Scripts\activate     # On Windows

2.4 The Interactive Shell (REPL)

Use the Python REPL (Read-Eval-Print Loop) for quick testing:

text
python
>>> 2 + 2
4
>>> import numpy as np
>>> np.array([1, 2, 3])

2.5 Integrated Development Environments (IDEs)

  • VS Code: Popular, free, with excellent Python support.

  • PyCharm: Full-featured IDE with integrated scientific tools.

  • Jupyter Notebook: Interactive environment for data exploration and visualization.


3. PYTHON DATA TYPES – THE BUILDING BLOCKS

3.1 Numeric Types

int: Whole numbers (arbitrary precision).

python
>>> x = 42
>>> type(x)
<class 'int'>

float: Floating-point numbers (double precision, IEEE 754).

python
>>> y = 3.14159
>>> type(y)
<class 'float'>

complex: Complex numbers.

python
>>> z = 1 + 2j
>>> type(z)
<class 'complex'>

3.2 String (str)

Immutable sequences of characters.

python
>>> s = "Financial Technology"
>>> s.upper()
'FINANCIAL TECHNOLOGY'
>>> s.split()
['Financial', 'Technology']
>>> len(s)
20

String formatting for financial reports:

python
price = 123.4567
print(f"Stock price: ${price:.2f}")  # Stock price: $123.46

3.3 Boolean (bool)

True and False values.

python
>>> is_profitable = True
>>> type(is_profitable)
<class 'bool'>

3.4 List (list)

Mutable, ordered sequences.

python
>>> prices = [100, 102, 98, 105, 110]
>>> prices.append(115)
>>> prices[0]
100
>>> len(prices)
6
>>> prices[-1]  # Last element
115

3.5 Tuple (tuple)

Immutable, ordered sequences.

python
>>> stock_data = ("AAPL", 150.25, 1000000)
>>> symbol, price, volume = stock_data  # Tuple unpacking
>>> print(symbol)
AAPL

3.6 Dictionary (dict)

Key-value pairs (hash maps). Essential for financial data structures.

python
>>> portfolio = {
...     "AAPL": {"shares": 100, "price": 150.25},
...     "GOOGL": {"shares": 50, "price": 2800.00},
...     "MSFT": {"shares": 75, "price": 330.50}
... }
>>> portfolio["AAPL"]["price"]
150.25
>>> portfolio.keys()
dict_keys(['AAPL', 'GOOGL', 'MSFT'])

3.7 Set (set)

Unordered collections of unique elements.

python
>>> unique_assets = {"AAPL", "GOOGL", "MSFT", "AAPL"}
>>> unique_assets
{'AAPL', 'GOOGL', 'MSFT'}
>>> "AAPL" in unique_assets
True

3.8 NoneType

Represents the absence of a value.

python
>>> price = None
>>> price is None
True

4. CONTROL FLOW – CONDITIONALS AND LOOPS

4.1 if-elif-else Statements

python
def investment_recommendation(price, moving_average):
    if price > moving_average * 1.1:
        return "STRONG BUY"
    elif price > moving_average:
        return "BUY"
    elif price < moving_average * 0.9:
        return "STRONG SELL"
    elif price < moving_average:
        return "SELL"
    else:
        return "HOLD"

4.2 for Loops

Iterating over sequences:

python
prices = [100, 102, 98, 105, 110]

# Basic iteration
for price in prices:
    print(price)

# With index
for i, price in enumerate(prices):
    print(f"Day {i+1}: ${price:.2f}")

# Dictionary iteration
for symbol, data in portfolio.items():
    print(f"{symbol}: {data['shares']} shares at ${data['price']:.2f}")

4.3 while Loops

python
def compound_interest(principal, rate, target):
    years = 0
    while principal < target:
        principal *= (1 + rate)
        years += 1
    return years

4.4 List Comprehensions

A powerful Python feature for transforming data:

python
# Compute daily returns
prices = [100, 102, 98, 105, 110]
returns = [(prices[i+1] - prices[i]) / prices[i] for i in range(len(prices)-1)]
# returns = [0.02, -0.0392, 0.0714, 0.0476]

# Filter positive returns
positive_returns = [r for r in returns if r > 0]

4.5 Dictionary Comprehensions

python
returns_dict = {f"Day_{i}": r for i, r in enumerate(returns)}

5. FUNCTIONS – MODULAR AND REUSABLE CODE

5.1 Function Definition

python
def calculate_sharpe_ratio(returns, risk_free_rate=0.02):
    """
    Calculate the Sharpe ratio of a set of returns.
    
    Parameters:
    returns (list/array): Historical returns
    risk_free_rate (float): Risk-free rate (default 0.02)
    
    Returns:
    float: Sharpe ratio
    """
    import numpy as np
    excess_returns = returns - risk_free_rate
    return np.mean(excess_returns) / np.std(excess_returns)

5.2 Default Parameters

python
def discount_cash_flows(cash_flows, discount_rate=0.10):
    return sum(cf / (1 + discount_rate)**i for i, cf in enumerate(cash_flows))

**5.3 *args and kwargs

For variable arguments:

python
def print_portfolio(*assets):
    for asset in assets:
        print(asset)

def portfolio_summary(**data):
    for key, value in data.items():
        print(f"{key}: {value}")

5.4 Lambda Functions

Anonymous functions for quick calculations:

python
# Sort by price
prices = [100, 102, 98, 105, 110]
sorted_prices = sorted(prices, key=lambda x: -x)  # Sort descending

# Map function
daily_returns = list(map(lambda x: (x - 100) / 100, prices))

5.5 Decorators

For logging, timing, and validation:

python
import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

@timer
def monte_carlo_simulation(n_paths=100000):
    # Simulation code
    return

6. EXCEPTION HANDLING – BUILDING ROBUST APPLICATIONS

6.1 Basic try-except

python
def safe_division(numerator, denominator):
    try:
        return numerator / denominator
    except ZeroDivisionError:
        return float('inf')
    except TypeError:
        return None

6.2 Multiple Exception Types

python
def parse_stock_price(price_str):
    try:
        return float(price_str)
    except ValueError:
        print(f"Invalid price: {price_str}")
        return 0.0
    except Exception as e:
        print(f"Unexpected error: {e}")
        return None

6.3 try-except-else-finally

python
def process_transaction(transaction):
    try:
        # Attempt to process
        result = execute_transfer(transaction)
    except InsufficientFundsError:
        result = "FAILED: Insufficient funds"
    except NetworkError:
        result = "FAILED: Network issue"
    else:
        # Runs only if no exception
        result = "SUCCESS"
    finally:
        # Always runs (e.g., logging, closing connections)
        log_transaction(transaction, result)
    return result

7. OBJECT-ORIENTED PROGRAMMING (OOP)

7.1 Classes and Objects

python
class FinancialInstrument:
    """Base class for financial instruments."""
    
    def __init__(self, symbol, price):
        self.symbol = symbol
        self._price = price
        self.history = []
        self._update_history()
    
    def _update_history(self):
        """Internal method to track price history."""
        self.history.append(self._price)
    
    @property
    def price(self):
        return self._price
    
    @price.setter
    def price(self, new_price):
        self._price = new_price
        self._update_history()
    
    def current_value(self):
        return self._price
    
    def __str__(self):
        return f"{self.symbol}: ${self._price:.2f}"
    
    def __repr__(self):
        return f"FinancialInstrument('{self.symbol}', {self._price})"

7.2 Inheritance and Polymorphism

python
class Stock(FinancialInstrument):
    """Stock class inheriting from FinancialInstrument."""
    
    def __init__(self, symbol, price, shares_outstanding):
        super().__init__(symbol, price)
        self.shares_outstanding = shares_outstanding
    
    def market_cap(self):
        return self._price * self.shares_outstanding
    
    def current_value(self):
        return self._price
    
    def __str__(self):
        return f"Stock {self.symbol}: ${self._price:.2f}, Mkt Cap: ${self.market_cap():.2e}"


class Bond(FinancialInstrument):
    """Bond class inheriting from FinancialInstrument."""
    
    def __init__(self, symbol, price, face_value, coupon_rate, maturity):
        super().__init__(symbol, price)
        self.face_value = face_value
        self.coupon_rate = coupon_rate
        self.maturity = maturity
    
    def annual_coupon(self):
        return self.face_value * self.coupon_rate
    
    def yield_to_maturity(self):
        # Simplified YTM calculation
        annual_coupon = self.annual_coupon()
        return (annual_coupon + (self.face_value - self._price) / self.maturity) / ((self.face_value + self._price) / 2)
    
    def current_value(self):
        # Bonds are valued at face value + accrued interest
        return self._price


class Option(FinancialInstrument):
    """Option class inheriting from FinancialInstrument."""
    
    def __init__(self, symbol, price, strike, expiry, option_type):
        super().__init__(symbol, price)
        self.strike = strike
        self.expiry = expiry
        self.option_type = option_type  # 'call' or 'put'
    
    def intrinsic_value(self, spot_price):
        if self.option_type == 'call':
            return max(spot_price - self.strike, 0)
        else:
            return max(self.strike - spot_price, 0)
    
    def current_value(self):
        # In practice, this would use a pricing model like Black-Scholes
        return self._price

7.3 Polymorphism Example

python
def analyze_instrument(instrument):
    """Polymorphic function to analyze any financial instrument."""
    print(f"Instrument: {instrument}")
    print(f"Current Value: ${instrument.current_value():.2f}")
    
    if isinstance(instrument, Stock):
        print(f"Market Cap: ${instrument.market_cap():.2e}")
    elif isinstance(instrument, Bond):
        print(f"YTM: {instrument.yield_to_maturity()*100:.2f}%")
    elif isinstance(instrument, Option):
        print(f"Intrinsic Value: ${instrument.intrinsic_value(100):.2f}")

7.4 Magic Methods (Dunders)

python
class Portfolio:
    def __init__(self, name):
        self.name = name
        self.instruments = []
        self.cash = 0.0
    
    def __len__(self):
        return len(self.instruments)
    
    def __getitem__(self, key):
        if isinstance(key, int):
            return self.instruments[key]
        elif isinstance(key, str):
            return [inst for inst in self.instruments if inst.symbol == key]
    
    def __add__(self, other):
        new_portfolio = Portfolio(f"{self.name}_{other.name}")
        new_portfolio.instruments = self.instruments + other.instruments
        new_portfolio.cash = self.cash + other.cash
        return new_portfolio
    
    def __str__(self):
        return f"Portfolio {self.name}: {len(self)} instruments, ${self.total_value():.2f}"
    
    def total_value(self):
        return self.cash + sum(inst.current_value() for inst in self.instruments)

8. FILE HANDLING – FINANCIAL DATA I/O

8.1 Reading CSV Files

python
import csv

def read_stock_prices(filename):
    prices = []
    with open(filename, 'r') as file:
        reader = csv.DictReader(file)
        for row in reader:
            prices.append({
                'symbol': row['Symbol'],
                'date': row['Date'],
                'close': float(row['Close'])
            })
    return prices

8.2 Using pandas for Data Processing

python
import pandas as pd

def load_financial_data(filename):
    df = pd.read_csv(filename, parse_dates=['Date'])
    df.set_index('Date', inplace=True)
    return df

# Example usage
data = load_financial_data('stock_data.csv')
data['Returns'] = data['Close'].pct_change()
data['Moving_Avg_50'] = data['Close'].rolling(window=50).mean()

8.3 Reading and Writing JSON

python
import json

def save_portfolio_to_json(portfolio, filename):
    data = {
        'name': portfolio.name,
        'cash': portfolio.cash,
        'instruments': [
            {'symbol': inst.symbol, 'price': inst.price}
            for inst in portfolio.instruments
        ]
    }
    with open(filename, 'w') as f:
        json.dump(data, f, indent=4)

def load_portfolio_from_json(filename):
    with open(filename, 'r') as f:
        data = json.load(f)
    # Parse and rebuild portfolio

8.4 Working with Excel Files

python
import pandas as pd

def read_excel_financials(filename):
    df = pd.read_excel(filename, sheet_name='Financial_Statements')
    return df

def write_excel_report(data, filename):
    with pd.ExcelWriter(filename) as writer:
        data.to_excel(writer, sheet_name='Portfolio_Analysis')

9. PRACTICAL IMPLEMENTATION

A. Building a Financial Calculator Class:

python
class FinancialCalculator:
    @staticmethod
    def future_value(pv, rate, periods):
        return pv * (1 + rate) ** periods
    
    @staticmethod
    def present_value(fv, rate, periods):
        return fv / (1 + rate) ** periods
    
    @staticmethod
    def annuity_pv(payment, rate, periods):
        return payment * (1 - (1 + rate) ** -periods) / rate
    
    @staticmethod
    def npv(cash_flows, discount_rate):
        return sum(cf / (1 + discount_rate) ** i for i, cf in enumerate(cash_flows))
    
    @staticmethod
    def irr(cash_flows, guess=0.1):
        from scipy.optimize import brentq
        def npv(rate):
            return sum(cf / (1 + rate) ** i for i, cf in enumerate(cash_flows))
        try:
            return brentq(npv, 0.0, 1.0)
        except ValueError:
            return None

# Example usage
calc = FinancialCalculator()
print(f"Future Value of $1000 at 5% for 10 years: ${calc.future_value(1000, 0.05, 10):.2f}")
print(f"NPV of [1000, -500, -500] at 10%: ${calc.npv([1000, -500, -500], 0.10):.2f}")

B. Portfolio Management Class:

python
class PortfolioManager:
    def __init__(self, risk_free_rate=0.02):
        self.risk_free_rate = risk_free_rate
    
    def calculate_returns(self, prices):
        return (prices[1:] / prices[:-1] - 1)
    
    def calculate_volatility(self, returns):
        return np.std(returns)
    
    def calculate_sharpe_ratio(self, returns):
        excess_returns = returns - self.risk_free_rate
        return np.mean(excess_returns) / np.std(excess_returns)
    
    def calculate_beta(self, asset_returns, market_returns):
        cov = np.cov(asset_returns, market_returns)[0, 1]
        var = np.var(market_returns)
        return cov / var
    
    def calculate_var(self, returns, confidence=0.95):
        return np.percentile(returns, (1 - confidence) * 100)
    
    def calculate_cvar(self, returns, confidence=0.95):
        var = self.calculate_var(returns, confidence)
        return np.mean(returns[returns <= var])

# Example usage
pm = PortfolioManager()
prices = np.array([100, 102, 98, 105, 110])
returns = pm.calculate_returns(prices)
sharpe = pm.calculate_sharpe_ratio(returns)
var_95 = pm.calculate_var(returns)
print(f"Sharpe Ratio: {sharpe:.4f}")
print(f"95% VaR: {var_95:.4f}")