Mathew K Analytics

Lesson 3 · Finance and Stock Market Analytics

Types of Financial Data: OHLCV, Fundamentals, and Sentiment Explained

Financial data helps us understand the real world of stocks and companies. We will explore common types: OHLCV (price/volume), Fundamentals (balance sheet),…

⬇ 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

Types of Financial Data in Stock Market Analytics#

  • Financial data helps us understand the real world of stocks and companies.
  • We will explore common types: OHLCV (price/volume), Fundamentals (balance sheet), and Sentiment (how people feel).
  • These data types are crucial for investors, analysts, and finance professionals.
  • You will learn to read, load, and analyze each type with real Python code.
  • You will see where mistakes happen and get hands-on with real datasets.
  • By the end, you will understand how to recognize and use these data types in practical stock analysis tasks.
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import yfinance as yf

Core Data Concepts: OHLCV, Fundamentals, and Sentiment#

  • OHLCV stands for Open, High, Low, Close, and Volume. It describes day-to-day stock prices and trading activity.
  • Fundamentals capture a company's business and health, such as earnings, valuation ratios, and sector.
  • Sentiment reflects market mood or crowd psychology, often scraped from news or social media.
  • OHLCV data is structured with one row for each date and ticker pair.
  • Fundamental data is typically one row per company, with columns for each metric (like P/E ratio, sector, market cap, etc).
  • Beginners often confuse wide (pivoted, one column per ticker) and long (one row per date/ticker) OHLCV formats.
  • Beginners might incorrectly assign columns to multi-ticker frames: always use datasets as given by yfinance.
  • Not all companies report every fundamental ratio, so expect missing or null values in real data.
  • Sentiment is not covered in-depth in this notebook, as robust labeled datasets are rarely public, but we discuss concepts.
# Beginner Example 1: Load multi-ticker OHLCV data for 5 tech stocks
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.795609  210.076568  206.719874  209.100429  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: Pivot OHLCV data to see closing prices in wide format
close_pivot = ohlcv.pivot(index='Date', columns='Ticker', values='Close')
print(close_pivot.shape)
print(close_pivot.head(3))
(251, 5)
Ticker            AAPL        AMZN       GOOGL        MSFT        TSLA
Date                                                                  
2025-07-14  207.795609  225.690002  181.043518  499.033936  316.899994
2025-07-15  208.283691  226.350006  181.482269  501.811768  310.779999
2025-07-16  209.329544  223.190002  182.449509  501.613312  321.670013
# Beginner Example 3: Filter OHLCV for just Tesla (TSLA)
tsla_ohlcv = ohlcv[ohlcv['Ticker'] == 'TSLA']
print(tsla_ohlcv.head(3))
         Date Ticker       Close        High         Low        Open    Volume
4  2025-07-14   TSLA  316.899994  322.600006  312.670013  317.730011  78043400
9  2025-07-15   TSLA  310.779999  321.200012  310.500000  319.679993  77556300
14 2025-07-16   TSLA  321.670013  323.500000  312.619995  312.799988  97284800
# Intermediate Example 1: Load fundamental data for 12 companies
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')
    })
fund_df = pd.DataFrame(rows)
print(fund_df.shape)
print(fund_df.head(3))
(12, 7)
  ticker                  sector     market_cap   pe_ratio    eps  \
0   AAPL              Technology  4654349418496  38.365010   8.26   
1   MSFT              Technology  2903255023616  23.277544  16.79   
2  GOOGL  Communication Services  4333255524352  27.086956  13.11   

   profit_margin  debt_equity  
0        0.27152       79.548  
1        0.39342       30.271  
2        0.37919       20.026  
# Intermediate Example 2: Which companies have the highest P/E ratio?
print(fund_df[['ticker','pe_ratio']].sort_values('pe_ratio', ascending=False))
   ticker    pe_ratio
4    TSLA  359.436280
10    WMT   40.239437
0    AAPL   38.365010
5    NVDA   31.630047
11      V   30.864983
8     JNJ   29.887600
3    AMZN   29.629630
2   GOOGL   27.086956
9     XOM   24.156567
6    META   23.985271
1    MSFT   23.277544
7     JPM   15.986596
# Intermediate Example 3: Check sector counts in the fundamentals DataFrame
print(fund_df['sector'].value_counts())
sector
Technology                3
Communication Services    2
Consumer Cyclical         2
Financial Services        2
Healthcare                1
Energy                    1
Consumer Defensive        1
Name: count, dtype: int64
# Intermediate Example 4: Find companies with missing debt-to-equity data
missing_de = fund_df[fund_df['debt_equity'].isnull()]
print(missing_de[['ticker','debt_equity']])
  ticker  debt_equity
7    JPM          NaN
# Intermediate Example 5: Calculate average profit margin by sector
avg_profit_margin = fund_df.groupby('sector')['profit_margin'].mean()
print(avg_profit_margin)
sector
Communication Services    0.353780
Consumer Cyclical         0.080850
Consumer Defensive        0.031350
Energy                    0.077650
Financial Services        0.428075
Healthcare                0.218340
Technology                0.431533
Name: profit_margin, dtype: float64
# Intermediate Example 6: Screen for high earnings per share (EPS) companies
top_eps = fund_df[['ticker','eps']].sort_values('eps', ascending=False).head(5)
print(top_eps)
   ticker    eps
6    META  27.50
7     JPM  20.89
1    MSFT  16.79
2   GOOGL  13.11
11      V  11.48
# Advanced Example 1: Load S&P 500 company list and join with fundamentals
url = 'https://raw.githubusercontent.com/datasets/s-and-p-500-companies/main/data/constituents.csv'
sp500 = pd.read_csv(url)
merged = pd.merge(sp500, fund_df, how='left', left_on='Symbol', right_on='ticker')
print(merged[['Symbol', 'Security', 'sector', 'market_cap']].head(5))
  Symbol             Security sector  market_cap
0    MMM                   3M    NaN         NaN
1    AOS          A. O. Smith    NaN         NaN
2    ABT  Abbott Laboratories    NaN         NaN
3   ABBV               AbbVie    NaN         NaN
4    ACN            Accenture    NaN         NaN
# Advanced Example 2: Select top 3 companies by market cap in each sector
top_by_sector = merged.sort_values(['sector', 'market_cap'], ascending=[True,False]).groupby('sector').head(3)
print(top_by_sector[['Symbol','sector','market_cap']])
    Symbol                  sector    market_cap
19   GOOGL  Communication Services  4.333256e+12
310   META  Communication Services  1.674331e+12
22    AMZN       Consumer Cyclical  2.667763e+12
438   TSLA       Consumer Cyclical  1.484938e+12
481    WMT      Consumer Defensive  9.094492e+11
188    XOM                  Energy  5.947585e+11
269    JPM      Financial Services  8.948496e+11
475      V      Financial Services  6.738448e+11
267    JNJ              Healthcare  6.208934e+11
344   NVDA              Technology  5.010368e+12
38    AAPL              Technology  4.654349e+12
316   MSFT              Technology  2.903255e+12
# Advanced Example 3: Visualize the distribution of P/E ratios
import matplotlib.pyplot as plt
plt.hist(fund_df['pe_ratio'].dropna(), bins=8, color='skyblue', edgecolor='black')
plt.title('Distribution of Price-to-Earnings (P/E) Ratios')
plt.xlabel('P/E Ratio')
plt.ylabel('Frequency')
plt.show()
No description has been provided for this image
# Advanced Example 4: End-to-End - Portfolio gain/loss calculation
np.random.seed(42)
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 = pd.DataFrame(rows)
df['gain_pct'] = np.round((df['cur_price'] - df['buy_price']) / df['buy_price'] * 100, 2)
print(df.head(5))
  ticker  shares  buy_price  cur_price    sector  gain_pct
0   AAPL      56     207.80     317.09      Tech     52.59
1   MSFT      97     499.03     390.81      Tech    -21.69
2  GOOGL      19     181.04     355.73      Tech     96.49
3   AMZN      76     225.69     247.88  Consumer      9.83
4   TSLA      65     316.90     395.26      Auto     24.73
# Error Example 1: What happens if you assign columns directly to multi-ticker OHLCV data?
df = yf.download(['AAPL','MSFT'], period='1y', auto_adjust=True, progress=False)
try:
    df.columns = ['Date','AAPL_Close','MSFT_Close','AAPL_High','MSFT_High','AAPL_Low','MSFT_Low',
               'AAPL_Open','MSFT_Open','AAPL_Volume','MSFT_Volume']
except Exception as e:
    print('Error:', e)
Error: Length mismatch: Expected axis has 10 elements, new values have 11 elements
# Error Example 2: Attempt to access a missing column in fundamentals
try:
    print(fund_df['dividend_yield'])
except KeyError as e:
    print('KeyError:', e)
KeyError: 'dividend_yield'
# Error Example 3: Handling missing values in financial ratios
fund_df['profit_margin_filled'] = fund_df['profit_margin'].fillna(0)
print(fund_df[['ticker','profit_margin','profit_margin_filled']])
   ticker  profit_margin  profit_margin_filled
0    AAPL        0.27152               0.27152
1    MSFT        0.39342               0.39342
2   GOOGL        0.37919               0.37919
3    AMZN        0.12224               0.12224
4    TSLA        0.03946               0.03946
5    NVDA        0.62966               0.62966
6    META        0.32837               0.32837
7     JPM        0.33936               0.33936
8     JNJ        0.21834               0.21834
9     XOM        0.07765               0.07765
10    WMT        0.03135               0.03135
11      V        0.51679               0.51679
# Best Practice: Always check data types before analysis
print(fund_df.dtypes)
ticker                   object
sector                   object
market_cap                int64
pe_ratio                float64
eps                     float64
profit_margin           float64
debt_equity             float64
profit_margin_filled    float64
dtype: object
# Common Pattern: Combine price and fundamental data for enhanced screening
combo = pd.merge(ohlcv[ohlcv['Ticker']=='AAPL'][['Date','Close']],
                 fund_df[fund_df['ticker']=='AAPL'][['pe_ratio','eps']], how='cross')
print(combo.head(5))
        Date       Close  pe_ratio   eps
0 2025-07-14  207.795609  38.36501  8.26
1 2025-07-15  208.283691  38.36501  8.26
2 2025-07-16  209.329544  38.36501  8.26
3 2025-07-17  209.190109  38.36501  8.26
4 2025-07-18  210.345505  38.36501  8.26
# End-to-End Example: From raw fundamentals to sector ranking by average EPS
avg_sector_eps = fund_df.groupby('sector')['eps'].mean().sort_values(ascending=False)
print(avg_sector_eps)
sector
Communication Services    20.305
Financial Services        16.185
Technology                10.530
Healthcare                 8.630
Energy                     5.940
Consumer Cyclical          4.735
Consumer Defensive         2.840
Name: eps, dtype: float64

End of Lesson: Summary and Practice Prompts#

  • You have learned how to load, compare, and process real stock OHLCV and fundamental data in Python.
  • You can now spot structure, handle errors, and combine data for practical analytics.
  • Remember: Always check your data shape, types, and missing values before analysis.
  • Watch our channel for more deep dives and hands-on market data walkthroughs!
  • Try out your own ticker list or join our group to share your questions.

Found this useful?

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