Saturday, August 29, 2026
HomeData SciencePython for Finance – Stock Analysis & Portfolio Optimization Guide

Python for Finance – Stock Analysis & Portfolio Optimization Guide

Table of Content

Python has become the dominant language in quantitative finance. From hedge funds to retail investors, Python powers stock screening, portfolio optimisation, risk modeling, and algorithmic strategy backtesting. This guide shows you the essential financial data science toolkit.

Downloading Stock Data with yfinance

pip install yfinance pandas numpy matplotlib scipy
import yfinance as yf
import pandas as pd

# Download OHLCV data
nifty50 = yf.download("^NSEI", start="2023-01-01", end="2026-08-01")
reliance = yf.download("RELIANCE.NS", start="2023-01-01", end="2026-08-01")

# Multiple tickers at once
tickers = ["RELIANCE.NS", "TCS.NS", "INFY.NS", "HDFCBANK.NS", "ICICIBANK.NS"]
prices  = yf.download(tickers, start="2023-01-01", end="2026-08-01")["Close"]

print(prices.tail())

Computing Returns and Statistics

graphs of performance analytics on a laptop screen
Photo by Luke Chesser on Unsplash
import numpy as np

# Daily returns
returns = prices.pct_change().dropna()

# Annualised statistics (252 trading days/year)
TRADING_DAYS = 252

annual_return = returns.mean() * TRADING_DAYS
annual_vol    = returns.std() * np.sqrt(TRADING_DAYS)
sharpe_ratio  = annual_return / annual_vol

stats = pd.DataFrame({
    'Annual Return (%)':  (annual_return * 100).round(2),
    'Annual Volatility (%)': (annual_vol * 100).round(2),
    'Sharpe Ratio':       sharpe_ratio.round(2),
}).sort_values('Sharpe Ratio', ascending=False)

print(stats)

Technical Indicators

import pandas as pd

def add_technical_indicators(df):
    close = df['Close']

    # Moving averages
    df['SMA_20'] = close.rolling(20).mean()
    df['SMA_50'] = close.rolling(50).mean()
    df['EMA_12'] = close.ewm(span=12).mean()
    df['EMA_26'] = close.ewm(span=26).mean()

    # MACD
    df['MACD']        = df['EMA_12'] - df['EMA_26']
    df['MACD_Signal'] = df['MACD'].ewm(span=9).mean()
    df['MACD_Hist']   = df['MACD'] - df['MACD_Signal']

    # RSI
    delta = close.diff()
    gain  = delta.where(delta > 0, 0).rolling(14).mean()
    loss  = (-delta.where(delta < 0, 0)).rolling(14).mean()
    rs    = gain / loss
    df['RSI'] = 100 - (100 / (1 + rs))

    # Bollinger Bands
    df['BB_Mid']   = close.rolling(20).mean()
    df['BB_Upper'] = df['BB_Mid'] + 2 * close.rolling(20).std()
    df['BB_Lower'] = df['BB_Mid'] - 2 * close.rolling(20).std()

    return df

df = add_technical_indicators(reliance)

Modern Portfolio Theory – Efficient Frontier

A graph showing a decreasing series of peaks
Photo by Bozhin Karaivanov on Unsplash
from scipy.optimize import minimize

def portfolio_stats(weights, returns, cov_matrix):
    port_return = np.sum(returns.mean() * weights) * TRADING_DAYS
    port_vol    = np.sqrt(weights @ cov_matrix @ weights) * np.sqrt(TRADING_DAYS)
    sharpe      = port_return / port_vol
    return port_return, port_vol, sharpe

n_assets    = len(returns.columns)
cov_matrix  = returns.cov() * TRADING_DAYS

# Monte Carlo simulation of random portfolios
n_portfolios = 5000
results = np.zeros((n_portfolios, 3))
all_weights = []

for i in range(n_portfolios):
    w = np.random.dirichlet(np.ones(n_assets))  # random weights summing to 1
    r, v, s = portfolio_stats(w, returns, cov_matrix)
    results[i] = [r, v, s]
    all_weights.append(w)

results_df = pd.DataFrame(results, columns=['Return', 'Volatility', 'Sharpe'])

# Find maximum Sharpe ratio portfolio
best_idx = results_df['Sharpe'].idxmax()
best_weights = all_weights[best_idx]
print("Optimal weights:")
for ticker, w in zip(tickers, best_weights):
    print(f"  {ticker}: {w:.1%}")

Simple Backtesting – Moving Average Crossover

def backtest_sma_crossover(prices, short=20, long=50, initial_capital=100000):
    df = prices.to_frame('price')
    df['SMA_short'] = df['price'].rolling(short).mean()
    df['SMA_long']  = df['price'].rolling(long).mean()

    df['signal'] = 0
    df.loc[df['SMA_short'] > df['SMA_long'], 'signal'] = 1  # long
    df.loc[df['SMA_short'] < df['SMA_long'], 'signal'] = -1 # short

    df['returns']    = df['price'].pct_change()
    df['strategy']   = df['signal'].shift(1) * df['returns']

    df['cumulative_market']   = (1 + df['returns']).cumprod() * initial_capital
    df['cumulative_strategy'] = (1 + df['strategy']).cumprod() * initial_capital

    total_return = df['cumulative_strategy'].iloc[-1] / initial_capital - 1
    buy_hold     = df['cumulative_market'].iloc[-1] / initial_capital - 1

    print(f"Strategy return:  {total_return:.1%}")
    print(f"Buy & hold return: {buy_hold:.1%}")
    return df

result = backtest_sma_crossover(reliance['Close'])

Risk Metrics – Value at Risk and Max Drawdown

# Value at Risk (VaR) — 95% confidence
portfolio_returns = returns @ best_weights
VaR_95 = np.percentile(portfolio_returns, 5)
print(f"Daily VaR (95%): {VaR_95:.2%}")
print(f"On ₹10L investment, max daily loss (95% confidence): ₹{abs(VaR_95)*1000000:,.0f}")

# Maximum Drawdown
cumulative = (1 + portfolio_returns).cumprod()
rolling_max = cumulative.cummax()
drawdown    = (cumulative - rolling_max) / rolling_max
max_drawdown = drawdown.min()
print(f"Maximum Drawdown: {max_drawdown:.2%}")

Disclaimer

This guide is for educational purposes only. Nothing here constitutes financial advice. Past performance does not guarantee future returns. Always consult a SEBI-registered financial advisor before making investment decisions.

Conclusion

Python gives you a complete quantitative finance toolkit: yfinance for data, pandas for manipulation, scipy for optimisation, and matplotlib for visualisation. Understanding how to compute returns, build an efficient frontier, backtest simple strategies, and measure risk with VaR and drawdown are foundational skills for any data scientist working in finance. The code in this guide is a starting point — real trading systems require much more rigorous backtesting, transaction cost modeling, and risk management.

Leave feedback about this

  • Rating

Durgesh Kekare
Durgesh Kekarehttps://www.dataexpertise.in
Durgesh Kekare is a data science educator and founder of DataExpertise.in. With expertise in Python, machine learning, and analytics, he helps 10,000+ learners break into data careers.

Latest Posts

List of Categories