Mathew K Analytics

Lesson 4 · Finance and Stock Market Analytics

Financial Analytics Workflow Using Python: Step-by-Step Training for Beginners

This lesson demonstrates a hands-on workflow for analyzing real financial and stock market data using Python. The stock market is an essential economic…

⬇ 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

Financial Analytics Workflow Using Python#

  • This lesson demonstrates a hands-on workflow for analyzing real financial and stock market data using Python.
  • The stock market is an essential economic engine, and analytics helps us identify trends, manage risks, and drive smart investment decisions.
  • You will learn how to load, explore, and analyze real stock data, work with trading signals, handle financial fundamentals, and automate analytics tasksall with practical examples.
  • Each step is broken down so that you understand both what the code does and why it matters for financial analysis.
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import yfinance as yf

Financial data concepts you need to know#

  • Most financial analytics problems involve tabular data: rows for time (or companies), columns for variables.
  • Prices (like Open, High, Low, Close, Volume) are usually historical time series.
  • Returns are percentage differences between prices over time, not raw values.
  • Company data can be in 'wide' (one row per date, many columns for tickers) or 'long' (one row per date/ticker) format.
  • Beginners often mix up price and returns, or mis-align data when merging different sources.
  • Real-world data may have missing days, odd index types, or naming issuesalways inspect carefully before analysis.
# BEGINNER EXAMPLE 1: Download multi-ticker OHLCV stock data in long format
tickers = ['AAPL','MSFT','GOOGL','AMZN','TSLA']
ohlcv = yf.download(tickers, period='1y', auto_adjust=True, progress=False)
ohlcv = ohlcv.stack(future_stack=True).rename_axis(['Date','Ticker']).reset_index()
ohlcv.columns.name = None
print(ohlcv.shape)
print(ohlcv.head(3))
(1255, 7)
        Date Ticker       Close        High         Low        Open    Volume
0 2025-07-14   AAPL  207.795624  210.076583  206.719890  209.100445  38840100
1 2025-07-14   AMZN  225.690002  226.660004  224.240005  225.070007  35702600
2 2025-07-14  GOOGL  181.043518  183.147516  179.168861  180.495080  32536600
# BEGINNER EXAMPLE 2: Get closing prices in wide format
tickers = ['AAPL','MSFT','GOOGL','AMZN','TSLA']
df = yf.download(tickers, period='1y', auto_adjust=True, progress=False)
df = df['Close'].reset_index()
df.columns.name = None
print(df.shape)
print(df.head(3))
(251, 6)
        Date        AAPL        AMZN       GOOGL        MSFT        TSLA
0 2025-07-14  207.795639  225.690002  181.043518  499.033905  316.899994
1 2025-07-15  208.283707  226.350006  181.482269  501.811768  310.779999
2 2025-07-16  209.329544  223.190002  182.449509  501.613312  321.670013
# BEGINNER EXAMPLE 3: Loading the S&P 500 companies master list
url = 'https://raw.githubusercontent.com/datasets/s-and-p-500-companies/main/data/constituents.csv'
sp500_companies = pd.read_csv(url)
print(sp500_companies.shape)
print(sp500_companies.head(3))
(503, 8)
  Symbol             Security  GICS Sector         GICS Sub-Industry  \
0    MMM                   3M  Industrials  Industrial Conglomerates   
1    AOS          A. O. Smith  Industrials         Building Products   
2    ABT  Abbott Laboratories  Health Care     Health Care Equipment   

     Headquarters Location  Date added    CIK Founded  
0    Saint Paul, Minnesota  1957-03-04  66740    1902  
1     Milwaukee, Wisconsin  2017-07-26  91142    1916  
2  North Chicago, Illinois  1957-03-04   1800    1888  
# INTERMEDIATE EXAMPLE 1: Compute daily percentage returns for SPY ETF
data = yf.download('SPY', period='2y', auto_adjust=True, progress=False)
spy = data[['Close', 'Volume']].reset_index()
spy.columns = ['date','price','volume']
spy['daily_return'] = (spy['price'].pct_change() * 100).round(4)
spy = spy.dropna().reset_index(drop=True)
print(spy.shape)
print(spy.head(3))
(499, 4)
        date       price    volume  daily_return
0 2024-07-16  551.796997  36475300        0.5930
1 2024-07-17  544.060242  57119000       -1.4021
2 2024-07-18  539.879150  56270400       -0.7685
# INTERMEDIATE EXAMPLE 2: Pivoting multi-ticker OHLCV data to wide format for close prices
close_wide = ohlcv.pivot(index='Date', columns='Ticker', values='Close')
print(close_wide.shape)
print(close_wide.head(3))
(251, 5)
Ticker            AAPL        AMZN       GOOGL        MSFT        TSLA
Date                                                                  
2025-07-14  207.795624  225.690002  181.043518  499.033936  316.899994
2025-07-15  208.283707  226.350006  181.482269  501.811768  310.779999
2025-07-16  209.329544  223.190002  182.449509  501.613342  321.670013
# INTERMEDIATE EXAMPLE 3: Select and compare a single ticker from long format
aapl = ohlcv[ohlcv['Ticker'] == 'AAPL']
print(aapl.head(3))
         Date Ticker       Close        High         Low        Open    Volume
0  2025-07-14   AAPL  207.795624  210.076583  206.719890  209.100445  38840100
5  2025-07-15   AAPL  208.283707  211.052720  208.094455  208.393273  42296300
10 2025-07-16   AAPL  209.329544  211.560683  207.815546  209.468990  47490500
# ADVANCED EXAMPLE 1: Compute moving averages and generate trading signals
data = yf.download('AAPL', period='1y', auto_adjust=True, progress=False)
df = data[['Close','Volume']].reset_index()
df.columns = ['date','close','volume']
df['sma20'] = df['close'].rolling(20).mean().round(2)
df['sma50'] = df['close'].rolling(50).mean().round(2)
df['signal'] = np.where(df['sma20'] > df['sma50'], 1, -1)
print(df.tail(3))
          date       close    volume   sma20   sma50  signal
248 2026-07-09  316.220001  48124500  296.92  296.83       1
249 2026-07-10  315.320007  34109200  298.10  297.73       1
250 2026-07-13  316.059906  20634815  299.12  298.65       1
# ADVANCED EXAMPLE 2: Build a portfolio from 10 stocks and calculate percent gain
np.random.seed(42)  # For reproducibility
tickers = ['AAPL','MSFT','GOOGL','AMZN','TSLA','NVDA','META','NFLX','JPM','JNJ']
sector_map = {'AAPL':'Tech','MSFT':'Tech','GOOGL':'Tech','AMZN':'Consumer',
              'TSLA':'Auto','NVDA':'Tech','META':'Tech','NFLX':'Media',
              'JPM':'Finance','JNJ':'Health'}
data = yf.download(tickers, period='1y', auto_adjust=True, progress=False)['Close']
rows = []
for tk in tickers:
    buy = round(float(data[tk].iloc[0]), 2)
    cur = round(float(data[tk].iloc[-1]), 2)
    rows.append({'ticker': tk, 'shares': int(np.random.randint(5, 100)),
                 'buy_price': buy, 'cur_price': cur, 'sector': sector_map[tk]})
df_port = pd.DataFrame(rows)
df_port['gain_pct'] = np.round((df_port['cur_price'] - df_port['buy_price']) / df_port['buy_price'] * 100, 2)
print(df_port.head(3))
  ticker  shares  buy_price  cur_price sector  gain_pct
0   AAPL      56     207.80     316.77   Tech     52.44
1   MSFT      97     499.03     393.10   Tech    -21.23
2  GOOGL      19     181.04     356.35   Tech     96.83
# ADVANCED EXAMPLE 3: Fetch real financial fundamentals for leading stocks
tickers = ['AAPL','MSFT','GOOGL','AMZN','TSLA','NVDA','META','JPM','JNJ','XOM','WMT','V']
rows = []
for tk in tickers:
    info = yf.Ticker(tk).info
    rows.append({
        'ticker':       tk,
        'sector':       info.get('sector', 'Unknown'),
        'market_cap':   info.get('marketCap'),
        'pe_ratio':     info.get('trailingPE'),
        'eps':          info.get('trailingEps'),
        'profit_margin':info.get('profitMargins'),
        'debt_equity':  info.get('debtToEquity')
    })
df_fund = pd.DataFrame(rows)
print(df_fund.head(3))
  ticker                  sector     market_cap   pe_ratio    eps  \
0   AAPL              Technology  4648694972416  38.318400   8.26   
1   MSFT              Technology  2916849287168  23.386540  16.79   
2  GOOGL  Communication Services  4351193251840  27.199085  13.11   

   profit_margin  debt_equity  
0        0.27152       79.548  
1        0.39342       30.271  
2        0.37919       20.026  
# ERROR HANDLING 1: What happens if a ticker is removed from S&P 500 list?
missing = sp500_companies[sp500_companies['Symbol'] == 'TWTR']
if missing.empty:
    print('TWTR is not in the current S&P 500 list.')
else:
    print(missing)
TWTR is not in the current S&P 500 list.
# ERROR HANDLING 2: Handling missing values when merging datasets
x = ohlcv.merge(sp500_companies, left_on='Ticker', right_on='Symbol', how='left')
n_missing = x['Security'].isna().sum()
print(f'Missing company matches: {n_missing}')
Missing company matches: 0
# ERROR HANDLING 3: Check for NaN in fundamentals and fill as needed
n_na = df_fund['debt_equity'].isna().sum()
print(f'{n_na} companies have missing debt-to-equity ratios.')
df_fund['debt_equity'] = df_fund['debt_equity'].fillna(-1)
1 companies have missing debt-to-equity ratios.
# BEST PRACTICES: Always sort portfolios by percent gain before display
df_port_sorted = df_port.sort_values('gain_pct', ascending=False).reset_index(drop=True)
print(df_port_sorted.head(5))
  ticker  shares  buy_price  cur_price  sector  gain_pct
0  GOOGL      19     181.04     356.35    Tech     96.83
1    JNJ      79     153.00     258.11  Health     68.70
2   AAPL      56     207.80     316.77    Tech     52.44
3   NVDA      25     163.85     205.19    Tech     25.23
4   TSLA      65     316.90     394.25    Auto     24.41
# BEST PRACTICES: Always reset seeds if using simulated numbers
np.random.seed(42)
example_shares = np.random.randint(5, 100, size=5)
print("Random share example (should be reproducible):", example_shares)
Random share example (should be reproducible): [56 97 19 76 65]
# END-TO-END CASE: Analyze AAPL performance vs sector peers over one year
apple = ohlcv[ohlcv['Ticker'] == 'AAPL'].copy()
techs = ['AAPL', 'MSFT', 'GOOGL']
tech_df = ohlcv[ohlcv['Ticker'].isin(techs)]
apple['return'] = apple['Close'].pct_change() * 100
peers = tech_df.pivot(index='Date', columns='Ticker', values='Close')
peers_ret = peers.pct_change() * 100
avg_peer = peers_ret[['MSFT', 'GOOGL']].mean(axis=1)
import matplotlib.pyplot as plt
plt.figure(figsize=(10,4))
plt.plot(apple['Date'], apple['return'], label='AAPL daily return', alpha=0.6)
plt.plot(apple['Date'], avg_peer.values, label='MSFT/GOOGL mean', alpha=0.6)
plt.title('AAPL vs Tech Peer Daily Returns (1yr)')
plt.ylabel('% daily return')
plt.xlabel('Date')
plt.legend()
plt.tight_layout()
plt.show()
No description has been provided for this image
# BONUS: Export your end-to-end analysis as a CSV file for reporting
export_df = pd.DataFrame({'date': apple['Date'], 'aapl_return': apple['return'], 'peer_avg_return': avg_peer.values})
export_df.to_csv('aapl_vs_peers_returns.csv', index=False)
print('Exported CSV shape:', export_df.shape)
Exported CSV shape: (251, 3)

Congratulationsyou have completed the financial analytics workflow!#

  • You have loaded real stock data, computed returns, built portfolios, worked with company fundamentals, handled common errors, and automated exports.
  • With these techniques you are ready to tackle practical finance analytics tasks in Python.
  • Practice with new data, adjust the code, and explore new ideas.
  • Subscribe on YouTube for expert tips and full project walk-throughs.

Found this useful?

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